diff --git a/Website/.DS_Store b/Website/.DS_Store new file mode 100644 index 0000000..d400a14 Binary files /dev/null and b/Website/.DS_Store differ diff --git a/Website/.vscode/settings.json b/Website/.vscode/settings.json new file mode 100644 index 0000000..6f3a291 --- /dev/null +++ b/Website/.vscode/settings.json @@ -0,0 +1,3 @@ +{ + "liveServer.settings.port": 5501 +} \ No newline at end of file diff --git a/Website/Readme.md b/Website/Readme.md new file mode 100644 index 0000000..ea75740 --- /dev/null +++ b/Website/Readme.md @@ -0,0 +1,28 @@ +# Nova Free Bootstrap 5 Business Template + +#### Preview + + - [Demo](https://themewagon.github.io/Nova-Bootstrap-5/) + +#### Download + - [Download from ThemeWagon](https://themewagon.com/themes/nova-bootstrap/) + +## Getting Started + +Clone from Github + +``` +git clone https://github.com/themewagon/Nova-Bootstrap-5.git +``` +## Author + +Design and code is completely written by Freebiesbug's design and development team. + + +## License + + - Design and Code is Copyright © [Freebiesbug](https://freebiesbug.com/) + - Licensed cover under [MIT] + - Distributed by [ThemeWagon](https://themewagon.com) + + diff --git a/Website/assets/.DS_Store b/Website/assets/.DS_Store new file mode 100644 index 0000000..e8b2f76 Binary files /dev/null and b/Website/assets/.DS_Store differ diff --git a/Website/assets/css/style.css b/Website/assets/css/style.css new file mode 100644 index 0000000..a3b792c --- /dev/null +++ b/Website/assets/css/style.css @@ -0,0 +1,1702 @@ +/* +Template name: Nova +Template author: FreeBootstrap.net +Author website: https://freebootstrap.net/ +License: https://freebootstrap.net/license +Buy Pro +*/ +:root, +[data-bs-theme=light] { + --bs-body-bg: #ffffff; + --bs-body-color: #494B5B; + --bs-primary: #215C5C; + --bs-primary-rgb: 33, 92, 92; + --bs-primary-hover: #144B4B; + --bs-primary-hover-rgb: 20, 75, 75; + --bs-secondary: #CCE8C9; + --bs-secondary-rgb: 204, 232, 201; + --bs-heading-color: #16181B; + --inverse-color: #494B5B; + --inverse-color-rgb: 73, 75, 91; + --bs-link-color: var(--bs-primary); + --nav-bg: #ffffff; + --nav-color: #fff; + --nav-hover-color: #1f6bff; + --dropdown-bg: #ffffff; + --dropdown-color: #1f6bff; + --dropdown-hover-bg: #f7f7f7; + --nav-inverse: #000000; + --nav-inverse-alt: #ffffff; +} + +.main-content { + display: grid; + margin-top: 5%; + grid-template-columns: 1fr 2fr; + gap: 30px; + padding: 30px; + } + + /* Coluna da Esquerda */ + .left-column { + display: flex; + flex-direction: column; + gap: 25px; + } + + .lgpd-consent { + background: #e8f4fc; + padding: 20px; + border-radius: 8px; + border-left: 4px solid #144B4B; + } + + .report-history { + background: #f8f9fa; + padding: 20px; + border-radius: 8px; + } + + .report-history h3 { + margin-bottom: 15px; + color: #2c3e50; + border-bottom: 2px solid #144B4B; + padding-bottom: 8px; + } + + .report-history ul { + list-style: none; + } + + .report-history li { + padding: 10px 0; + border-bottom: 1px solid #e1e4e8; + } + + .report-history li:last-child { + border-bottom: none; + } + + /* Coluna da Direita */ + .right-column { + background: #fff; + padding: 25px; + border-radius: 8px; + box-shadow: 0 0 10px rgba(0, 0, 0, 0.05); + } + + .report-title { + text-align: center; + margin-bottom: 25px; + color: #2c3e50; + border-bottom: 2px solid #144B4B; + padding-bottom: 15px; + } + + .action-buttons { + display: flex; + justify-content: flex-end; + gap: 15px; + margin-bottom: 25px; + } + + .btn { + padding: 10px 20px; + border: none; + border-radius: 5px; + cursor: pointer; + font-weight: 500; + transition: all 0.3s; + } + + .btn-draft { + background: #e8f4fc; + color: #144B4B; + border: 1px solid #144B4B; + } + + .btn-draft:hover { + background: #144B4B; + color: white; + } + + .btn-finish { + background: #144B4B; + color: white; + } + + .btn-finish:hover { + background: #2c3e50; + } + + .report-type-priority { + display: grid; + grid-template-columns: 1fr 1fr; + gap: 20px; + margin-bottom: 25px; + } + + .info-box { + background: #f8f9fa; + padding: 15px; + border-radius: 8px; + } + + .info-box h4 { + margin-bottom: 10px; + color: #2c3e50; + } + + .form-section { + margin-bottom: 25px; + } + + .form-section h3 { + margin-bottom: 15px; + color: #2c3e50; + border-bottom: 1px solid #e1e4e8; + padding-bottom: 8px; + } + + .form-section textarea { + width: 100%; + min-height: 120px; + padding: 15px; + border: 1px solid #dce1e6; + border-radius: 5px; + resize: vertical; + font-family: inherit; + } + + + /* Responsividade */ + @media (max-width: 900px) { + .main-content { + grid-template-columns: 1fr; + } + + .report-type-priority { + grid-template-columns: 1fr; + } + + .nav-links { + margin-top: 15px; + } + } + + +.footer{background-color: white;} +.logo{font-weight:800;letter-spacing:.4px} +.logo span{color:var(--bs-primary)} +.toolbar { + display: flex; + align-items: center; + justify-content: space-between; + padding: 14px 20px +} + +.toolbar { + background: var(--bg); + border-bottom: 10px solid #111827 +} + +.toolbar__actions { + display: flex; + gap: 10px; + align-items: center +} + +main.workspace { + padding-top: 100px; + /* ajuste conforme a altura real do seu header */ + padding-left: 40px; + padding-right: 40px; +} + +.patient-card .patient-info { + padding-left: 16px +} + +.patient-card .patient-info li { + margin: 6px 0 +} + +.container { + width: min(1100px, 92%); + margin: 0 auto +} + +.hero__panel.card { + padding: 22px +} + +.with-editor .editor { + display: grid; + grid-template-columns: 320px 1fr; + gap: 16px; + padding: 20px 0 +} + +.grid-3 { + display: grid; + grid-template-columns: repeat(3, 1fr); + gap: 16px +} + +.grid-2 { + display: grid; + grid-template-columns: repeat(2, 1fr); + gap: 14px +} + +.value-list { + display: grid; + grid-template-columns: 1fr 1fr; + gap: 8px 16px; + margin-top: 16px; + padding: 0 +} + +.hero { + display: grid; + grid-template-columns: 1.2fr .8fr; + gap: 26px; + padding: 42px 0 +} + +.card { + background: var(--card); + border: 1px solid #111827; + border-radius: var(--radius); + box-shadow: var(--shadow); + padding: 18px +} + +.card--soft { + background: var(--soft) +} + +.hero__panel.card { + padding: 22px +} + +.editor__panel .form textarea { + resize: vertical +} + + +@media (max-width: 980px) { + .patient-card { + order: 2 + } + + .editor__panel { + order: 1 + } + + .hero { + grid-template-columns: 1fr + } + + .grid-3 { + grid-template-columns: 1fr + } + + .with-editor .editor { + grid-template-columns: 1fr + } +} + + + +body { + font: 1rem/1.7 "Inter", sans-serif; + background-color: var(--bs-body-bg); + color: var(--bs-body-color); + -webkit-font-smoothing: antialiased; + -moz-osx-font-smoothing: grayscale; +} + +.site-wrap { + position: relative; +} + +::-moz-selection { + background-color: var(--bs-black); + color: var(--bs-white); +} + +::selection { + background-color: var(--bs-black); + color: var(--bs-white); +} + +a { + -webkit-transition: 0.3s all ease-in-out; + transition: 0.3s all ease-in-out; + color: var(--bs-primary); + text-decoration: underline; +} + +a:hover { + text-decoration: none; +} + +h1, +h2, +h3, +h4, +h5, +h6 { + color: var(--bs-heading-color); +} + +.container { + max-width: 1140px; +} + +.section { + padding: 70px 0; + scroll-margin-top: 60px; +} + +@media (max-width: 767.98px) { + .section { + padding: 40px 0; + } +} + +.section.first-section { + padding-top: 100px; +} + +@media (min-width: 992px) { + .section.first-section { + padding-top: 130px; + } +} + +.text-heading-color { + color: var(--bs-heading-color); +} + +.shadow-sm { + -webkit-box-shadow: 0 0.125rem 0.25rem 0 rgba(var(--bs-black-rgb), 0.05) !important; + box-shadow: 0 0.125rem 0.25rem 0 rgba(var(--bs-black-rgb), 0.05) !important; +} + +.shadow { + -webkit-box-shadow: 0 0.5rem 1rem 0 rgba(var(--bs-black-rgb), 0.05) !important; + box-shadow: 0 0.5rem 1rem 0 rgba(var(--bs-black-rgb), 0.05) !important; +} + +.shadow-lg { + -webkit-box-shadow: 0 1rem 3rem 0 rgba(var(--bs-black-rgb), 0.05) !important; + box-shadow: 0 1rem 3rem 0 rgba(var(--bs-black-rgb), 0.05) !important; +} + +.list-checked { + padding: 0; + margin: 0; + color: var(--bs-body-color); +} + +.list-checked li { + position: relative; + margin-bottom: 10px; + display: -webkit-box; + display: -ms-flexbox; + display: flex; + -webkit-box-align: start; + -ms-flex-align: start; + align-items: start; + gap: 10px; +} + +.list-checked li i { + width: 30px; + height: 30px; + text-align: center; + line-height: 30px; + display: inline-block; + background-color: rgba(var(--bs-primary-rgb), 0.1); + border-radius: 50%; + color: var(--bs-primary); + font-size: 20px; +} + +.list-checked li:before { + position: absolute; + display: inline-block; + left: 0; + content: ""; + vertical-align: -0.125em; + background-repeat: no-repeat; + background-position: right center; + background-size: 1.5625rem 1.5625rem; + width: 1.5625rem; + height: 1.5625rem; +} + +.btn { + padding: 12px 20px; + background-color: var(--bs-primary); + color: var(--bs-white); + border: 1px solid transparent; + border-radius: 7px; + font-weight: 600; + position: relative; +} + +.btn:hover, +.btn:active, +.btn:focus { + color: var(--bs-white); + background-color: var(--bs-primary-hover) !important; + border: 1px solid var(--bs-primary-hover) !important; +} + +.btn.btn-white.hover-outline { + background-color: var(--bs-white) !important; + color: var(--bs-black) !important; + border: 1px solid transparent !important; +} + +.btn.btn-white.hover-outline:hover { + color: var(--bs-white) !important; + background-color: transparent !important; + border: 1px solid var(--bs-white) !important; +} + +.btn.btn-white-outline { + border: 1px solid rgba(var(--inverse-color-rgb), 0.2) !important; + color: var(--inverse-color) !important; + background-color: transparent; +} + +.btn.btn-white-outline:hover, +.btn.btn-white-outline:focus, +.btn.btn-white-outline:active { + background-color: transparent !important; + border: 1px solid rgba(var(--inverse-color-rgb), 0.5) !important; +} + +.block-squares { + position: absolute; + bottom: -12px; + right: 0; + z-index: 1; + width: auto; +} + +.form-control { + padding-top: 10px; + padding-bottom: 10px; + border-width: 1px; + font-size: 16px; + background-color: transparent; + border-color: rgba(var(--inverse-color-rgb), 0.1); +} + +.form-control:focus { + outline: none; + -webkit-box-shadow: none; + box-shadow: none; + border-color: var(--bs-primary); +} + +.a-link i { + font-size: 26px; + margin-left: 10px; + -webkit-transition: 0.25s all ease-in-out; + transition: 0.25s all ease-in-out; +} + +.a-link:hover i { + margin-left: 15px; +} + +.page-title { + padding-top: 100px !important; + padding-bottom: 50px !important; + background-color: rgba(var(--inverse-color-rgb), 0.03); +} + +.special-link .icons { + display: inline-block; + width: 38px; + height: 38px; + line-height: 38px; + border-radius: 50%; + text-align: center; + position: relative; + border: 1px solid rgba(var(--inverse-color-rgb), 0.2); +} + +.special-link .icons i { + font-size: 25px; + position: relative; + display: inline-block; + position: absolute; + -webkit-transition: 0.3s all ease-in-out; + transition: 0.3s all ease-in-out; +} + +.special-link .icons i.icon-1 { + opacity: 1; + visibility: visible; + top: 50%; + left: 50%; + -webkit-transform: translate(-50%, -50%) rotate(-45deg); + transform: translate(-50%, -50%) rotate(-45deg); +} + +.special-link .icons i.icon-2 { + opacity: 0; + visibility: hidden; + top: 80%; + left: 20%; + -webkit-transform: translate(-50%, -50%) rotate(-45deg); + transform: translate(-50%, -50%) rotate(-45deg); +} + +.special-link:hover .icons .icon-1 { + opacity: 0; + visibility: hidden; + top: 20%; + left: 80%; + -webkit-transform: translate(-50%, -50%) rotate(-45deg); + transform: translate(-50%, -50%) rotate(-45deg); +} + +.special-link:hover .icons .icon-2 { + opacity: 1; + visibility: visible; + top: 50%; + left: 50%; + -webkit-transform: translate(-50%, -50%) rotate(-45deg); + transform: translate(-50%, -50%) rotate(-45deg); +} + +.fbs__net-navbar { + position: absolute; + top: 0; + -webkit-transition: 0.3s all ease-in-out; + transition: 0.3s all ease-in-out; + z-index: 99999; + width: 100%; + background-color: transparent !important; + border: none; +} + +@media (min-width: 992px) { + .fbs__net-navbar { + padding-top: 0; + padding-bottom: 0; + } +} + +.fbs__net-navbar>.container { + position: relative; +} + +.fbs__net-navbar.relative { + position: absolute; +} + +.fbs__net-navbar .navbar-brand { + color: var(--bs-white); + font-size: 30px; + font-weight: bold; +} + +.fbs__net-navbar .navbar-brand .logo.dark { + display: none; +} + +.fbs__net-navbar .navbar-brand .logo.light { + display: block; +} + +.fbs__net-navbar .navbar-nav>li>.nav-link { + position: relative; + padding-top: 22px; + padding-bottom: 22px; + padding-left: 10px; + padding-right: 10px; + font-size: 0.9375rem; + color: rgba(var(--bs-white-rgb), 1); + -webkit-transition: 0.3s all ease; + transition: 0.3s all ease; + position: relative; +} + +@media (max-width: 991.98px) { + .fbs__net-navbar .navbar-nav>li>.nav-link { + color: var(--inverse-color); + } +} + +.fbs__net-navbar .navbar-nav>li>.nav-link:before { + content: ""; + position: absolute; + width: calc(100% - 20px); + -webkit-transform: scaleX(0); + transform: scaleX(0); + border-radius: 5px; + height: 1.5px; + bottom: 0; + left: 10px; + background: currentcolor; + -webkit-transform-origin: bottom right; + transform-origin: bottom right; + -webkit-transition: -webkit-transform 0.35s ease-out; + transition: -webkit-transform 0.35s ease-out; + transition: transform 0.35s ease-out; + transition: transform 0.35s ease-out, -webkit-transform 0.35s ease-out; +} + +.fbs__net-navbar .navbar-nav>li>.nav-link:hover::before { + -webkit-transform: scaleX(1); + transform: scaleX(1); + -webkit-transform-origin: bottom left; + transform-origin: bottom left; +} + +.fbs__net-navbar .navbar-nav>li>.nav-link:hover, +.fbs__net-navbar .navbar-nav>li>.nav-link:focus { + color: var(--bs-white); +} + +@media (max-width: 991.98px) { + + .fbs__net-navbar .navbar-nav>li>.nav-link:hover, + .fbs__net-navbar .navbar-nav>li>.nav-link:focus { + color: var(--inverse-color) !important; + } +} + +@media (max-width: 767.98px) { + .fbs__net-navbar .navbar-nav>li>.nav-link { + padding-top: 5px; + padding-bottom: 5px; + } +} + +.fbs__net-navbar .navbar-nav>li>.nav-link.active { + color: var(--bs-white); +} + +@media (max-width: 991.98px) { + .fbs__net-navbar .navbar-nav>li>.nav-link.active { + color: var(--inverse-color); + } +} + +.fbs__net-navbar .navbar-nav>li>.nav-link.active:before { + content: ""; + -webkit-transform-origin: bottom left; + transform-origin: bottom left; + -webkit-transform: scaleX(1); + transform: scaleX(1); +} + +.fbs__net-navbar .navbar-nav>li>.nav-link.dropdown-toggle { + display: -webkit-box; + display: -ms-flexbox; + display: flex; + -webkit-box-align: center; + -ms-flex-align: center; + align-items: center; + -webkit-box-pack: justify; + -ms-flex-pack: justify; + justify-content: space-between; +} + +.fbs__net-navbar .navbar-nav>li>.nav-link.dropdown-toggle::after { + display: none; + content: ""; +} + +@media (max-width: 991.98px) { + .fbs__net-navbar .navbar-nav>li>.nav-link { + padding-top: 10px; + padding-bottom: 10px; + } +} + +.fbs__net-navbar .navbar-nav .dropdown-menu { + background-color: var(--nav-bg); +} + +.fbs__net-navbar .navbar-nav .dropdown-menu .nav-link:hover { + background-color: var(--dropdown-hover-bg) !important; + color: var(--inverse-color); +} + +.fbs__net-navbar .navbar-nav .dropdown-toggle::after { + display: none; +} + +.fbs__net-navbar .navbar-nav .dropdown-toggle .bi-chevron-down { + margin-left: 0.5rem; +} + +.fbs__net-navbar .fbs__net-navbar-toggler { + width: 50px; + height: 50px; + border-radius: 50%; + position: relative; + background-color: var(--bs-black); + color: var(--bs-white); + border: none; + display: none; +} + +@media (max-width: 991.98px) { + .fbs__net-navbar .fbs__net-navbar-toggler { + display: -webkit-box; + display: -ms-flexbox; + display: flex; + } +} + +.fbs__net-navbar .navbar-brand { + margin-right: 0 !important; +} + +.fbs__net-navbar .navbar-nav .dropdown>.dropdown-menu, +.fbs__net-navbar .navbar-nav .dropend>.dropdown-menu, +.fbs__net-navbar .navbar-nav .dropstart>.dropdown-menu { + border: none !important; + padding-left: 10px; + padding-right: 10px; + -webkit-box-shadow: 0 0.1875rem 0.375rem rgba(var(--bs-black-rgb), 0.1); + box-shadow: 0 0.1875rem 0.375rem rgba(var(--bs-black-rgb), 0.1); +} + +@media (min-width: 992px) { + + .fbs__net-navbar .navbar-nav .dropdown>.dropdown-menu, + .fbs__net-navbar .navbar-nav .dropend>.dropdown-menu, + .fbs__net-navbar .navbar-nav .dropstart>.dropdown-menu { + padding-left: 10px; + padding-right: 10px; + width: 200px; + border: none; + } +} + +.fbs__net-navbar .navbar-nav .dropdown>.dropdown-menu .dropdown-item, +.fbs__net-navbar .navbar-nav .dropdown>.dropdown-menu .nav-link, +.fbs__net-navbar .navbar-nav .dropend>.dropdown-menu .dropdown-item, +.fbs__net-navbar .navbar-nav .dropend>.dropdown-menu .nav-link, +.fbs__net-navbar .navbar-nav .dropstart>.dropdown-menu .dropdown-item, +.fbs__net-navbar .navbar-nav .dropstart>.dropdown-menu .nav-link { + font-size: 0.875rem; + padding: 10px 10px; + -webkit-transition: 0.3s all ease; + transition: 0.3s all ease; + border-radius: 0.5rem; +} + +@media (min-width: 992px) { + + .fbs__net-navbar .navbar-nav .dropdown:hover>.dropdown-menu, + .fbs__net-navbar .navbar-nav .dropend:hover>.dropdown-menu, + .fbs__net-navbar .navbar-nav .dropstart:hover>.dropdown-menu { + display: block; + -webkit-animation: fadeInUp 0.3s ease; + animation: fadeInUp 0.3s ease; + } +} + +@media (min-width: 992px) { + .fbs__net-navbar .navbar-nav .dropend:hover>.dropdown-menu { + position: absolute; + top: 0; + left: 100%; + -webkit-animation: fadeInRight 0.3s ease; + animation: fadeInRight 0.3s ease; + } +} + +@media (min-width: 992px) { + .fbs__net-navbar .navbar-nav .dropstart:hover>.dropdown-menu { + position: absolute; + top: 0; + right: 100%; + -webkit-animation: fadeInLeft 0.3s ease; + animation: fadeInLeft 0.3s ease; + } +} + +.fbs__net-navbar .navbar-nav .dropdown-toggle::before { + display: none; +} + +.fbs__net-navbar .navbar-nav .dropdown-toggle::after { + display: none; +} + +.fbs__net-navbar .navbar-nav li.dropend>a, +.fbs__net-navbar .navbar-nav li.dropstart>a { + position: relative; +} + +.fbs__net-navbar .navbar-nav li.dropend>a .bi, +.fbs__net-navbar .navbar-nav li.dropstart>a .bi { + position: absolute; + right: 10px; + top: 50%; + -webkit-transform: translateY(-50%); + transform: translateY(-50%); +} + +@media (max-width: 991.98px) { + + .fbs__net-navbar .navbar-nav li.dropend>a .bi, + .fbs__net-navbar .navbar-nav li.dropstart>a .bi { + -webkit-transform: rotate(90deg); + transform: rotate(90deg); + margin-top: -10px; + } +} + +.fbs__net-navbar .navbar-nav li.dropend>a::after, +.fbs__net-navbar .navbar-nav li.dropstart>a::after { + display: none; +} + +.fbs__net-navbar .navbar-nav li.dropend>a { + position: relative; +} + +.fbs__net-navbar .navbar-nav li.dropend>a:hover { + background-color: var(--dropdown-hover-bg) !important; +} + +.fbs__net-navbar .navbar-nav li.dropend>a .caret { + position: absolute; + right: 10px; +} + +.fbs__net-navbar .navbar-nav li.dropstart>a { + position: relative; +} + +.fbs__net-navbar .navbar-nav li.dropstart>a:hover { + background-color: var(--dropdown-hover-bg) !important; +} + +.fbs__net-navbar .navbar-nav li.dropstart>a .caret { + position: absolute; + left: 10px; +} + +.fbs__net-navbar .navbar-toggler:hover, +.fbs__net-navbar .navbar-toggler:focus, +.fbs__net-navbar .navbar-toggler:active { + outline: none; + -webkit-box-shadow: none; + box-shadow: none; +} + +.fbs__net-navbar .header-social a { + -webkit-transition: 0.3s all ease; + transition: 0.3s all ease; + color: var(--bs-white); + width: 30px; + height: 30px; + line-height: 30px; + text-align: center; + border-radius: 4px; + display: inline-block; + background-color: transparent; +} + +.fbs__net-navbar .header-social a:hover { + background-color: rgba(var(--bs-white-rgb), 0.1); +} + +@media (min-width: 992px) { + .fbs__net-navbar .header-social a { + color: var(--bs-white); + } +} + +.fbs__net-navbar .header-social .btn { + width: auto !important; + height: auto !important; + background-color: var(--bs-primary) !important; + color: var(--bs-white) !important; +} + +@media (max-width: 575.98px) { + .fbs__net-navbar .header-social .btn { + display: none; + } +} + +.fbs__net-navbar.active { + position: fixed; + top: 0; + width: 100%; + background-color: var(--nav-bg) !important; + -webkit-box-shadow: 0 0.1875rem 0.375rem rgba(var(--bs-black-rgb), 0.05) !important; + box-shadow: 0 0.1875rem 0.375rem rgba(var(--bs-black-rgb), 0.05) !important; +} + +.fbs__net-navbar.active .navbar-brand { + color: rgba(var(--inverse-color-rgb), 1) !important; +} + +.fbs__net-navbar.active .navbar-brand .logo.dark { + display: block; +} + +.fbs__net-navbar.active .navbar-brand .logo.light { + display: none; +} + +.fbs__net-navbar.active .nav-link { + color: rgba(var(--inverse-color-rgb), 1) !important; +} + +.fbs__net-navbar.active .nav-link:hover { + color: rgba(var(--inverse-color-rgb), 1) !important; +} + +.fbs__net-navbar.active .header-social a { + -webkit-transition: 0.3s all ease; + transition: 0.3s all ease; + color: var(--inverse-color); +} + +.fbs__net-navbar.active .header-social a:hover { + background-color: rgba(var(--inverse-color-rgb), 0.1); +} + +@media (min-width: 992px) { + .fbs__net-navbar.active .header-social a { + color: rgba(var(--inverse-color-rgb), 1) !important; + } +} + +.fbs__net-navbar.active .header-social .btn { + width: auto !important; + height: auto !important; + background-color: var(--bs-primary) !important; + color: var(--bs-white) !important; +} + +.fbs__net-navbar.active .navbar-nav>li>.dropdown-toggle:hover { + color: var(--inverse-color) !important; +} + +.fbs__net-navbar.active .navbar-nav>li>.nav-link.dropdown-toggle::after { + display: none; + content: ""; +} + +.fbs__net-navbar.active .navbar-nav li.dropdown a:hover, +.fbs__net-navbar.active .navbar-nav li.dropend a:hover, +.fbs__net-navbar.active .navbar-nav li.dropstart a:hover { + color: var(--inverse-color) !important; +} + +.fbs__net-navbar .fbs__net-icon-menu { + display: block; +} + +.fbs__net-navbar .fbs__net-icon-close { + display: none; +} + +.offcanvas-active .fbs__net-navbar .fbs__net-icon-menu { + display: none; +} + +.offcanvas-active .fbs__net-navbar .fbs__net-icon-close { + display: block; +} + +.fbs__net-navbar.dark { + -webkit-box-shadow: 0 0px 2px rgba(var(--inverse-color-rgb), 0.15); + box-shadow: 0 0px 2px rgba(var(--inverse-color-rgb), 0.15); +} + +.fbs__net-navbar.dark .navbar-brand .logo.dark { + display: block; +} + +.fbs__net-navbar.dark .navbar-brand .logo.light { + display: none; +} + +.fbs__net-navbar.dark a { + color: var(--inverse-color) !important; +} + +.fbs__net-navbar.dark a:hover { + color: var(--inverse-color); +} + +.fbs__net-navbar.dark .header-social a:hover { + background-color: rgba(var(--inverse-color-rgb), 0.05); +} + +.fbs__net-navbar.dark ul li a { + color: rgba(var(--inverse-color-rgb), 1) !important; +} + +.fbs__net-navbar.dark ul li a:hover { + color: var(--inverse-color) !important; +} + +.fbs__net-navbar .offcanvas-header .offcanvas-header-logo .logo-link .logo.dark { + display: block; +} + +.fbs__net-navbar .offcanvas-header .offcanvas-header-logo .logo-link .logo.light { + display: none; +} + + +@-webkit-keyframes fadeInUp { + from { + opacity: 0; + -webkit-transform: translateY(10px); + transform: translateY(10px); + } + + to { + opacity: 1; + -webkit-transform: translateY(0); + transform: translateY(0); + } +} + +@keyframes fadeInUp { + from { + opacity: 0; + -webkit-transform: translateY(10px); + transform: translateY(10px); + } + + to { + opacity: 1; + -webkit-transform: translateY(0); + transform: translateY(0); + } +} + +@-webkit-keyframes fadeInRight { + from { + opacity: 0; + -webkit-transform: translateX(10px); + transform: translateX(10px); + } + + to { + opacity: 1; + -webkit-transform: translateX(0); + transform: translateX(0); + } +} + +@keyframes fadeInRight { + from { + opacity: 0; + -webkit-transform: translateX(10px); + transform: translateX(10px); + } + + to { + opacity: 1; + -webkit-transform: translateX(0); + transform: translateX(0); + } +} + +@-webkit-keyframes fadeInLeft { + from { + opacity: 0; + -webkit-transform: translateX(-10px); + transform: translateX(-10px); + } + + to { + opacity: 1; + -webkit-transform: translateX(0); + transform: translateX(0); + } +} + +@keyframes fadeInLeft { + from { + opacity: 0; + -webkit-transform: translateX(-10px); + transform: translateX(-10px); + } + + to { + opacity: 1; + -webkit-transform: translateX(0); + transform: translateX(0); + } +} + +.hero__v6 { + padding: 10rem 0 !important; +} + +.hero__v6 .hero-subtitle { + background: color-mix(in srgb, var(--bs-secondary), transparent 70%); + color: var(--bs-primary); + display: inline-block; + padding: 5px 12px; + border-radius: 7px; + font-weight: 600; + font-size: 0.75rem; + margin-bottom: 10px; +} + +.hero__v6 .hero-title { + font-size: 48px; +} + +@media (max-width: 1199.98px) { + .hero__v6 .hero-title { + font-size: 40px; + } +} + +@media (max-width: 991.98px) { + .hero__v6 .hero-title { + font-size: 30px; + } +} + +.hero__v6 .logos .logos-title { + font-size: 12px; + letter-spacing: 1px; + color: color-mix(in srgb, var(--bs-primary), transparent 10%); +} + +.hero__v6 .logos img { + width: 140px; +} + +.hero__v6 .hero-img { + position: relative; +} + +.hero__v6 .hero-img .img-main { + position: relative; + z-index: 9; +} + +.hero__v6 .hero-img .img-card { + z-index: 10; + border-radius: 20px; + -webkit-box-shadow: 0 15px 40px 0 rgba(var(--bs-black-rgb), 0.1); + box-shadow: 0 15px 40px 0 rgba(var(--bs-black-rgb), 0.1); + position: absolute; + max-width: 280px; + bottom: -40px; + left: -60px; +} + +@media (max-width: 991.98px) { + .hero__v6 .hero-img .img-card { + left: 10px; + max-width: 380px; + } +} + +@media (max-width: 575.98px) { + .hero__v6 .hero-img .img-card { + left: 10px; + max-width: 280px; + } +} + +.features__v2 .icon { + width: 60px; + height: 60px; + line-height: 60px; + border-radius: 50%; + background-color: var(--bs-secondary); + color: var(--bs-primary); +} + +.features__v2 .content { + background-color: rgba(var(--bs-secondary-rgb), 0.2); +} + +.features__v2 .btn-play i { + width: 30px; + height: 30px; + line-height: 30px; + border-radius: 50%; + background-color: var(--bs-white); + color: var(--bs-primary); +} + +.about__v4 .subtitle { + background-color: rgba(var(--bs-secondary-rgb), 0.2); + color: var(--bs-primary); + display: inline-block; + padding: 5px 12px; + border-radius: 7px; + font-weight: 600; + font-size: 0.75rem; + margin-bottom: 10px; +} + +.about__v4 .features li .icon { + display: inline-block; + width: 20px; + height: 20px; + line-height: 20px; + background-color: var(--bs-primary); + color: var(--bs-white); +} + +.about__v4 .mission-statement { + background-color: var(--bs-primary); + position: relative; + bottom: -20px; + width: 100%; +} + +.about__v4 .mission-statement h3 { + font-size: 10px; + color: var(--bs-secondary); +} + +.about__v4 .mission-statement p { + color: var(--bs-white); +} + +.about__v4 .mission-icon { + width: 50px; + height: 50px; + -webkit-box-flex: 0; + -ms-flex: 0 0 50px; + flex: 0 0 50px; + line-height: 50px; + display: inline-block; + background-color: rgba(var(--bs-secondary-rgb), 0.1); +} + +.about__v4 .mission-icon i { + color: var(--bs-secondary); +} + +.pricing__v2 .subtitle { + background-color: rgba(var(--bs-secondary-rgb), 0.2); + color: var(--bs-primary); + display: inline-block; + padding: 5px 12px; + border-radius: 7px; + font-weight: 600; + font-size: 0.75rem; + margin-bottom: 10px; +} + +.pricing__v2 .price-table { + border: 1px solid rgba(var(--inverse-color-rgb), 0.2); +} + +.pricing__v2 .price-table .price { + color: var(--bs-heading-color); +} + +.pricing__v2 .price-table .price strong { + font-size: 40px; +} + +.pricing__v2 .price-table .icon { + -webkit-box-flex: 0; + -ms-flex: 0 0 20px; + flex: 0 0 20px; + width: 20px; + height: 20px; + line-height: 20px; + text-align: center; + border: 1px solid rgba(var(--bs-white-rgb), 0.2); +} + +.pricing__v2 .popular { + background-color: var(--bs-primary); + color: var(--bs-white); +} + +.pricing__v2 .popular .price { + color: var(--bs-secondary); +} + +.pricing__v2 .popular h3 { + color: var(--bs-white); +} + +.pricing__v2 .popular p { + color: var(--bs-white); +} + +.pricing__v2 .popular .pricing-features h4 { + font-size: 12px; + color: var(--bs-secondary); +} + +.howitworks__v1 .subtitle { + background-color: rgba(var(--bs-secondary-rgb), 0.2); + color: var(--bs-primary); + display: inline-block; + padding: 5px 12px; + border-radius: 7px; + font-weight: 600; + font-size: 0.75rem; + margin-bottom: 10px; +} + +.howitworks__v1 .step-card { + position: relative; +} + +.howitworks__v1 .step-card .step-number { + width: 60px; + height: 60px; + line-height: 60px; + display: inline-block; + color: var(--bs-secondary); + background-color: var(--bs-primary); + font-size: 18px; +} + +.howitworks__v1 .arch-line { + position: absolute; + top: 15px; + width: 150px; + right: 0; + left: calc(100% + 24px); + -webkit-transform: translateX(-50%); + transform: translateX(-50%); +} + +@media (max-width: 991.98px) { + .howitworks__v1 .arch-line { + display: none; + } +} + +.howitworks__v1 .arch-line.reverse { + top: 20px; +} + +.services__v3 .subtitle { + background-color: rgba(var(--bs-secondary-rgb), 0.2); + color: var(--bs-primary); + display: inline-block; + padding: 5px 12px; + border-radius: 7px; + font-weight: 600; + font-size: 0.75rem; + margin-bottom: 10px; +} + +.services__v3 .icon { + display: inline-block; + position: relative; + color: var(--bs-primary) !important; +} + +.services__v3 .icon:before { + content: ""; + position: absolute; + z-index: -1; + width: 40px; + height: 40px; + right: -10px; + bottom: 0px; + border-radius: 50%; + background-color: rgba(var(--bs-secondary-rgb), 1); +} + +.services__v3 .icon svg { + width: 50px; +} + +.services__v3 .service-card { + border: 1px solid rgba(var(--inverse-color-rgb), 0.2); +} + +.faq__v2 { + background-color: rgba(var(--inverse-color-rgb), 0.02); +} + +.faq__v2 .subtitle { + background-color: rgba(var(--bs-secondary-rgb), 0.2); + color: var(--bs-primary); + display: inline-block; + padding: 5px 12px; + border-radius: 7px; + font-weight: 600; + font-size: 0.75rem; + margin-bottom: 10px; +} + +.faq__v2 .custom-accordion { + --bs-accordion-bg: var(--bs-body-bg); + --bs-accordion-btn-icon: url(""); + --bs-accordion-btn-active-icon: url(""); +} + +.faq__v2 .custom-accordion .accordion-button { + position: relative; + padding-right: 50px; +} + +.faq__v2 .custom-accordion .accordion-button::after { + content: "\f4fe"; + font-family: "bootstrap-icons" !important; + font-style: normal; + font-weight: 400 !important; + font-variant: normal; + text-transform: none; + right: 20px; + top: 15px; + width: 18px; + height: 14px; + font-size: 30px; + color: var(--inverse-color); + -webkit-transform-origin: center center; + transform-origin: center center; + -webkit-transform: rotate(0deg); + transform: rotate(0deg); + line-height: 1; + position: absolute; + -webkit-font-smoothing: antialiased; +} + +.faq__v2 .custom-accordion .accordion-button:not(.collapsed)::after { + -webkit-transform: rotate(0deg); + transform: rotate(0deg); + content: "\f2ea"; +} + +.faq__v2 .custom-accordion .accordion-item { + border: none; + margin-bottom: 10px; + border-radius: 10px; + overflow: hidden; + -webkit-transition: 0.3s all ease-in-out; + transition: 0.3s all ease-in-out; +} + +.faq__v2 .custom-accordion .accordion-item .accordion-header .accordion-button { + outline: none; + -webkit-box-shadow: none; + box-shadow: none; + font-size: 18px; + font-weight: 500; + color: var(--bs-heading-color); + padding-top: 20px; + padding-bottom: 20px; + border: none; +} + +.faq__v2 .custom-accordion .accordion-item .accordion-header .accordion-button:not(.collapsed) { + background-color: transparent; + color: var(--inverse-color); + -webkit-box-shadow: 0 1px 1px rgba(var(--inverse-color-rgb), 0.05); + box-shadow: 0 1px 1px rgba(var(--inverse-color-rgb), 0.05); +} + +.faq__v2 .custom-accordion .accordion-item .accordion-body { + border-top: none; +} + +.stats__v3 .content { + padding-top: 70px; + padding-bottom: 70px; + background-color: var(--bs-primary); + color: var(--bs-white); + overflow: hidden; + position: relative; +} + +.stats__v3 .content h3 { + color: var(--bs-secondary); +} + +.stats__v3 .content .rounded-borders { + position: absolute; + bottom: 0; + right: 0; + width: 500px; + height: 500px; +} + +.stats__v3 .content .rounded-borders .rounded-border-1, +.stats__v3 .content .rounded-borders .rounded-border-2, +.stats__v3 .content .rounded-borders .rounded-border-3 { + border: 1px solid var(--bs-secondary); + width: 500px; + height: 500px; + border-radius: 50%; + position: absolute; +} + +.stats__v3 .content .rounded-borders .rounded-border-1 { + right: 20px; + top: 20px; +} + +.stats__v3 .content .rounded-borders .rounded-border-2 { + right: 40px; + top: 40px; +} + +.testimonials__v2 .subtitle { + background-color: rgba(var(--bs-secondary-rgb), 0.2); + color: var(--bs-primary); + display: inline-block; + padding: 5px 12px; + border-radius: 7px; + font-weight: 600; + font-size: 0.75rem; + margin-bottom: 10px; +} + +.testimonials__v2 .testimonial { + border: 1px solid rgba(var(--inverse-color-rgb), 0.2); +} + +.testimonials__v2 .testimonial-author .author-img { + -webkit-box-flex: 0; + -ms-flex: 0 0 50px; + flex: 0 0 50px; +} + +.contact__v2 .subtitle { + background-color: rgba(var(--bs-secondary-rgb), 0.2); + color: var(--bs-primary); + display: inline-block; + padding: 5px 12px; + border-radius: 7px; + font-weight: 600; + font-size: 0.75rem; + margin-bottom: 10px; +} + +.contact__v2 .icon { + width: 50px; + height: 50px; + line-height: 50px; + text-align: center; + border: 1px solid rgba(var(--inverse-color-rgb), 0.2); + border-radius: 50%; +} + +.footer { + border-top: 1px solid #111827; + background: var(--bg); + margin-top: 40px +} + +.footer .container { + display: flex; + align-items: center; + gap: 18px; + justify-content: space-between; + padding: 16px 0 +} + +.footer a { + color: var(--muted) +} + +.footer { + bottom: 0; + width: 95%; + text-align: center; +} + +.footer h2, +.footer h3, +.footer h4 { + font-size: 16px; +} + +.footer ul li { + padding: 0; + margin: 0 0 10px 0; +} + +.footer a { + color: rgba(var(--inverse-color-rgb), 0.6); + text-decoration: none; +} + +.footer a:hover { + color: var(--inverse-color); +} + +.footer a .badge { + -webkit-transition: 0.3s all ease; + transition: 0.3s all ease; + background-color: rgba(var(--bs-primary-rgb), 0.1); + color: var(--bs-primary); +} + +.footer a:hover .badge { + background-color: rgba(var(--bs-primary-rgb), 1); + color: var(--bs-white); +} + +.footer .quick-contact i { + color: var(--inverse-color); +} + +.footer .credits { + font-size: 13.5px; +} + +#back-to-top { + position: fixed; + bottom: 0px; + right: 20px; + visibility: hidden; + opacity: 0; + background-color: var(--bs-primary); + color: white; + border: none; + border-radius: 50%; + padding: 10px; + cursor: pointer; + -webkit-box-shadow: 0 4px 8px rgba(0, 0, 0, 0.1); + box-shadow: 0 4px 8px rgba(0, 0, 0, 0.1); + -webkit-transition: all 0.3s ease-in-out; + transition: all 0.3s ease-in-out; + z-index: 1000; + width: 40px; + height: 40px; +} + +#back-to-top i { + font-size: 24px; + position: absolute; + left: 50%; + top: 50%; + -webkit-transform: translate(-50%, -50%); + transform: translate(-50%, -50%); +} + +#back-to-top.show { + bottom: 20px; + opacity: 1; + visibility: visible; +} \ No newline at end of file diff --git a/Website/assets/images/.DS_Store b/Website/assets/images/.DS_Store new file mode 100644 index 0000000..ff3a152 Binary files /dev/null and b/Website/assets/images/.DS_Store differ diff --git a/Website/assets/images/3d-render-1-min.jpg b/Website/assets/images/3d-render-1-min.jpg new file mode 100644 index 0000000..f5106a5 Binary files /dev/null and b/Website/assets/images/3d-render-1-min.jpg differ diff --git a/Website/assets/images/Facebook_icon.svg b/Website/assets/images/Facebook_icon.svg new file mode 100644 index 0000000..c63c1ef --- /dev/null +++ b/Website/assets/images/Facebook_icon.svg @@ -0,0 +1,49 @@ + + + + + + + + + + + + + + + + + + + + + + diff --git a/Website/assets/images/Google_icon.svg b/Website/assets/images/Google_icon.svg new file mode 100644 index 0000000..088288f --- /dev/null +++ b/Website/assets/images/Google_icon.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/Website/assets/images/about_2-min.jpg b/Website/assets/images/about_2-min.jpg new file mode 100644 index 0000000..83fc2bb Binary files /dev/null and b/Website/assets/images/about_2-min.jpg differ diff --git a/Website/assets/images/arch-line-reverse.svg b/Website/assets/images/arch-line-reverse.svg new file mode 100644 index 0000000..b2a57a7 --- /dev/null +++ b/Website/assets/images/arch-line-reverse.svg @@ -0,0 +1,3 @@ + + + diff --git a/Website/assets/images/arch-line.svg b/Website/assets/images/arch-line.svg new file mode 100644 index 0000000..33e59af --- /dev/null +++ b/Website/assets/images/arch-line.svg @@ -0,0 +1,3 @@ + + + diff --git a/Website/assets/images/astronaut.svg b/Website/assets/images/astronaut.svg new file mode 100644 index 0000000..f403598 --- /dev/null +++ b/Website/assets/images/astronaut.svg @@ -0,0 +1,35 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Website/assets/images/block_squares.svg b/Website/assets/images/block_squares.svg new file mode 100644 index 0000000..16d4ec0 --- /dev/null +++ b/Website/assets/images/block_squares.svg @@ -0,0 +1,9 @@ + + + + + + + + + diff --git a/Website/assets/images/business/business-img-1-min.jpg b/Website/assets/images/business/business-img-1-min.jpg new file mode 100644 index 0000000..46ffb64 Binary files /dev/null and b/Website/assets/images/business/business-img-1-min.jpg differ diff --git a/Website/assets/images/business/business-img-10-min.jpg b/Website/assets/images/business/business-img-10-min.jpg new file mode 100644 index 0000000..6ff6926 Binary files /dev/null and b/Website/assets/images/business/business-img-10-min.jpg differ diff --git a/Website/assets/images/business/business-img-2-min.jpg b/Website/assets/images/business/business-img-2-min.jpg new file mode 100644 index 0000000..d6a37c9 Binary files /dev/null and b/Website/assets/images/business/business-img-2-min.jpg differ diff --git a/Website/assets/images/business/business-img-3-min.jpg b/Website/assets/images/business/business-img-3-min.jpg new file mode 100644 index 0000000..96e13a0 Binary files /dev/null and b/Website/assets/images/business/business-img-3-min.jpg differ diff --git a/Website/assets/images/business/business-img-4-min.jpg b/Website/assets/images/business/business-img-4-min.jpg new file mode 100644 index 0000000..0cf4630 Binary files /dev/null and b/Website/assets/images/business/business-img-4-min.jpg differ diff --git a/Website/assets/images/business/business-img-5-min.jpg b/Website/assets/images/business/business-img-5-min.jpg new file mode 100644 index 0000000..613f930 Binary files /dev/null and b/Website/assets/images/business/business-img-5-min.jpg differ diff --git a/Website/assets/images/business/business-img-6-min.jpg b/Website/assets/images/business/business-img-6-min.jpg new file mode 100644 index 0000000..82e7b27 Binary files /dev/null and b/Website/assets/images/business/business-img-6-min.jpg differ diff --git a/Website/assets/images/business/business-img-7-min.jpg b/Website/assets/images/business/business-img-7-min.jpg new file mode 100644 index 0000000..5a4cb71 Binary files /dev/null and b/Website/assets/images/business/business-img-7-min.jpg differ diff --git a/Website/assets/images/business/business-img-8-min.jpg b/Website/assets/images/business/business-img-8-min.jpg new file mode 100644 index 0000000..6aad1cb Binary files /dev/null and b/Website/assets/images/business/business-img-8-min.jpg differ diff --git a/Website/assets/images/business/business-img-9-min.jpg b/Website/assets/images/business/business-img-9-min.jpg new file mode 100644 index 0000000..d924c86 Binary files /dev/null and b/Website/assets/images/business/business-img-9-min.jpg differ diff --git a/Website/assets/images/card-expenses.png b/Website/assets/images/card-expenses.png new file mode 100644 index 0000000..7859088 Binary files /dev/null and b/Website/assets/images/card-expenses.png differ diff --git a/Website/assets/images/foto-main1.png b/Website/assets/images/foto-main1.png new file mode 100644 index 0000000..dd8da1c Binary files /dev/null and b/Website/assets/images/foto-main1.png differ diff --git a/Website/assets/images/hero-bg-1-min.jpg b/Website/assets/images/hero-bg-1-min.jpg new file mode 100644 index 0000000..a7fd595 Binary files /dev/null and b/Website/assets/images/hero-bg-1-min.jpg differ diff --git a/Website/assets/images/hero-bg-2-min.jpg b/Website/assets/images/hero-bg-2-min.jpg new file mode 100644 index 0000000..545dacc Binary files /dev/null and b/Website/assets/images/hero-bg-2-min.jpg differ diff --git a/Website/assets/images/hero-img-1-min.jpg b/Website/assets/images/hero-img-1-min.jpg new file mode 100644 index 0000000..3f01b04 Binary files /dev/null and b/Website/assets/images/hero-img-1-min.jpg differ diff --git a/Website/assets/images/hero-img-2-min.jpg b/Website/assets/images/hero-img-2-min.jpg new file mode 100644 index 0000000..2ff019f Binary files /dev/null and b/Website/assets/images/hero-img-2-min.jpg differ diff --git a/Website/assets/images/img-1-min.jpg b/Website/assets/images/img-1-min.jpg new file mode 100644 index 0000000..c34a8ec Binary files /dev/null and b/Website/assets/images/img-1-min.jpg differ diff --git a/Website/assets/images/img-10-min.jpg b/Website/assets/images/img-10-min.jpg new file mode 100644 index 0000000..025403e Binary files /dev/null and b/Website/assets/images/img-10-min.jpg differ diff --git a/Website/assets/images/img-11-min.jpg b/Website/assets/images/img-11-min.jpg new file mode 100644 index 0000000..b4540aa Binary files /dev/null and b/Website/assets/images/img-11-min.jpg differ diff --git a/Website/assets/images/img-12-min.jpg b/Website/assets/images/img-12-min.jpg new file mode 100644 index 0000000..a923ae4 Binary files /dev/null and b/Website/assets/images/img-12-min.jpg differ diff --git a/Website/assets/images/img-13-min.jpg b/Website/assets/images/img-13-min.jpg new file mode 100644 index 0000000..80fedf3 Binary files /dev/null and b/Website/assets/images/img-13-min.jpg differ diff --git a/Website/assets/images/img-14-min.jpg b/Website/assets/images/img-14-min.jpg new file mode 100644 index 0000000..77b22f8 Binary files /dev/null and b/Website/assets/images/img-14-min.jpg differ diff --git a/Website/assets/images/img-15-min.jpg b/Website/assets/images/img-15-min.jpg new file mode 100644 index 0000000..e5300c8 Binary files /dev/null and b/Website/assets/images/img-15-min.jpg differ diff --git a/Website/assets/images/img-16-min.jpg b/Website/assets/images/img-16-min.jpg new file mode 100644 index 0000000..71eeeb7 Binary files /dev/null and b/Website/assets/images/img-16-min.jpg differ diff --git a/Website/assets/images/img-2-min.jpg b/Website/assets/images/img-2-min.jpg new file mode 100644 index 0000000..e323b7a Binary files /dev/null and b/Website/assets/images/img-2-min.jpg differ diff --git a/Website/assets/images/img-3-min.jpg b/Website/assets/images/img-3-min.jpg new file mode 100644 index 0000000..630031d Binary files /dev/null and b/Website/assets/images/img-3-min.jpg differ diff --git a/Website/assets/images/img-4-min.jpg b/Website/assets/images/img-4-min.jpg new file mode 100644 index 0000000..f145f99 Binary files /dev/null and b/Website/assets/images/img-4-min.jpg differ diff --git a/Website/assets/images/img-5-min.jpg b/Website/assets/images/img-5-min.jpg new file mode 100644 index 0000000..829af23 Binary files /dev/null and b/Website/assets/images/img-5-min.jpg differ diff --git a/Website/assets/images/img-6-min.jpg b/Website/assets/images/img-6-min.jpg new file mode 100644 index 0000000..48916c5 Binary files /dev/null and b/Website/assets/images/img-6-min.jpg differ diff --git a/Website/assets/images/img-7-min.jpg b/Website/assets/images/img-7-min.jpg new file mode 100644 index 0000000..b48f429 Binary files /dev/null and b/Website/assets/images/img-7-min.jpg differ diff --git a/Website/assets/images/img-8-min.jpg b/Website/assets/images/img-8-min.jpg new file mode 100644 index 0000000..22a310d Binary files /dev/null and b/Website/assets/images/img-8-min.jpg differ diff --git a/Website/assets/images/img-9-min.jpg b/Website/assets/images/img-9-min.jpg new file mode 100644 index 0000000..7f50488 Binary files /dev/null and b/Website/assets/images/img-9-min.jpg differ diff --git a/Website/assets/images/img1-min.jpg b/Website/assets/images/img1-min.jpg new file mode 100644 index 0000000..6d77275 Binary files /dev/null and b/Website/assets/images/img1-min.jpg differ diff --git a/Website/assets/images/logo-dark.svg b/Website/assets/images/logo-dark.svg new file mode 100644 index 0000000..c992d28 --- /dev/null +++ b/Website/assets/images/logo-dark.svg @@ -0,0 +1,16 @@ + + + + + + + + + + + + + + + + diff --git a/Website/assets/images/logo-light.svg b/Website/assets/images/logo-light.svg new file mode 100644 index 0000000..1be0782 --- /dev/null +++ b/Website/assets/images/logo-light.svg @@ -0,0 +1,16 @@ + + + + + + + + + + + + + + + + diff --git a/Website/assets/images/logo/.DS_Store b/Website/assets/images/logo/.DS_Store new file mode 100644 index 0000000..f5895ce Binary files /dev/null and b/Website/assets/images/logo/.DS_Store differ diff --git a/Website/assets/images/logo/actual-size/.DS_Store b/Website/assets/images/logo/actual-size/.DS_Store new file mode 100644 index 0000000..784b5ea Binary files /dev/null and b/Website/assets/images/logo/actual-size/.DS_Store differ diff --git a/Website/assets/images/logo/actual-size/logo-air-bnb__black.svg b/Website/assets/images/logo/actual-size/logo-air-bnb__black.svg new file mode 100644 index 0000000..ee072da --- /dev/null +++ b/Website/assets/images/logo/actual-size/logo-air-bnb__black.svg @@ -0,0 +1,10 @@ + + + + + + + + + + diff --git a/Website/assets/images/logo/actual-size/logo-american-apparel__black.svg b/Website/assets/images/logo/actual-size/logo-american-apparel__black.svg new file mode 100644 index 0000000..5a6f44a --- /dev/null +++ b/Website/assets/images/logo/actual-size/logo-american-apparel__black.svg @@ -0,0 +1,11 @@ + + + + + + + + + + + diff --git a/Website/assets/images/logo/actual-size/logo-apple__black.svg b/Website/assets/images/logo/actual-size/logo-apple__black.svg new file mode 100644 index 0000000..f09a53f --- /dev/null +++ b/Website/assets/images/logo/actual-size/logo-apple__black.svg @@ -0,0 +1,10 @@ + + + + + + + + + + diff --git a/Website/assets/images/logo/actual-size/logo-ebay__black-1.svg b/Website/assets/images/logo/actual-size/logo-ebay__black-1.svg new file mode 100644 index 0000000..3658a56 --- /dev/null +++ b/Website/assets/images/logo/actual-size/logo-ebay__black-1.svg @@ -0,0 +1,11 @@ + + + + + + + + + + + diff --git a/Website/assets/images/logo/actual-size/logo-ebay__black.svg b/Website/assets/images/logo/actual-size/logo-ebay__black.svg new file mode 100644 index 0000000..8e76e38 --- /dev/null +++ b/Website/assets/images/logo/actual-size/logo-ebay__black.svg @@ -0,0 +1,13 @@ + + + + + + + + + + + + + diff --git a/Website/assets/images/logo/actual-size/logo-google__black.svg b/Website/assets/images/logo/actual-size/logo-google__black.svg new file mode 100644 index 0000000..e0cee2b --- /dev/null +++ b/Website/assets/images/logo/actual-size/logo-google__black.svg @@ -0,0 +1,8 @@ + + + + + + + + diff --git a/Website/assets/images/logo/actual-size/logo-ibm__black.svg b/Website/assets/images/logo/actual-size/logo-ibm__black.svg new file mode 100644 index 0000000..86ba469 --- /dev/null +++ b/Website/assets/images/logo/actual-size/logo-ibm__black.svg @@ -0,0 +1,10 @@ + + + + + + + + + + diff --git a/Website/assets/images/logo/actual-size/logo-invision__black.svg b/Website/assets/images/logo/actual-size/logo-invision__black.svg new file mode 100644 index 0000000..d8d8803 --- /dev/null +++ b/Website/assets/images/logo/actual-size/logo-invision__black.svg @@ -0,0 +1,10 @@ + + + + + + + + + + diff --git a/Website/assets/images/logo/actual-size/logo-microsoft_black.svg b/Website/assets/images/logo/actual-size/logo-microsoft_black.svg new file mode 100644 index 0000000..23b01c9 --- /dev/null +++ b/Website/assets/images/logo/actual-size/logo-microsoft_black.svg @@ -0,0 +1,14 @@ + + + + + + + + + + + + + + diff --git a/Website/assets/images/logo/actual-size/logo-netflix__black.svg b/Website/assets/images/logo/actual-size/logo-netflix__black.svg new file mode 100644 index 0000000..ff9c372 --- /dev/null +++ b/Website/assets/images/logo/actual-size/logo-netflix__black.svg @@ -0,0 +1,10 @@ + + + + + + + + + + diff --git a/Website/assets/images/logo/actual-size/logo-uber__black.svg b/Website/assets/images/logo/actual-size/logo-uber__black.svg new file mode 100644 index 0000000..57dd2d1 --- /dev/null +++ b/Website/assets/images/logo/actual-size/logo-uber__black.svg @@ -0,0 +1,10 @@ + + + + + + + + + + diff --git a/Website/assets/images/logo/actual-size/logo-youtube__black.svg b/Website/assets/images/logo/actual-size/logo-youtube__black.svg new file mode 100644 index 0000000..1ce365b --- /dev/null +++ b/Website/assets/images/logo/actual-size/logo-youtube__black.svg @@ -0,0 +1,13 @@ + + + + + + + + + + + + + diff --git a/Website/assets/images/logo/logo-air-bnb.svg b/Website/assets/images/logo/logo-air-bnb.svg new file mode 100644 index 0000000..0a9ee8d --- /dev/null +++ b/Website/assets/images/logo/logo-air-bnb.svg @@ -0,0 +1,10 @@ + + + + + + + + + + diff --git a/Website/assets/images/logo/logo-air-bnb__black.svg b/Website/assets/images/logo/logo-air-bnb__black.svg new file mode 100644 index 0000000..adccc40 --- /dev/null +++ b/Website/assets/images/logo/logo-air-bnb__black.svg @@ -0,0 +1,10 @@ + + + + + + + + + + diff --git a/Website/assets/images/logo/logo-american-apparel.svg b/Website/assets/images/logo/logo-american-apparel.svg new file mode 100644 index 0000000..15a9532 --- /dev/null +++ b/Website/assets/images/logo/logo-american-apparel.svg @@ -0,0 +1,11 @@ + + + + + + + + + + + diff --git a/Website/assets/images/logo/logo-american-apparel__black.svg b/Website/assets/images/logo/logo-american-apparel__black.svg new file mode 100644 index 0000000..9e13bc3 --- /dev/null +++ b/Website/assets/images/logo/logo-american-apparel__black.svg @@ -0,0 +1,11 @@ + + + + + + + + + + + diff --git a/Website/assets/images/logo/logo-apple.svg b/Website/assets/images/logo/logo-apple.svg new file mode 100644 index 0000000..fdbf8d0 --- /dev/null +++ b/Website/assets/images/logo/logo-apple.svg @@ -0,0 +1,10 @@ + + + + + + + + + + diff --git a/Website/assets/images/logo/logo-apple__black.svg b/Website/assets/images/logo/logo-apple__black.svg new file mode 100644 index 0000000..e58fb43 --- /dev/null +++ b/Website/assets/images/logo/logo-apple__black.svg @@ -0,0 +1,10 @@ + + + + + + + + + + diff --git a/Website/assets/images/logo/logo-ebay-1.svg b/Website/assets/images/logo/logo-ebay-1.svg new file mode 100644 index 0000000..79389f8 --- /dev/null +++ b/Website/assets/images/logo/logo-ebay-1.svg @@ -0,0 +1,11 @@ + + + + + + + + + + + diff --git a/Website/assets/images/logo/logo-ebay.svg b/Website/assets/images/logo/logo-ebay.svg new file mode 100644 index 0000000..cfc51e4 --- /dev/null +++ b/Website/assets/images/logo/logo-ebay.svg @@ -0,0 +1,13 @@ + + + + + + + + + + + + + diff --git a/Website/assets/images/logo/logo-ebay__black-1.svg b/Website/assets/images/logo/logo-ebay__black-1.svg new file mode 100644 index 0000000..1c5012b --- /dev/null +++ b/Website/assets/images/logo/logo-ebay__black-1.svg @@ -0,0 +1,11 @@ + + + + + + + + + + + diff --git a/Website/assets/images/logo/logo-ebay__black.svg b/Website/assets/images/logo/logo-ebay__black.svg new file mode 100644 index 0000000..8098db2 --- /dev/null +++ b/Website/assets/images/logo/logo-ebay__black.svg @@ -0,0 +1,13 @@ + + + + + + + + + + + + + diff --git a/Website/assets/images/logo/logo-google.svg b/Website/assets/images/logo/logo-google.svg new file mode 100644 index 0000000..8d979dc --- /dev/null +++ b/Website/assets/images/logo/logo-google.svg @@ -0,0 +1,8 @@ + + + + + + + + diff --git a/Website/assets/images/logo/logo-google__black.svg b/Website/assets/images/logo/logo-google__black.svg new file mode 100644 index 0000000..317374e --- /dev/null +++ b/Website/assets/images/logo/logo-google__black.svg @@ -0,0 +1,8 @@ + + + + + + + + diff --git a/Website/assets/images/logo/logo-ibm.svg b/Website/assets/images/logo/logo-ibm.svg new file mode 100644 index 0000000..d811ff2 --- /dev/null +++ b/Website/assets/images/logo/logo-ibm.svg @@ -0,0 +1,10 @@ + + + + + + + + + + diff --git a/Website/assets/images/logo/logo-ibm__black.svg b/Website/assets/images/logo/logo-ibm__black.svg new file mode 100644 index 0000000..d9cb4e9 --- /dev/null +++ b/Website/assets/images/logo/logo-ibm__black.svg @@ -0,0 +1,10 @@ + + + + + + + + + + diff --git a/Website/assets/images/logo/logo-invision.svg b/Website/assets/images/logo/logo-invision.svg new file mode 100644 index 0000000..84d31f1 --- /dev/null +++ b/Website/assets/images/logo/logo-invision.svg @@ -0,0 +1,10 @@ + + + + + + + + + + diff --git a/Website/assets/images/logo/logo-invision__black.svg b/Website/assets/images/logo/logo-invision__black.svg new file mode 100644 index 0000000..b29cebe --- /dev/null +++ b/Website/assets/images/logo/logo-invision__black.svg @@ -0,0 +1,10 @@ + + + + + + + + + + diff --git a/Website/assets/images/logo/logo-microsoft.svg b/Website/assets/images/logo/logo-microsoft.svg new file mode 100644 index 0000000..64bd9cd --- /dev/null +++ b/Website/assets/images/logo/logo-microsoft.svg @@ -0,0 +1,14 @@ + + + + + + + + + + + + + + diff --git a/Website/assets/images/logo/logo-microsoft__black.svg b/Website/assets/images/logo/logo-microsoft__black.svg new file mode 100644 index 0000000..3fd26cf --- /dev/null +++ b/Website/assets/images/logo/logo-microsoft__black.svg @@ -0,0 +1,14 @@ + + + + + + + + + + + + + + diff --git a/Website/assets/images/logo/logo-netflix.svg b/Website/assets/images/logo/logo-netflix.svg new file mode 100644 index 0000000..5745fd4 --- /dev/null +++ b/Website/assets/images/logo/logo-netflix.svg @@ -0,0 +1,10 @@ + + + + + + + + + + diff --git a/Website/assets/images/logo/logo-netflix__black.svg b/Website/assets/images/logo/logo-netflix__black.svg new file mode 100644 index 0000000..6e0863e --- /dev/null +++ b/Website/assets/images/logo/logo-netflix__black.svg @@ -0,0 +1,10 @@ + + + + + + + + + + diff --git a/Website/assets/images/logo/logo-uber.svg b/Website/assets/images/logo/logo-uber.svg new file mode 100644 index 0000000..008d5fc --- /dev/null +++ b/Website/assets/images/logo/logo-uber.svg @@ -0,0 +1,10 @@ + + + + + + + + + + diff --git a/Website/assets/images/logo/logo-uber__black.svg b/Website/assets/images/logo/logo-uber__black.svg new file mode 100644 index 0000000..f81a3c7 --- /dev/null +++ b/Website/assets/images/logo/logo-uber__black.svg @@ -0,0 +1,10 @@ + + + + + + + + + + diff --git a/Website/assets/images/logo/logo-youtube.svg b/Website/assets/images/logo/logo-youtube.svg new file mode 100644 index 0000000..0fbf451 --- /dev/null +++ b/Website/assets/images/logo/logo-youtube.svg @@ -0,0 +1,13 @@ + + + + + + + + + + + + + diff --git a/Website/assets/images/logo/logo-youtube__black.svg b/Website/assets/images/logo/logo-youtube__black.svg new file mode 100644 index 0000000..57aa918 --- /dev/null +++ b/Website/assets/images/logo/logo-youtube__black.svg @@ -0,0 +1,13 @@ + + + + + + + + + + + + + diff --git a/Website/assets/images/person-sq-1-min.jpg b/Website/assets/images/person-sq-1-min.jpg new file mode 100644 index 0000000..ebef70a Binary files /dev/null and b/Website/assets/images/person-sq-1-min.jpg differ diff --git a/Website/assets/images/person-sq-2-min.jpg b/Website/assets/images/person-sq-2-min.jpg new file mode 100644 index 0000000..a3cd500 Binary files /dev/null and b/Website/assets/images/person-sq-2-min.jpg differ diff --git a/Website/assets/images/person-sq-3-min.jpg b/Website/assets/images/person-sq-3-min.jpg new file mode 100644 index 0000000..433280e Binary files /dev/null and b/Website/assets/images/person-sq-3-min.jpg differ diff --git a/Website/assets/images/person-sq-4-min.jpg b/Website/assets/images/person-sq-4-min.jpg new file mode 100644 index 0000000..8fa7b38 Binary files /dev/null and b/Website/assets/images/person-sq-4-min.jpg differ diff --git a/Website/assets/images/person-sq-5-min.jpg b/Website/assets/images/person-sq-5-min.jpg new file mode 100644 index 0000000..cd3cc67 Binary files /dev/null and b/Website/assets/images/person-sq-5-min.jpg differ diff --git a/Website/assets/images/person-sq-6-min.jpg b/Website/assets/images/person-sq-6-min.jpg new file mode 100644 index 0000000..a70f2cf Binary files /dev/null and b/Website/assets/images/person-sq-6-min.jpg differ diff --git a/Website/assets/images/person-sq-7-min.jpg b/Website/assets/images/person-sq-7-min.jpg new file mode 100644 index 0000000..c9ea67e Binary files /dev/null and b/Website/assets/images/person-sq-7-min.jpg differ diff --git a/Website/assets/images/person-sq-8-min.jpg b/Website/assets/images/person-sq-8-min.jpg new file mode 100644 index 0000000..06f8e8b Binary files /dev/null and b/Website/assets/images/person-sq-8-min.jpg differ diff --git a/Website/assets/images/person-sq-9-min.jpg b/Website/assets/images/person-sq-9-min.jpg new file mode 100644 index 0000000..5e77dce Binary files /dev/null and b/Website/assets/images/person-sq-9-min.jpg differ diff --git a/Website/assets/images/portfolio-details-img-1-min.jpg b/Website/assets/images/portfolio-details-img-1-min.jpg new file mode 100644 index 0000000..e73cc22 Binary files /dev/null and b/Website/assets/images/portfolio-details-img-1-min.jpg differ diff --git a/Website/assets/images/portfolio-details-img-2-min.jpg b/Website/assets/images/portfolio-details-img-2-min.jpg new file mode 100644 index 0000000..72b1d81 Binary files /dev/null and b/Website/assets/images/portfolio-details-img-2-min.jpg differ diff --git a/Website/assets/images/portfolio-details-img-3-min.jpg b/Website/assets/images/portfolio-details-img-3-min.jpg new file mode 100644 index 0000000..0a992c1 Binary files /dev/null and b/Website/assets/images/portfolio-details-img-3-min.jpg differ diff --git a/Website/assets/images/portfolio-details-img-4-min.jpg b/Website/assets/images/portfolio-details-img-4-min.jpg new file mode 100644 index 0000000..88018c0 Binary files /dev/null and b/Website/assets/images/portfolio-details-img-4-min.jpg differ diff --git a/Website/assets/images/portfolio-details-img-5-min.jpg b/Website/assets/images/portfolio-details-img-5-min.jpg new file mode 100644 index 0000000..9996315 Binary files /dev/null and b/Website/assets/images/portfolio-details-img-5-min.jpg differ diff --git a/Website/assets/images/portfolio-details-img-6-min.jpg b/Website/assets/images/portfolio-details-img-6-min.jpg new file mode 100644 index 0000000..cb039b0 Binary files /dev/null and b/Website/assets/images/portfolio-details-img-6-min.jpg differ diff --git a/Website/assets/images/quote.svg b/Website/assets/images/quote.svg new file mode 100644 index 0000000..70bc94a --- /dev/null +++ b/Website/assets/images/quote.svg @@ -0,0 +1,3 @@ + + + diff --git a/Website/assets/images/scribble-1.svg b/Website/assets/images/scribble-1.svg new file mode 100644 index 0000000..880a4a1 --- /dev/null +++ b/Website/assets/images/scribble-1.svg @@ -0,0 +1,5 @@ + + + + + diff --git a/Website/assets/images/scribble-2.svg b/Website/assets/images/scribble-2.svg new file mode 100644 index 0000000..92244c3 --- /dev/null +++ b/Website/assets/images/scribble-2.svg @@ -0,0 +1,5 @@ + + + + + diff --git a/Website/assets/js/.DS_Store b/Website/assets/js/.DS_Store new file mode 100644 index 0000000..5008ddf Binary files /dev/null and b/Website/assets/js/.DS_Store differ diff --git a/Website/assets/js/custom.js b/Website/assets/js/custom.js new file mode 100644 index 0000000..a23e789 --- /dev/null +++ b/Website/assets/js/custom.js @@ -0,0 +1,570 @@ +document.addEventListener("DOMContentLoaded", function () { + + + + const sections = document.querySelectorAll(".section"); + const navLinks = document.querySelectorAll(".fbs__net-navbar .scroll-link"); + + function removeActiveClasses() { + if (navLinks) { + navLinks.forEach((link) => link.classList.remove("active")); + } + } + + function addActiveClass(currentSectionId) { + const activeLink = document.querySelector( + `.fbs__net-navbar .scroll-link[href="#${currentSectionId}"]` + ); + if (activeLink) { + activeLink.classList.add("active"); + } + } + + function openNewPatient(){ + document.getElementById('modal-new').showModal(); +} + +function exportCSV(){ + const rows = [['Paciente','CPF','Idade','Status']]; + document.querySelectorAll('#patients tbody tr').forEach(tr=>{ + const tds = [...tr.querySelectorAll('td')].map(td=>td.innerText.replace(/\n.*/,'').trim()); + rows.push([tds[0],tds[1],tds[2],tds[3]]); + }); + const csv = rows.map(r=>r.map(s=>`"${s.replaceAll('"','""')}"`).join(',')).join('\n'); + const blob = new Blob([csv], {type:'text/csv;charset=utf-8;'}); + const url = URL.createObjectURL(blob); + const a = Object.assign(document.createElement('a'), {href:url, download:'pacientes.csv'}); + document.body.appendChild(a); a.click(); a.remove(); + setTimeout(()=>URL.revokeObjectURL(url), 1000); +} + +function filterTable(q){ + q = (q||'').toLowerCase(); + document.querySelectorAll('#patients tbody tr').forEach(tr => { + const text = tr.innerText.toLowerCase(); + tr.style.display = text.includes(q) ? '' : 'none'; + }); +} + + function getCurrentSection() { + let currentSection = null; + let minDistance = Infinity; + if (sections) { + sections.forEach((section) => { + const rect = section.getBoundingClientRect(); + const distance = Math.abs(rect.top - window.innerHeight / 4); + + if (distance < minDistance && rect.top < window.innerHeight) { + minDistance = distance; + currentSection = section.getAttribute("id"); + } + }); + } + + return currentSection; + } + + function updateActiveLink() { + const currentSectionId = getCurrentSection(); + if (currentSectionId) { + removeActiveClasses(); + addActiveClass(currentSectionId); + } + } + + window.addEventListener("scroll", updateActiveLink); + + const portfolioGrid = document.querySelector('#portfolio-grid'); + if (portfolioGrid) { + var iso = new Isotope("#portfolio-grid", { + itemSelector: ".portfolio-item", + layoutMode: "masonry", + }); + + if (iso) { + iso.on("layoutComplete", updateActiveLink); + + imagesLoaded("#portfolio-grid", function () { + iso.layout(); + updateActiveLink(); + }); + } + + var filterButtons = document.querySelectorAll(".filter-button"); + if (filterButtons) { + filterButtons.forEach(function (button) { + button.addEventListener("click", function (e) { + e.preventDefault(); + var filterValue = button.getAttribute("data-filter"); + iso.arrange({ filter: filterValue }); + + filterButtons.forEach(function (btn) { + btn.classList.remove("active"); + }); + button.classList.add("active"); + updateActiveLink(); + }); + }); + } + + updateActiveLink(); + } +}); + +const navbarScrollInit = () => { + var navbar = document.querySelector(".fbs__net-navbar"); + + var scrollTop = window.pageYOffset || document.documentElement.scrollTop; + if (navbar) { + if (scrollTop > 0) { + navbar.classList.add("active"); + } else { + navbar.classList.remove("active"); + } + } +}; + +const navbarInit = () => { + document.querySelectorAll('.dropdown-toggle[href="#"]').forEach(function (el, index) { + el.addEventListener("click", function (event) { + event.stopPropagation(); + }); + }); +}; + +// ======= Marquee ======= +const logoMarqueeInit = () => { + const wrapper = document.querySelector(".logo-wrapper"); + const boxes = gsap.utils.toArray(".logo-item"); + + if (boxes.length > 0) { + const loop = horizontalLoop(boxes, { + paused: false, + repeat: -1, + speed: 0.25, + reversed: false, + }); + + function horizontalLoop(items, config) { + items = gsap.utils.toArray(items); + config = config || {}; + let tl = gsap.timeline({ + repeat: config.repeat, + paused: config.paused, + defaults: { ease: "none" }, + onReverseComplete: () => + tl.totalTime(tl.rawTime() + tl.duration() * 100), + }), + length = items.length, + startX = items[0].offsetLeft, + times = [], + widths = [], + xPercents = [], + curIndex = 0, + pixelsPerSecond = (config.speed || 1) * 100, + snap = + config.snap === false ? (v) => v : gsap.utils.snap(config.snap || 1), // some browsers shift by a pixel to accommodate flex layouts, so for example if width is 20% the first element's width might be 242px, and the next 243px, alternating back and forth. So we snap to 5 percentage points to make things look more natural + totalWidth, + curX, + distanceToStart, + distanceToLoop, + item, + i; + gsap.set(items, { + // convert "x" to "xPercent" to make things responsive, and populate the widths/xPercents Arrays to make lookups faster. + xPercent: (i, el) => { + let w = (widths[i] = parseFloat(gsap.getProperty(el, "width", "px"))); + xPercents[i] = snap( + (parseFloat(gsap.getProperty(el, "x", "px")) / w) * 100 + + gsap.getProperty(el, "xPercent") + ); + return xPercents[i]; + }, + }); + gsap.set(items, { x: 0 }); + totalWidth = + items[length - 1].offsetLeft + + (xPercents[length - 1] / 100) * widths[length - 1] - + startX + + items[length - 1].offsetWidth * + gsap.getProperty(items[length - 1], "scaleX") + + (parseFloat(config.paddingRight) || 0); + for (i = 0; i < length; i++) { + item = items[i]; + curX = (xPercents[i] / 100) * widths[i]; + distanceToStart = item.offsetLeft + curX - startX; + distanceToLoop = + distanceToStart + widths[i] * gsap.getProperty(item, "scaleX"); + tl.to( + item, + { + xPercent: snap(((curX - distanceToLoop) / widths[i]) * 100), + duration: distanceToLoop / pixelsPerSecond, + }, + 0 + ) + .fromTo( + item, + { + xPercent: snap( + ((curX - distanceToLoop + totalWidth) / widths[i]) * 100 + ), + }, + { + xPercent: xPercents[i], + duration: + (curX - distanceToLoop + totalWidth - curX) / pixelsPerSecond, + immediateRender: false, + }, + distanceToLoop / pixelsPerSecond + ) + .add("label" + i, distanceToStart / pixelsPerSecond); + times[i] = distanceToStart / pixelsPerSecond; + } + function toIndex(index, vars) { + vars = vars || {}; + Math.abs(index - curIndex) > length / 2 && + (index += index > curIndex ? -length : length); // always go in the shortest direction + let newIndex = gsap.utils.wrap(0, length, index), + time = times[newIndex]; + if (time > tl.time() !== index > curIndex) { + // if we're wrapping the timeline's playhead, make the proper adjustments + vars.modifiers = { time: gsap.utils.wrap(0, tl.duration()) }; + time += tl.duration() * (index > curIndex ? 1 : -1); + } + curIndex = newIndex; + vars.overwrite = true; + return tl.tweenTo(time, vars); + } + tl.next = (vars) => toIndex(curIndex + 1, vars); + tl.previous = (vars) => toIndex(curIndex - 1, vars); + tl.current = () => curIndex; + tl.toIndex = (index, vars) => toIndex(index, vars); + tl.times = times; + tl.progress(1, true).progress(0, true); // pre-render for performance + if (config.reversed) { + tl.vars.onReverseComplete(); + tl.reverse(); + } + return tl; + } + } +}; + +document.addEventListener("DOMContentLoaded", logoMarqueeInit); + +// ======= Navbar Scroll ======= +document.addEventListener("DOMContentLoaded", function () { + logoMarqueeInit(); + navbarInit(); + window.addEventListener("scroll", navbarScrollInit); +}); + +// ======= Swiper ======= +const swiperInit = () => { + var swiper = new Swiper(".testimonialSwiper", { + slidesPerView: 1, + speed: 700, + spaceBetween: 30, + loop: true, + pagination: { + el: ".swiper-pagination", + clickable: true, + }, + breakpoints: { + 640: { + slidesPerView: 1.5, + spaceBetween: 20, + }, + 768: { + slidesPerView: 2.5, + spaceBetween: 30, + }, + 1024: { + slidesPerView: 2.5, + spaceBetween: 30, + }, + }, + navigation: { + nextEl: ".custom-button-next", + prevEl: ".custom-button-prev", + }, + }); + + const progressCircle = document.querySelector(".autoplay-progress svg"); + const progressContent = document.querySelector(".autoplay-progress span"); + if (progressCircle && progressContent ) { + var swiper2 = new Swiper(".sliderSwiper", { + slidesPerView: 1, + speed: 700, + spaceBetween: 0, + loop: true, + centeredSlides: true, + autoplay: { + delay: 7000, + disableOnInteraction: false + }, + pagination: { + el: ".swiper-pagination", + clickable: true, + }, + navigation: { + nextEl: ".custom-button-next", + prevEl: ".custom-button-prev", + }, + + on: { + autoplayTimeLeft(s, time, progress) { + progressCircle.style.setProperty("--progress", 1 - progress); + progressContent.textContent = `${Math.ceil(time / 1000)}s`; + } + } + }); + } + +}; + +document.addEventListener("DOMContentLoaded", swiperInit); + +// ======= Glightbox ======= +const glightBoxInit = () => { + const lightbox = GLightbox({ + touchNavigation: true, + loop: true, + autoplayVideos: true, + }); +}; +document.addEventListener("DOMContentLoaded", glightBoxInit); + +// ======= BS OffCanvass ======= +const bsOffCanvasInit = () => { + var offcanvasElement = document.getElementById("fbs__net-navbars"); + if (offcanvasElement) { + offcanvasElement.addEventListener("show.bs.offcanvas", function () { + document.body.classList.add("offcanvas-active"); + }); + + offcanvasElement.addEventListener("hidden.bs.offcanvas", function () { + document.body.classList.remove("offcanvas-active"); + }); + } +}; +document.addEventListener("DOMContentLoaded", bsOffCanvasInit); + +// ======= Back To Top ======= +const backToTopInit = () => { + const backToTopButton = document.getElementById("back-to-top"); + if (backToTopButton) { + window.addEventListener("scroll", () => { + if (window.scrollY > 170) { + backToTopButton.classList.add("show"); + } else { + backToTopButton.classList.remove("show"); + } + }); + backToTopButton.addEventListener("click", () => { + window.scrollTo({ + top: 0, + behavior: "smooth", + }); + }); + } +}; + +document.addEventListener("DOMContentLoaded", backToTopInit); + + +// ======= Inline SVG ======= +const inlineSvgInit = () => { + const imgElements = document.querySelectorAll(".js-img-to-inline-svg"); + if (imgElements) { + imgElements.forEach((imgElement) => { + const imgURL = imgElement.getAttribute("src"); + + fetch(imgURL) + .then((response) => response.text()) + .then((svgText) => { + const parser = new DOMParser(); + const svgDocument = parser.parseFromString(svgText, "image/svg+xml"); + const svgElement = svgDocument.documentElement; + + Array.from(imgElement.attributes).forEach((attr) => { + if (attr.name !== "class") { + svgElement.setAttribute(attr.name, attr.value); + } else { + const classes = attr.value + .split(" ") + .filter((className) => className !== "js-img-to-inline-svg"); + if (classes.length > 0) { + svgElement.setAttribute("class", classes.join(" ")); + } + } + }); + + imgElement.replaceWith(svgElement); + }) + .catch((error) => console.error("Error fetching SVG:", error)); + }); + } +}; + +document.addEventListener("DOMContentLoaded", inlineSvgInit); + +// ======= AOS ======= +const aosInit = () => { + AOS.init({ + duration: 800, + easing: 'slide', + once: true + }); +} +document.addEventListener("DOMContentLoaded", aosInit); + +// ======= PureCounter ======= +const pureCounterInit = () => { + new PureCounter({ + selector: ".purecounter", + }); +} +document.addEventListener("DOMContentLoaded", pureCounterInit); + +// ======= Disable Click Navbar Dropdown ======= +const addHoverEvents = (dropdown) => { + const dropdownToggle = dropdown.querySelector('.dropdown-toggle'); + + const preventClick = (event) => event.preventDefault(); + const showDropdown = () => { + dropdown.classList.add('show'); + dropdownToggle.setAttribute('aria-expanded', 'true'); + const dropdownMenu = dropdown.querySelector('.dropdown-menu'); + dropdownMenu.classList.add('show'); + }; + const hideDropdown = () => { + dropdown.classList.remove('show'); + dropdownToggle.setAttribute('aria-expanded', 'false'); + const dropdownMenu = dropdown.querySelector('.dropdown-menu'); + dropdownMenu.classList.remove('show'); + }; + + // Disable the click event for toggling the dropdown + dropdownToggle.addEventListener('click', preventClick); + + // Open dropdown on hover + dropdown.addEventListener('mouseover', showDropdown); + + // Close dropdown when mouse leaves + dropdown.addEventListener('mouseleave', hideDropdown); + + // Store references to the event listeners for later removal + dropdown.__events = { preventClick, showDropdown, hideDropdown }; +}; + +const removeHoverEvents = (dropdown) => { + const dropdownToggle = dropdown.querySelector('.dropdown-toggle'); + const { preventClick, showDropdown, hideDropdown } = dropdown.__events || {}; + + if (preventClick) { + // Remove the event listeners + dropdownToggle.removeEventListener('click', preventClick); + dropdown.removeEventListener('mouseover', showDropdown); + dropdown.removeEventListener('mouseleave', hideDropdown); + + // Remove the reference to the stored events + delete dropdown.__events; + } +}; + +const handleNavbarEvents = () => { + const dropdowns = document.querySelectorAll('.navbar .dropdown'); + const dropstarts = document.querySelectorAll('.navbar .dropstart'); + const dropends = document.querySelectorAll('.navbar .dropend'); + + if (window.innerWidth >= 992) { + + // Add hover events to dropdowns + dropdowns.forEach(addHoverEvents); + dropstarts.forEach(addHoverEvents); + dropends.forEach(addHoverEvents); + } else { + + // Remove hover events from dropdowns + dropdowns.forEach(removeHoverEvents); + dropstarts.forEach(removeHoverEvents); + dropends.forEach(removeHoverEvents); + } +}; + +// Function to handle resizing +const handleResize = () => { + const dropdowns = document.querySelectorAll('.navbar .dropdown'); + const dropstarts = document.querySelectorAll('.navbar .dropstart'); + const dropends = document.querySelectorAll('.navbar .dropend'); + + // Remove hover events before rechecking window size + dropdowns.forEach(removeHoverEvents); + dropstarts.forEach(removeHoverEvents); + dropends.forEach(removeHoverEvents); + + // Re-apply hover events based on window size + handleNavbarEvents(); +}; + +// Call the function on resize event and initially +window.addEventListener('resize', handleResize); +handleNavbarEvents(); + + + +// ======= Coming Soon Countdown ======= +const countdownInit = () => { + + // Get the current year + const currentYear = new Date().getFullYear(); + const nextYear = currentYear + 1; + const launchDate = new Date(`December 31, ${nextYear} 23:59:59`).getTime(); + + // Change this "December 31, 2024 23:59:59" to your your website launch date + // const launchDate = new Date("December 31, 2024 23:59:59").getTime(); + + + const x = setInterval(function () { + + const now = new Date().getTime(); + + const distance = launchDate - now; + + const days = Math.floor(distance / (1000 * 60 * 60 * 24)); + const hours = Math.floor((distance % (1000 * 60 * 60 * 24)) / (1000 * 60 * 60)); + const minutes = Math.floor((distance % (1000 * 60 * 60)) / (1000 * 60)); + const seconds = Math.floor((distance % (1000 * 60)) / 1000); + + // Output the result in an element with id + const daysEl = document.getElementById("days"); + const hoursEl = document.getElementById("hours"); + const minutesEl = document.getElementById("minutes"); + const secondsEl = document.getElementById("seconds"); + if (daysEl) { + daysEl.innerText = days; + } + if (hoursEl) { + hoursEl.innerText = hours; + } + if (minutesEl) { + minutesEl.innerText = minutes; + } + if (secondsEl) { + secondsEl.innerText = seconds; + } + + // If the count down is finished, write some text + if (distance < 0) { + clearInterval(x); + document.querySelector(".countdown").innerText = "Launched!"; + } + }, 1000); +}; +document.addEventListener('DOMContentLoaded', countdownInit); + +window.openNewPatient = openNewPatient; +window.exportCSV = exportCSV; +window.filterTable = filterTable; \ No newline at end of file diff --git a/Website/assets/vendors/.DS_Store b/Website/assets/vendors/.DS_Store new file mode 100644 index 0000000..ed5d414 Binary files /dev/null and b/Website/assets/vendors/.DS_Store differ diff --git a/Website/assets/vendors/PHPMailer/index.html b/Website/assets/vendors/PHPMailer/index.html new file mode 100644 index 0000000..91535fa --- /dev/null +++ b/Website/assets/vendors/PHPMailer/index.html @@ -0,0 +1,11 @@ + + + + + + Silence is golden. + + + + + \ No newline at end of file diff --git a/Website/assets/vendors/PHPMailer/src/DSNConfigurator.php b/Website/assets/vendors/PHPMailer/src/DSNConfigurator.php new file mode 100644 index 0000000..7058c1f --- /dev/null +++ b/Website/assets/vendors/PHPMailer/src/DSNConfigurator.php @@ -0,0 +1,245 @@ + + * @author Jim Jagielski (jimjag) + * @author Andy Prevost (codeworxtech) + * @author Brent R. Matzelle (original founder) + * @copyright 2012 - 2023 Marcus Bointon + * @copyright 2010 - 2012 Jim Jagielski + * @copyright 2004 - 2009 Andy Prevost + * @license https://www.gnu.org/licenses/old-licenses/lgpl-2.1.html GNU Lesser General Public License + * @note This program is distributed in the hope that it will be useful - WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. + */ + +namespace PHPMailer\PHPMailer; + +/** + * Configure PHPMailer with DSN string. + * + * @see https://en.wikipedia.org/wiki/Data_source_name + * + * @author Oleg Voronkovich + */ +class DSNConfigurator +{ + /** + * Create new PHPMailer instance configured by DSN. + * + * @param string $dsn DSN + * @param bool $exceptions Should we throw external exceptions? + * + * @return PHPMailer + */ + public static function mailer($dsn, $exceptions = null) + { + static $configurator = null; + + if (null === $configurator) { + $configurator = new DSNConfigurator(); + } + + return $configurator->configure(new PHPMailer($exceptions), $dsn); + } + + /** + * Configure PHPMailer instance with DSN string. + * + * @param PHPMailer $mailer PHPMailer instance + * @param string $dsn DSN + * + * @return PHPMailer + */ + public function configure(PHPMailer $mailer, $dsn) + { + $config = $this->parseDSN($dsn); + + $this->applyConfig($mailer, $config); + + return $mailer; + } + + /** + * Parse DSN string. + * + * @param string $dsn DSN + * + * @throws Exception If DSN is malformed + * + * @return array Configuration + */ + private function parseDSN($dsn) + { + $config = $this->parseUrl($dsn); + + if (false === $config || !isset($config['scheme']) || !isset($config['host'])) { + throw new Exception('Malformed DSN'); + } + + if (isset($config['query'])) { + parse_str($config['query'], $config['query']); + } + + return $config; + } + + /** + * Apply configuration to mailer. + * + * @param PHPMailer $mailer PHPMailer instance + * @param array $config Configuration + * + * @throws Exception If scheme is invalid + */ + private function applyConfig(PHPMailer $mailer, $config) + { + switch ($config['scheme']) { + case 'mail': + $mailer->isMail(); + break; + case 'sendmail': + $mailer->isSendmail(); + break; + case 'qmail': + $mailer->isQmail(); + break; + case 'smtp': + case 'smtps': + $mailer->isSMTP(); + $this->configureSMTP($mailer, $config); + break; + default: + throw new Exception( + sprintf( + 'Invalid scheme: "%s". Allowed values: "mail", "sendmail", "qmail", "smtp", "smtps".', + $config['scheme'] + ) + ); + } + + if (isset($config['query'])) { + $this->configureOptions($mailer, $config['query']); + } + } + + /** + * Configure SMTP. + * + * @param PHPMailer $mailer PHPMailer instance + * @param array $config Configuration + */ + private function configureSMTP($mailer, $config) + { + $isSMTPS = 'smtps' === $config['scheme']; + + if ($isSMTPS) { + $mailer->SMTPSecure = PHPMailer::ENCRYPTION_STARTTLS; + } + + $mailer->Host = $config['host']; + + if (isset($config['port'])) { + $mailer->Port = $config['port']; + } elseif ($isSMTPS) { + $mailer->Port = SMTP::DEFAULT_SECURE_PORT; + } + + $mailer->SMTPAuth = isset($config['user']) || isset($config['pass']); + + if (isset($config['user'])) { + $mailer->Username = $config['user']; + } + + if (isset($config['pass'])) { + $mailer->Password = $config['pass']; + } + } + + /** + * Configure options. + * + * @param PHPMailer $mailer PHPMailer instance + * @param array $options Options + * + * @throws Exception If option is unknown + */ + private function configureOptions(PHPMailer $mailer, $options) + { + $allowedOptions = get_object_vars($mailer); + + unset($allowedOptions['Mailer']); + unset($allowedOptions['SMTPAuth']); + unset($allowedOptions['Username']); + unset($allowedOptions['Password']); + unset($allowedOptions['Hostname']); + unset($allowedOptions['Port']); + unset($allowedOptions['ErrorInfo']); + + $allowedOptions = \array_keys($allowedOptions); + + foreach ($options as $key => $value) { + if (!in_array($key, $allowedOptions)) { + throw new Exception( + sprintf( + 'Unknown option: "%s". Allowed values: "%s"', + $key, + implode('", "', $allowedOptions) + ) + ); + } + + switch ($key) { + case 'AllowEmpty': + case 'SMTPAutoTLS': + case 'SMTPKeepAlive': + case 'SingleTo': + case 'UseSendmailOptions': + case 'do_verp': + case 'DKIM_copyHeaderFields': + $mailer->$key = (bool) $value; + break; + case 'Priority': + case 'SMTPDebug': + case 'WordWrap': + $mailer->$key = (int) $value; + break; + default: + $mailer->$key = $value; + break; + } + } + } + + /** + * Parse a URL. + * Wrapper for the built-in parse_url function to work around a bug in PHP 5.5. + * + * @param string $url URL + * + * @return array|false + */ + protected function parseUrl($url) + { + if (\PHP_VERSION_ID >= 50600 || false === strpos($url, '?')) { + return parse_url($url); + } + + $chunks = explode('?', $url); + if (is_array($chunks)) { + $result = parse_url($chunks[0]); + if (is_array($result)) { + $result['query'] = $chunks[1]; + } + return $result; + } + + return false; + } +} diff --git a/Website/assets/vendors/PHPMailer/src/Exception.php b/Website/assets/vendors/PHPMailer/src/Exception.php new file mode 100644 index 0000000..09c1a2c --- /dev/null +++ b/Website/assets/vendors/PHPMailer/src/Exception.php @@ -0,0 +1,40 @@ + + * @author Jim Jagielski (jimjag) + * @author Andy Prevost (codeworxtech) + * @author Brent R. Matzelle (original founder) + * @copyright 2012 - 2020 Marcus Bointon + * @copyright 2010 - 2012 Jim Jagielski + * @copyright 2004 - 2009 Andy Prevost + * @license https://www.gnu.org/licenses/old-licenses/lgpl-2.1.html GNU Lesser General Public License + * @note This program is distributed in the hope that it will be useful - WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. + */ + +namespace PHPMailer\PHPMailer; + +/** + * PHPMailer exception handler. + * + * @author Marcus Bointon + */ +class Exception extends \Exception +{ + /** + * Prettify error message output. + * + * @return string + */ + public function errorMessage() + { + return '' . htmlspecialchars($this->getMessage(), ENT_COMPAT | ENT_HTML401) . "
\n"; + } +} diff --git a/Website/assets/vendors/PHPMailer/src/OAuth.php b/Website/assets/vendors/PHPMailer/src/OAuth.php new file mode 100644 index 0000000..a7e9588 --- /dev/null +++ b/Website/assets/vendors/PHPMailer/src/OAuth.php @@ -0,0 +1,139 @@ + + * @author Jim Jagielski (jimjag) + * @author Andy Prevost (codeworxtech) + * @author Brent R. Matzelle (original founder) + * @copyright 2012 - 2020 Marcus Bointon + * @copyright 2010 - 2012 Jim Jagielski + * @copyright 2004 - 2009 Andy Prevost + * @license https://www.gnu.org/licenses/old-licenses/lgpl-2.1.html GNU Lesser General Public License + * @note This program is distributed in the hope that it will be useful - WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. + */ + +namespace PHPMailer\PHPMailer; + +use League\OAuth2\Client\Grant\RefreshToken; +use League\OAuth2\Client\Provider\AbstractProvider; +use League\OAuth2\Client\Token\AccessToken; + +/** + * OAuth - OAuth2 authentication wrapper class. + * Uses the oauth2-client package from the League of Extraordinary Packages. + * + * @see https://oauth2-client.thephpleague.com + * + * @author Marcus Bointon (Synchro/coolbru) + */ +class OAuth implements OAuthTokenProvider +{ + /** + * An instance of the League OAuth Client Provider. + * + * @var AbstractProvider + */ + protected $provider; + + /** + * The current OAuth access token. + * + * @var AccessToken + */ + protected $oauthToken; + + /** + * The user's email address, usually used as the login ID + * and also the from address when sending email. + * + * @var string + */ + protected $oauthUserEmail = ''; + + /** + * The client secret, generated in the app definition of the service you're connecting to. + * + * @var string + */ + protected $oauthClientSecret = ''; + + /** + * The client ID, generated in the app definition of the service you're connecting to. + * + * @var string + */ + protected $oauthClientId = ''; + + /** + * The refresh token, used to obtain new AccessTokens. + * + * @var string + */ + protected $oauthRefreshToken = ''; + + /** + * OAuth constructor. + * + * @param array $options Associative array containing + * `provider`, `userName`, `clientSecret`, `clientId` and `refreshToken` elements + */ + public function __construct($options) + { + $this->provider = $options['provider']; + $this->oauthUserEmail = $options['userName']; + $this->oauthClientSecret = $options['clientSecret']; + $this->oauthClientId = $options['clientId']; + $this->oauthRefreshToken = $options['refreshToken']; + } + + /** + * Get a new RefreshToken. + * + * @return RefreshToken + */ + protected function getGrant() + { + return new RefreshToken(); + } + + /** + * Get a new AccessToken. + * + * @return AccessToken + */ + protected function getToken() + { + return $this->provider->getAccessToken( + $this->getGrant(), + ['refresh_token' => $this->oauthRefreshToken] + ); + } + + /** + * Generate a base64-encoded OAuth token. + * + * @return string + */ + public function getOauth64() + { + //Get a new token if it's not available or has expired + if (null === $this->oauthToken || $this->oauthToken->hasExpired()) { + $this->oauthToken = $this->getToken(); + } + + return base64_encode( + 'user=' . + $this->oauthUserEmail . + "\001auth=Bearer " . + $this->oauthToken . + "\001\001" + ); + } +} diff --git a/Website/assets/vendors/PHPMailer/src/OAuthTokenProvider.php b/Website/assets/vendors/PHPMailer/src/OAuthTokenProvider.php new file mode 100644 index 0000000..cbda1a1 --- /dev/null +++ b/Website/assets/vendors/PHPMailer/src/OAuthTokenProvider.php @@ -0,0 +1,44 @@ + + * @author Jim Jagielski (jimjag) + * @author Andy Prevost (codeworxtech) + * @author Brent R. Matzelle (original founder) + * @copyright 2012 - 2020 Marcus Bointon + * @copyright 2010 - 2012 Jim Jagielski + * @copyright 2004 - 2009 Andy Prevost + * @license https://www.gnu.org/licenses/old-licenses/lgpl-2.1.html GNU Lesser General Public License + * @note This program is distributed in the hope that it will be useful - WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. + */ + +namespace PHPMailer\PHPMailer; + +/** + * OAuthTokenProvider - OAuth2 token provider interface. + * Provides base64 encoded OAuth2 auth strings for SMTP authentication. + * + * @see OAuth + * @see SMTP::authenticate() + * + * @author Peter Scopes (pdscopes) + * @author Marcus Bointon (Synchro/coolbru) + */ +interface OAuthTokenProvider +{ + /** + * Generate a base64-encoded OAuth token ensuring that the access token has not expired. + * The string to be base 64 encoded should be in the form: + * "user=\001auth=Bearer \001\001" + * + * @return string + */ + public function getOauth64(); +} diff --git a/Website/assets/vendors/PHPMailer/src/PHPMailer.php b/Website/assets/vendors/PHPMailer/src/PHPMailer.php new file mode 100644 index 0000000..11a0a68 --- /dev/null +++ b/Website/assets/vendors/PHPMailer/src/PHPMailer.php @@ -0,0 +1,5248 @@ + + * @author Jim Jagielski (jimjag) + * @author Andy Prevost (codeworxtech) + * @author Brent R. Matzelle (original founder) + * @copyright 2012 - 2020 Marcus Bointon + * @copyright 2010 - 2012 Jim Jagielski + * @copyright 2004 - 2009 Andy Prevost + * @license https://www.gnu.org/licenses/old-licenses/lgpl-2.1.html GNU Lesser General Public License + * @note This program is distributed in the hope that it will be useful - WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. + */ + +namespace PHPMailer\PHPMailer; + +/** + * PHPMailer - PHP email creation and transport class. + * + * @author Marcus Bointon (Synchro/coolbru) + * @author Jim Jagielski (jimjag) + * @author Andy Prevost (codeworxtech) + * @author Brent R. Matzelle (original founder) + */ +class PHPMailer +{ + const CHARSET_ASCII = 'us-ascii'; + const CHARSET_ISO88591 = 'iso-8859-1'; + const CHARSET_UTF8 = 'utf-8'; + + const CONTENT_TYPE_PLAINTEXT = 'text/plain'; + const CONTENT_TYPE_TEXT_CALENDAR = 'text/calendar'; + const CONTENT_TYPE_TEXT_HTML = 'text/html'; + const CONTENT_TYPE_MULTIPART_ALTERNATIVE = 'multipart/alternative'; + const CONTENT_TYPE_MULTIPART_MIXED = 'multipart/mixed'; + const CONTENT_TYPE_MULTIPART_RELATED = 'multipart/related'; + + const ENCODING_7BIT = '7bit'; + const ENCODING_8BIT = '8bit'; + const ENCODING_BASE64 = 'base64'; + const ENCODING_BINARY = 'binary'; + const ENCODING_QUOTED_PRINTABLE = 'quoted-printable'; + + const ENCRYPTION_STARTTLS = 'tls'; + const ENCRYPTION_SMTPS = 'ssl'; + + const ICAL_METHOD_REQUEST = 'REQUEST'; + const ICAL_METHOD_PUBLISH = 'PUBLISH'; + const ICAL_METHOD_REPLY = 'REPLY'; + const ICAL_METHOD_ADD = 'ADD'; + const ICAL_METHOD_CANCEL = 'CANCEL'; + const ICAL_METHOD_REFRESH = 'REFRESH'; + const ICAL_METHOD_COUNTER = 'COUNTER'; + const ICAL_METHOD_DECLINECOUNTER = 'DECLINECOUNTER'; + + /** + * Email priority. + * Options: null (default), 1 = High, 3 = Normal, 5 = low. + * When null, the header is not set at all. + * + * @var int|null + */ + public $Priority; + + /** + * The character set of the message. + * + * @var string + */ + public $CharSet = self::CHARSET_ISO88591; + + /** + * The MIME Content-type of the message. + * + * @var string + */ + public $ContentType = self::CONTENT_TYPE_PLAINTEXT; + + /** + * The message encoding. + * Options: "8bit", "7bit", "binary", "base64", and "quoted-printable". + * + * @var string + */ + public $Encoding = self::ENCODING_8BIT; + + /** + * Holds the most recent mailer error message. + * + * @var string + */ + public $ErrorInfo = ''; + + /** + * The From email address for the message. + * + * @var string + */ + public $From = ''; + + /** + * The From name of the message. + * + * @var string + */ + public $FromName = ''; + + /** + * The envelope sender of the message. + * This will usually be turned into a Return-Path header by the receiver, + * and is the address that bounces will be sent to. + * If not empty, will be passed via `-f` to sendmail or as the 'MAIL FROM' value over SMTP. + * + * @var string + */ + public $Sender = ''; + + /** + * The Subject of the message. + * + * @var string + */ + public $Subject = ''; + + /** + * An HTML or plain text message body. + * If HTML then call isHTML(true). + * + * @var string + */ + public $Body = ''; + + /** + * The plain-text message body. + * This body can be read by mail clients that do not have HTML email + * capability such as mutt & Eudora. + * Clients that can read HTML will view the normal Body. + * + * @var string + */ + public $AltBody = ''; + + /** + * An iCal message part body. + * Only supported in simple alt or alt_inline message types + * To generate iCal event structures, use classes like EasyPeasyICS or iCalcreator. + * + * @see https://kigkonsult.se/iCalcreator/ + * + * @var string + */ + public $Ical = ''; + + /** + * Value-array of "method" in Contenttype header "text/calendar" + * + * @var string[] + */ + protected static $IcalMethods = [ + self::ICAL_METHOD_REQUEST, + self::ICAL_METHOD_PUBLISH, + self::ICAL_METHOD_REPLY, + self::ICAL_METHOD_ADD, + self::ICAL_METHOD_CANCEL, + self::ICAL_METHOD_REFRESH, + self::ICAL_METHOD_COUNTER, + self::ICAL_METHOD_DECLINECOUNTER, + ]; + + /** + * The complete compiled MIME message body. + * + * @var string + */ + protected $MIMEBody = ''; + + /** + * The complete compiled MIME message headers. + * + * @var string + */ + protected $MIMEHeader = ''; + + /** + * Extra headers that createHeader() doesn't fold in. + * + * @var string + */ + protected $mailHeader = ''; + + /** + * Word-wrap the message body to this number of chars. + * Set to 0 to not wrap. A useful value here is 78, for RFC2822 section 2.1.1 compliance. + * + * @see static::STD_LINE_LENGTH + * + * @var int + */ + public $WordWrap = 0; + + /** + * Which method to use to send mail. + * Options: "mail", "sendmail", or "smtp". + * + * @var string + */ + public $Mailer = 'mail'; + + /** + * The path to the sendmail program. + * + * @var string + */ + public $Sendmail = '/usr/sbin/sendmail'; + + /** + * Whether mail() uses a fully sendmail-compatible MTA. + * One which supports sendmail's "-oi -f" options. + * + * @var bool + */ + public $UseSendmailOptions = true; + + /** + * The email address that a reading confirmation should be sent to, also known as read receipt. + * + * @var string + */ + public $ConfirmReadingTo = ''; + + /** + * The hostname to use in the Message-ID header and as default HELO string. + * If empty, PHPMailer attempts to find one with, in order, + * $_SERVER['SERVER_NAME'], gethostname(), php_uname('n'), or the value + * 'localhost.localdomain'. + * + * @see PHPMailer::$Helo + * + * @var string + */ + public $Hostname = ''; + + /** + * An ID to be used in the Message-ID header. + * If empty, a unique id will be generated. + * You can set your own, but it must be in the format "", + * as defined in RFC5322 section 3.6.4 or it will be ignored. + * + * @see https://tools.ietf.org/html/rfc5322#section-3.6.4 + * + * @var string + */ + public $MessageID = ''; + + /** + * The message Date to be used in the Date header. + * If empty, the current date will be added. + * + * @var string + */ + public $MessageDate = ''; + + /** + * SMTP hosts. + * Either a single hostname or multiple semicolon-delimited hostnames. + * You can also specify a different port + * for each host by using this format: [hostname:port] + * (e.g. "smtp1.example.com:25;smtp2.example.com"). + * You can also specify encryption type, for example: + * (e.g. "tls://smtp1.example.com:587;ssl://smtp2.example.com:465"). + * Hosts will be tried in order. + * + * @var string + */ + public $Host = 'localhost'; + + /** + * The default SMTP server port. + * + * @var int + */ + public $Port = 25; + + /** + * The SMTP HELO/EHLO name used for the SMTP connection. + * Default is $Hostname. If $Hostname is empty, PHPMailer attempts to find + * one with the same method described above for $Hostname. + * + * @see PHPMailer::$Hostname + * + * @var string + */ + public $Helo = ''; + + /** + * What kind of encryption to use on the SMTP connection. + * Options: '', static::ENCRYPTION_STARTTLS, or static::ENCRYPTION_SMTPS. + * + * @var string + */ + public $SMTPSecure = ''; + + /** + * Whether to enable TLS encryption automatically if a server supports it, + * even if `SMTPSecure` is not set to 'tls'. + * Be aware that in PHP >= 5.6 this requires that the server's certificates are valid. + * + * @var bool + */ + public $SMTPAutoTLS = true; + + /** + * Whether to use SMTP authentication. + * Uses the Username and Password properties. + * + * @see PHPMailer::$Username + * @see PHPMailer::$Password + * + * @var bool + */ + public $SMTPAuth = false; + + /** + * Options array passed to stream_context_create when connecting via SMTP. + * + * @var array + */ + public $SMTPOptions = []; + + /** + * SMTP username. + * + * @var string + */ + public $Username = ''; + + /** + * SMTP password. + * + * @var string + */ + public $Password = ''; + + /** + * SMTP authentication type. Options are CRAM-MD5, LOGIN, PLAIN, XOAUTH2. + * If not specified, the first one from that list that the server supports will be selected. + * + * @var string + */ + public $AuthType = ''; + + /** + * SMTP SMTPXClient command attibutes + * + * @var array + */ + protected $SMTPXClient = []; + + /** + * An implementation of the PHPMailer OAuthTokenProvider interface. + * + * @var OAuthTokenProvider + */ + protected $oauth; + + /** + * The SMTP server timeout in seconds. + * Default of 5 minutes (300sec) is from RFC2821 section 4.5.3.2. + * + * @var int + */ + public $Timeout = 300; + + /** + * Comma separated list of DSN notifications + * 'NEVER' under no circumstances a DSN must be returned to the sender. + * If you use NEVER all other notifications will be ignored. + * 'SUCCESS' will notify you when your mail has arrived at its destination. + * 'FAILURE' will arrive if an error occurred during delivery. + * 'DELAY' will notify you if there is an unusual delay in delivery, but the actual + * delivery's outcome (success or failure) is not yet decided. + * + * @see https://tools.ietf.org/html/rfc3461 See section 4.1 for more information about NOTIFY + */ + public $dsn = ''; + + /** + * SMTP class debug output mode. + * Debug output level. + * Options: + * @see SMTP::DEBUG_OFF: No output + * @see SMTP::DEBUG_CLIENT: Client messages + * @see SMTP::DEBUG_SERVER: Client and server messages + * @see SMTP::DEBUG_CONNECTION: As SERVER plus connection status + * @see SMTP::DEBUG_LOWLEVEL: Noisy, low-level data output, rarely needed + * + * @see SMTP::$do_debug + * + * @var int + */ + public $SMTPDebug = 0; + + /** + * How to handle debug output. + * Options: + * * `echo` Output plain-text as-is, appropriate for CLI + * * `html` Output escaped, line breaks converted to `
`, appropriate for browser output + * * `error_log` Output to error log as configured in php.ini + * By default PHPMailer will use `echo` if run from a `cli` or `cli-server` SAPI, `html` otherwise. + * Alternatively, you can provide a callable expecting two params: a message string and the debug level: + * + * ```php + * $mail->Debugoutput = function($str, $level) {echo "debug level $level; message: $str";}; + * ``` + * + * Alternatively, you can pass in an instance of a PSR-3 compatible logger, though only `debug` + * level output is used: + * + * ```php + * $mail->Debugoutput = new myPsr3Logger; + * ``` + * + * @see SMTP::$Debugoutput + * + * @var string|callable|\Psr\Log\LoggerInterface + */ + public $Debugoutput = 'echo'; + + /** + * Whether to keep the SMTP connection open after each message. + * If this is set to true then the connection will remain open after a send, + * and closing the connection will require an explicit call to smtpClose(). + * It's a good idea to use this if you are sending multiple messages as it reduces overhead. + * See the mailing list example for how to use it. + * + * @var bool + */ + public $SMTPKeepAlive = false; + + /** + * Whether to split multiple to addresses into multiple messages + * or send them all in one message. + * Only supported in `mail` and `sendmail` transports, not in SMTP. + * + * @var bool + * + * @deprecated 6.0.0 PHPMailer isn't a mailing list manager! + */ + public $SingleTo = false; + + /** + * Storage for addresses when SingleTo is enabled. + * + * @var array + */ + protected $SingleToArray = []; + + /** + * Whether to generate VERP addresses on send. + * Only applicable when sending via SMTP. + * + * @see https://en.wikipedia.org/wiki/Variable_envelope_return_path + * @see https://www.postfix.org/VERP_README.html Postfix VERP info + * + * @var bool + */ + public $do_verp = false; + + /** + * Whether to allow sending messages with an empty body. + * + * @var bool + */ + public $AllowEmpty = false; + + /** + * DKIM selector. + * + * @var string + */ + public $DKIM_selector = ''; + + /** + * DKIM Identity. + * Usually the email address used as the source of the email. + * + * @var string + */ + public $DKIM_identity = ''; + + /** + * DKIM passphrase. + * Used if your key is encrypted. + * + * @var string + */ + public $DKIM_passphrase = ''; + + /** + * DKIM signing domain name. + * + * @example 'example.com' + * + * @var string + */ + public $DKIM_domain = ''; + + /** + * DKIM Copy header field values for diagnostic use. + * + * @var bool + */ + public $DKIM_copyHeaderFields = true; + + /** + * DKIM Extra signing headers. + * + * @example ['List-Unsubscribe', 'List-Help'] + * + * @var array + */ + public $DKIM_extraHeaders = []; + + /** + * DKIM private key file path. + * + * @var string + */ + public $DKIM_private = ''; + + /** + * DKIM private key string. + * + * If set, takes precedence over `$DKIM_private`. + * + * @var string + */ + public $DKIM_private_string = ''; + + /** + * Callback Action function name. + * + * The function that handles the result of the send email action. + * It is called out by send() for each email sent. + * + * Value can be any php callable: https://www.php.net/is_callable + * + * Parameters: + * bool $result result of the send action + * array $to email addresses of the recipients + * array $cc cc email addresses + * array $bcc bcc email addresses + * string $subject the subject + * string $body the email body + * string $from email address of sender + * string $extra extra information of possible use + * "smtp_transaction_id' => last smtp transaction id + * + * @var string + */ + public $action_function = ''; + + /** + * What to put in the X-Mailer header. + * Options: An empty string for PHPMailer default, whitespace/null for none, or a string to use. + * + * @var string|null + */ + public $XMailer = ''; + + /** + * Which validator to use by default when validating email addresses. + * May be a callable to inject your own validator, but there are several built-in validators. + * The default validator uses PHP's FILTER_VALIDATE_EMAIL filter_var option. + * + * @see PHPMailer::validateAddress() + * + * @var string|callable + */ + public static $validator = 'php'; + + /** + * An instance of the SMTP sender class. + * + * @var SMTP + */ + protected $smtp; + + /** + * The array of 'to' names and addresses. + * + * @var array + */ + protected $to = []; + + /** + * The array of 'cc' names and addresses. + * + * @var array + */ + protected $cc = []; + + /** + * The array of 'bcc' names and addresses. + * + * @var array + */ + protected $bcc = []; + + /** + * The array of reply-to names and addresses. + * + * @var array + */ + protected $ReplyTo = []; + + /** + * An array of all kinds of addresses. + * Includes all of $to, $cc, $bcc. + * + * @see PHPMailer::$to + * @see PHPMailer::$cc + * @see PHPMailer::$bcc + * + * @var array + */ + protected $all_recipients = []; + + /** + * An array of names and addresses queued for validation. + * In send(), valid and non duplicate entries are moved to $all_recipients + * and one of $to, $cc, or $bcc. + * This array is used only for addresses with IDN. + * + * @see PHPMailer::$to + * @see PHPMailer::$cc + * @see PHPMailer::$bcc + * @see PHPMailer::$all_recipients + * + * @var array + */ + protected $RecipientsQueue = []; + + /** + * An array of reply-to names and addresses queued for validation. + * In send(), valid and non duplicate entries are moved to $ReplyTo. + * This array is used only for addresses with IDN. + * + * @see PHPMailer::$ReplyTo + * + * @var array + */ + protected $ReplyToQueue = []; + + /** + * The array of attachments. + * + * @var array + */ + protected $attachment = []; + + /** + * The array of custom headers. + * + * @var array + */ + protected $CustomHeader = []; + + /** + * The most recent Message-ID (including angular brackets). + * + * @var string + */ + protected $lastMessageID = ''; + + /** + * The message's MIME type. + * + * @var string + */ + protected $message_type = ''; + + /** + * The array of MIME boundary strings. + * + * @var array + */ + protected $boundary = []; + + /** + * The array of available text strings for the current language. + * + * @var array + */ + protected $language = []; + + /** + * The number of errors encountered. + * + * @var int + */ + protected $error_count = 0; + + /** + * The S/MIME certificate file path. + * + * @var string + */ + protected $sign_cert_file = ''; + + /** + * The S/MIME key file path. + * + * @var string + */ + protected $sign_key_file = ''; + + /** + * The optional S/MIME extra certificates ("CA Chain") file path. + * + * @var string + */ + protected $sign_extracerts_file = ''; + + /** + * The S/MIME password for the key. + * Used only if the key is encrypted. + * + * @var string + */ + protected $sign_key_pass = ''; + + /** + * Whether to throw exceptions for errors. + * + * @var bool + */ + protected $exceptions = false; + + /** + * Unique ID used for message ID and boundaries. + * + * @var string + */ + protected $uniqueid = ''; + + /** + * The PHPMailer Version number. + * + * @var string + */ + const VERSION = '6.9.1'; + + /** + * Error severity: message only, continue processing. + * + * @var int + */ + const STOP_MESSAGE = 0; + + /** + * Error severity: message, likely ok to continue processing. + * + * @var int + */ + const STOP_CONTINUE = 1; + + /** + * Error severity: message, plus full stop, critical error reached. + * + * @var int + */ + const STOP_CRITICAL = 2; + + /** + * The SMTP standard CRLF line break. + * If you want to change line break format, change static::$LE, not this. + */ + const CRLF = "\r\n"; + + /** + * "Folding White Space" a white space string used for line folding. + */ + const FWS = ' '; + + /** + * SMTP RFC standard line ending; Carriage Return, Line Feed. + * + * @var string + */ + protected static $LE = self::CRLF; + + /** + * The maximum line length supported by mail(). + * + * Background: mail() will sometimes corrupt messages + * with headers longer than 65 chars, see #818. + * + * @var int + */ + const MAIL_MAX_LINE_LENGTH = 63; + + /** + * The maximum line length allowed by RFC 2822 section 2.1.1. + * + * @var int + */ + const MAX_LINE_LENGTH = 998; + + /** + * The lower maximum line length allowed by RFC 2822 section 2.1.1. + * This length does NOT include the line break + * 76 means that lines will be 77 or 78 chars depending on whether + * the line break format is LF or CRLF; both are valid. + * + * @var int + */ + const STD_LINE_LENGTH = 76; + + /** + * Constructor. + * + * @param bool $exceptions Should we throw external exceptions? + */ + public function __construct($exceptions = null) + { + if (null !== $exceptions) { + $this->exceptions = (bool) $exceptions; + } + //Pick an appropriate debug output format automatically + $this->Debugoutput = (strpos(PHP_SAPI, 'cli') !== false ? 'echo' : 'html'); + } + + /** + * Destructor. + */ + public function __destruct() + { + //Close any open SMTP connection nicely + $this->smtpClose(); + } + + /** + * Call mail() in a safe_mode-aware fashion. + * Also, unless sendmail_path points to sendmail (or something that + * claims to be sendmail), don't pass params (not a perfect fix, + * but it will do). + * + * @param string $to To + * @param string $subject Subject + * @param string $body Message Body + * @param string $header Additional Header(s) + * @param string|null $params Params + * + * @return bool + */ + private function mailPassthru($to, $subject, $body, $header, $params) + { + //Check overloading of mail function to avoid double-encoding + if ((int)ini_get('mbstring.func_overload') & 1) { + $subject = $this->secureHeader($subject); + } else { + $subject = $this->encodeHeader($this->secureHeader($subject)); + } + //Calling mail() with null params breaks + $this->edebug('Sending with mail()'); + $this->edebug('Sendmail path: ' . ini_get('sendmail_path')); + $this->edebug("Envelope sender: {$this->Sender}"); + $this->edebug("To: {$to}"); + $this->edebug("Subject: {$subject}"); + $this->edebug("Headers: {$header}"); + if (!$this->UseSendmailOptions || null === $params) { + $result = @mail($to, $subject, $body, $header); + } else { + $this->edebug("Additional params: {$params}"); + $result = @mail($to, $subject, $body, $header, $params); + } + $this->edebug('Result: ' . ($result ? 'true' : 'false')); + return $result; + } + + /** + * Output debugging info via a user-defined method. + * Only generates output if debug output is enabled. + * + * @see PHPMailer::$Debugoutput + * @see PHPMailer::$SMTPDebug + * + * @param string $str + */ + protected function edebug($str) + { + if ($this->SMTPDebug <= 0) { + return; + } + //Is this a PSR-3 logger? + if ($this->Debugoutput instanceof \Psr\Log\LoggerInterface) { + $this->Debugoutput->debug(rtrim($str, "\r\n")); + + return; + } + //Avoid clash with built-in function names + if (is_callable($this->Debugoutput) && !in_array($this->Debugoutput, ['error_log', 'html', 'echo'])) { + call_user_func($this->Debugoutput, $str, $this->SMTPDebug); + + return; + } + switch ($this->Debugoutput) { + case 'error_log': + //Don't output, just log + /** @noinspection ForgottenDebugOutputInspection */ + error_log($str); + break; + case 'html': + //Cleans up output a bit for a better looking, HTML-safe output + echo htmlentities( + preg_replace('/[\r\n]+/', '', $str), + ENT_QUOTES, + 'UTF-8' + ), "
\n"; + break; + case 'echo': + default: + //Normalize line breaks + $str = preg_replace('/\r\n|\r/m', "\n", $str); + echo gmdate('Y-m-d H:i:s'), + "\t", + //Trim trailing space + trim( + //Indent for readability, except for trailing break + str_replace( + "\n", + "\n \t ", + trim($str) + ) + ), + "\n"; + } + } + + /** + * Sets message type to HTML or plain. + * + * @param bool $isHtml True for HTML mode + */ + public function isHTML($isHtml = true) + { + if ($isHtml) { + $this->ContentType = static::CONTENT_TYPE_TEXT_HTML; + } else { + $this->ContentType = static::CONTENT_TYPE_PLAINTEXT; + } + } + + /** + * Send messages using SMTP. + */ + public function isSMTP() + { + $this->Mailer = 'smtp'; + } + + /** + * Send messages using PHP's mail() function. + */ + public function isMail() + { + $this->Mailer = 'mail'; + } + + /** + * Send messages using $Sendmail. + */ + public function isSendmail() + { + $ini_sendmail_path = ini_get('sendmail_path'); + + if (false === stripos($ini_sendmail_path, 'sendmail')) { + $this->Sendmail = '/usr/sbin/sendmail'; + } else { + $this->Sendmail = $ini_sendmail_path; + } + $this->Mailer = 'sendmail'; + } + + /** + * Send messages using qmail. + */ + public function isQmail() + { + $ini_sendmail_path = ini_get('sendmail_path'); + + if (false === stripos($ini_sendmail_path, 'qmail')) { + $this->Sendmail = '/var/qmail/bin/qmail-inject'; + } else { + $this->Sendmail = $ini_sendmail_path; + } + $this->Mailer = 'qmail'; + } + + /** + * Add a "To" address. + * + * @param string $address The email address to send to + * @param string $name + * + * @throws Exception + * + * @return bool true on success, false if address already used or invalid in some way + */ + public function addAddress($address, $name = '') + { + return $this->addOrEnqueueAnAddress('to', $address, $name); + } + + /** + * Add a "CC" address. + * + * @param string $address The email address to send to + * @param string $name + * + * @throws Exception + * + * @return bool true on success, false if address already used or invalid in some way + */ + public function addCC($address, $name = '') + { + return $this->addOrEnqueueAnAddress('cc', $address, $name); + } + + /** + * Add a "BCC" address. + * + * @param string $address The email address to send to + * @param string $name + * + * @throws Exception + * + * @return bool true on success, false if address already used or invalid in some way + */ + public function addBCC($address, $name = '') + { + return $this->addOrEnqueueAnAddress('bcc', $address, $name); + } + + /** + * Add a "Reply-To" address. + * + * @param string $address The email address to reply to + * @param string $name + * + * @throws Exception + * + * @return bool true on success, false if address already used or invalid in some way + */ + public function addReplyTo($address, $name = '') + { + return $this->addOrEnqueueAnAddress('Reply-To', $address, $name); + } + + /** + * Add an address to one of the recipient arrays or to the ReplyTo array. Because PHPMailer + * can't validate addresses with an IDN without knowing the PHPMailer::$CharSet (that can still + * be modified after calling this function), addition of such addresses is delayed until send(). + * Addresses that have been added already return false, but do not throw exceptions. + * + * @param string $kind One of 'to', 'cc', 'bcc', or 'ReplyTo' + * @param string $address The email address + * @param string $name An optional username associated with the address + * + * @throws Exception + * + * @return bool true on success, false if address already used or invalid in some way + */ + protected function addOrEnqueueAnAddress($kind, $address, $name) + { + $pos = false; + if ($address !== null) { + $address = trim($address); + $pos = strrpos($address, '@'); + } + if (false === $pos) { + //At-sign is missing. + $error_message = sprintf( + '%s (%s): %s', + $this->lang('invalid_address'), + $kind, + $address + ); + $this->setError($error_message); + $this->edebug($error_message); + if ($this->exceptions) { + throw new Exception($error_message); + } + + return false; + } + if ($name !== null && is_string($name)) { + $name = trim(preg_replace('/[\r\n]+/', '', $name)); //Strip breaks and trim + } else { + $name = ''; + } + $params = [$kind, $address, $name]; + //Enqueue addresses with IDN until we know the PHPMailer::$CharSet. + //Domain is assumed to be whatever is after the last @ symbol in the address + if (static::idnSupported() && $this->has8bitChars(substr($address, ++$pos))) { + if ('Reply-To' !== $kind) { + if (!array_key_exists($address, $this->RecipientsQueue)) { + $this->RecipientsQueue[$address] = $params; + + return true; + } + } elseif (!array_key_exists($address, $this->ReplyToQueue)) { + $this->ReplyToQueue[$address] = $params; + + return true; + } + + return false; + } + + //Immediately add standard addresses without IDN. + return call_user_func_array([$this, 'addAnAddress'], $params); + } + + /** + * Set the boundaries to use for delimiting MIME parts. + * If you override this, ensure you set all 3 boundaries to unique values. + * The default boundaries include a "=_" sequence which cannot occur in quoted-printable bodies, + * as suggested by https://www.rfc-editor.org/rfc/rfc2045#section-6.7 + * + * @return void + */ + public function setBoundaries() + { + $this->uniqueid = $this->generateId(); + $this->boundary[1] = 'b1=_' . $this->uniqueid; + $this->boundary[2] = 'b2=_' . $this->uniqueid; + $this->boundary[3] = 'b3=_' . $this->uniqueid; + } + + /** + * Add an address to one of the recipient arrays or to the ReplyTo array. + * Addresses that have been added already return false, but do not throw exceptions. + * + * @param string $kind One of 'to', 'cc', 'bcc', or 'ReplyTo' + * @param string $address The email address to send, resp. to reply to + * @param string $name + * + * @throws Exception + * + * @return bool true on success, false if address already used or invalid in some way + */ + protected function addAnAddress($kind, $address, $name = '') + { + if (!in_array($kind, ['to', 'cc', 'bcc', 'Reply-To'])) { + $error_message = sprintf( + '%s: %s', + $this->lang('Invalid recipient kind'), + $kind + ); + $this->setError($error_message); + $this->edebug($error_message); + if ($this->exceptions) { + throw new Exception($error_message); + } + + return false; + } + if (!static::validateAddress($address)) { + $error_message = sprintf( + '%s (%s): %s', + $this->lang('invalid_address'), + $kind, + $address + ); + $this->setError($error_message); + $this->edebug($error_message); + if ($this->exceptions) { + throw new Exception($error_message); + } + + return false; + } + if ('Reply-To' !== $kind) { + if (!array_key_exists(strtolower($address), $this->all_recipients)) { + $this->{$kind}[] = [$address, $name]; + $this->all_recipients[strtolower($address)] = true; + + return true; + } + } elseif (!array_key_exists(strtolower($address), $this->ReplyTo)) { + $this->ReplyTo[strtolower($address)] = [$address, $name]; + + return true; + } + + return false; + } + + /** + * Parse and validate a string containing one or more RFC822-style comma-separated email addresses + * of the form "display name
" into an array of name/address pairs. + * Uses the imap_rfc822_parse_adrlist function if the IMAP extension is available. + * Note that quotes in the name part are removed. + * + * @see https://www.andrew.cmu.edu/user/agreen1/testing/mrbs/web/Mail/RFC822.php A more careful implementation + * + * @param string $addrstr The address list string + * @param bool $useimap Whether to use the IMAP extension to parse the list + * @param string $charset The charset to use when decoding the address list string. + * + * @return array + */ + public static function parseAddresses($addrstr, $useimap = true, $charset = self::CHARSET_ISO88591) + { + $addresses = []; + if ($useimap && function_exists('imap_rfc822_parse_adrlist')) { + //Use this built-in parser if it's available + $list = imap_rfc822_parse_adrlist($addrstr, ''); + // Clear any potential IMAP errors to get rid of notices being thrown at end of script. + imap_errors(); + foreach ($list as $address) { + if ( + '.SYNTAX-ERROR.' !== $address->host && + static::validateAddress($address->mailbox . '@' . $address->host) + ) { + //Decode the name part if it's present and encoded + if ( + property_exists($address, 'personal') && + //Check for a Mbstring constant rather than using extension_loaded, which is sometimes disabled + defined('MB_CASE_UPPER') && + preg_match('/^=\?.*\?=$/s', $address->personal) + ) { + $origCharset = mb_internal_encoding(); + mb_internal_encoding($charset); + //Undo any RFC2047-encoded spaces-as-underscores + $address->personal = str_replace('_', '=20', $address->personal); + //Decode the name + $address->personal = mb_decode_mimeheader($address->personal); + mb_internal_encoding($origCharset); + } + + $addresses[] = [ + 'name' => (property_exists($address, 'personal') ? $address->personal : ''), + 'address' => $address->mailbox . '@' . $address->host, + ]; + } + } + } else { + //Use this simpler parser + $list = explode(',', $addrstr); + foreach ($list as $address) { + $address = trim($address); + //Is there a separate name part? + if (strpos($address, '<') === false) { + //No separate name, just use the whole thing + if (static::validateAddress($address)) { + $addresses[] = [ + 'name' => '', + 'address' => $address, + ]; + } + } else { + list($name, $email) = explode('<', $address); + $email = trim(str_replace('>', '', $email)); + $name = trim($name); + if (static::validateAddress($email)) { + //Check for a Mbstring constant rather than using extension_loaded, which is sometimes disabled + //If this name is encoded, decode it + if (defined('MB_CASE_UPPER') && preg_match('/^=\?.*\?=$/s', $name)) { + $origCharset = mb_internal_encoding(); + mb_internal_encoding($charset); + //Undo any RFC2047-encoded spaces-as-underscores + $name = str_replace('_', '=20', $name); + //Decode the name + $name = mb_decode_mimeheader($name); + mb_internal_encoding($origCharset); + } + $addresses[] = [ + //Remove any surrounding quotes and spaces from the name + 'name' => trim($name, '\'" '), + 'address' => $email, + ]; + } + } + } + } + + return $addresses; + } + + /** + * Set the From and FromName properties. + * + * @param string $address + * @param string $name + * @param bool $auto Whether to also set the Sender address, defaults to true + * + * @throws Exception + * + * @return bool + */ + public function setFrom($address, $name = '', $auto = true) + { + $address = trim((string)$address); + $name = trim(preg_replace('/[\r\n]+/', '', $name)); //Strip breaks and trim + //Don't validate now addresses with IDN. Will be done in send(). + $pos = strrpos($address, '@'); + if ( + (false === $pos) + || ((!$this->has8bitChars(substr($address, ++$pos)) || !static::idnSupported()) + && !static::validateAddress($address)) + ) { + $error_message = sprintf( + '%s (From): %s', + $this->lang('invalid_address'), + $address + ); + $this->setError($error_message); + $this->edebug($error_message); + if ($this->exceptions) { + throw new Exception($error_message); + } + + return false; + } + $this->From = $address; + $this->FromName = $name; + if ($auto && empty($this->Sender)) { + $this->Sender = $address; + } + + return true; + } + + /** + * Return the Message-ID header of the last email. + * Technically this is the value from the last time the headers were created, + * but it's also the message ID of the last sent message except in + * pathological cases. + * + * @return string + */ + public function getLastMessageID() + { + return $this->lastMessageID; + } + + /** + * Check that a string looks like an email address. + * Validation patterns supported: + * * `auto` Pick best pattern automatically; + * * `pcre8` Use the squiloople.com pattern, requires PCRE > 8.0; + * * `pcre` Use old PCRE implementation; + * * `php` Use PHP built-in FILTER_VALIDATE_EMAIL; + * * `html5` Use the pattern given by the HTML5 spec for 'email' type form input elements. + * * `noregex` Don't use a regex: super fast, really dumb. + * Alternatively you may pass in a callable to inject your own validator, for example: + * + * ```php + * PHPMailer::validateAddress('user@example.com', function($address) { + * return (strpos($address, '@') !== false); + * }); + * ``` + * + * You can also set the PHPMailer::$validator static to a callable, allowing built-in methods to use your validator. + * + * @param string $address The email address to check + * @param string|callable $patternselect Which pattern to use + * + * @return bool + */ + public static function validateAddress($address, $patternselect = null) + { + if (null === $patternselect) { + $patternselect = static::$validator; + } + //Don't allow strings as callables, see SECURITY.md and CVE-2021-3603 + if (is_callable($patternselect) && !is_string($patternselect)) { + return call_user_func($patternselect, $address); + } + //Reject line breaks in addresses; it's valid RFC5322, but not RFC5321 + if (strpos($address, "\n") !== false || strpos($address, "\r") !== false) { + return false; + } + switch ($patternselect) { + case 'pcre': //Kept for BC + case 'pcre8': + /* + * A more complex and more permissive version of the RFC5322 regex on which FILTER_VALIDATE_EMAIL + * is based. + * In addition to the addresses allowed by filter_var, also permits: + * * dotless domains: `a@b` + * * comments: `1234 @ local(blah) .machine .example` + * * quoted elements: `'"test blah"@example.org'` + * * numeric TLDs: `a@b.123` + * * unbracketed IPv4 literals: `a@192.168.0.1` + * * IPv6 literals: 'first.last@[IPv6:a1::]' + * Not all of these will necessarily work for sending! + * + * @copyright 2009-2010 Michael Rushton + * Feel free to use and redistribute this code. But please keep this copyright notice. + */ + return (bool) preg_match( + '/^(?!(?>(?1)"?(?>\\\[ -~]|[^"])"?(?1)){255,})(?!(?>(?1)"?(?>\\\[ -~]|[^"])"?(?1)){65,}@)' . + '((?>(?>(?>((?>(?>(?>\x0D\x0A)?[\t ])+|(?>[\t ]*\x0D\x0A)?[\t ]+)?)(\((?>(?2)' . + '(?>[\x01-\x08\x0B\x0C\x0E-\'*-\[\]-\x7F]|\\\[\x00-\x7F]|(?3)))*(?2)\)))+(?2))|(?2))?)' . + '([!#-\'*+\/-9=?^-~-]+|"(?>(?2)(?>[\x01-\x08\x0B\x0C\x0E-!#-\[\]-\x7F]|\\\[\x00-\x7F]))*' . + '(?2)")(?>(?1)\.(?1)(?4))*(?1)@(?!(?1)[a-z0-9-]{64,})(?1)(?>([a-z0-9](?>[a-z0-9-]*[a-z0-9])?)' . + '(?>(?1)\.(?!(?1)[a-z0-9-]{64,})(?1)(?5)){0,126}|\[(?:(?>IPv6:(?>([a-f0-9]{1,4})(?>:(?6)){7}' . + '|(?!(?:.*[a-f0-9][:\]]){8,})((?6)(?>:(?6)){0,6})?::(?7)?))|(?>(?>IPv6:(?>(?6)(?>:(?6)){5}:' . + '|(?!(?:.*[a-f0-9]:){6,})(?8)?::(?>((?6)(?>:(?6)){0,4}):)?))?(25[0-5]|2[0-4][0-9]|1[0-9]{2}' . + '|[1-9]?[0-9])(?>\.(?9)){3}))\])(?1)$/isD', + $address + ); + case 'html5': + /* + * This is the pattern used in the HTML5 spec for validation of 'email' type form input elements. + * + * @see https://html.spec.whatwg.org/#e-mail-state-(type=email) + */ + return (bool) preg_match( + '/^[a-zA-Z0-9.!#$%&\'*+\/=?^_`{|}~-]+@[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}' . + '[a-zA-Z0-9])?(?:\.[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?)*$/sD', + $address + ); + case 'php': + default: + return filter_var($address, FILTER_VALIDATE_EMAIL) !== false; + } + } + + /** + * Tells whether IDNs (Internationalized Domain Names) are supported or not. This requires the + * `intl` and `mbstring` PHP extensions. + * + * @return bool `true` if required functions for IDN support are present + */ + public static function idnSupported() + { + return function_exists('idn_to_ascii') && function_exists('mb_convert_encoding'); + } + + /** + * Converts IDN in given email address to its ASCII form, also known as punycode, if possible. + * Important: Address must be passed in same encoding as currently set in PHPMailer::$CharSet. + * This function silently returns unmodified address if: + * - No conversion is necessary (i.e. domain name is not an IDN, or is already in ASCII form) + * - Conversion to punycode is impossible (e.g. required PHP functions are not available) + * or fails for any reason (e.g. domain contains characters not allowed in an IDN). + * + * @see PHPMailer::$CharSet + * + * @param string $address The email address to convert + * + * @return string The encoded address in ASCII form + */ + public function punyencodeAddress($address) + { + //Verify we have required functions, CharSet, and at-sign. + $pos = strrpos($address, '@'); + if ( + !empty($this->CharSet) && + false !== $pos && + static::idnSupported() + ) { + $domain = substr($address, ++$pos); + //Verify CharSet string is a valid one, and domain properly encoded in this CharSet. + if ($this->has8bitChars($domain) && @mb_check_encoding($domain, $this->CharSet)) { + //Convert the domain from whatever charset it's in to UTF-8 + $domain = mb_convert_encoding($domain, self::CHARSET_UTF8, $this->CharSet); + //Ignore IDE complaints about this line - method signature changed in PHP 5.4 + $errorcode = 0; + if (defined('INTL_IDNA_VARIANT_UTS46')) { + //Use the current punycode standard (appeared in PHP 7.2) + $punycode = idn_to_ascii( + $domain, + \IDNA_DEFAULT | \IDNA_USE_STD3_RULES | \IDNA_CHECK_BIDI | + \IDNA_CHECK_CONTEXTJ | \IDNA_NONTRANSITIONAL_TO_ASCII, + \INTL_IDNA_VARIANT_UTS46 + ); + } elseif (defined('INTL_IDNA_VARIANT_2003')) { + //Fall back to this old, deprecated/removed encoding + $punycode = idn_to_ascii($domain, $errorcode, \INTL_IDNA_VARIANT_2003); + } else { + //Fall back to a default we don't know about + $punycode = idn_to_ascii($domain, $errorcode); + } + if (false !== $punycode) { + return substr($address, 0, $pos) . $punycode; + } + } + } + + return $address; + } + + /** + * Create a message and send it. + * Uses the sending method specified by $Mailer. + * + * @throws Exception + * + * @return bool false on error - See the ErrorInfo property for details of the error + */ + public function send() + { + try { + if (!$this->preSend()) { + return false; + } + + return $this->postSend(); + } catch (Exception $exc) { + $this->mailHeader = ''; + $this->setError($exc->getMessage()); + if ($this->exceptions) { + throw $exc; + } + + return false; + } + } + + /** + * Prepare a message for sending. + * + * @throws Exception + * + * @return bool + */ + public function preSend() + { + if ( + 'smtp' === $this->Mailer + || ('mail' === $this->Mailer && (\PHP_VERSION_ID >= 80000 || stripos(PHP_OS, 'WIN') === 0)) + ) { + //SMTP mandates RFC-compliant line endings + //and it's also used with mail() on Windows + static::setLE(self::CRLF); + } else { + //Maintain backward compatibility with legacy Linux command line mailers + static::setLE(PHP_EOL); + } + //Check for buggy PHP versions that add a header with an incorrect line break + if ( + 'mail' === $this->Mailer + && ((\PHP_VERSION_ID >= 70000 && \PHP_VERSION_ID < 70017) + || (\PHP_VERSION_ID >= 70100 && \PHP_VERSION_ID < 70103)) + && ini_get('mail.add_x_header') === '1' + && stripos(PHP_OS, 'WIN') === 0 + ) { + trigger_error($this->lang('buggy_php'), E_USER_WARNING); + } + + try { + $this->error_count = 0; //Reset errors + $this->mailHeader = ''; + + //Dequeue recipient and Reply-To addresses with IDN + foreach (array_merge($this->RecipientsQueue, $this->ReplyToQueue) as $params) { + $params[1] = $this->punyencodeAddress($params[1]); + call_user_func_array([$this, 'addAnAddress'], $params); + } + if (count($this->to) + count($this->cc) + count($this->bcc) < 1) { + throw new Exception($this->lang('provide_address'), self::STOP_CRITICAL); + } + + //Validate From, Sender, and ConfirmReadingTo addresses + foreach (['From', 'Sender', 'ConfirmReadingTo'] as $address_kind) { + if ($this->{$address_kind} === null) { + $this->{$address_kind} = ''; + continue; + } + $this->{$address_kind} = trim($this->{$address_kind}); + if (empty($this->{$address_kind})) { + continue; + } + $this->{$address_kind} = $this->punyencodeAddress($this->{$address_kind}); + if (!static::validateAddress($this->{$address_kind})) { + $error_message = sprintf( + '%s (%s): %s', + $this->lang('invalid_address'), + $address_kind, + $this->{$address_kind} + ); + $this->setError($error_message); + $this->edebug($error_message); + if ($this->exceptions) { + throw new Exception($error_message); + } + + return false; + } + } + + //Set whether the message is multipart/alternative + if ($this->alternativeExists()) { + $this->ContentType = static::CONTENT_TYPE_MULTIPART_ALTERNATIVE; + } + + $this->setMessageType(); + //Refuse to send an empty message unless we are specifically allowing it + if (!$this->AllowEmpty && empty($this->Body)) { + throw new Exception($this->lang('empty_message'), self::STOP_CRITICAL); + } + + //Trim subject consistently + $this->Subject = trim($this->Subject); + //Create body before headers in case body makes changes to headers (e.g. altering transfer encoding) + $this->MIMEHeader = ''; + $this->MIMEBody = $this->createBody(); + //createBody may have added some headers, so retain them + $tempheaders = $this->MIMEHeader; + $this->MIMEHeader = $this->createHeader(); + $this->MIMEHeader .= $tempheaders; + + //To capture the complete message when using mail(), create + //an extra header list which createHeader() doesn't fold in + if ('mail' === $this->Mailer) { + if (count($this->to) > 0) { + $this->mailHeader .= $this->addrAppend('To', $this->to); + } else { + $this->mailHeader .= $this->headerLine('To', 'undisclosed-recipients:;'); + } + $this->mailHeader .= $this->headerLine( + 'Subject', + $this->encodeHeader($this->secureHeader($this->Subject)) + ); + } + + //Sign with DKIM if enabled + if ( + !empty($this->DKIM_domain) + && !empty($this->DKIM_selector) + && (!empty($this->DKIM_private_string) + || (!empty($this->DKIM_private) + && static::isPermittedPath($this->DKIM_private) + && file_exists($this->DKIM_private) + ) + ) + ) { + $header_dkim = $this->DKIM_Add( + $this->MIMEHeader . $this->mailHeader, + $this->encodeHeader($this->secureHeader($this->Subject)), + $this->MIMEBody + ); + $this->MIMEHeader = static::stripTrailingWSP($this->MIMEHeader) . static::$LE . + static::normalizeBreaks($header_dkim) . static::$LE; + } + + return true; + } catch (Exception $exc) { + $this->setError($exc->getMessage()); + if ($this->exceptions) { + throw $exc; + } + + return false; + } + } + + /** + * Actually send a message via the selected mechanism. + * + * @throws Exception + * + * @return bool + */ + public function postSend() + { + try { + //Choose the mailer and send through it + switch ($this->Mailer) { + case 'sendmail': + case 'qmail': + return $this->sendmailSend($this->MIMEHeader, $this->MIMEBody); + case 'smtp': + return $this->smtpSend($this->MIMEHeader, $this->MIMEBody); + case 'mail': + return $this->mailSend($this->MIMEHeader, $this->MIMEBody); + default: + $sendMethod = $this->Mailer . 'Send'; + if (method_exists($this, $sendMethod)) { + return $this->{$sendMethod}($this->MIMEHeader, $this->MIMEBody); + } + + return $this->mailSend($this->MIMEHeader, $this->MIMEBody); + } + } catch (Exception $exc) { + $this->setError($exc->getMessage()); + $this->edebug($exc->getMessage()); + if ($this->Mailer === 'smtp' && $this->SMTPKeepAlive == true && $this->smtp->connected()) { + $this->smtp->reset(); + } + if ($this->exceptions) { + throw $exc; + } + } + + return false; + } + + /** + * Send mail using the $Sendmail program. + * + * @see PHPMailer::$Sendmail + * + * @param string $header The message headers + * @param string $body The message body + * + * @throws Exception + * + * @return bool + */ + protected function sendmailSend($header, $body) + { + if ($this->Mailer === 'qmail') { + $this->edebug('Sending with qmail'); + } else { + $this->edebug('Sending with sendmail'); + } + $header = static::stripTrailingWSP($header) . static::$LE . static::$LE; + //This sets the SMTP envelope sender which gets turned into a return-path header by the receiver + //A space after `-f` is optional, but there is a long history of its presence + //causing problems, so we don't use one + //Exim docs: https://www.exim.org/exim-html-current/doc/html/spec_html/ch-the_exim_command_line.html + //Sendmail docs: https://www.sendmail.org/~ca/email/man/sendmail.html + //Example problem: https://www.drupal.org/node/1057954 + + //PHP 5.6 workaround + $sendmail_from_value = ini_get('sendmail_from'); + if (empty($this->Sender) && !empty($sendmail_from_value)) { + //PHP config has a sender address we can use + $this->Sender = ini_get('sendmail_from'); + } + //CVE-2016-10033, CVE-2016-10045: Don't pass -f if characters will be escaped. + if (!empty($this->Sender) && static::validateAddress($this->Sender) && self::isShellSafe($this->Sender)) { + if ($this->Mailer === 'qmail') { + $sendmailFmt = '%s -f%s'; + } else { + $sendmailFmt = '%s -oi -f%s -t'; + } + } else { + //allow sendmail to choose a default envelope sender. It may + //seem preferable to force it to use the From header as with + //SMTP, but that introduces new problems (see + //), and + //it has historically worked this way. + $sendmailFmt = '%s -oi -t'; + } + + $sendmail = sprintf($sendmailFmt, escapeshellcmd($this->Sendmail), $this->Sender); + $this->edebug('Sendmail path: ' . $this->Sendmail); + $this->edebug('Sendmail command: ' . $sendmail); + $this->edebug('Envelope sender: ' . $this->Sender); + $this->edebug("Headers: {$header}"); + + if ($this->SingleTo) { + foreach ($this->SingleToArray as $toAddr) { + $mail = @popen($sendmail, 'w'); + if (!$mail) { + throw new Exception($this->lang('execute') . $this->Sendmail, self::STOP_CRITICAL); + } + $this->edebug("To: {$toAddr}"); + fwrite($mail, 'To: ' . $toAddr . "\n"); + fwrite($mail, $header); + fwrite($mail, $body); + $result = pclose($mail); + $addrinfo = static::parseAddresses($toAddr, true, $this->CharSet); + $this->doCallback( + ($result === 0), + [[$addrinfo['address'], $addrinfo['name']]], + $this->cc, + $this->bcc, + $this->Subject, + $body, + $this->From, + [] + ); + $this->edebug("Result: " . ($result === 0 ? 'true' : 'false')); + if (0 !== $result) { + throw new Exception($this->lang('execute') . $this->Sendmail, self::STOP_CRITICAL); + } + } + } else { + $mail = @popen($sendmail, 'w'); + if (!$mail) { + throw new Exception($this->lang('execute') . $this->Sendmail, self::STOP_CRITICAL); + } + fwrite($mail, $header); + fwrite($mail, $body); + $result = pclose($mail); + $this->doCallback( + ($result === 0), + $this->to, + $this->cc, + $this->bcc, + $this->Subject, + $body, + $this->From, + [] + ); + $this->edebug("Result: " . ($result === 0 ? 'true' : 'false')); + if (0 !== $result) { + throw new Exception($this->lang('execute') . $this->Sendmail, self::STOP_CRITICAL); + } + } + + return true; + } + + /** + * Fix CVE-2016-10033 and CVE-2016-10045 by disallowing potentially unsafe shell characters. + * Note that escapeshellarg and escapeshellcmd are inadequate for our purposes, especially on Windows. + * + * @see https://github.com/PHPMailer/PHPMailer/issues/924 CVE-2016-10045 bug report + * + * @param string $string The string to be validated + * + * @return bool + */ + protected static function isShellSafe($string) + { + //It's not possible to use shell commands safely (which includes the mail() function) without escapeshellarg, + //but some hosting providers disable it, creating a security problem that we don't want to have to deal with, + //so we don't. + if (!function_exists('escapeshellarg') || !function_exists('escapeshellcmd')) { + return false; + } + + if ( + escapeshellcmd($string) !== $string + || !in_array(escapeshellarg($string), ["'$string'", "\"$string\""]) + ) { + return false; + } + + $length = strlen($string); + + for ($i = 0; $i < $length; ++$i) { + $c = $string[$i]; + + //All other characters have a special meaning in at least one common shell, including = and +. + //Full stop (.) has a special meaning in cmd.exe, but its impact should be negligible here. + //Note that this does permit non-Latin alphanumeric characters based on the current locale. + if (!ctype_alnum($c) && strpos('@_-.', $c) === false) { + return false; + } + } + + return true; + } + + /** + * Check whether a file path is of a permitted type. + * Used to reject URLs and phar files from functions that access local file paths, + * such as addAttachment. + * + * @param string $path A relative or absolute path to a file + * + * @return bool + */ + protected static function isPermittedPath($path) + { + //Matches scheme definition from https://tools.ietf.org/html/rfc3986#section-3.1 + return !preg_match('#^[a-z][a-z\d+.-]*://#i', $path); + } + + /** + * Check whether a file path is safe, accessible, and readable. + * + * @param string $path A relative or absolute path to a file + * + * @return bool + */ + protected static function fileIsAccessible($path) + { + if (!static::isPermittedPath($path)) { + return false; + } + $readable = is_file($path); + //If not a UNC path (expected to start with \\), check read permission, see #2069 + if (strpos($path, '\\\\') !== 0) { + $readable = $readable && is_readable($path); + } + return $readable; + } + + /** + * Send mail using the PHP mail() function. + * + * @see https://www.php.net/manual/en/book.mail.php + * + * @param string $header The message headers + * @param string $body The message body + * + * @throws Exception + * + * @return bool + */ + protected function mailSend($header, $body) + { + $header = static::stripTrailingWSP($header) . static::$LE . static::$LE; + + $toArr = []; + foreach ($this->to as $toaddr) { + $toArr[] = $this->addrFormat($toaddr); + } + $to = trim(implode(', ', $toArr)); + + //If there are no To-addresses (e.g. when sending only to BCC-addresses) + //the following should be added to get a correct DKIM-signature. + //Compare with $this->preSend() + if ($to === '') { + $to = 'undisclosed-recipients:;'; + } + + $params = null; + //This sets the SMTP envelope sender which gets turned into a return-path header by the receiver + //A space after `-f` is optional, but there is a long history of its presence + //causing problems, so we don't use one + //Exim docs: https://www.exim.org/exim-html-current/doc/html/spec_html/ch-the_exim_command_line.html + //Sendmail docs: https://www.sendmail.org/~ca/email/man/sendmail.html + //Example problem: https://www.drupal.org/node/1057954 + //CVE-2016-10033, CVE-2016-10045: Don't pass -f if characters will be escaped. + + //PHP 5.6 workaround + $sendmail_from_value = ini_get('sendmail_from'); + if (empty($this->Sender) && !empty($sendmail_from_value)) { + //PHP config has a sender address we can use + $this->Sender = ini_get('sendmail_from'); + } + if (!empty($this->Sender) && static::validateAddress($this->Sender)) { + if (self::isShellSafe($this->Sender)) { + $params = sprintf('-f%s', $this->Sender); + } + $old_from = ini_get('sendmail_from'); + ini_set('sendmail_from', $this->Sender); + } + $result = false; + if ($this->SingleTo && count($toArr) > 1) { + foreach ($toArr as $toAddr) { + $result = $this->mailPassthru($toAddr, $this->Subject, $body, $header, $params); + $addrinfo = static::parseAddresses($toAddr, true, $this->CharSet); + $this->doCallback( + $result, + [[$addrinfo['address'], $addrinfo['name']]], + $this->cc, + $this->bcc, + $this->Subject, + $body, + $this->From, + [] + ); + } + } else { + $result = $this->mailPassthru($to, $this->Subject, $body, $header, $params); + $this->doCallback($result, $this->to, $this->cc, $this->bcc, $this->Subject, $body, $this->From, []); + } + if (isset($old_from)) { + ini_set('sendmail_from', $old_from); + } + if (!$result) { + throw new Exception($this->lang('instantiate'), self::STOP_CRITICAL); + } + + return true; + } + + /** + * Get an instance to use for SMTP operations. + * Override this function to load your own SMTP implementation, + * or set one with setSMTPInstance. + * + * @return SMTP + */ + public function getSMTPInstance() + { + if (!is_object($this->smtp)) { + $this->smtp = new SMTP(); + } + + return $this->smtp; + } + + /** + * Provide an instance to use for SMTP operations. + * + * @return SMTP + */ + public function setSMTPInstance(SMTP $smtp) + { + $this->smtp = $smtp; + + return $this->smtp; + } + + /** + * Provide SMTP XCLIENT attributes + * + * @param string $name Attribute name + * @param ?string $value Attribute value + * + * @return bool + */ + public function setSMTPXclientAttribute($name, $value) + { + if (!in_array($name, SMTP::$xclient_allowed_attributes)) { + return false; + } + if (isset($this->SMTPXClient[$name]) && $value === null) { + unset($this->SMTPXClient[$name]); + } elseif ($value !== null) { + $this->SMTPXClient[$name] = $value; + } + + return true; + } + + /** + * Get SMTP XCLIENT attributes + * + * @return array + */ + public function getSMTPXclientAttributes() + { + return $this->SMTPXClient; + } + + /** + * Send mail via SMTP. + * Returns false if there is a bad MAIL FROM, RCPT, or DATA input. + * + * @see PHPMailer::setSMTPInstance() to use a different class. + * + * @uses \PHPMailer\PHPMailer\SMTP + * + * @param string $header The message headers + * @param string $body The message body + * + * @throws Exception + * + * @return bool + */ + protected function smtpSend($header, $body) + { + $header = static::stripTrailingWSP($header) . static::$LE . static::$LE; + $bad_rcpt = []; + if (!$this->smtpConnect($this->SMTPOptions)) { + throw new Exception($this->lang('smtp_connect_failed'), self::STOP_CRITICAL); + } + //Sender already validated in preSend() + if ('' === $this->Sender) { + $smtp_from = $this->From; + } else { + $smtp_from = $this->Sender; + } + if (count($this->SMTPXClient)) { + $this->smtp->xclient($this->SMTPXClient); + } + if (!$this->smtp->mail($smtp_from)) { + $this->setError($this->lang('from_failed') . $smtp_from . ' : ' . implode(',', $this->smtp->getError())); + throw new Exception($this->ErrorInfo, self::STOP_CRITICAL); + } + + $callbacks = []; + //Attempt to send to all recipients + foreach ([$this->to, $this->cc, $this->bcc] as $togroup) { + foreach ($togroup as $to) { + if (!$this->smtp->recipient($to[0], $this->dsn)) { + $error = $this->smtp->getError(); + $bad_rcpt[] = ['to' => $to[0], 'error' => $error['detail']]; + $isSent = false; + } else { + $isSent = true; + } + + $callbacks[] = ['issent' => $isSent, 'to' => $to[0], 'name' => $to[1]]; + } + } + + //Only send the DATA command if we have viable recipients + if ((count($this->all_recipients) > count($bad_rcpt)) && !$this->smtp->data($header . $body)) { + throw new Exception($this->lang('data_not_accepted'), self::STOP_CRITICAL); + } + + $smtp_transaction_id = $this->smtp->getLastTransactionID(); + + if ($this->SMTPKeepAlive) { + $this->smtp->reset(); + } else { + $this->smtp->quit(); + $this->smtp->close(); + } + + foreach ($callbacks as $cb) { + $this->doCallback( + $cb['issent'], + [[$cb['to'], $cb['name']]], + [], + [], + $this->Subject, + $body, + $this->From, + ['smtp_transaction_id' => $smtp_transaction_id] + ); + } + + //Create error message for any bad addresses + if (count($bad_rcpt) > 0) { + $errstr = ''; + foreach ($bad_rcpt as $bad) { + $errstr .= $bad['to'] . ': ' . $bad['error']; + } + throw new Exception($this->lang('recipients_failed') . $errstr, self::STOP_CONTINUE); + } + + return true; + } + + /** + * Initiate a connection to an SMTP server. + * Returns false if the operation failed. + * + * @param array $options An array of options compatible with stream_context_create() + * + * @throws Exception + * + * @uses \PHPMailer\PHPMailer\SMTP + * + * @return bool + */ + public function smtpConnect($options = null) + { + if (null === $this->smtp) { + $this->smtp = $this->getSMTPInstance(); + } + + //If no options are provided, use whatever is set in the instance + if (null === $options) { + $options = $this->SMTPOptions; + } + + //Already connected? + if ($this->smtp->connected()) { + return true; + } + + $this->smtp->setTimeout($this->Timeout); + $this->smtp->setDebugLevel($this->SMTPDebug); + $this->smtp->setDebugOutput($this->Debugoutput); + $this->smtp->setVerp($this->do_verp); + if ($this->Host === null) { + $this->Host = 'localhost'; + } + $hosts = explode(';', $this->Host); + $lastexception = null; + + foreach ($hosts as $hostentry) { + $hostinfo = []; + if ( + !preg_match( + '/^(?:(ssl|tls):\/\/)?(.+?)(?::(\d+))?$/', + trim($hostentry), + $hostinfo + ) + ) { + $this->edebug($this->lang('invalid_hostentry') . ' ' . trim($hostentry)); + //Not a valid host entry + continue; + } + //$hostinfo[1]: optional ssl or tls prefix + //$hostinfo[2]: the hostname + //$hostinfo[3]: optional port number + //The host string prefix can temporarily override the current setting for SMTPSecure + //If it's not specified, the default value is used + + //Check the host name is a valid name or IP address before trying to use it + if (!static::isValidHost($hostinfo[2])) { + $this->edebug($this->lang('invalid_host') . ' ' . $hostinfo[2]); + continue; + } + $prefix = ''; + $secure = $this->SMTPSecure; + $tls = (static::ENCRYPTION_STARTTLS === $this->SMTPSecure); + if ('ssl' === $hostinfo[1] || ('' === $hostinfo[1] && static::ENCRYPTION_SMTPS === $this->SMTPSecure)) { + $prefix = 'ssl://'; + $tls = false; //Can't have SSL and TLS at the same time + $secure = static::ENCRYPTION_SMTPS; + } elseif ('tls' === $hostinfo[1]) { + $tls = true; + //TLS doesn't use a prefix + $secure = static::ENCRYPTION_STARTTLS; + } + //Do we need the OpenSSL extension? + $sslext = defined('OPENSSL_ALGO_SHA256'); + if (static::ENCRYPTION_STARTTLS === $secure || static::ENCRYPTION_SMTPS === $secure) { + //Check for an OpenSSL constant rather than using extension_loaded, which is sometimes disabled + if (!$sslext) { + throw new Exception($this->lang('extension_missing') . 'openssl', self::STOP_CRITICAL); + } + } + $host = $hostinfo[2]; + $port = $this->Port; + if ( + array_key_exists(3, $hostinfo) && + is_numeric($hostinfo[3]) && + $hostinfo[3] > 0 && + $hostinfo[3] < 65536 + ) { + $port = (int) $hostinfo[3]; + } + if ($this->smtp->connect($prefix . $host, $port, $this->Timeout, $options)) { + try { + if ($this->Helo) { + $hello = $this->Helo; + } else { + $hello = $this->serverHostname(); + } + $this->smtp->hello($hello); + //Automatically enable TLS encryption if: + //* it's not disabled + //* we are not connecting to localhost + //* we have openssl extension + //* we are not already using SSL + //* the server offers STARTTLS + if ( + $this->SMTPAutoTLS && + $this->Host !== 'localhost' && + $sslext && + $secure !== 'ssl' && + $this->smtp->getServerExt('STARTTLS') + ) { + $tls = true; + } + if ($tls) { + if (!$this->smtp->startTLS()) { + $message = $this->getSmtpErrorMessage('connect_host'); + throw new Exception($message); + } + //We must resend EHLO after TLS negotiation + $this->smtp->hello($hello); + } + if ( + $this->SMTPAuth && !$this->smtp->authenticate( + $this->Username, + $this->Password, + $this->AuthType, + $this->oauth + ) + ) { + throw new Exception($this->lang('authenticate')); + } + + return true; + } catch (Exception $exc) { + $lastexception = $exc; + $this->edebug($exc->getMessage()); + //We must have connected, but then failed TLS or Auth, so close connection nicely + $this->smtp->quit(); + } + } + } + //If we get here, all connection attempts have failed, so close connection hard + $this->smtp->close(); + //As we've caught all exceptions, just report whatever the last one was + if ($this->exceptions && null !== $lastexception) { + throw $lastexception; + } + if ($this->exceptions) { + // no exception was thrown, likely $this->smtp->connect() failed + $message = $this->getSmtpErrorMessage('connect_host'); + throw new Exception($message); + } + + return false; + } + + /** + * Close the active SMTP session if one exists. + */ + public function smtpClose() + { + if ((null !== $this->smtp) && $this->smtp->connected()) { + $this->smtp->quit(); + $this->smtp->close(); + } + } + + /** + * Set the language for error messages. + * The default language is English. + * + * @param string $langcode ISO 639-1 2-character language code (e.g. French is "fr") + * Optionally, the language code can be enhanced with a 4-character + * script annotation and/or a 2-character country annotation. + * @param string $lang_path Path to the language file directory, with trailing separator (slash) + * Do not set this from user input! + * + * @return bool Returns true if the requested language was loaded, false otherwise. + */ + public function setLanguage($langcode = 'en', $lang_path = '') + { + //Backwards compatibility for renamed language codes + $renamed_langcodes = [ + 'br' => 'pt_br', + 'cz' => 'cs', + 'dk' => 'da', + 'no' => 'nb', + 'se' => 'sv', + 'rs' => 'sr', + 'tg' => 'tl', + 'am' => 'hy', + ]; + + if (array_key_exists($langcode, $renamed_langcodes)) { + $langcode = $renamed_langcodes[$langcode]; + } + + //Define full set of translatable strings in English + $PHPMAILER_LANG = [ + 'authenticate' => 'SMTP Error: Could not authenticate.', + 'buggy_php' => 'Your version of PHP is affected by a bug that may result in corrupted messages.' . + ' To fix it, switch to sending using SMTP, disable the mail.add_x_header option in' . + ' your php.ini, switch to MacOS or Linux, or upgrade your PHP to version 7.0.17+ or 7.1.3+.', + 'connect_host' => 'SMTP Error: Could not connect to SMTP host.', + 'data_not_accepted' => 'SMTP Error: data not accepted.', + 'empty_message' => 'Message body empty', + 'encoding' => 'Unknown encoding: ', + 'execute' => 'Could not execute: ', + 'extension_missing' => 'Extension missing: ', + 'file_access' => 'Could not access file: ', + 'file_open' => 'File Error: Could not open file: ', + 'from_failed' => 'The following From address failed: ', + 'instantiate' => 'Could not instantiate mail function.', + 'invalid_address' => 'Invalid address: ', + 'invalid_header' => 'Invalid header name or value', + 'invalid_hostentry' => 'Invalid hostentry: ', + 'invalid_host' => 'Invalid host: ', + 'mailer_not_supported' => ' mailer is not supported.', + 'provide_address' => 'You must provide at least one recipient email address.', + 'recipients_failed' => 'SMTP Error: The following recipients failed: ', + 'signing' => 'Signing Error: ', + 'smtp_code' => 'SMTP code: ', + 'smtp_code_ex' => 'Additional SMTP info: ', + 'smtp_connect_failed' => 'SMTP connect() failed.', + 'smtp_detail' => 'Detail: ', + 'smtp_error' => 'SMTP server error: ', + 'variable_set' => 'Cannot set or reset variable: ', + ]; + if (empty($lang_path)) { + //Calculate an absolute path so it can work if CWD is not here + $lang_path = dirname(__DIR__) . DIRECTORY_SEPARATOR . 'language' . DIRECTORY_SEPARATOR; + } + + //Validate $langcode + $foundlang = true; + $langcode = strtolower($langcode); + if ( + !preg_match('/^(?P[a-z]{2})(?P + + + + + + +
+ + + + + + + +
+ + + +
+
+
+
+
+
Aproximando médicos e pacientes +

Segurança, Confiabilidade e + Rapidez

+

Experimente o futuro + dos agendamentos.
Encontre profissionais capacitados e marque já sua consulta.

+ +
+
    +
  • 📄 Laudos digitais e padronizados
  • +
  • 🔔 Notificações automáticas ao paciente
  • +
+
+
+
+
+
+
Hero Image
+ +
+
  • 🔒 LGPD: controle de acesso e consentimento
  • +
    +
    +
    +
    + +
    + + + +
    +
    +
    +
    +
    +
    Sobre nós +

    Experimente o futuro do gerenciamento dos + seus atendimentos médicos

    +
    +

    Somos uma plataforma inovadora que conecta pacientes e médicos de forma prática, segura e + humanizada. Nosso objetivo é simplificar o processo de emissão e acompanhamento de laudos médicos, + oferecendo um ambiente online confiável e acessível. +

    +

    Aqui, os pacientes podem registrar suas informações de saúde e solicitar laudos de forma rápida, + enquanto os médicos têm acesso a ferramentas que facilitam a análise, validação e emissão dos + documentos.

    +
    +

    Nossos valores

    +
      +
    • Inovação
    • +
    • Segurança
    • +
    • Discrição
    • +
    • Transparência
    • +
    • Agilidade
    • +
    +
    +
    +
    +
    +
    FreeBootstrap.net image placeholder +
    +
    +
    +

    Nosso objetivo

    +

    Nosso compromisso é garantir qualidade, segurança e sigilo em cada atendimento, + unindo tecnologia à responsabilidade médica.

    +
    +
    +
    +
    +
    +
    +
    + + + + + + +
    +
    + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/Website/manage.html b/Website/manage.html new file mode 100644 index 0000000..7a2af4f --- /dev/null +++ b/Website/manage.html @@ -0,0 +1,204 @@ + + + + + + + + Nova Free Bootstrap Template for Agency — by FreeBootstrap.net + + + + + + + + + + + + + + + + + + + + + + + + + + +
    + + + + + + + + + +
    +
    +

    Gerenciamento de Pacientes

    +
    + +
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    PacienteCPFIdadeStatus do LaudoAções
    Ana Souza123.456.789-0042Finalizado + Abrir laudo +
    Bruno Lima987.654.321-0033Pendente + Abrir laudo +
    Carla Menezes111.222.333-4467Rascunho + Abrir laudo +
    +
    + + + + + + + + +
    + + +
    + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/Website/paciente.html b/Website/paciente.html new file mode 100644 index 0000000..90a15f5 --- /dev/null +++ b/Website/paciente.html @@ -0,0 +1,208 @@ + + + + + + + + Nova Free Bootstrap Template for Agency — by FreeBootstrap.net + + + + + + + + + + + + + + + + + + + + + + + + + + +
    + + + + + +
    + +
    + + +
    +

    Histórico de Laudos

    +
      +
    • 10/07/2025 — Exame de sangue (finalizado)
    • +
    • 18/06/2025 — Raio-X torácico (finalizado)
    • +
    • 03/05/2025 — Consulta clínica (rascunho)
    • +
    +
    +
    + + +
    +

    Laudo Clínico

    + +
    + + +
    + +
    +
    +

    Tipo de laudo

    +

    Consulta Clínica

    +
    +
    +

    Prioridade

    +

    Normal

    +
    +
    + +
    +

    Queixa principal

    + +
    + +
    +

    História/Exame físico

    + +
    + +
    +

    Resultados/Imagens

    + +
    + +
    +

    Conclusão/Diagnóstico

    + +
    + +
    +

    Conduta/Orientações ao paciente

    + +
    +
    +
    + + +
    + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/Website/pacienteantgo.html b/Website/pacienteantgo.html new file mode 100644 index 0000000..7fad4aa --- /dev/null +++ b/Website/pacienteantgo.html @@ -0,0 +1,359 @@ + + + + + + Laudo Clínico - Reformulado + + + +
    +
    + + +
    +
    CPF: 000.000.000-00
    +
    Idade: 00
    +
    Contato: paciente@exemplo.com
    +
    +
    + +
    + +
    + + +
    +

    Histórico de Laudos

    +
      +
    • 10/07/2025 — Exame de sangue (finalizado)
    • +
    • 18/06/2025 — Raio-X torácico (finalizado)
    • +
    • 03/05/2025 — Consulta clínica (rascunho)
    • +
    +
    +
    + + +
    +

    Laudo Clínico

    + +
    + + +
    + +
    +
    +

    Tipo de laudo

    +

    Consulta Clínica

    +
    +
    +

    Prioridade

    +

    Normal

    +
    +
    + +
    +

    Queixa principal

    + +
    + +
    +

    História/Exame físico

    + +
    + +
    +

    Resultados/Imagens

    + +
    + +
    +

    Conclusão/Diagnóstico

    + +
    + +
    +

    Conduta/Orientações ao paciente

    + +
    +
    +
    + + +
    + + \ No newline at end of file