From a69aa8fb30ffc70d0f6b8e75382dc6ea2c396f54 Mon Sep 17 00:00:00 2001 From: woon Date: Wed, 10 Mar 2021 20:41:53 +0800 Subject: [PATCH] :tada:: Initial Commit --- .gitignore | 63 + README.md | 1 + aboutus.php | 53 + add_message.php | 24 + cart.php | 214 + change_password.php | 21 + conn.php | 8 + contact.php | 24 + contactus.php | 61 + conversation.php | 122 + delete_review.php | 24 + employee_add_banner.php | 65 + employee_adding_banner.php | 69 + employee_approval.php | 73 + employee_banner.php | 79 + employee_delete_banner.php | 18 + employee_edit_banner.php | 101 + employee_feedback.php | 68 + employee_refunds.php | 72 + employee_refunds_detail.php | 77 + employee_reports.php | 74 + employee_reports_ticket.php | 77 + employee_statistic.php | 182 + employee_stats_api.php | 26 + error.php | 63 + fonts/XRXV3I6Li01BKofIMeaBXso.woff2 | Bin 0 -> 12652 bytes fonts/XRXV3I6Li01BKofINeaB.woff2 | Bin 0 -> 20448 bytes fonts/XRXV3I6Li01BKofIO-aBXso.woff2 | Bin 0 -> 20120 bytes fonts/XRXV3I6Li01BKofIOOaBXso.woff2 | Bin 0 -> 17332 bytes fonts/XRXV3I6Li01BKofIOuaBXso.woff2 | Bin 0 -> 7820 bytes fonts/flUhRq6tzZclQEJ-Vdg-IuiaDsNc.woff2 | Bin 0 -> 81916 bytes forgotpassword.php | 54 + get_cart.php | 66 + helpers/randompassword.php | 12 + inbox.php | 85 + includes/database.php | 113 + includes/employee_session.php | 7 + includes/employee_sidenav.php | 51 + includes/footer.php | 44 + includes/footeremployee.html | 44 + includes/pagetopbar.php | 18 + includes/pagetopbaremployee.php | 28 + includes/pagetopbarorderinfo.html | 18 + includes/pagetopbarrefund.html | 18 + includes/sessioncheck.php | 7 + includes/startsession.php | 5 + includes/store_scripts.php | 2 + includes/storetopbar.php | 82 + includes/vendor_session.php | 7 + includes/vendor_sidenav.php | 61 + index.php | 164 + item.php | 255 ++ login.php | 86 + loginpage.php | 61 + logout.php | 5 + orderinformation.php | 141 + phpmailer/Exception.php | 39 + phpmailer/OAuth.php | 138 + phpmailer/PHPMailer.php | 4836 ++++++++++++++++++++++ phpmailer/POP3.php | 421 ++ phpmailer/SMTP.php | 1426 +++++++ privacy.php | 56 + product_approval.php | 12 + product_refund.php | 13 + product_reports.php | 13 + purchase_history.php | 207 + refund_submission.php | 33 + refundapplication.php | 98 + register.php | 53 + registerpage.php | 61 + report.php | 17 + reset_password.php | 73 + review.php | 28 + scripts/Chart.min.js | 7 + scripts/carousel.js | 99 + scripts/cart_page.js | 64 + scripts/contact_page.js | 45 + scripts/conversation_page.js | 24 + scripts/employee_add_banner.js | 34 + scripts/employee_sidenav.js | 9 + scripts/item_page.js | 113 + scripts/jquery-3.5.1.min.js | 2 + scripts/khor.js | 48 + scripts/luxon.adapter.js | 7 + scripts/luxon.min.js | 1 + scripts/main.js | 7 + scripts/pagination.js | 52 + scripts/purchase_history_page.js | 14 + scripts/search_filter.js | 75 + scripts/shopping_cart.js | 27 + scripts/store_page.js | 36 + scripts/user_profile_page.js | 137 + scripts/vendor_conversation_page.js | 24 + scripts/vendor_page.js | 34 + scripts/yeap.js | 66 + search.php | 185 + sellerfaq.php | 64 + shopping_cart.php | 44 + storepage.php | 34 + styles/khor.css | 319 ++ styles/pagetopbar.css | 126 + styles/storetopbar.css | 291 ++ styles/style.css | 2381 +++++++++++ styles/template.css | 958 +++++ styles/user_profile.css | 6 + styles/vendor.css | 183 + terms.php | 155 + transaction.php | 91 + update_review.php | 32 + update_user.php | 74 + user_profile.php | 155 + vendor_add_message.php | 24 + vendor_add_product.php | 114 + vendor_adding_products.php | 34 + vendor_conversation.php | 123 + vendor_delete_product.php | 25 + vendor_edit_product.php | 93 + vendor_editing_products.php | 54 + vendor_inbox.php | 86 + vendor_income.php | 181 + vendor_order_info.php | 108 + vendor_order_statuschange.php | 14 + vendor_product_approval.php | 188 + vendor_product_page.php | 100 + vendor_profile.php | 108 + vendor_profile_edit.php | 34 + vendor_refund.php | 120 + vendor_refund_approval.php | 13 + vendor_refund_ticket.php | 76 + vendor_withdraw.php | 8 + 130 files changed, 18143 insertions(+) create mode 100644 .gitignore create mode 100644 README.md create mode 100644 aboutus.php create mode 100644 add_message.php create mode 100644 cart.php create mode 100644 change_password.php create mode 100644 conn.php create mode 100644 contact.php create mode 100644 contactus.php create mode 100644 conversation.php create mode 100644 delete_review.php create mode 100644 employee_add_banner.php create mode 100644 employee_adding_banner.php create mode 100644 employee_approval.php create mode 100644 employee_banner.php create mode 100644 employee_delete_banner.php create mode 100644 employee_edit_banner.php create mode 100644 employee_feedback.php create mode 100644 employee_refunds.php create mode 100644 employee_refunds_detail.php create mode 100644 employee_reports.php create mode 100644 employee_reports_ticket.php create mode 100644 employee_statistic.php create mode 100644 employee_stats_api.php create mode 100644 error.php create mode 100644 fonts/XRXV3I6Li01BKofIMeaBXso.woff2 create mode 100644 fonts/XRXV3I6Li01BKofINeaB.woff2 create mode 100644 fonts/XRXV3I6Li01BKofIO-aBXso.woff2 create mode 100644 fonts/XRXV3I6Li01BKofIOOaBXso.woff2 create mode 100644 fonts/XRXV3I6Li01BKofIOuaBXso.woff2 create mode 100644 fonts/flUhRq6tzZclQEJ-Vdg-IuiaDsNc.woff2 create mode 100644 forgotpassword.php create mode 100644 get_cart.php create mode 100644 helpers/randompassword.php create mode 100644 inbox.php create mode 100644 includes/database.php create mode 100644 includes/employee_session.php create mode 100644 includes/employee_sidenav.php create mode 100644 includes/footer.php create mode 100644 includes/footeremployee.html create mode 100644 includes/pagetopbar.php create mode 100644 includes/pagetopbaremployee.php create mode 100644 includes/pagetopbarorderinfo.html create mode 100644 includes/pagetopbarrefund.html create mode 100644 includes/sessioncheck.php create mode 100644 includes/startsession.php create mode 100644 includes/store_scripts.php create mode 100644 includes/storetopbar.php create mode 100644 includes/vendor_session.php create mode 100644 includes/vendor_sidenav.php create mode 100644 index.php create mode 100644 item.php create mode 100644 login.php create mode 100644 loginpage.php create mode 100644 logout.php create mode 100644 orderinformation.php create mode 100644 phpmailer/Exception.php create mode 100644 phpmailer/OAuth.php create mode 100644 phpmailer/PHPMailer.php create mode 100644 phpmailer/POP3.php create mode 100644 phpmailer/SMTP.php create mode 100644 privacy.php create mode 100644 product_approval.php create mode 100644 product_refund.php create mode 100644 product_reports.php create mode 100644 purchase_history.php create mode 100644 refund_submission.php create mode 100644 refundapplication.php create mode 100644 register.php create mode 100644 registerpage.php create mode 100644 report.php create mode 100644 reset_password.php create mode 100644 review.php create mode 100644 scripts/Chart.min.js create mode 100644 scripts/carousel.js create mode 100644 scripts/cart_page.js create mode 100644 scripts/contact_page.js create mode 100644 scripts/conversation_page.js create mode 100644 scripts/employee_add_banner.js create mode 100644 scripts/employee_sidenav.js create mode 100644 scripts/item_page.js create mode 100644 scripts/jquery-3.5.1.min.js create mode 100644 scripts/khor.js create mode 100644 scripts/luxon.adapter.js create mode 100644 scripts/luxon.min.js create mode 100644 scripts/main.js create mode 100644 scripts/pagination.js create mode 100644 scripts/purchase_history_page.js create mode 100644 scripts/search_filter.js create mode 100644 scripts/shopping_cart.js create mode 100644 scripts/store_page.js create mode 100644 scripts/user_profile_page.js create mode 100644 scripts/vendor_conversation_page.js create mode 100644 scripts/vendor_page.js create mode 100644 scripts/yeap.js create mode 100644 search.php create mode 100644 sellerfaq.php create mode 100644 shopping_cart.php create mode 100644 storepage.php create mode 100644 styles/khor.css create mode 100644 styles/pagetopbar.css create mode 100644 styles/storetopbar.css create mode 100644 styles/style.css create mode 100644 styles/template.css create mode 100644 styles/user_profile.css create mode 100644 styles/vendor.css create mode 100644 terms.php create mode 100644 transaction.php create mode 100644 update_review.php create mode 100644 update_user.php create mode 100644 user_profile.php create mode 100644 vendor_add_message.php create mode 100644 vendor_add_product.php create mode 100644 vendor_adding_products.php create mode 100644 vendor_conversation.php create mode 100644 vendor_delete_product.php create mode 100644 vendor_edit_product.php create mode 100644 vendor_editing_products.php create mode 100644 vendor_inbox.php create mode 100644 vendor_income.php create mode 100644 vendor_order_info.php create mode 100644 vendor_order_statuschange.php create mode 100644 vendor_product_approval.php create mode 100644 vendor_product_page.php create mode 100644 vendor_profile.php create mode 100644 vendor_profile_edit.php create mode 100644 vendor_refund.php create mode 100644 vendor_refund_approval.php create mode 100644 vendor_refund_ticket.php create mode 100644 vendor_withdraw.php diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..ad534f6 --- /dev/null +++ b/.gitignore @@ -0,0 +1,63 @@ +# JPEG +*.jpg +*.jpeg +*.jpe +*.jif +*.jfif +*.jfi + +# JPEG 2000 +*.jp2 +*.j2k +*.jpf +*.jpx +*.jpm +*.mj2 + +# JPEG XR +*.jxr +*.hdp +*.wdp + +# Graphics Interchange Format +*.gif + +# RAW +*.raw + +# Web P +*.webp + +# Portable Network Graphics +*.png + +# Animated Portable Network Graphics +*.apng + +# Multiple-image Network Graphics +*.mng + +# Tagged Image File Format +*.tiff +*.tif + +# Scalable Vector Graphics +*.svg +*.svgz + +# Portable Document Format +*.pdf + +# X BitMap +*.xbm + +# BMP +*.bmp +*.dib + +# ICO +*.ico + +# 3D Images +*.3dm +*.max \ No newline at end of file diff --git a/README.md b/README.md new file mode 100644 index 0000000..73cd74c --- /dev/null +++ b/README.md @@ -0,0 +1 @@ +# Chapalang Clothes diff --git a/aboutus.php b/aboutus.php new file mode 100644 index 0000000..754969a --- /dev/null +++ b/aboutus.php @@ -0,0 +1,53 @@ + + + + + + + + + + About Us - Chapalang Clothes + + + + + + + + + + + +
+
+ +
+

About Us

+
+

+ Welcome to Chapalang Clothing, your number one source for all indie fashion. We're dedicated to giving you the very best of fashion, with a focus on small, indie, and upcoming fashion designers. + + Founded in 2020 by 3 university students, Chapalang Clothing has come a long way from its beginnings in Asia Pacific University. When we first started out, our passion for indie fashion drove us to do tons of research so that Chapalang Clothing can offer you a place where all indie fashion is located. We now serve customers all over Malaysia, and are thrilled that we're able to turn our passion into our own website. + + We hope you enjoy our products as much as we enjoy offering them to you. If you have any questions or comments, please don't hesitate to contact us. + + Sincerely, + + Ryan Yeap, Khor Zhen Win & Woon Eusean +

+
+
+ + + + + + + + + + + + + \ No newline at end of file diff --git a/add_message.php b/add_message.php new file mode 100644 index 0000000..38a6fc4 --- /dev/null +++ b/add_message.php @@ -0,0 +1,24 @@ + 1024 || + strlen($_POST["conversationMessage"]) < 5 +) { + die(header("Location: inbox.php?error=true")); +} + +$messageBody = mysqlidb::escape(trim($_POST["conversationMessage"])); +$vendorId = mysqlidb::escape($_POST["vendorId"]); +$userId = $_SESSION["userid"]; + +try { + mysqlidb::query("INSERT INTO `message` (`UserId`, `VendorId`, `Sender`, `MessageBody`) VALUES ($userId, $vendorId, 0, '$messageBody')"); +} catch (\Throwable $th) { + die(header("Location: inbox.php?error=true")); +} + +die(header("Location: conversation.php?vendorid=$vendorId")); diff --git a/cart.php b/cart.php new file mode 100644 index 0000000..f053278 --- /dev/null +++ b/cart.php @@ -0,0 +1,214 @@ + + + + + + + + + + + Shopping Cart + + + + + + + + 0) { + $hasItems = true; + } + ?> + + + + + '; + } + ?> + + +
+ +
+ + + +
+ + + + + + + +
+
Product
+
Unit Price
+
Quantity
+
Total Price
+
Actions
+
+
'; + $vendors = []; + foreach ($rows as $row) { + if (!in_array($row["ShopName"], $vendors)) { + array_push($vendors, $row["ShopName"]); + } + } + foreach ($vendors as $vendor) { + echo "
+ from $vendor +
"; + + foreach ($rows as $row) { + if ($row["ShopName"] == $vendor) { + $productImage = explode(",", $row["ProductImage"])[0]; + echo '
+ + + ' . $row["ProductName"] . ' + + +
RM ' . number_format($row["ProductPrice"], 2) . '
+
' . $row["CartQuantity"] . '
+
RM ' . number_format($row["ProductTotal"], 2) . '
+
+ Remove +
+
'; + } + } + } + echo '
'; + } else { + echo '
+
shopping_bag
+

Your shopping cart is empty!

+ +
+
Go shopping now!
+
+
+
'; + } + ?> +
+
+ + +
+
Merchandise Subtotal (' . $totalItems . ' items)
+
RM ' . number_format($checkoutTotal, 2) . '
+
+
Checkout
+
+
+ '; + } + ?> + + + + + + + + + + + + \ No newline at end of file diff --git a/change_password.php b/change_password.php new file mode 100644 index 0000000..35c9200 --- /dev/null +++ b/change_password.php @@ -0,0 +1,21 @@ + \ No newline at end of file diff --git a/contact.php b/contact.php new file mode 100644 index 0000000..f864418 --- /dev/null +++ b/contact.php @@ -0,0 +1,24 @@ + + alert('Error: Message failed to be sent.'); + window.location.href = 'index.php'; + "; + } + + echo ""; +} diff --git a/contactus.php b/contactus.php new file mode 100644 index 0000000..1321242 --- /dev/null +++ b/contactus.php @@ -0,0 +1,61 @@ + + + + + + + + + + + Contact Us - Chapalang Clothes + + + + + + + + + + + +
+
+ +
+

Contact Us

+
+
+
+ + +
+
+ + +
+
+ + +
+
+ +
+
+
+
+ + + + + + + + + + + + + + \ No newline at end of file diff --git a/conversation.php b/conversation.php new file mode 100644 index 0000000..51e27e0 --- /dev/null +++ b/conversation.php @@ -0,0 +1,122 @@ + + + + + + + + + + + Conversation with <?php echo $vendorData["ShopName"]; ?> - Chapalang Clothes + + + + + + + + + + + +
+
+ +
+
+ Back to Inbox +
+ Conversation with +
+ 0) { + foreach ($convoMessages as $message) { + echo " + + + + + + + + + + +
+
Posted on + " . date('g:ia d/n/Y', strtotime($message['MessageTimestamp'])) . " +
+
+ +
+
+
+
" . ($message['Sender'] == 0 ? $userData['Username'] : $vendorData['ShopName']) . "
+ \"\" +
+
+
+
$message[MessageBody]
+
"; + } + } else { + echo "

You have no conversations with this seller.

+

Start one by sending a message.

"; + } + ?> + Back to Inbox + +
+ +
+ + +
+
+ +
+
+
+
+
+ + + + + + + + + + + + + \ No newline at end of file diff --git a/delete_review.php b/delete_review.php new file mode 100644 index 0000000..4da14fa --- /dev/null +++ b/delete_review.php @@ -0,0 +1,24 @@ + + alert('Failed to delete review.'); + window.location.href = 'index.php'; + "; + } + + echo ""; +} diff --git a/employee_add_banner.php b/employee_add_banner.php new file mode 100644 index 0000000..d78517d --- /dev/null +++ b/employee_add_banner.php @@ -0,0 +1,65 @@ + + + + + + + + + + + + + + Add Banner + + + + + + +
+
+ +
+

Add Banner

+
+ +
+
+
+ + + +
+
+ +
+ + +
+
+ + +
+
+ +
+
+
+
+
+ + + + + + + + + + + + \ No newline at end of file diff --git a/employee_adding_banner.php b/employee_adding_banner.php new file mode 100644 index 0000000..784a445 --- /dev/null +++ b/employee_adding_banner.php @@ -0,0 +1,69 @@ +file($_FILES['bannerimage']['tmp_name']), + array( + 'jpg' => 'image/jpeg', + 'png' => 'image/png', + 'gif' => 'image/gif', + ), + true + )) { + die(header("Location:employee_banner.php?error=true")); + } + + // You should name it uniquely. + // DO NOT USE $_FILES['picture']['name'] WITHOUT ANY VALIDATION !! + // On this example, obtain safe unique name from its binary data. + if (!move_uploaded_file( + $_FILES['bannerimage']['tmp_name'], + $imageUrl = sprintf( + 'images/banners/%s.%s', + sha1_file($_FILES['bannerimage']['tmp_name']), + $ext + ) + )) { + die(header("Location:employee_banner.php?error=true")); + } + } catch (RuntimeException $e) { + die(header("Location:employee_banner.php?error=true")); + } + } else { + die(header("Location:employee_banner.php?error=true")); + } + + if (!empty($_POST['bannername']) && !empty($_POST['bannerdesc'])) { + try { + include_once("includes/database.php"); + $bannerName = mysqlidb::escape($_POST['bannername']); + $bannerDesc = mysqlidb::escape($_POST['bannerdesc']); + $sql = "INSERT INTO banner (BannerName,BannerDescription,BannerImage) VALUES('$bannerName','$bannerDesc','$imageUrl')"; + try { + mysqlidb::query($sql); + } catch (\Throwable $th) { + die(header("Location:employee_banner.php?error=true")); + } + die(header("Location:employee_banner.php?success=true")); + } catch (\Throwable $th) { + die(header("Location:employee_banner.php?error=true")); + } + } else { + die(header("Location:employee_banner.php?error=true")); + } +} diff --git a/employee_approval.php b/employee_approval.php new file mode 100644 index 0000000..bf0e3d3 --- /dev/null +++ b/employee_approval.php @@ -0,0 +1,73 @@ + + + + + + + + + + + + + Product Approvals + + + + + + +
+
+ +
+

Product Approvals

+
+ + + + + + + + + 0) { + foreach ($rows as $row) { + $imgurl = explode(",", $row['ProductImage'])[0]; + echo " + + + + + "; + } + } else { + echo ""; + } + ?> +
Item ImageItem NameItem VendorAction
\"\"{$row['ProductName']}{$row['ShopName']} + Approve + Deny +

There are no product approvals.

+
+
+ + + + + +
+ + + + + \ No newline at end of file diff --git a/employee_banner.php b/employee_banner.php new file mode 100644 index 0000000..88e8af4 --- /dev/null +++ b/employee_banner.php @@ -0,0 +1,79 @@ + + + + + + + + + + + + + Manage Banners + + + + + + +
+
+ +
+

Manage Banners

+
+ +
+ Error occured.
"; + } else if (isset($_GET["success"])) { + echo "
Success!
"; + } ?> + + +
+ add + Add Banner +
+
+ + + + + + + 0) { + foreach ($rows as $row) { + echo " + + + + "; + } + } else { + echo ""; + } + ?> +
Banner HeaderBanner DescriptionAction
{$row['BannerName']}{$row['BannerDescription']} + editEditdeleteDelete +

There are no product approvals.

+
+
+
+ + + + + + + + + + + \ No newline at end of file diff --git a/employee_delete_banner.php b/employee_delete_banner.php new file mode 100644 index 0000000..eb83a06 --- /dev/null +++ b/employee_delete_banner.php @@ -0,0 +1,18 @@ + 0) { + die(header("Location:employee_banner.php?success=true")); + } else { + die(header("Location:employee_banner.php?error=true")); + } +} diff --git a/employee_edit_banner.php b/employee_edit_banner.php new file mode 100644 index 0000000..8d3d57d --- /dev/null +++ b/employee_edit_banner.php @@ -0,0 +1,101 @@ + + + + + + + + + + + + + Edit Banner + + + + + + +
+
+ +
+

Edit Banners

+
+ +
+ $error
"; + } else if (!empty($success)) { + echo "
$success
"; + } + ?> +
+
+ + " alt="" required> +
+ +
+ + " required> +
+
+ + +
+
+ +
+
+
+
+
+ + + + + + + + + + + \ No newline at end of file diff --git a/employee_feedback.php b/employee_feedback.php new file mode 100644 index 0000000..db9df5b --- /dev/null +++ b/employee_feedback.php @@ -0,0 +1,68 @@ + + + + + + + + + + + + + View Feedback + + + + + + +
+
+ +
+

View Feedback

+
+ + + + + + + + + + + 0) { + foreach ($rows as $row) { + echo " + + + + "; + } + } else { + echo ""; + } + ?> + +
NameEmailMessage
{$row['FeedbackName']}{$row['FeedbackEmail']}{$row['FeedbackMessage']}

There is no feedback yet.

+
+
+ + + + + +
+ + + + + \ No newline at end of file diff --git a/employee_refunds.php b/employee_refunds.php new file mode 100644 index 0000000..bcffbfb --- /dev/null +++ b/employee_refunds.php @@ -0,0 +1,72 @@ + + + + + + + + + + + + + Product Refunds + + + + + + +
+
+ +
+

Product Refunds

+
+ + + + + + + + + + 0) { + foreach ($rows as $row) { + echo " + + + + + + "; + } + } else { + echo ""; + } + + ?> +
OrderIDProductIDDetailsDate RequestedAction
{$row['OrderId']}{$row['ProductId']}{$row['Reason']}{$row['DateRequested']} + View Ticket +

There are no product refunds.

+
+
+ + + + + +
+ + + + + \ No newline at end of file diff --git a/employee_refunds_detail.php b/employee_refunds_detail.php new file mode 100644 index 0000000..41a4bc0 --- /dev/null +++ b/employee_refunds_detail.php @@ -0,0 +1,77 @@ + + + + + + + + + + + + + Refund Details + + + + + + +
+
+ +
+

Product Refunds

+
+ +
+ + Refund Details for {$rows[0]['ProductName']}"; + foreach ($rows as $row) { + echo "

RefundID : $row[RefundId]

+

OrderID : $row[OrderId]

+

ProductID : $row[ProductId]

+

Customer Name : $row[ProductName]

+

Customer Address : $row[Address]

+

Order Total : RM $row[OrderTotal]

+
+

Refund Reason : $row[Reason]

+

Customer Image :

+
+
+
+ "; + } + echo " + "; + ?> + +
+
+
+ + + + + +
+ + + + + \ No newline at end of file diff --git a/employee_reports.php b/employee_reports.php new file mode 100644 index 0000000..a7116d2 --- /dev/null +++ b/employee_reports.php @@ -0,0 +1,74 @@ + + + + + + + + + + + + + Product Reports + + + + + + +
+
+ +
+

Product Reports

+
+ + + + + + + + + 0) { + foreach ($rows as $row) { + $imgurl = explode(",", $row['ProductImage'])[0]; + echo " + + + + + "; + } + } else { + echo ""; + } + ?> +
Item ImageItem NameTotal ReportsAction
\"\"{$row['ProductName']}{$row['TotalReports']} + View Reports +

There are no product reports.

+
+
+ + + + + +
+ + + + + \ No newline at end of file diff --git a/employee_reports_ticket.php b/employee_reports_ticket.php new file mode 100644 index 0000000..e7a0378 --- /dev/null +++ b/employee_reports_ticket.php @@ -0,0 +1,77 @@ + + + + + + + + + + + + + Report Tickets + + + + + + +
+
+ +
+

Product Reports

+
+ +
+ + Reports for {$rows[0]['ProductName']} +

This item has been reported for

+
    "; + foreach ($rows as $row) { + if ($row['ReportReason'] == 'FalseAd') { + echo "
  • False Advertisement : $row[TotalReports] times
  • "; + } + if ($row['ReportReason'] == 'Fake') { + echo "
  • Fake Item : $row[TotalReports] times
  • "; + } + if ($row['ReportReason'] == 'InappropriateName') { + echo "
  • Innappropriate Name : $row[TotalReports] times
  • "; + } + if ($row['ReportReason'] == 'InappropriatePicture') { + echo "
  • Inappropriate Picture : $row[TotalReports] times
  • "; + } + } + echo "
+ "; + ?> + +

+
+
+ + + + + +
+ + + + + \ No newline at end of file diff --git a/employee_statistic.php b/employee_statistic.php new file mode 100644 index 0000000..0e81191 --- /dev/null +++ b/employee_statistic.php @@ -0,0 +1,182 @@ + + + + + + + + + + + + + + + View Statistics + + + + + + +
+
+ +
+

View Statistics

+
+ +
+
+ +
+
+ +
+
+
+
+ + + + + +
+ + + + + + \ No newline at end of file diff --git a/employee_stats_api.php b/employee_stats_api.php new file mode 100644 index 0000000..edbc501 --- /dev/null +++ b/employee_stats_api.php @@ -0,0 +1,26 @@ + + + + + + + + + + Error - Chapalang Clothes + + + + + + +
+
+ +
+ There has been an error in handling your shopping cart.'; + break; + case 'upload': + echo '

There has been an error in uploading your file(s).

'; + break; + case 'bad_item': + echo '

The item you are looking for does not exist or is blacklisted.

'; + break; + case 'review': + echo '

There is an error submitting your review.

'; + break; + case 'user_not_exist': + echo '

The user does not exist.

'; + break; + default: + echo '

An unknown error occured.

'; + break; + } + } else { + echo '

An unknown error occured.

'; + } + ?> + +

Return to home

+
+
+ + + + + +
+ + + + + + \ No newline at end of file diff --git a/fonts/XRXV3I6Li01BKofIMeaBXso.woff2 b/fonts/XRXV3I6Li01BKofIMeaBXso.woff2 new file mode 100644 index 0000000000000000000000000000000000000000..b06af7d8263f498ca50bb71eefab096fe84bd794 GIT binary patch literal 12652 zcmV-yF_X@BPew8T0RR9105NO;5dZ)H0CXS#05J&w0RR9100000000000000000000 z0000Qfe0I#G#q9IU;u&;2!SLCoD~rW3W1U!fq*Itf*t??HUcCAgeC+a1%qM-iVO^a zRvX}GCG40{fb&3z?k+p$h$7fH01)+$D*pd_f)qJQic1Ilc;O+PQ6=jAPzFQi(Ykia zO`}Us)JM=yH0nE+XozAWPHT_jV1uEy=w~cE%GvN5hVbMHj#2d0ZQlxKF^oCI`L|xu zP+(vb?`J8nsj(EJKlS<#TS%1}HbnKlss5WgLSvugLLcsj&2Ewcf{;}JD0C$VRY_=U zr9nLM$N%|H{`B+QnRzGz%lr()A~K*%ECUKY0QH-s3|Cui8^9`UiuQM@2q3^VYl2sl zI3*%sg{v&)Dh_q#1<0{ukO?H;ofLkklLBd3 zJ9bL<5tFw{S9bTz?COC{2fkaGg9ss%i6Wg$vNCh%k}-@O0Za$mHRZtnu4!&Ed2?ko zV#wtR!jMrjqHFE0{h-wanO6~@f0e~OozI7K7 z2{K_2YY|&1mT%yHKbAq0Z)Zdg*W6BTy4J<~Y;T)6T2ZY{GR%tIX9UTkw6g!WiwB{Q zbfpPG7$9mjqmdmZqDFEi_0JPjm`)-w{jOC>d}Zkm<4fq`aik8NfqhG88-Vz6h{v)(+<8O9xE9c#ROopJtk z&wG6Cf6x0qoqdhWLh*3x#6x8fP5%3}pZ2$C!ZlL{34s-ntcb`NQs#Y@dW_BgkFo7X zFF8b|BoF0YERO+2UjMl{w@Q^rwHl;e2hybn=`)0k7)1gCNLUz&N+2`lkVQ*K#x8Q` z5IL4bPMskaE|G!)a@SqtzDLOOUPK5e5VVc&O{ACLzPO*efbqCjxI#MtSF8Sty7ciF z{;-z2f?L!AS7y>kDddZx6TcJ`e>uteB1Umr)WiJm8;Z|I^ zL7w><%Wnzr|3Lt=3ebgm^~HDVoMN%rYj>;<`9m+f>AgjtMRnoiKKh`yxDdtK`{q+2 zE)LQg@7%@oo~S5SsbToJ_0xYxAA`R5W>5$}yb<)eLQB1R3dFZT)_G2yHe>zp${&c; zm)-L$s5jr+!~O(FWkY2jh>VKXL+6Ce8nF6oo3pNmKB<|l!R0T3TgZy<;m@W2?vMUQ zXNTi1dp>i1J#^Ss_w2CMIu|bUpVr^nh*lgp?3}3Ns`}KUkapjr|H}(cvJiDNuc-Y> z`WXkt2=|Ck`b(9C7mE)B*k6x^ z#^caOOYHE=_e}VsfVW%&?p3Y-oX058HF`>~4dQcK94;962jinZVW)bxhvg$kHuo$$ z@3UXBh^{N&K&dD)?CrWs>V!|wWTgBKm|K7XhU#7wfRLAh7N+BP5+S9<=lq3p;CeannIy{G zw9L)O!raa!8Ea&RvMkHxbo;6ckTrK@#XVW}{Fiq7D z)S_RQY_xKEqIWhbzgvIc)NQoJ1%G!hCx!o227_{?-MLE^|H#ayD#(A_L7N@?{qut+ zI;u(I?!~eqps~sqfj^;#(eHx>Y^;|f4&9RtS4IKQMDMHxQ#0log74F6j zZpDe4B)q!ha7*@Zdcxt_vyG_H9O9h$4x>d@`DG@;H6}97N)fjb;G!ULI}Km4S7gbd zY?tMBVzfkwCg1E5Wv9F>E6S34vfTr2yFYNoldFv>RB~c30yD zx)K@HtmAR>-~uXO0tf)8T;|u6iFrWeBz2O&pHl~z4#BL_oVYCf=f108$Fa6dxCx;U$NbpUZdytIFNnmP za2FyWiJk36jr$KOAg z(S~F<$@wG2TCnf+Q>iTdjW$Pi1TbIS_eXz?>gR@MPo`Inhw&RGiLYr70^S5Ql` zFEWe*Dk5PZxfkIjJINt|S_BbsNKn}IP~1UvXRH-a%^^>^!l)EfX*)6@qqfZ`i0DD8 zwX(a}j@*1wGCsc5qTPKB5NXbbu8BSIELI5qQ_q%(j($Pw%b@@o&sAWvjntVncT7N? zi(u_B=;`#JL8fsDh2&8~bZ>~07m z0<}_IL&GUgp?qGJz?g@B+C1=x+0B8*W9h|3{VE`|MpiPerRFW8!XqQoTf}T zCq%gP!3OWLXUo$}VH*@;s?t7}7TDN<9aL|CIg2urm#Kh@n;-&{6{&vQJP$I3MV@;& z-BAlxcd$65>IiOo)&OdjeKQkqWVgFy=zw1Rbag3A0wUBv;uN)7Jv zMuOvXS)EHb*^NlNY@eE(qX$GCn%z9dDK7KTah8bz>ZPxFwf3(sz;&S~tE7`He^Ekp z`X(tdvb!I2jeh$8G8kjsa}HDpg=%MocL4wpzQt48ubzNEB!i%mIzf0qNWOt`458Rp z<{0jf1o`Sz?-3{y?+GeiS)lIAbIASlVbeAXG74fDS zduo>Mq$ZvlrA{X*(=aqJ)XtVdlzVjky0PL+3Iy=k=6XzVH)Mdy)e7X@%#V0Y}be+Vg|MB$kSr zzJ$JOdg7^+Lc7&{E>b`6>OIpPJLn*?-K7ozqh;8o!@yWc#z8XeIHutaN~ntky4O%7 z4R4m(DgIuBd8ZRVniZK!DtO9EaTKcehMQdN3;R!^lglJ9Qp_$~@|3Yzld(b;#X2y0 zE;eS|QjUz}jUJLQ5s2wc{Bg!Y*NGuZ#rt1w{uk5Qjs7&`}u)91F^`wsQhLN76yub^}aUQ1K(Mw)`R z(iFVY0lmkBK49wph(FVV?sSmnE79s%_iT+72^xICjo38 z;5-z6gR*_VzJfi1<^$*efaB1muMe!LBxyVL(i<2JTaZgaQ8CoA%wb)itA5(rS<3@P$T$qa8ymtz=aR;6M~plDYj zt-@~iObNqR6v4{Ccx3{sm?X81c$ny*+SV@WKP|v#J2PdRFgcmrYHvItN#yt9As%y_ zlPg4I#(5_iJy>;oajXE<$Jcu}MYI=59i?|3!?u{hVn0))HQu`$Oe@iGF&wN6026OJLnx3yIh)BEg)_0z3$QT zv_+D6S-Vq}AHXJ(3+LPlANdfd@Hw_I7ZZ)Rar?f@*W@0rXKu_YH&&`QmTTH*)$Mh=H4*?y z^=BJYn;PPhdS6ZaQbY1%02m1%dt#$OftP@bSRf=6w4INJ(h6Po8ssnrR5)`np$dk| z?m%TtY--n`H!&x-vxam`^w|9!mZRvL4{6w{eq`A;z7R6nzKl%Or4gq;a@6LubzTem zY-*(aTSn9`)ICOV@c|>57Nv14w;R~j&~Xc}%J6f`+3Xn(^97%gYv(sCYy%SAmt=!8 zZH#yj%*`yqXjatf-EL6@}IMfHb(}iqZj!B-v=OFaA)e#rNPa`$JxHcrZq5m{$ z{ygCwH6;vAFkpZc2Z%c|fM&i7=L?lF@);eS3%G3qRPr7Wq&^{cTIs7$u;S8d1?dsM zsxp}{8n63yun?^)mY~IA*|@Y9`xV+Q09%%>!e@)ZFDc=RjyCl}UFqivU2d8JkDgJq zdRT3GUEDM0jP4lYVKs?A`=^-}UT*1o(iod;AB!;M6>kf*YtA~S^Qkq$^h#`M?X1Oh z+*qNY85p3`5Tek8Fku+4W9Gu$DMid~(YidUz{?n@0O;DR0o7^krbw&-x^Bjbzgmf9 z>Ct!S5GWP@8X1(TNHk|pjA~`Pq)gfdR!;(t4N#X7M!1iUzAf%2*)X|Cof`J4bmKO* z_yu4j0IBat^usFjjlMF_H9Epl_wg1g@NHO93+cSl!`#txdAVHNz*3EYf_f!_N_8~9 zc4X4$Q~11>Z>Ws~)X$jfcyw%X(k>=L294c_S1zNVp!@_6LojvdH*oe|JR~+Z(tb=@ zYmJwm(g;ot$rSUZ*yFW~`7{1|IcSVa)q_%^Cqg#DvyEI&f6qc~!-_^cNvX z!&OT~UMP_22%Qo%*9g`vKJN^Z+g4{pPrN6T6X`1pZ2`}%ji=_%R3U_}6;Ol3P=hJT zP=&NiC+j$i^+;$J%w77uPA3S}qmPzPqJNr>X9<&Ze484Say#H5q~f9YaBQ;lTx5;; zM1ws8ud`0I?S>!dM}5-Ezd^TzuiW%QIeUV9-@_HTin!f1IX%vz*gISoZj{DZI6~;GixoK^7MY!U^tzED&V;j% zUd}D_p76k`gz4Vpd{qv$6UoG=Ummf|k*Z`MM}!x;U1*Pn{MQ)3kh_OJr(v!o-zl zD4*iey0ob)t16WOb1N2`eRupOkdG`j(<{S8P~SFh`V5%7K0MRKi^*mUqQvhVu{N=w zvj6p%K`@2|1NA3SyOxNe20?f)bKuk?GS~>I2x_5opR+VWr40LESLrCKR*;sqW{^JZ z0uDS*bKq}umOLr_uh7Le0^ilci;iUY#yGo!|Pv9 zzds$v=Tte-Ii z3BIXn89DK=wvMhm@)K&|r1F{5SM0Ul+w__?h2QbU2KmV1yv&g|Y7Gf7ZvAqHGwGnr zCtej79iLMlk|JyJo%{y|??aX&Ez?DmX^_N*r-fz0Y@1SdNm8sRUf&R`6xdNqp!OOF z#_?ca)rwyuMgH&7j|@bx1`Ip%i1(Pc4xnZfo%)Xdkh=&)O(?$a2D}dU;iwryrat1n z=`$V*jbF8&>jiyCNr0&aEYzu-=ecg1ne8*kNw0JJi z84x4SYog!(=d8!i_=pCdBCQ9S&MZ(;w7aSb-J|tB;L^ReNi;l z1B7##zd6;`TX448f;hN`sR_@Ti5=#YsM@kPaeh^61z`b+^#2xl)mxS^-X<#5)(R)9 zBx!0~Tp_g>_Q7HRG_a4z1k#Wl<8ddqO$RqbT-F`toF+Llb=W3&SzfJDsg$N&DlJ!_ z@=4^0$~=Q!RUnt+n)KPqdA^az~lW}8gCAx4tj2EF6Tm05zP!8l5y*qIeOm+w4-p(YI7v!;L9*1ZU3 zLa;N7HWl{_b|vxMj^aMeqqe@YN9$0Az&4+wHdsMo~%F07y`U@lcG1$#kn0 zxtqNnXkpAKAj5GPm;y>br2ZCk@Rv$q7nDMeNS){BRfSLjo1g}2MDX>QOX7fp4Wb4} z0+&lpesSx32ZOREhaRPBAumNF3%_F+SZj~X!_D9A{h@mxl>{#Ib(t| zB*r*954tDy%xmc+1+vE zSVKQ_Z20RZFF?0SlpJf)1b5i4sZu1mH8u z1o=CrHpQ9^>xJJQF_dKsWPH$^^jbkk2+!ZNncQjTcQ!rHYXjxU!1L4m{_ecnQPWsv zN{lKV8L7DCAx_Vb&s?$IYkklc3DoSw6EQRqe*iV`)hgo}$ALH_Q+DRXUW*0&RW7bI zuTT-0G~h{%ht|qLe}(gF(Bsupnm&Y$%2B&E`^b?Fd*-i!L-Gj;@$xH-LCk@<12Qpn z-*RaE7}vuj!K0}qpc$0lpz?<`4P!O;p)P)L*f_Sfs;lqY6ll-wib9<%m8haKdhi~T zGp#!-b!BCx`n>Men#2u$J5h+>9OUM+Oy|gvtrKWXNq8cPCgRD%+S(?M4$I^Y-r#Vo z5SxI%>V4u9mecZ;{saditUeE|Q|QPsQ)w+pcm|4Q;7Qgkjbld#>A3g$SSSx6oL1F) z#w}dC>+dS{njU>|f`v-Ltj$;^ie_TZ-q!Qzj>bN%zO}e^&%FJ_w~=~DT9}mmJp!6Q zy9;fqS~X{=X^BfVO+zeY%w6@*bmL5I@`|b|vS!{V!Q5${TU`xma}Uj{?&MNU@W)@q z(Cm`UtL?B!7)!68H)Up|hG{5ED;ir-cNc0>bxWbwgbkUbTrz9*R{aMWmdJ71+2t@z zmXVSgo`S9N%_{BEH`X9AMDs|!SfK@BO|^B!VxciVVsePnVH_bbbn0ur30MG!6NpXz zNtggI5kDtkfgDZ%HsOumA_TiT{SzEWD2T2NpDbDufLsR|yl8TiG zj&LwU$~U<2$Z&oI>5e5;C9(F2PLzka)*{P~iUF1lQsc9GBn} z$yBQ(KQ`MhX_^&9Qd@nGm3?lm4qqAinp-ssskI=(3t+wVdf+GvD65ye0-f8fcY=sj zY8YEgSZZHtzW_C$gOVm+e}>wzXV0o#*^1A4M#Yng21vZ8vnC{^Dl1DVO}2A8eltB9 zzFmWQmIf0?r_{*hw8UU#{^|tk|4k$`wlJB`b@Gs!2%^ z)m6mFJmsJ>n#N&Uv6*?j7hJ^|Dw^UfLg64*$ho1)jC7gngL9RvgqRO46YG@1hLH(! zpH#a~jsO7&qyAx>U60EN?>;_u#n@z=AcMn;Px6zVBl^9<*;2iyO+0W_z~UB*5)Q!sWGED7&Cxr zHWV?68|n(AB{6~;l}ahibW&NOBF~VWlrNJd=Vu#?Kg7s8JE$5sC)T^&7+-YB25mr@ zZ9a+detlwwC@Dmdty2CENw5S;fcN^U@TCF4&qGFs?s%)4FmJ`kOGTKg5n=8>q_CCt zbuELvXYrH_->`W;IMsm1BZwL|81d|xaHEOTD86IJk^diy7p^t0sbBlt zoboY$yEqL&0%HF%K0%N;2q*ZJw1D-jX(^vr8ow2gnkf;h8d8}$Q!JUY%>7e=e?WnU zS3!V(wdcfw5EKmtw?XiRLT3Fy+3dHm*=1vM!p3%&?fjFRoK&7B4OyqZWoLWK-u{-Y zou0d=VlTurn4N9id@Y@Eo53*6oaiv!mCo!W=~_n_R7d*S1kIS}f9w2xyHY* z;+u}-Pa7ma@$%OC;p{jRd%%}-rK0HM6_(Da8-7*8%PX-A+$;(3iZDu;z%8YqW2Wcc zG7juNX=|hbz(D=&=QmJWDWs%IGk^VgjChgX_yPW)66Xui0<9RNc?+t2Ni&~`3vpGY z#g9VovJds^2J!^tbbg?MCa@r#cwcjEn6Iq<-6%i85^a%HzMtLE?`2zRH2y$;ZIk`Q zjPX&U@4k)E8arWwBxHjm>=+e%>{ z&7#7p^ZK`E_MQ%cjVyZR8=C*`A$y{TQh#3i!7M{R6U>!T@J@R?-W+J1XpEF>jun%L z3&yjE4Pu&wp2gfpnrhavsGEda=#FfY5mt^nV>b@0Ll$cm8|Rn}WZ~K_WAgjl(-A9eB(OhCjujJo-Sfj|ZG)QX1A_}K% zbC2)pC=3qsWwO;d-nI#<`TV_p_|L{zAe_=Ab2$cj1K97En2^u=p?<(0>{zVoXe(n zMi`73!^0tjg+HeZysikPKUbdXG}(f1EN^4R&N5=Ub{l91L7B$T!uFX3=cxJl(!qUd zG5or3ma~y=8oEVo)fUjY7CEJ$#ZV2?;f87>989}!oeKLRYo0j9 z`qUfVvLgHSBwfzJ?53$}$ZNP3CPZ|*rmg`4`pJMZog`b?O@oDp0tKtA=crLykp9iV zLw)0~va`Lv$lX&x`mn;&}S>fKr)hpAnfL@{ABPB{XsY$Y)Q6dI>yG<`U2niIi zUg@;F8hdKssYy>g+92?6-2z4T$8luDdflhSZywfOj~yG%Ce~JQoTMAs&C~#rb}d)1 zV%xkisN$MWepRna-tEd(hNcQ z#1_+z>u)x5WjqGPs*qg+BL<2tOPTm#SSUyouutT7$a5$mA|xniO|v*Q6>8ghG6Wz# zB2Ce<44r5Ws@*Aqe<4lrphOaOF1fq2Ap?y(lXY#qAcH+4r6N}r9vs&Suv*+_zJNuk z84_Zj@yaWirN>5$4IalzyH`cf&^xkDX_>2}Ug91k!1XGWD>PTssA#>-EbCCkL<|^%J7*Ma|czOh`C2S!{6UQ1d z4E2N0padRjN`9LU)-0zoP50i&^LUEEbKVPkFP(|ufr;Q|KRWMWLMa0D4|0AqNT#;%f!dX-!k?8N2WBhK=1{W04I^U;J(*# zl21^?lzD9`rpz?dRN0Bh7)XHv5hcim#!>+u`|lLSpMEZZYWHLi|KMGK`>G-Mjx0ap z5nuCieFoN7=j-L&>7WT>q7+f1>)wx9t&^AvPZp_fq@kH`h6A943lg$eo0v(>q&0Ok zjx&_8@IgyJ^Ol;&<)#HYA6Zs25jZ7yB{LBPJ=;#h6fn}txett`5> z-aH-O7xq%~J}V6)4rZ8m&EhxS#OlVymcEZoN^_Zw<#PmQ|sm z;J~^nl44W$g(;ykODLXwZt!eQJ8{9!aw&7|R%uO`8I_2JSEb7Nu2Kmf)oaX%k5*4j z*I7?g%)2URS4b6=P9bpav$)8Cf8`AN2DzisD=b0acI1>q@mRuNiR+YN^S#L z`FOhLBXF>+&xTGJ)jQSIVBrwT!k<%i80UCEB$n%u_sE1nk*TR&8bP(^2!J};^boGs zMd0sRE>@%(NcWmLFVG~LBSxg3y=7K#F?hr|w8~8gcsE-I*ZvmHW@!q{bW5nxCatRi z`uL}n93^2*3L6WX6!!Te_+NjaKBOJ18#e5XD{Qz*!@&&=&iAVubbBT5BX>&1utB<0{w%KLn!PKm@dDVWVNw!ht`w2$pBqYbYT?1NMe|uE}TCtLvcX zYeLq!u4)p%;*yraVlezA(PS6fW=2XNTa~KgL2AxNjW-Z@aF1;6VT7d-I(&iwSi&Iv zXf``<7!Q;PqAUqsOd%6y+IChbo=bI;SCVS#s#5tmuA8!=`>9(vxefLwH7tg-1#1hQ zM=fy(2Z^(&t*NB&O8by(n~5m$bX;xLz&th@Rg7f*+K2RoK~jyp4uVEqv7IX=1fUAF zoRFd`a}~Ms5dfHklr|r;osp22nCt0!<4HJEEx$|LbtResz2f6xx084Se&*4q4XrRp zs_yG>zR^}pTgY89P}ZL*{K{_fF^@TtIY|wV$R3m~kN}f>;UHBHz>X~$%^7XZ*YJHO zs$fG3kK`kmjGC*;=*GyfdTcBOX|ceI@7s}KZ9SZF@Jy?uXbrJfbrm(T!r4LcgNzO| z&={USs;yY)xCg)JGRIV#1V%&7-X zKhSC7;}q}Khm$szsJz=RS!O_Z40{F#h7z5VSz~7(-b{adJs)?5`nE2zC|JpnqdZO4 zVgVxs_BBh3lET6ko%$mEym+g#2W85~Fx8rN4c1U$t?KK04|P_}4BxwJM39#W^DN?I zHHnGx{g|xN^_<~6o}0B4gQz2tOie!-I$d_VrU6c`r|0{d-LQN2bo$J7Gv3JE`^S0v zZjAv*yfOepXFE57kLnGeezY;_1yIBKsY;Koq;Ypzx?->xQU-k*KULc@XaJs(dqG6X`AiI6I345(IVEUS3f)aP!$f=i zWh}pwX%^&6>j7`gW<1n_Xoj6do~UiTg6sRYiCm*IC=n?C|2Gp26OXfPG}cfMa}7Cw z8Rlk`+JS=USb%!+845ir2K3&_8@C8EKEU8b@spXN^o1)s93;}B77rs5ITG1??8gI5 z$bMgEB)j7)9gfg%x3Ozb*u1#9jVIK6Q~eWH{vwfg<2klHK_#b5Z;Fo8*Lxj4l!>ks z8QCGQCGIlXaAY8Rq_Av_z(rs? z5VN1}z*v2^1J~=fI|vp2wu7WBzz(vC;6FR8`a?m;wHx>4oXR>;VCdDYM?b@o6Gsd) zimse0xMrA>bM4HL11mqEh~|m2Y)+n`*%>eVAWDB!SIK3a;?`BPuhW5Rmpbzri?c2q zIX=y+;7Rkkd^%C>$d;|Su=bnID!Q;Ir%$H=-TIB0aU^5O*mjf-2qGFXax0F4;ivwG z89t)BWnt58F7F_&{3a~k>C(T@NswDdmswG1%;8OHTfO*65t+~MADq9THtjle>(%!I zi8mY;6_@sBzGLW*1^pjv!#8dU0u@3;sZpmvlNN0{ovz>O4~C=hBnYRoxl~b{=rqfV zakH{0F*a6Fx_u5WJd?jIhXo&&*9I1-J;6UkJ1dS-TReqnKGc_p*Dw!X2swY{^u zw|{VWbeugoJv+a+yt=-*&E=O-5Hriybt_)|Es#oE&<#5d3D^=j8dK87X3Pmrm=~Nm zIm46GZ)_tTn391rgS2GABrdBG#@bx0VRk!Mk0-khnz2~Kf_ z2S(CaK#KOk?oO=F5BqH!ej7Ll8Uq6(p(HUBCYBCZ7#J8BSQuCs7#J8Bn3$NDSeTfY zn3$MYn3$MYSXfw?SXfwCn3yG!yuLEwSB!N)_qkxh-r(C;?rcVq z|NK+mi#M9L+R%n}WN^Ty{m&!J#KEua3hn)JvWGSP*z-#IBfs>+?`HX^P=ZUq)CIc+ zt*5Tw&gS&$@aq2cjq7~tq~SB`n464Gj<4-lQ@!S8W^?erYjww+E&dI^Og38sQ$mgm z{6`A2u7Cq!t=D&RSMa~BD$$|P+*Yfy>4C5qZQ@J)R$ns`;nst*^W-q+JDX=FqmzSc ao7a@Bk;ZdP{~hT4*49wZd8xA&;U)mIj!Q8B literal 0 HcmV?d00001 diff --git a/fonts/XRXV3I6Li01BKofINeaB.woff2 b/fonts/XRXV3I6Li01BKofINeaB.woff2 new file mode 100644 index 0000000000000000000000000000000000000000..93ddafa329c2b962d68d7c34de4f3b28538b1ddd GIT binary patch literal 20448 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 bU!H08TUj0RR9100000000000000000000 z0000Qf?ykx794>N24Db)2nc~B37i!X2nvF!2!e=d3x$3F0X7081B-YBAO(YB2b&NK zfmRz&xi^?@`fm{w)J~8zSLQ#P8%81o=ZO$H_Z@`Uff$?zD8cGy6#xJKsYzwXY|59m z0Dyz(RqutGxrGpDxm0N4(wmG~Gy1sspqvuZgJ)f`R+CQ8GV7L1VUX0$tTi)i!*r+0 z%Z#wt^wH&h$k_TA~l-qI) zSvh&F|FzBRKT|aMVkwK3YM~JevG8aIDct_R-}1fx>*Ar-bP^{w7#)Nf(T?ok!8J%7 zlmdDtci#s$JhIZH4&tAG4zv6Ij0M91j)%JisAB?}Ii4JkN9TCJO54Td(!_Rvwpqg+ z+%=%0AgCA^fB^~$NVSxr7(J&wm*zTLm%?}JZO-I4~FG*sI%a9e92MeSOuDHDZ#zM*Av5IcwnVsjLkW5DSE z0f4X9R5iZcspC?YN&OWO$WxnN?q>z3Yvh}Wkn4O&Xi}_z57-^60#mXd^4}jI0OoVk zzYAYE?u+{9!zOkW@#*jI{swCj(&mfR%sJ|$PR*$t>6Q0|qDp!6R|l}$OD51{_3|O7 z^}ZR%8W2h-AZZZ@P)r*@vpFB%IW~o>bJy%SrlvDPCTz%)<{xB7sqpq~6gC9VKkDeo*qQuc)smGwP}*nfEt)uQs&*urqRxtDkgHuF8>$YNOUKd)NweG$K2g7tqUb0iw zko#%#0WU$Anr8ZpQJy?63Tz{;s5{d zo1T5{eSgYAN^zVio0qo&%t8n#|NE)E^vPGpW&Y@=Xww?2jq2FxNM4i>8l;h)^4#ur zZC6U|a++=A`AECSG=K$=AQ;AiFpD`q`ZPExmj1c^ z!W9)AVq-%pxDXEynqUGni8`do7&2uA*|LWmxj`O0ATM5!4w(lA%bFkjj*zS0l^B>Y<*x%*KZ@Gff+LHGKo5?G=Dz!V`+n&R6ckXKKU zfOpOSkXNQ4(2>~c?uL+I{Y&S3E7)tJh|))(2*4)5tElnrkJcikv8V~%nJovEF!#{5(J~CDKO@B3xiQzBkhIlI zZ`Ar{RPx(S?}BJD`kjJjW+Y>!5vCQQ6!rjD+JuahxGnuGcY5}xlG$9nGzBlOY}Mx zbh`+h;w;JHdTJU=DB_e&hO!xmXpeVs1SnZD2Mer&cOf z!z|1a>>(h^AEJ0s03T*)%9sGD;q2@+DtLDZh!!Nl!s?&k86!gR`Vmt}fQ+wKu1v7* z93vZQb715SsxW{XGsr)+)#rd{EN7(B=aMy@yrz5};u3+0mxzH?(604o6LYZ^Ho!*0 zpOdJPYqZ+t_%m_*?2x)ir3?9cso9rJt_i%6Pd8nPa188pTbs{zkZ4U zQIcWDjuk72LiLJ23^n`_e}kq3@B6#sAE;OSQ+Q$=aKH*yv4(U7CmEfWN0!Nbl5F-is*ss3#b4(xf^AX(u9RWdu^qHFu8pXjP zOvVpU>@QptUX<~+E-H}QSsY*|UI$OIFX>+hO+^;2;ln;YO$W_^-lh}(J>cz%c?a(U zfPbguFKzNGxNHvzMLlKl|GWE{Z&cQal^C>Ema|ISblXFpeDiBGCVo;Ub8<#DfmhD0 zxg~dKG+As8m&fO+XzA=*EM2yIrwmrVE_Au8i7TqC%Ia#I#{Crtyhm@-v(G5j9{Au( zouRW+L@44<55Ud1J%h|vyVLEpl~=XR8I~sN3;uhp^If{l!b+>CrUtcaXjd-DRE3Al z>S5=M7d?Jh-iQC)?>YQ-dI>(^gMpZQp?ep&ukwK#qTLmWi0dy zb((eR);spq-*w+ZZ~b;Z;QqM33s-mmK&TJ6M36Wq$;t*1O4e!{c}2o`4Oibe*__h+ zo9ByBY^x}%^fl8|*U}Vfqouy5mno*u&C*R4Zm{!!4c)4T=iEHynCtEt7hW6o0ssHk zv%o$H=%b)Mo7#7?`fX1C%8qJ7sNZd7dc3hl z1TbBT?Rx7_K*_~uE~l$8eUhBWxO!r7!&V4^8w7U1ie*{?iw=q@f?k)>wxkgofv~hu z$9biuq)DoptU4pD47JeFkd<5PJ!I=1XO9K<#gx98-cNJ;Z(ec>8?YV~HUMC?a9AY_ zwnJejyhGR>7TX?-_F;8IT#*clVptlxl310^xeN|vaVno%X9XnWQ33ZvJUhXgg@auw z4=Wo3KOZL-Z^gWRLjh!wEW^Knh49+m{97qwnMBUK?x@=+9SR#4XIzg3A_SM|M$hG7oTS{Ig3L(Siyg)5X`y3d z434etx+P_=n47bw*Fj2grI6Np!3pu;546~6OfR)a%n^bmdQv)(t@Zn04La~Y2F3id zbkZUR;{oO3Ed|%jU*3~z(kJD^^@GV5hZ}rg^P{Ja7OSa%8mj`DY@`m>%n69~RwC(WLh_xUGBhML zz~)?&<1vXLJz$XhhA!j|oV1)z zcb`0L2?}nr`idrE2#K)W3vj(gCUI-fp|K$0MYFs$V)c=&nMI~!h2J-?uW4a20Ld&r ziwTwqb1D%MJPB*iP)@1U&@78HQRFXUDolJRD=-%*O zwjLtJin!3Yh>U!1$H?dTB+W4w6EBa{UCus&wJph(fP$~9Nf7fXx=Wv-e7!Za;?@b4 z0U;X9)aSv2RrDT!P$1Dnz(A(0f4($lI-n8~1J+cR$t1DfV&7$t*(W|3be zkhIgv&aB`wOEc~y@i2=01h9%+oC*H6+P|$eZDf?Hs*K7es+g*3K{bnVEU9i;4J&F| z)sD4GACiv0lkMBRcq=k!z;-j=t z0+bLXLg}D%QF^E;nGw*AUo_4QtW@-*DS88Jkd2RWxOW1M<7N!khADuNdvenfmt~$W z9mY)z(2U-vvpH2Va3M~ISxTD&|3rNi^B|Q0%q)PH_IE5XgT^VEQzT)LYiCv1M*w!; z%5q+$O^@FwE1<>5iv)3;evWFc(Ohn8ZWz{*$U)b(yU0{nchSTo;ZL64PwDf>uKozW zZ;J4?=E^3e%?po)+&vIB%j$^eHx(r^ljf`$RtIThK$|wu!hQ^m)QyzQ@g%`-mzZt& zjXxB1QBB^T7x&Z^-mFd}pD!W9kjCV~%R}*_OQ~5zhE6Ckb}873L_<6jzM`CMQP8uW zEW@~LggS?$UZ}b*GT@pEGDYSgC2f+&tZ3gP#cYbtO(E9`Yk@{X_s~H^Oh|DlqlC1} zilgHwb&xRxqM8h4C;_N0(ReHTt-SaKtyrT!a9C%H{UWk+qdu+6&{VpPd2PtJWqxS; zrVfGj%){nRQOET}Cpg4RVyR@ym+9Bdrh2s6Il& zi9<*`&h~CfupVmWcr>mw?idqWPl0^T#z^yyv9Qh3ItUNKhX^1- zhzKHvNZ^$sW2JGEkxmrH%#qeb=qDBOvV7hVsH!klg<796vf1RY%~|izlu7#?`{_`q zW1&uGL@MVDmkieow;57hsC(h+0d;w1d1ZNL`OJ{Mh58jf|0u$mu1mb{L@4hUacsPJ zvBs*pifTvUWPQ5K9$xn-2>4uaeB>Ci{Or*G0{;gpKLZ*7jBf$Tr+}4)0ssK$^YoGi zsvTGTT;O94Zw;z8Cvz1w*+~kLHn%#KLzXnPt4Y~~X=Bz(gIkTe?%n{ywC4+h4)J)X z&SP0QzQj$0eT-t5Y;Z>c`<mZ zhP|BX6?9U0ROOs)#)iTYoSbANH?Zkb8XX{Vdt`%KDZ_}yQ z=WO+`W#(=cKO8PTf!FG0R~xBYv%I}3nac{Y4kK&Xc9k({`ni0y#7Y&@4Du1G9Wt7M zWmSsb8AewfCP07%2(l1Cf|<`Iy(@*_vx*bKbpZ=40_1Pm{)YL0;4K6YgSmJV2$`DJGQ3P{FCHrj zY~NF@ITKmQkJ$cPV^8QyI!(Y49ddrme1H@E3bp3Rw(H3NAIl;E1x^ezPHvLkN}&md z7|Af+fz{zmLpoZJ9Kq(bjO}<$oU#opDAEc%+yW8E0jf(KL(uJnL!KVCvK0FF5*Cj_ z$KA$WWM44LOSG+yDt6-GQwHes07 zy4vC%+3pM4l;!3jCmJ$nxSQ~OJZwM+nUenpjgN(l2YCyEj(E8eltwEGduAC|*scz3 z%QvO-{ntFFi_u|zYs{RZWcS&#K^?(C<3Xmq>rZj=C@I9Bc1XnV)3DAIRQ0wRLQ+!N#4|km$3s7kDFDWDwu=j*XZJQt zV6U$^HJ=bCp5Zn5@i7D)ni)siTstFZI<0e_T7pC(SDCk+HM3%ZLPjNZjr8%kaT9AS zh5J?7NAZa4xE4qZqlyz<@+e~$OJVR>N1Q}e?^aDx;Ol8!YFPm zBHf7DJgZ9ub_kQu0Ol@Yz}9Fh8_5d76X0u7WF>-|KQpax6mqahcH}bp(@aiG_3Y_v zBMg@`gE@N`@3oFFYe&*RfH^}D)h^{ zADJ5#DxezZdMX27^JwyoM*`4>e%E-UKeN20Y-R1l*S5&5H~?p%_S4ntzOI`Dl0RF51bGiExZ^Ub5&vbMXJc8Qg1d%dcl#}3>5 zM5`@O7Og%Mv~ccT7i9x^--Q`Oy?IH-h+J=jV8|K8lClO&=bD#R(i z+yie&60N`#s^{30YXJGO&eDz@U9m|I$mZV8j(eYn_1livevF~zd_&0-VmqVyW}NR* z^xt%;B>#nr5nnd%7+X*U7RPAgsYOSy~IOj-c4B_3$1fb%DSLs zeS*}!vgwUnr`EGxtR`Z%W_`sP*jQiMd97okC9`#V%}g(AIk@uL5Z+$V6Ee-eA+$1m z_zbuhOV1f%0(DHL`PAc+9c86>mIJ%6Dez;jB-L2EE_} zFq&A?jbRq;&=%-QMprju=zZ!pa^#F#jn5q>&(4^4W2gJ&~!W3({<`c^lNUDuhW!#jtQUg<(pP0Uf%%w!%@IcuE@ z*RtSs!^J)ZJ!@+vlD(2e(LX17mS&%$NO87vxd=O%c}6x?>jNyU2jrjqpf~rkPb;|- zti!T~CLyf9+T`Jk&#=*u4@LeOSaYV=ASUx9`Ndee2!%JS(g%MFZc%Cxvfp=_v@nx2UiBM&I)sJ#<+WTfUeP+0jF)5s>X zg=+Zd(S|xOk=-PY+9kLkdG9bIvWg*j(2(4;tTwqx+B5sl?jCwA;-u{^%{G?($w2zM8z_D*m&b`(`D{-AxVvnn&>>6`_B$eA9DgY&SZZ2Wb7&`+THYloTr!In1qmE8YOo_uy*tj8xC5_l z;*U$YxP(}nJ7K61wu*e|3+B;tiI)1g9O`<%V{d0irc|qI(9RNGdci;@=@2c&9WU+e z-@I}a%-);liBF+H^a+&l;*#xPwbEq|Dc~Cb*i_g0FNtUo=1{Nilj)GZ<1T6!-pM(O z#P3ZIe3dWwqh}Qryhe-U9pc2@#^L8EJvst-ol4b|QHRTQJEm#=*zR;R65T{2dH2ZW zi${)JxpesMJNj5Qu{m#Z#^YO*`N<|cPKApxOoFQjmIyr0xb&9E{Wu@Pc=-MoTMC6) zvtrQh{9n;Lha$vDi1AZri2!ZuOI9cDiKM459AMf_%KVG_OL#cw6b8%y^qfxIijC&; zqhoL3&|eTuW^$$Hj*qL#ybA!lj5~?=gh+d7d6u-B^KfOe2S-+|9o{rhX^s7O11>>O zF@9CK{GNfFO&UnjPjU}(^Ds$t$b;x`nxC6=F_ed^e_%8cL?eTHfcr4(4=P-YVq#q7 zA)9bx6X`jCwpFjcIdouXdt1a8e6}>&Na=x_ZYHUN^;2p~xhQF_D=Mg;ZYRzBj}r& zSZ|uFqXC~){fK@oA;ED8(H`GXBS<-qQj{H+L&Z(V*X;&qdR_ykIYXb0&=3+P(_NWq zZ+sFN2J*>T_W=M-gbDWPCQK{);+}z=OXLv*lbHH+&zG_6KR?_4gb)x9=3L+6F@HY) zdFbcx47hXlJ!yEm-rX%EDg|llwHWX2;y%#t>87AN=!<->YZOEI*n1kYlD1vIaUARP zay_xsb*1*vj!xU&b^iMLOsNtb9d>5c(z;H4|K?Sz`bN6DVDXhJ`?qvu06<@zW_xol ztB#r5C+Z)d*bmN;iceEBlL$&ok>38X%_RV}SKo*ga`+tPF$opC4r0G%r{w3`%KWbi zSCv^u+lv+%Y?Rp!QyU0_pvD$)Vd|Lj_^CZki=+kd@$R#RckXOgsxt9U+ z!nE?!*DmNRu|LOX_}2y#?#`-B#Q%N7)>r@ln4JQlb!gPx#UE@P6c0v{euDIqy+kAV zXvX2jdT&i*fVOmos{7mvx!a}H>zu`&*U_>+smn4J|HVWm>G1DC<7|ILaZ%!!G3Koj zVp^Kot}0Xqd$X;EIl`q=Br-{-v#FkVg{MNN%yZ>>$Z}?LlFqDDr~@-4HYotemnh2G zvkhT6ll?7{s+s7q5G((W;&5t>tz4&&2I?g0nz@-Dz{qzHC3@tC9-h{sR}USkC6W*{ zkLR&QZLACgYhNP$B;hCb4=o;EIK8j2RGM4z{*F7Zj`*FUo$c1WE6S>>SNg2G z+gZ)5KfpP|^E>lPxpSUC^ht#A;#EPKv42JH(#Zbt$o^SNn^aK|O{D*m31fve5})|V zt36P7t1UL!GaoxW@e5a86B*xytDjrDCV4}B3_lHjG_CS1$~~Kq^mpv)5NNA0D&X2q zXdOQ98|vz{tCo2+@7m^u=DNE2=H|6z)6mbmY~`AD%lQv}^s(xPTE<<$upgosKketI zCM9O11FiSG3w?1|vU3iXgL9($#`G!pFWJd2Om!4n|$X4Nrag<6wcA`oeWvi@QA zj3}-$S|<5Fbc9NQ)W0%v%)+X^WdCbr8PdY?d`YvO5lYCzy(NB2vi84`H0o~}tCM14 zgqn4Z!pwPAkD5)ERU&l>siRqq?@*={<#$`H@)<2EeX@mdF7{^x`3|B!JcON@297{-{14${e+Js^@Oz2D=ARds`7el*1Yc8B6WdRQV9_{vUj4?#p&F~C2^)`M6)f?(J|+4}mC zK3rD(b-J`~!Q*dt{fERPjR2YRAn|NxChNm_^0ih5~OA&c;bI0Bn`l5Ycj>y|2eG_V{Y3&PJ z^&ZC5o&@xVytCl&KBEaLc&HYzWo+S|xroR>Xe+Zl`|9-u=5650}e|X7ETYgWI zQd`$GuQfSQ6YeSTTT&Oj$Mjzd>_n5$ZM|xBQp~YoR-Ij~0)PG2$35KM+0oHzNXua( z-$Arpd@WOCG+Io4hhL&l*D0j!CX=+iPNA0jB$ZM^PtM-$47y7kL8H+XEOEPn#z8Sn z2}2G{?3|`4k$G8prP1)$5Y==rOwANm@HGRN5`vZ38a`QW^NSVgI=QsPAbFppk2s9Q zCf=%cl$R*@;7VNO#6-9k0{^j_oPp$?)aLvKyx6ifLM#wSypk4oS_=1+k6JTWG z?YS5SEgk^=xeFqRFp=)c%As~gJUswzQpU1``tqOteo%a7;&u%EL_>r2X-<(|^@g<8 z=N1|$r1v1vNZ|GU#O3zPoSuYgt1m~^{rCM3)PV0v)5Tx$%;h5qJ)YC=%aS!%{|IyS zncdFFGu?ZLxP;N~*XJpUUqn}tO!U}nnNoG=^(_R{GK*1TtEvdtYm7!*JWy_{He~?7 z+xs%7YkPV1%zu#RXDJeD#x|ZnfQPX}BJD}zQVD4G^DByMj$hAwTaQKA@s&Wwk~SN1 z%k2X?)^D4H9m=8jB6MqJnYV8jK!Kry84Tgwu-OvWk1UL7cWjb9!Rcx!kW) zm}md$imj@mtmY6CFrwsrO{ChLKf&;X`y{d#$Nmj6-)hSw-__otxS12N zKceur5Y+MB{tW4bBv!dsUP@S1vUE|gS0GI>G0sM@;s5AR*BMeETAs+VM0}=2EQu4T zsuo+R6aY?{sw~ck+||T$#7Gq>{7XJ1a>zLN@cdC{q}Ahls^#zyl#gq>NiRuw$?i^! zk0Ly*bLxES&S;{CA?A=2^ou3w;*50#C$AH9i};gCc68J$0PS;d7%#z47eWbR59a)$ zf9Co8)IhumK>vain^KZUQ2CKaEN77qz<2)Da}VKqlF;GVt?ta+($bo0Y~{5*Qe2p? zw!3xzH{Uc3<%rs%-`W4yDCkKq73C!nVXPkD@xZpv8%-qG>I$WD)B2m?Fmn5>9+PwU zoT!y@;P1bq&>1K-<-opR2Lg8lmsc&TvJE2eVDP}+6bg0#i{7%VYgK#D4#9TYsRk#<$Zlu%`v2r{_ z^(~WH&k^hz3&g%XvJQkpVfvykHOv%eNr_fzPLyk@=*ZlZc^7n0*$!si?3BW z`EPNP0keN!kmwI|bgb9P%V_A_vSLN&)`mfQQS(V%7!YBl9C(Tvhoh7(Bh2dRzrU_G`Qs88X&|u-$AJTld~lkrr}{2lQth&kghm0Lf$B}w zH|uYbT~{#lVKMSl|EO?g=YoX_kNiIA#qkD`_cE8EC-J7O*10MOK92E;yor~6kAp*X zM4{Lq7VaiH_}G)^htl6|R9k#e?uJ>&;%gCK3QIzdK2>p(6#Xz@@ed!*LR2uEg?0oiucBEntU{oB zhFJc*LaCK=;{b^05r({3Kd;Vb$=BgI3%9!|9T8|dZOdR;moA&oQ=Ld1kVVTb(noY zRasMWMPYmT=EH`IJfS*x>A7EP3U>Fu)~Vob1wWv3SuVl4Nrap_^aY{bVQ1+^H&LNb z2!B*&Uz-|HAOgZ3^Xjd|{3cSg)?E)!GQXT?s3lhYTOVUi$0Q zj>y9Z2KliO?hI8n6YrCL-AP-2p*(sl91{>9F0e;@E1!eSpMSDDzV@H13pqfr&hq@iPMdN2h`#qYQy)dIB#*jl5tN zoeHsTN9*!dmEHd_!#BLbWikpef+Y$;sNt4|lLO9HhQ@3PrT+*X^z zt#n_%o)O&16WiG>>{hys5C*{*q%}= z!W{LrUfS%9L&K+b!e|4={C?+0pLJwTC-;C}F?nSxb*K))U^pJCjH+08r^O1hYlr01 zV;)`1qWIO-=Kw@`Fw8^2f0z5ocRt2F80yBSa5sv1@Q*|O-6mpiAH|P;WLkbb6(S+* z(IR6l!5ZJY@uP2P&aVgHFo>isG}NNd#|JFz*G9z(PR@657DUhD_@9;zb8v;XoQdx? zK|w|Jf#t@2fH$Ft3eEe(`BhvN9pZ3Y-{yUesu0YN8>}4YKV1rX(AXD>$PD>2AMC^+ ze>07?%(wV7p9x!g9T=5*%4a-&p8u>gC+rmHa0RnF>%YDzXa8(OeGQ3C*7mn zv_BLMl)xB7i-Su7a43v~93fUmQbK}bOy(6P^ED>x3Tw;jP4cKb?U8+AZTt^OvgtaB zd9f(gj4gfu{1OBkS1*WJxkJ3__&doXk+d7a%xz*d4D%6MrD=UP9Rm3;f5tYo8bbIe zSZQ9*X$gN70KcHEBeW=@!6@O;t7866ARr8)w^S^B$YMPV#<$QQK18f_&Uen|ALBo) zNVx98*ExnSW38hR9|FpQ%PxNEYdeh{xi`}eSC9v<`uPy^YweQZ%AItazjpd|DtMhv zR1C$Cl#?<3p4O9Y(DC5h8{^wW2r9zw<02F(zO2dfM1Rrf|DudS{#PeJ$VRd*^C-0A zAWnf3#C|SIUi_5Fb>zGTA=xmbR%dJrjRdbyp$hZ*9@d}XuHGjt|Eankr?6--^J;zY+b%55~>I`)q9~)z_#-doSe|-kYeVSf^$$4n5 z*WP#Ms8XrW6vx>2TpRFw9|@uAP|iZ}heQaHh2O`s5Z}@m$sS97EA#by2u>=wOGx`6 zYV}8bEW_G3L<~q{@W}x|bg@~JZ#l?BNm;cv#?>UBm zp95g?nxatFD=g+^rYj`j76AZ?v%QNA7HRM60i_G7={ipRr)oWSmCZF-Hz@k#NefuBnkjbmIy-!yE4qjY-V?wu~W=vkiD>e;9-#Jjx4a~5^E zdU(q`=4&+Ol5-3@Sv!PkJEDaX&hN_XXF7TyRLC=cshQNEjO+xax(5TawOv}K+e@qSHQrGXXc|63sFx%C68VY`2E#oZyz2;*ys<@h_5k_ zpM%}P?A@~Lb1jtqcngW2{h7PAoX<&@Z2 z6m<32EHj^{(P*O8vA+~W4x~~~jnk?3q%;&Xv)yS7ae+C{9E{ANlxVY=PM(glvwS?W zg^QCQBa3U&`yURAxDY!(JEAyNR9HD7lRaeLFK;ulItS1alnCOdA_iu>F^^JFdugV~ zIq_?0{;ESG)|u>u?T8Pau!t;vDBwAcY0fW>`9CnlC93!;kAdeAu9tt2L`-h|^Vze1 z9;vqc_0R4X5AXc=`{C1XlIt)1{H*UsXok?PmNd2#E z4qKtDQkd9{!}z2i{J^IoMMo}qI4q|HZOGu$odT~K`Awo_5`+*f61e=$`{#zN`7o9& zMTG4Y^uQOSA)n&tG@WF)qi_VZz5XYvZPGgURPHF6#BCbqmaE&U6l^fmahc)k)p9f5 z(Kc*n|4wIzz4mOY!>RYjAf#fwuJ1F29?V3Y!$&gomGyx zNa=QIr+@1=Yhn#9m#z9{If(iDHB;dC-^P#o z?Q}g|FK1P&x;h^AyX|JZDzU&K&m<*~fZ*wJF`rH2C=4v$@;%qFn`^eVQLt^tw#xQ# z(RvHhDM6WEe`MbEi5DS)Fg}CoQ>6eL+s^fe;qdPvF5&$I-`6z#b8#jdXyv1#Gy4QP zkQJ5DmJI;G6K&v%Pc zT~x(!y9Om3=;fpGTH(g|^+&Y%#3M)u$-{sI#>4?;x`65f)rb*21z3;0D}DaR0`wu= zLS&1sQ=SLb@z!nGRy^MS3Z3Bo9`Up~XW_?OT zRB%3&V-Q>c8dfOHq$!g+<)m#U?eZN(6bCm=?veA@kG7S5S^Rc{pH=H&_Qc(G_=7GZ z#dJ(wA%3TLSV9q#=1zn_Kjf6xEX(@M`t8-qTdJBFJkU;<(JM}Q1;8<0f4JShUQRW@7r}8n@5RZ!$S>Lv z1;h&6KwIjFDc07rYXiJ6N6#ZBlXii&eV}==o^LKPm~E=MEERf$@WkJC_`Ry%q>^JU zu_C4%(^B)ykPGcZO7!cK@?I@rsC}dM^{=I`&rc6GmnTQt_;EW``<=>BqzhI>#Ruv3 z4$BvlKltv)@?;*p56~n?HNJe2#gzd<5)~O$Pf((}g9fg==UVyup<&K>LR(QK)W`t0 z-~&|e`BP&fs2GzEV2q&`Sp#`DACoj@+X>nj=CK6T-FVo$d|i59kV0KY6cJ@!fgg~pP&k-Rb3xU^a0P?VWYUV@o z(?=^icZ&qT$x1Kz!mOaRbJYV%mR-QNSjuxi6r_tN&usd-n5I9ts*1GIyz zs%aKc@2msj>rq$LvKTDKN&=5%F}>jXC2lhRod2hf#^%!>8yGovPLPQ7+RD5Rfx3bj zdkXoqP$Fo?uw15$$OGjZ6SOPkX7|i57P$f;W1fx8f+IAwm!2{C(|QZ3TA{T9rC?~g zBRz7V>iV3V*NDmOeMwEXM7YjV zdF-C;r2BA;GdlN8 z3DF0LCc&<2HHV(G^$K1{3We@A-0iCR!<0beuFZWxvX_t0h4PdDS%7>1Z){6gh0AT| zT!l3JIERf=y8=zUQqsY#u)rCf_R>YAeF!CH(PIj0PIRGmx!lxu*e@|MQwAjS82TexRwC*&G@USE{@xJ&@K$N2|Q#NKWfWmO0$q z=8!5tqaao&Qr0xWtbX2NraC$MpHiwd#%Rfi&-p*c=Rr3Ekc#X2S9(wmer(=2W4gwYlqq@AYT{Bf!WV0`W72?vkn1Kh1jrs3QqchKd zvpSrRsGASVTh>i$B>adqs(N-(?-jKcY3T(pAlj;O1)wWiuIhZ2>6hc_)zh-xZ^u`% zmt(RZPELByvZA*!8R>EC`k;<+=1)s#q@*-?9T{?FpYc^f2)c^MC+)3P8E+ZJd)sS$ zWFrjGtTX`*LvjCsLK7_`Wq-SvXRfLhRE;f#9y6lO#u|Sv(q*iL@ySwJFF~9l*`-)q z;3`k1?WCPfDw5%A5^HE_UHc3jp+RVc0$I-wdNj>UF`10C43SWS*_mov+=88>-`&6$ z`TtN-`olD&gz410SC*8cUe(Y~?dbizT4i|u#{1X5N~>?x*Qba3+spIh;C`)-yKRvH zUoKb$WnV}S;Xw`bOZM$@^cA89kD+ElPJ-3og9p@BdI}+|q)}VZi!nQMFNbpk91lmJ zFDx~#Z8AgLU#}$XiT#>5`o>F$q{U;A2DXvwrlO{OE<S|=74j28fEcWB$f_Kw~9e%Y{JH^lZW<|bYPN%VlWm__{XQs9Z*t}+_% zrSFbgG$`i+4E@S2fM6HojP0Og=0gA*Lv1`cv`Px0#s7xbO(dK`aBLJL2=AzjTnGD$ zsi(iXs^0nNB$Wku4dIb9S((m=ix4HZo}EsQW{)h0GZgSbTE9MtCO}j206`R)L?2bS zB&j$ml@Gd7rH~Oir8CLX=X&o~rY68T&uOdgJ_Hyrr30Q z$8FXA68{nnFQ4mGs3$PW-1!NKnW|jS#U``nymnTEQ6-Ify`@?f7+id8bY24ThQXX} zp|O)#VgYy-dI?}O6rF_aeC$G(E)hN3G(pCn#zrXcDG8t7NlkMnwZCNTBg7jn8_1|F z(w2+#2gEX4f13Uw5T{idnAl{7w*o6Hue*W_Z)yV-&Dh6*HUZz!s9_uEO>B}R)G$a< zDpvw2q1XfMLFJUR7(e}-R!qT9CAcG+uJYsux+{Kx+v|%np7@H?*~2Q@1*NRu@Li)V z1R?#%+!ef@C{~a)-go~fGxBaE6OoBgMnroF)kn_udWEo?Q}ku`FBy$1^(=yhEu+L; z0C2h_L9GF44wmsPwIvQ=UO4mGq)mAXv9QNkBPb#MITvr5@)<^BR5tn%4foKigz!iy z`w!rR$5mfcFHODPlOI&e!j!6GB>6jO$!NwfB{B(y6#!h~tx0^l0AM!G<|yWUG2|D| zOHAa9K2hol=G*zB1P7&IB=|yVc#UjVkqu zW7wqcmxsE}8K&!q$sC|T5UNwv*%iP5F}{?Rn!Q()jN3aOOn0+8;bLs~q>4l0E!s4L z=u5M@R;b=);1u$_ntu_7o1LB`_(28bV%O;cg#M4;e(oM9SP zWFus~cwDpt5Mn20$b#<%lCU}(=Q(PWOyyb>j73J&HId7>sT=D_D%4E|h&y}K?IPa2 zF|wu&YNbA8{jE#UN!Y$otfZ$$rrPb9>H3K1HwZBp7E(|x(;5ijE16dFtPvt9p3=z@ zfvYv=r?oCRD%H^<(7|1e+1q8(pfu&>Lcf_oS5F0np{rCimf9uV{X3}edhcKCpB-(X z1>dB524L36CSijJ7czge_Eg#NC-w}0LXqm2sqxJj8v=N6DhiQovNW;<%pMTQNiCF8 z$}9A}d=cm>A)A&L>K1*%#+n@RFRor+G;LFP7xVQ{V0zR+Uq|f7gWG~iA7IbBcFL~( zer@+iF~n4iXA46^1sR4-mJ=F7@r;ijps^Jx`WHVt;mO_w2sskve1Xv}I_O0iRI5cxf{uq-hVF zCdcmMSr5Y$*bT z1v_O61K^lNIpsOZ4W-wj$;cnsUVdNJC>TOx{44)Xk1ZJ8QxJJtJ;M;ML)hp|p2kpS zC6kD0$h3wvEg4;z{LqyxRSQ>Zx1e*}&ou!?2?(9l&(0ZKvwE&rL%~|9Xqniix9}R z;&+NWux{O49EB^Mz8xfYm-Rot`0dcGIOWb|BtlP>rJ z?GHzj#1VWDfsz6aD5d4)i$GT%H*4pTC15$LM}^l4rpwh~(lFP^A`b)tx?0n3Cokc4@)D*KSnmE; zR2aI-{yz=uNGCW5E&H*6KZ0?-JJt|4_e}d7_Wk9#A2Cw0mllfCEsK{v#(1SX7Fok61=zv#K+%e4Rs18>#v7}Z81+iL zVD5kw$S;I`F30F(ab(y;m>;G6)#N-6RM03{ULve}4Zx&@f4eb}hVozohdsP6{0IYzZ=xY{8A zs*q7aN!>w`Ca%NTe*F;{qlwOWLbI0EGN5-7PU4qQ%5$6duY?RQIw6kQDkRUGnSyWD85j-uG3VB%nABKLXVdFTd{1T zl)F38t|o2^z=!7}-Co#EJSxMC;kSLYhxla#0SI3VqgDF%mitrTFH$8x2f*iMf&qZ-AeU=v<_>=J;JS|z8r>ow0Kj$_6D0r|vY~I7Y>=`|36G-X)&&udQ zB62@&;d_LAll46qgMV3g?;CWDWnV--@-r>#Kz`Gss|%|5=AAy%@&7CK8c?k@LU}xl z|4qN!KBodiBIW1Z;K5bu;bH*@-wTr;KRi4~)3e4ySKD+509VUTyxFYG>#UIPT}7*+ zz!eXRq^VqmGVpMZC9Ow#!a()m)}Oh019B}}J!N?VticQPVH!As3oyX6*1eyxENKC2 z$STi@=#xd&xew9D1MN?MIWjz&3m7J$t;e_D%?|=9e3+jKo~)wI?2yYOb91p!_zDZX z8i9A{-S_fUqGc;Wz*Prg+7%UHQNmIKZm5ipf&_?cjD$-D0N#Oe1H^Xi1_TvVHz3(- zzX64&z8g@fN8CV(W*OfeDjYKv(CdfMA9pNUYK2_Bq-|`_W-4?fY734o=v8LZV@kh? zW>#piFl93%ZDb>xwH)6<>YULruyPSCpG|W&L+9Qi+tTitqvH>ARcBi2pQlfGF;mW! zzGkrBeC=bTNp`fPrFPJbaN2vc7A42BzUd<+^s$PlFOi9C^D_*5f$Y3DxhhWxcC5_a ze?sFb-~fSSSU6IUib_aBI%Hsgu~7=7Aq&}H5)5BbK~+?PBQlM|z{C&r$HEO4Xuc@XV#JCQFQGFM3vMjH zTgg%iu^M8UbQw*tUY2Y*a^=ZaKuF}c6HYd*!A?2tjI+);-*o$3bjf8`l6BQJV%HTa zQrt|Zl{U*+RF3?=|vxU*+{Rv_QqT9;``ADAAR!K7hhZK58wUp(=WgM@z+29 z$@R6=Ljwj4wcJx9MveJU1(SFYorDa8o@ z5h;@^D5aIPjjf%%gQH6AGXle)ClE3IFycWz|*K;^Z(dk64L}^l0!Zm$HYL} z9D@dN(y>CQ^R$zhpy;d+g{ssa*mpOy06~s30duD`qd)2Bn_SPijXZqG6ZR(-UTI{*J>oa)4%LQ#K(^YF_D91Y`?ANG~Kh`Qk9L;h|DOV9;y) z8<2es5ECQB6MJF*E(iv$Kb{a1du;+I4hLd@`!)#Z`u-GXK=5%pWXT{2_D9fMC#r8y z3tBM+g?g$UDjm~RG#HR0QKZ%y{6v3RKmJemKkU9T{Gke7tHXcSf9w7mvhe3JwpIW6 zV$(BpJ#IHQRYGTmzJHbX>>tA5mAy!-+cA00uW)3 z`t(u;d3W2fh%~FNaAe7N$6xUKC;CG8ogI=3@~iUeFNm-I6R;ig7OqrR!r8a@EFOm! zfgt^R;*T5B~oX z5xwH;wEsoLO&(5C=Sy}s=x=HT&D{FEZ8~{hHD_!3N>BYu-Vx8ISALD>u@t}kL#oRE Lz4VG~-gp84*bzN^ literal 0 HcmV?d00001 diff --git a/fonts/XRXV3I6Li01BKofIOOaBXso.woff2 b/fonts/XRXV3I6Li01BKofIOOaBXso.woff2 new file mode 100644 index 0000000000000000000000000000000000000000..711fcdbf8bd4a6e7b92e22510ebc18177df8db5a GIT binary patch literal 17332 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 literal 0 HcmV?d00001 diff --git a/fonts/XRXV3I6Li01BKofIOuaBXso.woff2 b/fonts/XRXV3I6Li01BKofIOuaBXso.woff2 new file mode 100644 index 0000000000000000000000000000000000000000..5ce4c48b3bbf09b04c97e27da2b20eb96e670649 GIT binary patch literal 7820 zcmV;79&_P$Pew8T0RR9103M705dZ)H08X?303Is<0RR9100000000000000000000 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 literal 0 HcmV?d00001 diff --git a/forgotpassword.php b/forgotpassword.php new file mode 100644 index 0000000..49a447c --- /dev/null +++ b/forgotpassword.php @@ -0,0 +1,54 @@ + + + + + + + + Forgot Password Page + + + + +

+ + + +
+ +
+ + +
+ + + + \ No newline at end of file diff --git a/get_cart.php b/get_cart.php new file mode 100644 index 0000000..d7e6735 --- /dev/null +++ b/get_cart.php @@ -0,0 +1,66 @@ +'; + +include 'includes/startsession.php'; +if (isset($_SESSION["userid"])) { + include_once 'includes/database.php'; + $userId = $_SESSION["userid"]; + $shoppingCartItems = mysqlidb::fetchAllRows("SELECT * FROM shopping_cart INNER JOIN product ON shopping_cart.ProductId = product.ProductId WHERE shopping_cart.UserId=$userId"); + if (count($shoppingCartItems) > 0) { + foreach ($shoppingCartItems as $cartItem) { + $cartItemImage = explode(",", $cartItem["ProductImage"])[0]; + echo ' + '; + } + echo ' +
Checkout Now
'; + } else { + echo '
+
shopping_bag
+
Your shopping cart is empty!
+
+ '; + } +} else if (isset($_SESSION["employeeid"])) { + echo '
+
shopping_bag
+
You cannot have a shopping cart as an employee!
+
+ '; +} else { + echo '
+
shopping_bag
+
You need to login first!
+
+ + '; +} diff --git a/helpers/randompassword.php b/helpers/randompassword.php new file mode 100644 index 0000000..d885f7b --- /dev/null +++ b/helpers/randompassword.php @@ -0,0 +1,12 @@ + + + + + + + + + + + Inbox - Chapalang Clothes + + + + + + + + + + + +
+
+ +
+
+
Inbox
+ Unknown error occured
'; + } + if (count($conversations) > 0) { + + echo ' + + + + + + + '; + + foreach ($conversations as $conversation) { + echo " + + + "; + } + + echo ' +
Last MessageShop Name
+ + {$conversation['MessageBody']} + +
Posted on " . date("g:ia d/n/Y", strtotime($conversation['MessageTimestamp'])) . "
+
+
{$conversation['ShopName']}
+
'; + } else { + echo "

Your inbox is empty!

"; + } + ?> +
+
+ + + + + + + + + + + + + \ No newline at end of file diff --git a/includes/database.php b/includes/database.php new file mode 100644 index 0000000..4bb216c --- /dev/null +++ b/includes/database.php @@ -0,0 +1,113 @@ +error; + } + + static function getAffectedRows() + { + return self::getConn()->affected_rows; + } + + static function query(string $queryString) + { + $result = self::getConn()->query($queryString); + if ($result) { + return $result; + } else { + throw new Exception("Query $queryString: " . self::getError()); + } + } + + static function multiQuery(string $queryString) + { + $results = []; + if (self::getConn()->multi_query($queryString)) { + do { + if ($result = self::getConn()->store_result()) { + while ($resultSet = $result->fetch_all(MYSQLI_ASSOC)) { + array_push($results, $resultSet); + } + $result->free(); + } + } while (self::getConn()->more_results() && self::getConn()->next_result()); + + return $results; + } else { + throw new Exception("Query $queryString: " . self::getError()); + } + } + + static function lastInsertId() + { + return self::getConn()->insert_id; + } + + static function escape(string $string) + { + return mysqli_real_escape_string(self::getConn(), $string); + } + + static function fetchAllRows(string $queryString) + { + $result = self::query($queryString); + $rows = $result->fetch_all(MYSQLI_ASSOC); + $result->free_result(); + + return $rows; + } + + static function fetchRow(string $queryString) + { + $result = self::query($queryString); + $row = $result->fetch_assoc(); + $result->free_result(); + + return $row; + } + + static function checkRecordExists(string $queryString): bool + { + $result = self::query($queryString); + if ($result->num_rows > 0) { + return true; + } else { + return false; + } + } +} diff --git a/includes/employee_session.php b/includes/employee_session.php new file mode 100644 index 0000000..b822356 --- /dev/null +++ b/includes/employee_session.php @@ -0,0 +1,7 @@ + + \ No newline at end of file diff --git a/includes/footer.php b/includes/footer.php new file mode 100644 index 0000000..5d7f1d3 --- /dev/null +++ b/includes/footer.php @@ -0,0 +1,44 @@ + + \ No newline at end of file diff --git a/includes/footeremployee.html b/includes/footeremployee.html new file mode 100644 index 0000000..6d88b1a --- /dev/null +++ b/includes/footeremployee.html @@ -0,0 +1,44 @@ + + diff --git a/includes/pagetopbar.php b/includes/pagetopbar.php new file mode 100644 index 0000000..9708b5a --- /dev/null +++ b/includes/pagetopbar.php @@ -0,0 +1,18 @@ +
+
+ +
+ +
+ Vendor +
+
+
+
\ No newline at end of file diff --git a/includes/pagetopbaremployee.php b/includes/pagetopbaremployee.php new file mode 100644 index 0000000..d7ae8b5 --- /dev/null +++ b/includes/pagetopbaremployee.php @@ -0,0 +1,28 @@ + + +
+
+
+
+
+
+ + | + Logout +
+
+
+ +
+ Employee +
+
+
+
+ + + diff --git a/includes/pagetopbarorderinfo.html b/includes/pagetopbarorderinfo.html new file mode 100644 index 0000000..8f58473 --- /dev/null +++ b/includes/pagetopbarorderinfo.html @@ -0,0 +1,18 @@ +
+
+ +
+ +
+ Order Information +
+
+
+
\ No newline at end of file diff --git a/includes/pagetopbarrefund.html b/includes/pagetopbarrefund.html new file mode 100644 index 0000000..8f10f34 --- /dev/null +++ b/includes/pagetopbarrefund.html @@ -0,0 +1,18 @@ +
+
+ +
+ +
+ Refund Application +
+
+
+
\ No newline at end of file diff --git a/includes/sessioncheck.php b/includes/sessioncheck.php new file mode 100644 index 0000000..855b436 --- /dev/null +++ b/includes/sessioncheck.php @@ -0,0 +1,7 @@ + \ No newline at end of file diff --git a/includes/store_scripts.php b/includes/store_scripts.php new file mode 100644 index 0000000..d03705c --- /dev/null +++ b/includes/store_scripts.php @@ -0,0 +1,2 @@ + + \ No newline at end of file diff --git a/includes/storetopbar.php b/includes/storetopbar.php new file mode 100644 index 0000000..bf4debb --- /dev/null +++ b/includes/storetopbar.php @@ -0,0 +1,82 @@ +
+
+
+ + '; + } else if (isset($_SESSION['employeeid'])) { + echo ""; + } else if (isset($_SESSION['vendorid'])) { + echo ""; + } else { + echo ""; + } + ?> + +
+
+
+ + +
+ + shopping_cart + + $cartItems
"; + } + ?> + +
+
+ + \ No newline at end of file diff --git a/includes/vendor_session.php b/includes/vendor_session.php new file mode 100644 index 0000000..7520912 --- /dev/null +++ b/includes/vendor_session.php @@ -0,0 +1,7 @@ + + \ No newline at end of file diff --git a/index.php b/index.php new file mode 100644 index 0000000..ce3184b --- /dev/null +++ b/index.php @@ -0,0 +1,164 @@ + 0 ) GROUP BY product.ProductId, orderitem.ProductId ORDER BY `TotalSold` DESC ) t1 LEFT JOIN review ON review.ProductId = t1.ProductId GROUP BY t1.ProductId ORDER BY t1.TotalSold DESC , t1.ProductId ASC LIMIT $offset, $perPage" +); + +$trending = mysqlidb::fetchAllRows( + "SELECT COUNT(*) as Purchases, orderitem.ProductId, product.ProductName, product.ProductPrice, product.ProductImage + FROM orderitem + INNER JOIN product ON product.ProductId = orderitem.ProductId + WHERE product.ProductStatus='Approved' + GROUP BY orderitem.ProductId + ORDER BY Purchases DESC LIMIT 6" +); +?> + + + + + + + + + + Chapalang Clothes + + + + + + + + + +
+ + +
+ + + +
+
+
+ + + +
+ +
+
+ + \"\" +
+
+ {$product['ProductName']} +
+
+ " . number_format($product['TotalSold'], 0) . " sold
+
+
+ RM + " . number_format($product['ProductPrice'], 2) . " + +
+
+ " . number_format($product['ProductRating'], 1) . " + star_rate +
+
+
+ "; + } + ?> +
+ 0)")["NumRows"]; + if (($numrows / $perPage) > 1) { + ?> + + +
+
+ + + + + + + + + + + + + \ No newline at end of file diff --git a/item.php b/item.php new file mode 100644 index 0000000..5280a6d --- /dev/null +++ b/item.php @@ -0,0 +1,255 @@ + + + + + + + + + + + <?php echo $item['ProductName']; ?> - Chapalang Clothes + + + + + + + + + + + +
+
+ +
+
+
+ + +
+ + \"\" +
"; + } + + ?> +
+
+
+
+ +
+
+
+ 0 ? 1 : 0); + $emptyStars = 5 - ($fullStars + $halfStars); + + for ($i = 0; $i < $fullStars; $i++) { + echo 'star'; + } + for ($i = 0; $i < $halfStars; $i++) { + echo 'star_half'; + } + for ($i = 0; $i < $emptyStars; $i++) { + echo 'star_outline'; + } + ?> +
+
Sold
+
+
RM
+
+
Quantity
+
remove
+
1
+
add
+
in stock
+
+
" class="product-addtocart w-3 flex btn-inset"> + add_shopping_cart +
Add To Cart
+
+
+
Product Description
+
+
+
+
+
+
+ +
+
+
+ About +
+
+ + mailContact + +
+
+
+
+ Product Reviews +
Add Review
+
+ +
+ + + +
+ star_outline + star_outline + star_outline + star_outline + star_outline +
+
+
+ + +
+
+ + +
+
+ +
+ '; + } else { + echo ''; + } + } else if ($isEmployee) { + echo ''; + } else { + echo ''; + } + + $reviews = mysqlidb::fetchAllRows("SELECT review.*,user.UserImage,user.Username FROM `review` INNER JOIN user on review.UserId = user.UserId WHERE ProductId=$productId ORDER BY `review`.`ReviewDate` DESC LIMIT 25 "); + + if (count($reviews) > 0) { + foreach ($reviews as $review) { + + echo '
+
+
' . $review["Username"] . '
+
' . $review["ReviewTitle"] . '
+
'; + + $fullStars = intval($review['ReviewRating']); + $emptyStars = 5 - $fullStars; + for ($i = 0; $i < $fullStars; $i++) { + echo 'star'; + } + for ($i = 0; $i < $emptyStars; $i++) { + echo 'star_outline'; + } + + echo '
+
+
' . $review["ReviewComment"] . '
+
+
' . $review["ReviewDate"] . '
'; + + if ($isEmployee) { + echo ''; + } + + echo '
+
'; + } + } else { + echo '

There are no reviews for this product.

'; + } + ?> +
+
+ + + + + + + + + + + + + + \ No newline at end of file diff --git a/login.php b/login.php new file mode 100644 index 0000000..fbb3d4e --- /dev/null +++ b/login.php @@ -0,0 +1,86 @@ + + alert('Username and Password does not match! Please try again.'); + window.location.href = 'loginpage.php' + "; + } + } + } elseif ($_POST["userrole"] === "vendor") { + + if ($vendorresult = mysqli_query($con, $vendorlogin)) { + + $rowcount = mysqli_num_rows($vendorresult); + while ($row = mysqli_fetch_array($vendorresult)) { + $vendorid = $row["VendorId"]; + } + + if ($rowcount === 1) { + session_start(); + $_SESSION["mySession"] = $username; + $_SESSION["vendorid"] = $vendorid; + header("location:vendor_profile.php"); + } else { + echo ""; + } + } + } else { + + if ($employeeresult = mysqli_query($con, $employeelogin)) { + + $rowcount = mysqli_num_rows($employeeresult); + while ($row = mysqli_fetch_array($employeeresult)) { + $employeeid = $row["EmployeeId"]; + } + + if ($rowcount === 1) { + session_start(); + $_SESSION["mySession"] = $username; + $_SESSION["employeeid"] = $employeeid; + header("location:employee_approval.php"); + } else { + echo ""; + } + } + } +} diff --git a/loginpage.php b/loginpage.php new file mode 100644 index 0000000..bb6de75 --- /dev/null +++ b/loginpage.php @@ -0,0 +1,61 @@ + + + + + + + + + + Login Page + + + +
+ + + +
+ +
+ + +
+ + + \ No newline at end of file diff --git a/logout.php b/logout.php new file mode 100644 index 0000000..02b079e --- /dev/null +++ b/logout.php @@ -0,0 +1,5 @@ + + + + + + + + + + + + + + Order Information + + + + + + +
+
+ +
+
+
+

location_on Delivery Address

+
+
+ +

$userinformation[Username]
+ $userinformation[Email]

+
+
+ $userinformation[Address] +
+
+

EDIT

+
+ "; + ?> +
+
+ +
+
+

shopping_bag Products Ordered

+ arrow_right_alt +

OrderID :

+
+
+ + + + + + + + + + + + + + + + + + + + "; + } ?> + + + + + + + + + + + + +
Item ImageItem NameShop NameShipping StatusUnit PriceQuantityItem Subtotal
$row[ProductName]$row[ShopName]$row[OrderStatus]RM $unitprice_format$row[ProductQuantity]RM $itemsub_format
Order Total +
Shipping Fee + Tax
Total
+
+
+
+ + + + + + + + + + + + \ No newline at end of file diff --git a/phpmailer/Exception.php b/phpmailer/Exception.php new file mode 100644 index 0000000..b1e552f --- /dev/null +++ b/phpmailer/Exception.php @@ -0,0 +1,39 @@ + + * @author Jim Jagielski (jimjag) + * @author Andy Prevost (codeworxtech) + * @author Brent R. Matzelle (original founder) + * @copyright 2012 - 2017 Marcus Bointon + * @copyright 2010 - 2012 Jim Jagielski + * @copyright 2004 - 2009 Andy Prevost + * @license http://www.gnu.org/copyleft/lesser.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()) . "
\n"; + } +} diff --git a/phpmailer/OAuth.php b/phpmailer/OAuth.php new file mode 100644 index 0000000..0271963 --- /dev/null +++ b/phpmailer/OAuth.php @@ -0,0 +1,138 @@ + + * @author Jim Jagielski (jimjag) + * @author Andy Prevost (codeworxtech) + * @author Brent R. Matzelle (original founder) + * @copyright 2012 - 2015 Marcus Bointon + * @copyright 2010 - 2012 Jim Jagielski + * @copyright 2004 - 2009 Andy Prevost + * @license http://www.gnu.org/copyleft/lesser.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 http://oauth2-client.thephpleague.com + * + * @author Marcus Bointon (Synchro/coolbru) + */ +class OAuth +{ + /** + * 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/phpmailer/PHPMailer.php b/phpmailer/PHPMailer.php new file mode 100644 index 0000000..2d4156f --- /dev/null +++ b/phpmailer/PHPMailer.php @@ -0,0 +1,4836 @@ + + * @author Jim Jagielski (jimjag) + * @author Andy Prevost (codeworxtech) + * @author Brent R. Matzelle (original founder) + * @copyright 2012 - 2019 Marcus Bointon + * @copyright 2010 - 2012 Jim Jagielski + * @copyright 2004 - 2009 Andy Prevost + * @license http://www.gnu.org/copyleft/lesser.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 = 'root@localhost'; + + /** + * The From name of the message. + * + * @var string + */ + public $FromName = 'Root User'; + + /** + * 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 http://sprain.ch/blog/downloads/php-class-easypeasyics-create-ical-files-with-php/ + * @see http://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 auth type. + * Options are CRAM-MD5, LOGIN, PLAIN, XOAUTH2, attempted in that order if not specified. + * + * @var string + */ + public $AuthType = ''; + + /** + * An instance of the PHPMailer OAuth class. + * + * @var OAuth + */ + 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: + * * SMTP::DEBUG_OFF: No output + * * SMTP::DEBUG_CLIENT: Client messages + * * SMTP::DEBUG_SERVER: Client and server messages + * * SMTP::DEBUG_CONNECTION: As SERVER plus connection status + * * 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 SMTP connection open after each message. + * If this is set to true then to close the connection + * requires an explicit call to smtpClose(). + * + * @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 http://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: http://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 languages. + * + * @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.1.7'; + + /** + * 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 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 (ini_get('mbstring.func_overload') & 1) { + $subject = $this->secureHeader($subject); + } else { + $subject = $this->encodeHeader($this->secureHeader($subject)); + } + //Calling mail() with null params breaks + if (!$this->UseSendmailOptions || null === $params) { + $result = @mail($to, $subject, $body, $header); + } else { + $result = @mail($to, $subject, $body, $header, $params); + } + + return $result; + } + + /** + * Output debugging info via user-defined method. + * Only generates output if SMTP debug output is enabled (@see SMTP::$do_debug). + * + * @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($str); + + 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 + 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 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 addOrEnqueueAnAddress($kind, $address, $name) + { + $address = trim($address); + $name = trim(preg_replace('/[\r\n]+/', '', $name)); //Strip breaks and trim + $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; + } + $params = [$kind, $address, $name]; + // Enqueue addresses with IDN until we know the PHPMailer::$CharSet. + 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); + } + + /** + * 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 http://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 + * + * @return array + */ + public static function parseAddresses($addrstr, $useimap = true) + { + $addresses = []; + if ($useimap && function_exists('imap_rfc822_parse_adrlist')) { + //Use this built-in parser if it's available + $list = imap_rfc822_parse_adrlist($addrstr, ''); + foreach ($list as $address) { + if (('.SYNTAX-ERROR.' !== $address->host) && static::validateAddress( + $address->mailbox . '@' . $address->host + )) { + $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)); + if (static::validateAddress($email)) { + $addresses[] = [ + 'name' => trim(str_replace(['"', "'"], '', $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($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; + } + if (is_callable($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! + * + * @see http://squiloople.com/2009/12/20/email-address-validation/ + * @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 http://www.whatwg.org/specs/web-apps/current-work/#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)) { + $domain = mb_convert_encoding($domain, 'UTF-8', $this->CharSet); + //Ignore IDE complaints about this line - method signature changed in PHP 5.4 + $errorcode = 0; + if (defined('INTL_IDNA_VARIANT_UTS46')) { + $punycode = idn_to_ascii($domain, $errorcode, INTL_IDNA_VARIANT_UTS46); + } elseif (defined('INTL_IDNA_VARIANT_2003')) { + $punycode = idn_to_ascii($domain, $errorcode, INTL_IDNA_VARIANT_2003); + } else { + $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 && 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( + '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+.', + 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) { + $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->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) + { + $header = static::stripTrailingWSP($header) . static::$LE . static::$LE; + + // CVE-2016-10033, CVE-2016-10045: Don't pass -f if characters will be escaped. + if (!empty($this->Sender) && self::isShellSafe($this->Sender)) { + if ('qmail' === $this->Mailer) { + $sendmailFmt = '%s -f%s'; + } else { + $sendmailFmt = '%s -oi -f%s -t'; + } + } elseif ('qmail' === $this->Mailer) { + $sendmailFmt = '%s'; + } else { + $sendmailFmt = '%s -oi -t'; + } + + $sendmail = sprintf($sendmailFmt, escapeshellcmd($this->Sendmail), $this->Sender); + + 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); + } + fwrite($mail, 'To: ' . $toAddr . "\n"); + fwrite($mail, $header); + fwrite($mail, $body); + $result = pclose($mail); + $this->doCallback( + ($result === 0), + [$toAddr], + $this->cc, + $this->bcc, + $this->Subject, + $body, + $this->From, + [] + ); + 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, + [] + ); + 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) + { + // Future-proof + 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) + { + return !preg_match('#^[a-z]+://#i', $path); + } + + /** + * Send mail using the PHP mail() function. + * + * @see http://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 = implode(', ', $toArr); + + $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: http://www.exim.org/exim-html-current/doc/html/spec_html/ch-the_exim_command_line.html + //Sendmail docs: http://www.sendmail.org/~ca/email/man/sendmail.html + //Qmail docs: http://www.qmail.org/man/man8/qmail-inject.html + //Example problem: https://www.drupal.org/node/1057954 + // 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)) { + $params = sprintf('-f%s', $this->Sender); + } + if (!empty($this->Sender) && static::validateAddress($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); + $this->doCallback($result, [$toAddr], $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; + } + + /** + * 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 (!$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]]; + } + } + + // 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']], + [], + [], + $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); + $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 have openssl extension + // * we are not already using SSL + // * the server offers STARTTLS + if ($this->SMTPAutoTLS && $sslext && 'ssl' !== $secure && $this->smtp->getServerExt('STARTTLS')) { + $tls = true; + } + if ($tls) { + if (!$this->smtp->startTLS()) { + throw new Exception($this->lang('connect_host')); + } + // 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; + } + + 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. + * Returns false if it cannot load the language file. + * The default language is English. + * + * @param string $langcode ISO 639-1 2-character language code (e.g. French is "fr") + * @param string $lang_path Path to the language file directory, with trailing separator (slash) + * + * @return bool + */ + 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 (isset($renamed_langcodes[$langcode])) { + $langcode = $renamed_langcodes[$langcode]; + } + + // Define full set of translatable strings in English + $PHPMAILER_LANG = [ + 'authenticate' => 'SMTP Error: Could not authenticate.', + '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: ', + '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_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_connect_failed' => 'SMTP connect() failed.', + 'smtp_error' => 'SMTP server error: ', + 'variable_set' => 'Cannot set or reset variable: ', + 'extension_missing' => 'Extension missing: ', + ]; + 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 + if (!preg_match('/^[a-z]{2}(?:_[a-zA-Z]{2})?$/', $langcode)) { + $langcode = 'en'; + } + $foundlang = true; + $lang_file = $lang_path . 'phpmailer.lang-' . $langcode . '.php'; + // There is no English translation file + if ('en' !== $langcode) { + // Make sure language file path is readable + if (!static::isPermittedPath($lang_file) || !file_exists($lang_file)) { + $foundlang = false; + } else { + // Overwrite language-specific strings. + // This way we'll never have missing translation keys. + $foundlang = include $lang_file; + } + } + $this->language = $PHPMAILER_LANG; + + return (bool) $foundlang; // Returns false if language not found + } + + /** + * Get the array of strings for the current language. + * + * @return array + */ + public function getTranslations() + { + return $this->language; + } + + /** + * Create recipient headers. + * + * @param string $type + * @param array $addr An array of recipients, + * where each recipient is a 2-element indexed array with element 0 containing an address + * and element 1 containing a name, like: + * [['joe@example.com', 'Joe User'], ['zoe@example.com', 'Zoe User']] + * + * @return string + */ + public function addrAppend($type, $addr) + { + $addresses = []; + foreach ($addr as $address) { + $addresses[] = $this->addrFormat($address); + } + + return $type . ': ' . implode(', ', $addresses) . static::$LE; + } + + /** + * Format an address for use in a message header. + * + * @param array $addr A 2-element indexed array, element 0 containing an address, element 1 containing a name like + * ['joe@example.com', 'Joe User'] + * + * @return string + */ + public function addrFormat($addr) + { + if (empty($addr[1])) { // No name provided + return $this->secureHeader($addr[0]); + } + + return $this->encodeHeader($this->secureHeader($addr[1]), 'phrase') . + ' <' . $this->secureHeader($addr[0]) . '>'; + } + + /** + * Word-wrap message. + * For use with mailers that do not automatically perform wrapping + * and for quoted-printable encoded messages. + * Original written by philippe. + * + * @param string $message The message to wrap + * @param int $length The line length to wrap to + * @param bool $qp_mode Whether to run in Quoted-Printable mode + * + * @return string + */ + public function wrapText($message, $length, $qp_mode = false) + { + if ($qp_mode) { + $soft_break = sprintf(' =%s', static::$LE); + } else { + $soft_break = static::$LE; + } + // If utf-8 encoding is used, we will need to make sure we don't + // split multibyte characters when we wrap + $is_utf8 = static::CHARSET_UTF8 === strtolower($this->CharSet); + $lelen = strlen(static::$LE); + $crlflen = strlen(static::$LE); + + $message = static::normalizeBreaks($message); + //Remove a trailing line break + if (substr($message, -$lelen) === static::$LE) { + $message = substr($message, 0, -$lelen); + } + + //Split message into lines + $lines = explode(static::$LE, $message); + //Message will be rebuilt in here + $message = ''; + foreach ($lines as $line) { + $words = explode(' ', $line); + $buf = ''; + $firstword = true; + foreach ($words as $word) { + if ($qp_mode && (strlen($word) > $length)) { + $space_left = $length - strlen($buf) - $crlflen; + if (!$firstword) { + if ($space_left > 20) { + $len = $space_left; + if ($is_utf8) { + $len = $this->utf8CharBoundary($word, $len); + } elseif ('=' === substr($word, $len - 1, 1)) { + --$len; + } elseif ('=' === substr($word, $len - 2, 1)) { + $len -= 2; + } + $part = substr($word, 0, $len); + $word = substr($word, $len); + $buf .= ' ' . $part; + $message .= $buf . sprintf('=%s', static::$LE); + } else { + $message .= $buf . $soft_break; + } + $buf = ''; + } + while ($word !== '') { + if ($length <= 0) { + break; + } + $len = $length; + if ($is_utf8) { + $len = $this->utf8CharBoundary($word, $len); + } elseif ('=' === substr($word, $len - 1, 1)) { + --$len; + } elseif ('=' === substr($word, $len - 2, 1)) { + $len -= 2; + } + $part = substr($word, 0, $len); + $word = (string) substr($word, $len); + + if ($word !== '') { + $message .= $part . sprintf('=%s', static::$LE); + } else { + $buf = $part; + } + } + } else { + $buf_o = $buf; + if (!$firstword) { + $buf .= ' '; + } + $buf .= $word; + + if ('' !== $buf_o && strlen($buf) > $length) { + $message .= $buf_o . $soft_break; + $buf = $word; + } + } + $firstword = false; + } + $message .= $buf . static::$LE; + } + + return $message; + } + + /** + * Find the last character boundary prior to $maxLength in a utf-8 + * quoted-printable encoded string. + * Original written by Colin Brown. + * + * @param string $encodedText utf-8 QP text + * @param int $maxLength Find the last character boundary prior to this length + * + * @return int + */ + public function utf8CharBoundary($encodedText, $maxLength) + { + $foundSplitPos = false; + $lookBack = 3; + while (!$foundSplitPos) { + $lastChunk = substr($encodedText, $maxLength - $lookBack, $lookBack); + $encodedCharPos = strpos($lastChunk, '='); + if (false !== $encodedCharPos) { + // Found start of encoded character byte within $lookBack block. + // Check the encoded byte value (the 2 chars after the '=') + $hex = substr($encodedText, $maxLength - $lookBack + $encodedCharPos + 1, 2); + $dec = hexdec($hex); + if ($dec < 128) { + // Single byte character. + // If the encoded char was found at pos 0, it will fit + // otherwise reduce maxLength to start of the encoded char + if ($encodedCharPos > 0) { + $maxLength -= $lookBack - $encodedCharPos; + } + $foundSplitPos = true; + } elseif ($dec >= 192) { + // First byte of a multi byte character + // Reduce maxLength to split at start of character + $maxLength -= $lookBack - $encodedCharPos; + $foundSplitPos = true; + } elseif ($dec < 192) { + // Middle byte of a multi byte character, look further back + $lookBack += 3; + } + } else { + // No encoded character found + $foundSplitPos = true; + } + } + + return $maxLength; + } + + /** + * Apply word wrapping to the message body. + * Wraps the message body to the number of chars set in the WordWrap property. + * You should only do this to plain-text bodies as wrapping HTML tags may break them. + * This is called automatically by createBody(), so you don't need to call it yourself. + */ + public function setWordWrap() + { + if ($this->WordWrap < 1) { + return; + } + + switch ($this->message_type) { + case 'alt': + case 'alt_inline': + case 'alt_attach': + case 'alt_inline_attach': + $this->AltBody = $this->wrapText($this->AltBody, $this->WordWrap); + break; + default: + $this->Body = $this->wrapText($this->Body, $this->WordWrap); + break; + } + } + + /** + * Assemble message headers. + * + * @return string The assembled headers + */ + public function createHeader() + { + $result = ''; + + $result .= $this->headerLine('Date', '' === $this->MessageDate ? self::rfcDate() : $this->MessageDate); + + // To be created automatically by mail() + if ($this->SingleTo) { + if ('mail' !== $this->Mailer) { + foreach ($this->to as $toaddr) { + $this->SingleToArray[] = $this->addrFormat($toaddr); + } + } + } elseif (count($this->to) > 0) { + if ('mail' !== $this->Mailer) { + $result .= $this->addrAppend('To', $this->to); + } + } elseif (count($this->cc) === 0) { + $result .= $this->headerLine('To', 'undisclosed-recipients:;'); + } + + $result .= $this->addrAppend('From', [[trim($this->From), $this->FromName]]); + + // sendmail and mail() extract Cc from the header before sending + if (count($this->cc) > 0) { + $result .= $this->addrAppend('Cc', $this->cc); + } + + // sendmail and mail() extract Bcc from the header before sending + if (( + 'sendmail' === $this->Mailer || 'qmail' === $this->Mailer || 'mail' === $this->Mailer + ) + && count($this->bcc) > 0 + ) { + $result .= $this->addrAppend('Bcc', $this->bcc); + } + + if (count($this->ReplyTo) > 0) { + $result .= $this->addrAppend('Reply-To', $this->ReplyTo); + } + + // mail() sets the subject itself + if ('mail' !== $this->Mailer) { + $result .= $this->headerLine('Subject', $this->encodeHeader($this->secureHeader($this->Subject))); + } + + // Only allow a custom message ID if it conforms to RFC 5322 section 3.6.4 + // https://tools.ietf.org/html/rfc5322#section-3.6.4 + if ('' !== $this->MessageID && preg_match('/^<.*@.*>$/', $this->MessageID)) { + $this->lastMessageID = $this->MessageID; + } else { + $this->lastMessageID = sprintf('<%s@%s>', $this->uniqueid, $this->serverHostname()); + } + $result .= $this->headerLine('Message-ID', $this->lastMessageID); + if (null !== $this->Priority) { + $result .= $this->headerLine('X-Priority', $this->Priority); + } + if ('' === $this->XMailer) { + $result .= $this->headerLine( + 'X-Mailer', + 'PHPMailer ' . self::VERSION . ' (https://github.com/PHPMailer/PHPMailer)' + ); + } else { + $myXmailer = trim($this->XMailer); + if ($myXmailer) { + $result .= $this->headerLine('X-Mailer', $myXmailer); + } + } + + if ('' !== $this->ConfirmReadingTo) { + $result .= $this->headerLine('Disposition-Notification-To', '<' . $this->ConfirmReadingTo . '>'); + } + + // Add custom headers + foreach ($this->CustomHeader as $header) { + $result .= $this->headerLine( + trim($header[0]), + $this->encodeHeader(trim($header[1])) + ); + } + if (!$this->sign_key_file) { + $result .= $this->headerLine('MIME-Version', '1.0'); + $result .= $this->getMailMIME(); + } + + return $result; + } + + /** + * Get the message MIME type headers. + * + * @return string + */ + public function getMailMIME() + { + $result = ''; + $ismultipart = true; + switch ($this->message_type) { + case 'inline': + $result .= $this->headerLine('Content-Type', static::CONTENT_TYPE_MULTIPART_RELATED . ';'); + $result .= $this->textLine(' boundary="' . $this->boundary[1] . '"'); + break; + case 'attach': + case 'inline_attach': + case 'alt_attach': + case 'alt_inline_attach': + $result .= $this->headerLine('Content-Type', static::CONTENT_TYPE_MULTIPART_MIXED . ';'); + $result .= $this->textLine(' boundary="' . $this->boundary[1] . '"'); + break; + case 'alt': + case 'alt_inline': + $result .= $this->headerLine('Content-Type', static::CONTENT_TYPE_MULTIPART_ALTERNATIVE . ';'); + $result .= $this->textLine(' boundary="' . $this->boundary[1] . '"'); + break; + default: + // Catches case 'plain': and case '': + $result .= $this->textLine('Content-Type: ' . $this->ContentType . '; charset=' . $this->CharSet); + $ismultipart = false; + break; + } + // RFC1341 part 5 says 7bit is assumed if not specified + if (static::ENCODING_7BIT !== $this->Encoding) { + // RFC 2045 section 6.4 says multipart MIME parts may only use 7bit, 8bit or binary CTE + if ($ismultipart) { + if (static::ENCODING_8BIT === $this->Encoding) { + $result .= $this->headerLine('Content-Transfer-Encoding', static::ENCODING_8BIT); + } + // The only remaining alternatives are quoted-printable and base64, which are both 7bit compatible + } else { + $result .= $this->headerLine('Content-Transfer-Encoding', $this->Encoding); + } + } + + if ('mail' !== $this->Mailer) { +// $result .= static::$LE; + } + + return $result; + } + + /** + * Returns the whole MIME message. + * Includes complete headers and body. + * Only valid post preSend(). + * + * @see PHPMailer::preSend() + * + * @return string + */ + public function getSentMIMEMessage() + { + return static::stripTrailingWSP($this->MIMEHeader . $this->mailHeader) . + static::$LE . static::$LE . $this->MIMEBody; + } + + /** + * Create a unique ID to use for boundaries. + * + * @return string + */ + protected function generateId() + { + $len = 32; //32 bytes = 256 bits + $bytes = ''; + if (function_exists('random_bytes')) { + try { + $bytes = random_bytes($len); + } catch (\Exception $e) { + //Do nothing + } + } elseif (function_exists('openssl_random_pseudo_bytes')) { + /** @noinspection CryptographicallySecureRandomnessInspection */ + $bytes = openssl_random_pseudo_bytes($len); + } + if ($bytes === '') { + //We failed to produce a proper random string, so make do. + //Use a hash to force the length to the same as the other methods + $bytes = hash('sha256', uniqid((string) mt_rand(), true), true); + } + + //We don't care about messing up base64 format here, just want a random string + return str_replace(['=', '+', '/'], '', base64_encode(hash('sha256', $bytes, true))); + } + + /** + * Assemble the message body. + * Returns an empty string on failure. + * + * @throws Exception + * + * @return string The assembled message body + */ + public function createBody() + { + $body = ''; + //Create unique IDs and preset boundaries + $this->uniqueid = $this->generateId(); + $this->boundary[1] = 'b1_' . $this->uniqueid; + $this->boundary[2] = 'b2_' . $this->uniqueid; + $this->boundary[3] = 'b3_' . $this->uniqueid; + + if ($this->sign_key_file) { + $body .= $this->getMailMIME() . static::$LE; + } + + $this->setWordWrap(); + + $bodyEncoding = $this->Encoding; + $bodyCharSet = $this->CharSet; + //Can we do a 7-bit downgrade? + if (static::ENCODING_8BIT === $bodyEncoding && !$this->has8bitChars($this->Body)) { + $bodyEncoding = static::ENCODING_7BIT; + //All ISO 8859, Windows codepage and UTF-8 charsets are ascii compatible up to 7-bit + $bodyCharSet = static::CHARSET_ASCII; + } + //If lines are too long, and we're not already using an encoding that will shorten them, + //change to quoted-printable transfer encoding for the body part only + if (static::ENCODING_BASE64 !== $this->Encoding && static::hasLineLongerThanMax($this->Body)) { + $bodyEncoding = static::ENCODING_QUOTED_PRINTABLE; + } + + $altBodyEncoding = $this->Encoding; + $altBodyCharSet = $this->CharSet; + //Can we do a 7-bit downgrade? + if (static::ENCODING_8BIT === $altBodyEncoding && !$this->has8bitChars($this->AltBody)) { + $altBodyEncoding = static::ENCODING_7BIT; + //All ISO 8859, Windows codepage and UTF-8 charsets are ascii compatible up to 7-bit + $altBodyCharSet = static::CHARSET_ASCII; + } + //If lines are too long, and we're not already using an encoding that will shorten them, + //change to quoted-printable transfer encoding for the alt body part only + if (static::ENCODING_BASE64 !== $altBodyEncoding && static::hasLineLongerThanMax($this->AltBody)) { + $altBodyEncoding = static::ENCODING_QUOTED_PRINTABLE; + } + //Use this as a preamble in all multipart message types + $mimepre = 'This is a multi-part message in MIME format.' . static::$LE . static::$LE; + switch ($this->message_type) { + case 'inline': + $body .= $mimepre; + $body .= $this->getBoundary($this->boundary[1], $bodyCharSet, '', $bodyEncoding); + $body .= $this->encodeString($this->Body, $bodyEncoding); + $body .= static::$LE; + $body .= $this->attachAll('inline', $this->boundary[1]); + break; + case 'attach': + $body .= $mimepre; + $body .= $this->getBoundary($this->boundary[1], $bodyCharSet, '', $bodyEncoding); + $body .= $this->encodeString($this->Body, $bodyEncoding); + $body .= static::$LE; + $body .= $this->attachAll('attachment', $this->boundary[1]); + break; + case 'inline_attach': + $body .= $mimepre; + $body .= $this->textLine('--' . $this->boundary[1]); + $body .= $this->headerLine('Content-Type', static::CONTENT_TYPE_MULTIPART_RELATED . ';'); + $body .= $this->textLine(' boundary="' . $this->boundary[2] . '";'); + $body .= $this->textLine(' type="' . static::CONTENT_TYPE_TEXT_HTML . '"'); + $body .= static::$LE; + $body .= $this->getBoundary($this->boundary[2], $bodyCharSet, '', $bodyEncoding); + $body .= $this->encodeString($this->Body, $bodyEncoding); + $body .= static::$LE; + $body .= $this->attachAll('inline', $this->boundary[2]); + $body .= static::$LE; + $body .= $this->attachAll('attachment', $this->boundary[1]); + break; + case 'alt': + $body .= $mimepre; + $body .= $this->getBoundary( + $this->boundary[1], + $altBodyCharSet, + static::CONTENT_TYPE_PLAINTEXT, + $altBodyEncoding + ); + $body .= $this->encodeString($this->AltBody, $altBodyEncoding); + $body .= static::$LE; + $body .= $this->getBoundary( + $this->boundary[1], + $bodyCharSet, + static::CONTENT_TYPE_TEXT_HTML, + $bodyEncoding + ); + $body .= $this->encodeString($this->Body, $bodyEncoding); + $body .= static::$LE; + if (!empty($this->Ical)) { + $method = static::ICAL_METHOD_REQUEST; + foreach (static::$IcalMethods as $imethod) { + if (stripos($this->Ical, 'METHOD:' . $imethod) !== false) { + $method = $imethod; + break; + } + } + $body .= $this->getBoundary( + $this->boundary[1], + '', + static::CONTENT_TYPE_TEXT_CALENDAR . '; method=' . $method, + '' + ); + $body .= $this->encodeString($this->Ical, $this->Encoding); + $body .= static::$LE; + } + $body .= $this->endBoundary($this->boundary[1]); + break; + case 'alt_inline': + $body .= $mimepre; + $body .= $this->getBoundary( + $this->boundary[1], + $altBodyCharSet, + static::CONTENT_TYPE_PLAINTEXT, + $altBodyEncoding + ); + $body .= $this->encodeString($this->AltBody, $altBodyEncoding); + $body .= static::$LE; + $body .= $this->textLine('--' . $this->boundary[1]); + $body .= $this->headerLine('Content-Type', static::CONTENT_TYPE_MULTIPART_RELATED . ';'); + $body .= $this->textLine(' boundary="' . $this->boundary[2] . '";'); + $body .= $this->textLine(' type="' . static::CONTENT_TYPE_TEXT_HTML . '"'); + $body .= static::$LE; + $body .= $this->getBoundary( + $this->boundary[2], + $bodyCharSet, + static::CONTENT_TYPE_TEXT_HTML, + $bodyEncoding + ); + $body .= $this->encodeString($this->Body, $bodyEncoding); + $body .= static::$LE; + $body .= $this->attachAll('inline', $this->boundary[2]); + $body .= static::$LE; + $body .= $this->endBoundary($this->boundary[1]); + break; + case 'alt_attach': + $body .= $mimepre; + $body .= $this->textLine('--' . $this->boundary[1]); + $body .= $this->headerLine('Content-Type', static::CONTENT_TYPE_MULTIPART_ALTERNATIVE . ';'); + $body .= $this->textLine(' boundary="' . $this->boundary[2] . '"'); + $body .= static::$LE; + $body .= $this->getBoundary( + $this->boundary[2], + $altBodyCharSet, + static::CONTENT_TYPE_PLAINTEXT, + $altBodyEncoding + ); + $body .= $this->encodeString($this->AltBody, $altBodyEncoding); + $body .= static::$LE; + $body .= $this->getBoundary( + $this->boundary[2], + $bodyCharSet, + static::CONTENT_TYPE_TEXT_HTML, + $bodyEncoding + ); + $body .= $this->encodeString($this->Body, $bodyEncoding); + $body .= static::$LE; + if (!empty($this->Ical)) { + $method = static::ICAL_METHOD_REQUEST; + foreach (static::$IcalMethods as $imethod) { + if (stripos($this->Ical, 'METHOD:' . $imethod) !== false) { + $method = $imethod; + break; + } + } + $body .= $this->getBoundary( + $this->boundary[2], + '', + static::CONTENT_TYPE_TEXT_CALENDAR . '; method=' . $method, + '' + ); + $body .= $this->encodeString($this->Ical, $this->Encoding); + } + $body .= $this->endBoundary($this->boundary[2]); + $body .= static::$LE; + $body .= $this->attachAll('attachment', $this->boundary[1]); + break; + case 'alt_inline_attach': + $body .= $mimepre; + $body .= $this->textLine('--' . $this->boundary[1]); + $body .= $this->headerLine('Content-Type', static::CONTENT_TYPE_MULTIPART_ALTERNATIVE . ';'); + $body .= $this->textLine(' boundary="' . $this->boundary[2] . '"'); + $body .= static::$LE; + $body .= $this->getBoundary( + $this->boundary[2], + $altBodyCharSet, + static::CONTENT_TYPE_PLAINTEXT, + $altBodyEncoding + ); + $body .= $this->encodeString($this->AltBody, $altBodyEncoding); + $body .= static::$LE; + $body .= $this->textLine('--' . $this->boundary[2]); + $body .= $this->headerLine('Content-Type', static::CONTENT_TYPE_MULTIPART_RELATED . ';'); + $body .= $this->textLine(' boundary="' . $this->boundary[3] . '";'); + $body .= $this->textLine(' type="' . static::CONTENT_TYPE_TEXT_HTML . '"'); + $body .= static::$LE; + $body .= $this->getBoundary( + $this->boundary[3], + $bodyCharSet, + static::CONTENT_TYPE_TEXT_HTML, + $bodyEncoding + ); + $body .= $this->encodeString($this->Body, $bodyEncoding); + $body .= static::$LE; + $body .= $this->attachAll('inline', $this->boundary[3]); + $body .= static::$LE; + $body .= $this->endBoundary($this->boundary[2]); + $body .= static::$LE; + $body .= $this->attachAll('attachment', $this->boundary[1]); + break; + default: + // Catch case 'plain' and case '', applies to simple `text/plain` and `text/html` body content types + //Reset the `Encoding` property in case we changed it for line length reasons + $this->Encoding = $bodyEncoding; + $body .= $this->encodeString($this->Body, $this->Encoding); + break; + } + + if ($this->isError()) { + $body = ''; + if ($this->exceptions) { + throw new Exception($this->lang('empty_message'), self::STOP_CRITICAL); + } + } elseif ($this->sign_key_file) { + try { + if (!defined('PKCS7_TEXT')) { + throw new Exception($this->lang('extension_missing') . 'openssl'); + } + + $file = tempnam(sys_get_temp_dir(), 'srcsign'); + $signed = tempnam(sys_get_temp_dir(), 'mailsign'); + file_put_contents($file, $body); + + //Workaround for PHP bug https://bugs.php.net/bug.php?id=69197 + if (empty($this->sign_extracerts_file)) { + $sign = @openssl_pkcs7_sign( + $file, + $signed, + 'file://' . realpath($this->sign_cert_file), + ['file://' . realpath($this->sign_key_file), $this->sign_key_pass], + [] + ); + } else { + $sign = @openssl_pkcs7_sign( + $file, + $signed, + 'file://' . realpath($this->sign_cert_file), + ['file://' . realpath($this->sign_key_file), $this->sign_key_pass], + [], + PKCS7_DETACHED, + $this->sign_extracerts_file + ); + } + + @unlink($file); + if ($sign) { + $body = file_get_contents($signed); + @unlink($signed); + //The message returned by openssl contains both headers and body, so need to split them up + $parts = explode("\n\n", $body, 2); + $this->MIMEHeader .= $parts[0] . static::$LE . static::$LE; + $body = $parts[1]; + } else { + @unlink($signed); + throw new Exception($this->lang('signing') . openssl_error_string()); + } + } catch (Exception $exc) { + $body = ''; + if ($this->exceptions) { + throw $exc; + } + } + } + + return $body; + } + + /** + * Return the start of a message boundary. + * + * @param string $boundary + * @param string $charSet + * @param string $contentType + * @param string $encoding + * + * @return string + */ + protected function getBoundary($boundary, $charSet, $contentType, $encoding) + { + $result = ''; + if ('' === $charSet) { + $charSet = $this->CharSet; + } + if ('' === $contentType) { + $contentType = $this->ContentType; + } + if ('' === $encoding) { + $encoding = $this->Encoding; + } + $result .= $this->textLine('--' . $boundary); + $result .= sprintf('Content-Type: %s; charset=%s', $contentType, $charSet); + $result .= static::$LE; + // RFC1341 part 5 says 7bit is assumed if not specified + if (static::ENCODING_7BIT !== $encoding) { + $result .= $this->headerLine('Content-Transfer-Encoding', $encoding); + } + $result .= static::$LE; + + return $result; + } + + /** + * Return the end of a message boundary. + * + * @param string $boundary + * + * @return string + */ + protected function endBoundary($boundary) + { + return static::$LE . '--' . $boundary . '--' . static::$LE; + } + + /** + * Set the message type. + * PHPMailer only supports some preset message types, not arbitrary MIME structures. + */ + protected function setMessageType() + { + $type = []; + if ($this->alternativeExists()) { + $type[] = 'alt'; + } + if ($this->inlineImageExists()) { + $type[] = 'inline'; + } + if ($this->attachmentExists()) { + $type[] = 'attach'; + } + $this->message_type = implode('_', $type); + if ('' === $this->message_type) { + //The 'plain' message_type refers to the message having a single body element, not that it is plain-text + $this->message_type = 'plain'; + } + } + + /** + * Format a header line. + * + * @param string $name + * @param string|int $value + * + * @return string + */ + public function headerLine($name, $value) + { + return $name . ': ' . $value . static::$LE; + } + + /** + * Return a formatted mail line. + * + * @param string $value + * + * @return string + */ + public function textLine($value) + { + return $value . static::$LE; + } + + /** + * Add an attachment from a path on the filesystem. + * Never use a user-supplied path to a file! + * Returns false if the file could not be found or read. + * Explicitly *does not* support passing URLs; PHPMailer is not an HTTP client. + * If you need to do that, fetch the resource yourself and pass it in via a local file or string. + * + * @param string $path Path to the attachment + * @param string $name Overrides the attachment name + * @param string $encoding File encoding (see $Encoding) + * @param string $type File extension (MIME) type + * @param string $disposition Disposition to use + * + * @throws Exception + * + * @return bool + */ + public function addAttachment( + $path, + $name = '', + $encoding = self::ENCODING_BASE64, + $type = '', + $disposition = 'attachment' + ) { + try { + if (!static::isPermittedPath($path) || !@is_file($path) || !is_readable($path)) { + throw new Exception($this->lang('file_access') . $path, self::STOP_CONTINUE); + } + + // If a MIME type is not specified, try to work it out from the file name + if ('' === $type) { + $type = static::filenameToType($path); + } + + $filename = (string) static::mb_pathinfo($path, PATHINFO_BASENAME); + if ('' === $name) { + $name = $filename; + } + if (!$this->validateEncoding($encoding)) { + throw new Exception($this->lang('encoding') . $encoding); + } + + $this->attachment[] = [ + 0 => $path, + 1 => $filename, + 2 => $name, + 3 => $encoding, + 4 => $type, + 5 => false, // isStringAttachment + 6 => $disposition, + 7 => $name, + ]; + } catch (Exception $exc) { + $this->setError($exc->getMessage()); + $this->edebug($exc->getMessage()); + if ($this->exceptions) { + throw $exc; + } + + return false; + } + + return true; + } + + /** + * Return the array of attachments. + * + * @return array + */ + public function getAttachments() + { + return $this->attachment; + } + + /** + * Attach all file, string, and binary attachments to the message. + * Returns an empty string on failure. + * + * @param string $disposition_type + * @param string $boundary + * + * @throws Exception + * + * @return string + */ + protected function attachAll($disposition_type, $boundary) + { + // Return text of body + $mime = []; + $cidUniq = []; + $incl = []; + + // Add all attachments + foreach ($this->attachment as $attachment) { + // Check if it is a valid disposition_filter + if ($attachment[6] === $disposition_type) { + // Check for string attachment + $string = ''; + $path = ''; + $bString = $attachment[5]; + if ($bString) { + $string = $attachment[0]; + } else { + $path = $attachment[0]; + } + + $inclhash = hash('sha256', serialize($attachment)); + if (in_array($inclhash, $incl, true)) { + continue; + } + $incl[] = $inclhash; + $name = $attachment[2]; + $encoding = $attachment[3]; + $type = $attachment[4]; + $disposition = $attachment[6]; + $cid = $attachment[7]; + if ('inline' === $disposition && array_key_exists($cid, $cidUniq)) { + continue; + } + $cidUniq[$cid] = true; + + $mime[] = sprintf('--%s%s', $boundary, static::$LE); + //Only include a filename property if we have one + if (!empty($name)) { + $mime[] = sprintf( + 'Content-Type: %s; name=%s%s', + $type, + static::quotedString($this->encodeHeader($this->secureHeader($name))), + static::$LE + ); + } else { + $mime[] = sprintf( + 'Content-Type: %s%s', + $type, + static::$LE + ); + } + // RFC1341 part 5 says 7bit is assumed if not specified + if (static::ENCODING_7BIT !== $encoding) { + $mime[] = sprintf('Content-Transfer-Encoding: %s%s', $encoding, static::$LE); + } + + //Only set Content-IDs on inline attachments + if ((string) $cid !== '' && $disposition === 'inline') { + $mime[] = 'Content-ID: <' . $this->encodeHeader($this->secureHeader($cid)) . '>' . static::$LE; + } + + // Allow for bypassing the Content-Disposition header + if (!empty($disposition)) { + $encoded_name = $this->encodeHeader($this->secureHeader($name)); + if (!empty($encoded_name)) { + $mime[] = sprintf( + 'Content-Disposition: %s; filename=%s%s', + $disposition, + static::quotedString($encoded_name), + static::$LE . static::$LE + ); + } else { + $mime[] = sprintf( + 'Content-Disposition: %s%s', + $disposition, + static::$LE . static::$LE + ); + } + } else { + $mime[] = static::$LE; + } + + // Encode as string attachment + if ($bString) { + $mime[] = $this->encodeString($string, $encoding); + } else { + $mime[] = $this->encodeFile($path, $encoding); + } + if ($this->isError()) { + return ''; + } + $mime[] = static::$LE; + } + } + + $mime[] = sprintf('--%s--%s', $boundary, static::$LE); + + return implode('', $mime); + } + + /** + * Encode a file attachment in requested format. + * Returns an empty string on failure. + * + * @param string $path The full path to the file + * @param string $encoding The encoding to use; one of 'base64', '7bit', '8bit', 'binary', 'quoted-printable' + * + * @return string + */ + protected function encodeFile($path, $encoding = self::ENCODING_BASE64) + { + try { + if (!static::isPermittedPath($path) || !file_exists($path) || !is_readable($path)) { + throw new Exception($this->lang('file_open') . $path, self::STOP_CONTINUE); + } + $file_buffer = file_get_contents($path); + if (false === $file_buffer) { + throw new Exception($this->lang('file_open') . $path, self::STOP_CONTINUE); + } + $file_buffer = $this->encodeString($file_buffer, $encoding); + + return $file_buffer; + } catch (Exception $exc) { + $this->setError($exc->getMessage()); + $this->edebug($exc->getMessage()); + if ($this->exceptions) { + throw $exc; + } + + return ''; + } + } + + /** + * Encode a string in requested format. + * Returns an empty string on failure. + * + * @param string $str The text to encode + * @param string $encoding The encoding to use; one of 'base64', '7bit', '8bit', 'binary', 'quoted-printable' + * + * @throws Exception + * + * @return string + */ + public function encodeString($str, $encoding = self::ENCODING_BASE64) + { + $encoded = ''; + switch (strtolower($encoding)) { + case static::ENCODING_BASE64: + $encoded = chunk_split( + base64_encode($str), + static::STD_LINE_LENGTH, + static::$LE + ); + break; + case static::ENCODING_7BIT: + case static::ENCODING_8BIT: + $encoded = static::normalizeBreaks($str); + // Make sure it ends with a line break + if (substr($encoded, -(strlen(static::$LE))) !== static::$LE) { + $encoded .= static::$LE; + } + break; + case static::ENCODING_BINARY: + $encoded = $str; + break; + case static::ENCODING_QUOTED_PRINTABLE: + $encoded = $this->encodeQP($str); + break; + default: + $this->setError($this->lang('encoding') . $encoding); + if ($this->exceptions) { + throw new Exception($this->lang('encoding') . $encoding); + } + break; + } + + return $encoded; + } + + /** + * Encode a header value (not including its label) optimally. + * Picks shortest of Q, B, or none. Result includes folding if needed. + * See RFC822 definitions for phrase, comment and text positions. + * + * @param string $str The header value to encode + * @param string $position What context the string will be used in + * + * @return string + */ + public function encodeHeader($str, $position = 'text') + { + $matchcount = 0; + switch (strtolower($position)) { + case 'phrase': + if (!preg_match('/[\200-\377]/', $str)) { + // Can't use addslashes as we don't know the value of magic_quotes_sybase + $encoded = addcslashes($str, "\0..\37\177\\\""); + if (($str === $encoded) && !preg_match('/[^A-Za-z0-9!#$%&\'*+\/=?^_`{|}~ -]/', $str)) { + return $encoded; + } + + return "\"$encoded\""; + } + $matchcount = preg_match_all('/[^\040\041\043-\133\135-\176]/', $str, $matches); + break; + /* @noinspection PhpMissingBreakStatementInspection */ + case 'comment': + $matchcount = preg_match_all('/[()"]/', $str, $matches); + //fallthrough + case 'text': + default: + $matchcount += preg_match_all('/[\000-\010\013\014\016-\037\177-\377]/', $str, $matches); + break; + } + + if ($this->has8bitChars($str)) { + $charset = $this->CharSet; + } else { + $charset = static::CHARSET_ASCII; + } + + // Q/B encoding adds 8 chars and the charset ("` =??[QB]??=`"). + $overhead = 8 + strlen($charset); + + if ('mail' === $this->Mailer) { + $maxlen = static::MAIL_MAX_LINE_LENGTH - $overhead; + } else { + $maxlen = static::MAX_LINE_LENGTH - $overhead; + } + + // Select the encoding that produces the shortest output and/or prevents corruption. + if ($matchcount > strlen($str) / 3) { + // More than 1/3 of the content needs encoding, use B-encode. + $encoding = 'B'; + } elseif ($matchcount > 0) { + // Less than 1/3 of the content needs encoding, use Q-encode. + $encoding = 'Q'; + } elseif (strlen($str) > $maxlen) { + // No encoding needed, but value exceeds max line length, use Q-encode to prevent corruption. + $encoding = 'Q'; + } else { + // No reformatting needed + $encoding = false; + } + + switch ($encoding) { + case 'B': + if ($this->hasMultiBytes($str)) { + // Use a custom function which correctly encodes and wraps long + // multibyte strings without breaking lines within a character + $encoded = $this->base64EncodeWrapMB($str, "\n"); + } else { + $encoded = base64_encode($str); + $maxlen -= $maxlen % 4; + $encoded = trim(chunk_split($encoded, $maxlen, "\n")); + } + $encoded = preg_replace('/^(.*)$/m', ' =?' . $charset . "?$encoding?\\1?=", $encoded); + break; + case 'Q': + $encoded = $this->encodeQ($str, $position); + $encoded = $this->wrapText($encoded, $maxlen, true); + $encoded = str_replace('=' . static::$LE, "\n", trim($encoded)); + $encoded = preg_replace('/^(.*)$/m', ' =?' . $charset . "?$encoding?\\1?=", $encoded); + break; + default: + return $str; + } + + return trim(static::normalizeBreaks($encoded)); + } + + /** + * Check if a string contains multi-byte characters. + * + * @param string $str multi-byte text to wrap encode + * + * @return bool + */ + public function hasMultiBytes($str) + { + if (function_exists('mb_strlen')) { + return strlen($str) > mb_strlen($str, $this->CharSet); + } + + // Assume no multibytes (we can't handle without mbstring functions anyway) + return false; + } + + /** + * Does a string contain any 8-bit chars (in any charset)? + * + * @param string $text + * + * @return bool + */ + public function has8bitChars($text) + { + return (bool) preg_match('/[\x80-\xFF]/', $text); + } + + /** + * Encode and wrap long multibyte strings for mail headers + * without breaking lines within a character. + * Adapted from a function by paravoid. + * + * @see http://www.php.net/manual/en/function.mb-encode-mimeheader.php#60283 + * + * @param string $str multi-byte text to wrap encode + * @param string $linebreak string to use as linefeed/end-of-line + * + * @return string + */ + public function base64EncodeWrapMB($str, $linebreak = null) + { + $start = '=?' . $this->CharSet . '?B?'; + $end = '?='; + $encoded = ''; + if (null === $linebreak) { + $linebreak = static::$LE; + } + + $mb_length = mb_strlen($str, $this->CharSet); + // Each line must have length <= 75, including $start and $end + $length = 75 - strlen($start) - strlen($end); + // Average multi-byte ratio + $ratio = $mb_length / strlen($str); + // Base64 has a 4:3 ratio + $avgLength = floor($length * $ratio * .75); + + $offset = 0; + for ($i = 0; $i < $mb_length; $i += $offset) { + $lookBack = 0; + do { + $offset = $avgLength - $lookBack; + $chunk = mb_substr($str, $i, $offset, $this->CharSet); + $chunk = base64_encode($chunk); + ++$lookBack; + } while (strlen($chunk) > $length); + $encoded .= $chunk . $linebreak; + } + + // Chomp the last linefeed + return substr($encoded, 0, -strlen($linebreak)); + } + + /** + * Encode a string in quoted-printable format. + * According to RFC2045 section 6.7. + * + * @param string $string The text to encode + * + * @return string + */ + public function encodeQP($string) + { + return static::normalizeBreaks(quoted_printable_encode($string)); + } + + /** + * Encode a string using Q encoding. + * + * @see http://tools.ietf.org/html/rfc2047#section-4.2 + * + * @param string $str the text to encode + * @param string $position Where the text is going to be used, see the RFC for what that means + * + * @return string + */ + public function encodeQ($str, $position = 'text') + { + // There should not be any EOL in the string + $pattern = ''; + $encoded = str_replace(["\r", "\n"], '', $str); + switch (strtolower($position)) { + case 'phrase': + // RFC 2047 section 5.3 + $pattern = '^A-Za-z0-9!*+\/ -'; + break; + /* + * RFC 2047 section 5.2. + * Build $pattern without including delimiters and [] + */ + /* @noinspection PhpMissingBreakStatementInspection */ + case 'comment': + $pattern = '\(\)"'; + /* Intentional fall through */ + case 'text': + default: + // RFC 2047 section 5.1 + // Replace every high ascii, control, =, ? and _ characters + $pattern = '\000-\011\013\014\016-\037\075\077\137\177-\377' . $pattern; + break; + } + $matches = []; + if (preg_match_all("/[{$pattern}]/", $encoded, $matches)) { + // If the string contains an '=', make sure it's the first thing we replace + // so as to avoid double-encoding + $eqkey = array_search('=', $matches[0], true); + if (false !== $eqkey) { + unset($matches[0][$eqkey]); + array_unshift($matches[0], '='); + } + foreach (array_unique($matches[0]) as $char) { + $encoded = str_replace($char, '=' . sprintf('%02X', ord($char)), $encoded); + } + } + // Replace spaces with _ (more readable than =20) + // RFC 2047 section 4.2(2) + return str_replace(' ', '_', $encoded); + } + + /** + * Add a string or binary attachment (non-filesystem). + * This method can be used to attach ascii or binary data, + * such as a BLOB record from a database. + * + * @param string $string String attachment data + * @param string $filename Name of the attachment + * @param string $encoding File encoding (see $Encoding) + * @param string $type File extension (MIME) type + * @param string $disposition Disposition to use + * + * @throws Exception + * + * @return bool True on successfully adding an attachment + */ + public function addStringAttachment( + $string, + $filename, + $encoding = self::ENCODING_BASE64, + $type = '', + $disposition = 'attachment' + ) { + try { + // If a MIME type is not specified, try to work it out from the file name + if ('' === $type) { + $type = static::filenameToType($filename); + } + + if (!$this->validateEncoding($encoding)) { + throw new Exception($this->lang('encoding') . $encoding); + } + + // Append to $attachment array + $this->attachment[] = [ + 0 => $string, + 1 => $filename, + 2 => static::mb_pathinfo($filename, PATHINFO_BASENAME), + 3 => $encoding, + 4 => $type, + 5 => true, // isStringAttachment + 6 => $disposition, + 7 => 0, + ]; + } catch (Exception $exc) { + $this->setError($exc->getMessage()); + $this->edebug($exc->getMessage()); + if ($this->exceptions) { + throw $exc; + } + + return false; + } + + return true; + } + + /** + * Add an embedded (inline) attachment from a file. + * This can include images, sounds, and just about any other document type. + * These differ from 'regular' attachments in that they are intended to be + * displayed inline with the message, not just attached for download. + * This is used in HTML messages that embed the images + * the HTML refers to using the $cid value. + * Never use a user-supplied path to a file! + * + * @param string $path Path to the attachment + * @param string $cid Content ID of the attachment; Use this to reference + * the content when using an embedded image in HTML + * @param string $name Overrides the attachment name + * @param string $encoding File encoding (see $Encoding) + * @param string $type File MIME type + * @param string $disposition Disposition to use + * + * @throws Exception + * + * @return bool True on successfully adding an attachment + */ + public function addEmbeddedImage( + $path, + $cid, + $name = '', + $encoding = self::ENCODING_BASE64, + $type = '', + $disposition = 'inline' + ) { + try { + if (!static::isPermittedPath($path) || !@is_file($path) || !is_readable($path)) { + throw new Exception($this->lang('file_access') . $path, self::STOP_CONTINUE); + } + + // If a MIME type is not specified, try to work it out from the file name + if ('' === $type) { + $type = static::filenameToType($path); + } + + if (!$this->validateEncoding($encoding)) { + throw new Exception($this->lang('encoding') . $encoding); + } + + $filename = (string) static::mb_pathinfo($path, PATHINFO_BASENAME); + if ('' === $name) { + $name = $filename; + } + + // Append to $attachment array + $this->attachment[] = [ + 0 => $path, + 1 => $filename, + 2 => $name, + 3 => $encoding, + 4 => $type, + 5 => false, // isStringAttachment + 6 => $disposition, + 7 => $cid, + ]; + } catch (Exception $exc) { + $this->setError($exc->getMessage()); + $this->edebug($exc->getMessage()); + if ($this->exceptions) { + throw $exc; + } + + return false; + } + + return true; + } + + /** + * Add an embedded stringified attachment. + * This can include images, sounds, and just about any other document type. + * If your filename doesn't contain an extension, be sure to set the $type to an appropriate MIME type. + * + * @param string $string The attachment binary data + * @param string $cid Content ID of the attachment; Use this to reference + * the content when using an embedded image in HTML + * @param string $name A filename for the attachment. If this contains an extension, + * PHPMailer will attempt to set a MIME type for the attachment. + * For example 'file.jpg' would get an 'image/jpeg' MIME type. + * @param string $encoding File encoding (see $Encoding), defaults to 'base64' + * @param string $type MIME type - will be used in preference to any automatically derived type + * @param string $disposition Disposition to use + * + * @throws Exception + * + * @return bool True on successfully adding an attachment + */ + public function addStringEmbeddedImage( + $string, + $cid, + $name = '', + $encoding = self::ENCODING_BASE64, + $type = '', + $disposition = 'inline' + ) { + try { + // If a MIME type is not specified, try to work it out from the name + if ('' === $type && !empty($name)) { + $type = static::filenameToType($name); + } + + if (!$this->validateEncoding($encoding)) { + throw new Exception($this->lang('encoding') . $encoding); + } + + // Append to $attachment array + $this->attachment[] = [ + 0 => $string, + 1 => $name, + 2 => $name, + 3 => $encoding, + 4 => $type, + 5 => true, // isStringAttachment + 6 => $disposition, + 7 => $cid, + ]; + } catch (Exception $exc) { + $this->setError($exc->getMessage()); + $this->edebug($exc->getMessage()); + if ($this->exceptions) { + throw $exc; + } + + return false; + } + + return true; + } + + /** + * Validate encodings. + * + * @param string $encoding + * + * @return bool + */ + protected function validateEncoding($encoding) + { + return in_array( + $encoding, + [ + self::ENCODING_7BIT, + self::ENCODING_QUOTED_PRINTABLE, + self::ENCODING_BASE64, + self::ENCODING_8BIT, + self::ENCODING_BINARY, + ], + true + ); + } + + /** + * Check if an embedded attachment is present with this cid. + * + * @param string $cid + * + * @return bool + */ + protected function cidExists($cid) + { + foreach ($this->attachment as $attachment) { + if ('inline' === $attachment[6] && $cid === $attachment[7]) { + return true; + } + } + + return false; + } + + /** + * Check if an inline attachment is present. + * + * @return bool + */ + public function inlineImageExists() + { + foreach ($this->attachment as $attachment) { + if ('inline' === $attachment[6]) { + return true; + } + } + + return false; + } + + /** + * Check if an attachment (non-inline) is present. + * + * @return bool + */ + public function attachmentExists() + { + foreach ($this->attachment as $attachment) { + if ('attachment' === $attachment[6]) { + return true; + } + } + + return false; + } + + /** + * Check if this message has an alternative body set. + * + * @return bool + */ + public function alternativeExists() + { + return !empty($this->AltBody); + } + + /** + * Clear queued addresses of given kind. + * + * @param string $kind 'to', 'cc', or 'bcc' + */ + public function clearQueuedAddresses($kind) + { + $this->RecipientsQueue = array_filter( + $this->RecipientsQueue, + static function ($params) use ($kind) { + return $params[0] !== $kind; + } + ); + } + + /** + * Clear all To recipients. + */ + public function clearAddresses() + { + foreach ($this->to as $to) { + unset($this->all_recipients[strtolower($to[0])]); + } + $this->to = []; + $this->clearQueuedAddresses('to'); + } + + /** + * Clear all CC recipients. + */ + public function clearCCs() + { + foreach ($this->cc as $cc) { + unset($this->all_recipients[strtolower($cc[0])]); + } + $this->cc = []; + $this->clearQueuedAddresses('cc'); + } + + /** + * Clear all BCC recipients. + */ + public function clearBCCs() + { + foreach ($this->bcc as $bcc) { + unset($this->all_recipients[strtolower($bcc[0])]); + } + $this->bcc = []; + $this->clearQueuedAddresses('bcc'); + } + + /** + * Clear all ReplyTo recipients. + */ + public function clearReplyTos() + { + $this->ReplyTo = []; + $this->ReplyToQueue = []; + } + + /** + * Clear all recipient types. + */ + public function clearAllRecipients() + { + $this->to = []; + $this->cc = []; + $this->bcc = []; + $this->all_recipients = []; + $this->RecipientsQueue = []; + } + + /** + * Clear all filesystem, string, and binary attachments. + */ + public function clearAttachments() + { + $this->attachment = []; + } + + /** + * Clear all custom headers. + */ + public function clearCustomHeaders() + { + $this->CustomHeader = []; + } + + /** + * Add an error message to the error container. + * + * @param string $msg + */ + protected function setError($msg) + { + ++$this->error_count; + if ('smtp' === $this->Mailer && null !== $this->smtp) { + $lasterror = $this->smtp->getError(); + if (!empty($lasterror['error'])) { + $msg .= $this->lang('smtp_error') . $lasterror['error']; + if (!empty($lasterror['detail'])) { + $msg .= ' Detail: ' . $lasterror['detail']; + } + if (!empty($lasterror['smtp_code'])) { + $msg .= ' SMTP code: ' . $lasterror['smtp_code']; + } + if (!empty($lasterror['smtp_code_ex'])) { + $msg .= ' Additional SMTP info: ' . $lasterror['smtp_code_ex']; + } + } + } + $this->ErrorInfo = $msg; + } + + /** + * Return an RFC 822 formatted date. + * + * @return string + */ + public static function rfcDate() + { + // Set the time zone to whatever the default is to avoid 500 errors + // Will default to UTC if it's not set properly in php.ini + date_default_timezone_set(@date_default_timezone_get()); + + return date('D, j M Y H:i:s O'); + } + + /** + * Get the server hostname. + * Returns 'localhost.localdomain' if unknown. + * + * @return string + */ + protected function serverHostname() + { + $result = ''; + if (!empty($this->Hostname)) { + $result = $this->Hostname; + } elseif (isset($_SERVER) && array_key_exists('SERVER_NAME', $_SERVER)) { + $result = $_SERVER['SERVER_NAME']; + } elseif (function_exists('gethostname') && gethostname() !== false) { + $result = gethostname(); + } elseif (php_uname('n') !== false) { + $result = php_uname('n'); + } + if (!static::isValidHost($result)) { + return 'localhost.localdomain'; + } + + return $result; + } + + /** + * Validate whether a string contains a valid value to use as a hostname or IP address. + * IPv6 addresses must include [], e.g. `[::1]`, not just `::1`. + * + * @param string $host The host name or IP address to check + * + * @return bool + */ + public static function isValidHost($host) + { + //Simple syntax limits + if (empty($host) + || !is_string($host) + || strlen($host) > 256 + || !preg_match('/^([a-zA-Z\d.-]*|\[[a-fA-F\d:]+])$/', $host) + ) { + return false; + } + //Looks like a bracketed IPv6 address + if (strlen($host) > 2 && substr($host, 0, 1) === '[' && substr($host, -1, 1) === ']') { + return filter_var(substr($host, 1, -1), FILTER_VALIDATE_IP, FILTER_FLAG_IPV6) !== false; + } + //If removing all the dots results in a numeric string, it must be an IPv4 address. + //Need to check this first because otherwise things like `999.0.0.0` are considered valid host names + if (is_numeric(str_replace('.', '', $host))) { + //Is it a valid IPv4 address? + return filter_var($host, FILTER_VALIDATE_IP, FILTER_FLAG_IPV4) !== false; + } + if (filter_var('http://' . $host, FILTER_VALIDATE_URL) !== false) { + //Is it a syntactically valid hostname? + return true; + } + + return false; + } + + /** + * Get an error message in the current language. + * + * @param string $key + * + * @return string + */ + protected function lang($key) + { + if (count($this->language) < 1) { + $this->setLanguage(); // set the default language + } + + if (array_key_exists($key, $this->language)) { + if ('smtp_connect_failed' === $key) { + //Include a link to troubleshooting docs on SMTP connection failure + //this is by far the biggest cause of support questions + //but it's usually not PHPMailer's fault. + return $this->language[$key] . ' https://github.com/PHPMailer/PHPMailer/wiki/Troubleshooting'; + } + + return $this->language[$key]; + } + + //Return the key as a fallback + return $key; + } + + /** + * Check if an error occurred. + * + * @return bool True if an error did occur + */ + public function isError() + { + return $this->error_count > 0; + } + + /** + * Add a custom header. + * $name value can be overloaded to contain + * both header name and value (name:value). + * + * @param string $name Custom header name + * @param string|null $value Header value + * + * @throws Exception + */ + public function addCustomHeader($name, $value = null) + { + if (null === $value && strpos($name, ':') !== false) { + // Value passed in as name:value + list($name, $value) = explode(':', $name, 2); + } + $name = trim($name); + $value = trim($value); + //Ensure name is not empty, and that neither name nor value contain line breaks + if (empty($name) || strpbrk($name . $value, "\r\n") !== false) { + if ($this->exceptions) { + throw new Exception('Invalid header name or value'); + } + + return false; + } + $this->CustomHeader[] = [$name, $value]; + + return true; + } + + /** + * Returns all custom headers. + * + * @return array + */ + public function getCustomHeaders() + { + return $this->CustomHeader; + } + + /** + * Create a message body from an HTML string. + * Automatically inlines images and creates a plain-text version by converting the HTML, + * overwriting any existing values in Body and AltBody. + * Do not source $message content from user input! + * $basedir is prepended when handling relative URLs, e.g. and must not be empty + * will look for an image file in $basedir/images/a.png and convert it to inline. + * If you don't provide a $basedir, relative paths will be left untouched (and thus probably break in email) + * Converts data-uri images into embedded attachments. + * If you don't want to apply these transformations to your HTML, just set Body and AltBody directly. + * + * @param string $message HTML message string + * @param string $basedir Absolute path to a base directory to prepend to relative paths to images + * @param bool|callable $advanced Whether to use the internal HTML to text converter + * or your own custom converter + * @return string The transformed message body + * + * @throws Exception + * + * @see PHPMailer::html2text() + */ + public function msgHTML($message, $basedir = '', $advanced = false) + { + preg_match_all('/(? 1 && '/' !== substr($basedir, -1)) { + // Ensure $basedir has a trailing / + $basedir .= '/'; + } + foreach ($images[2] as $imgindex => $url) { + // Convert data URIs into embedded images + //e.g. "data:image/gif;base64,R0lGODlhAQABAAAAACH5BAEKAAEALAAAAAABAAEAAAICTAEAOw==" + $match = []; + if (preg_match('#^data:(image/(?:jpe?g|gif|png));?(base64)?,(.+)#', $url, $match)) { + if (count($match) === 4 && static::ENCODING_BASE64 === $match[2]) { + $data = base64_decode($match[3]); + } elseif ('' === $match[2]) { + $data = rawurldecode($match[3]); + } else { + //Not recognised so leave it alone + continue; + } + //Hash the decoded data, not the URL, so that the same data-URI image used in multiple places + //will only be embedded once, even if it used a different encoding + $cid = substr(hash('sha256', $data), 0, 32) . '@phpmailer.0'; // RFC2392 S 2 + + if (!$this->cidExists($cid)) { + $this->addStringEmbeddedImage( + $data, + $cid, + 'embed' . $imgindex, + static::ENCODING_BASE64, + $match[1] + ); + } + $message = str_replace( + $images[0][$imgindex], + $images[1][$imgindex] . '="cid:' . $cid . '"', + $message + ); + continue; + } + if (// Only process relative URLs if a basedir is provided (i.e. no absolute local paths) + !empty($basedir) + // Ignore URLs containing parent dir traversal (..) + && (strpos($url, '..') === false) + // Do not change urls that are already inline images + && 0 !== strpos($url, 'cid:') + // Do not change absolute URLs, including anonymous protocol + && !preg_match('#^[a-z][a-z0-9+.-]*:?//#i', $url) + ) { + $filename = static::mb_pathinfo($url, PATHINFO_BASENAME); + $directory = dirname($url); + if ('.' === $directory) { + $directory = ''; + } + // RFC2392 S 2 + $cid = substr(hash('sha256', $url), 0, 32) . '@phpmailer.0'; + if (strlen($basedir) > 1 && '/' !== substr($basedir, -1)) { + $basedir .= '/'; + } + if (strlen($directory) > 1 && '/' !== substr($directory, -1)) { + $directory .= '/'; + } + if ($this->addEmbeddedImage( + $basedir . $directory . $filename, + $cid, + $filename, + static::ENCODING_BASE64, + static::_mime_types((string) static::mb_pathinfo($filename, PATHINFO_EXTENSION)) + ) + ) { + $message = preg_replace( + '/' . $images[1][$imgindex] . '=["\']' . preg_quote($url, '/') . '["\']/Ui', + $images[1][$imgindex] . '="cid:' . $cid . '"', + $message + ); + } + } + } + } + $this->isHTML(); + // Convert all message body line breaks to LE, makes quoted-printable encoding work much better + $this->Body = static::normalizeBreaks($message); + $this->AltBody = static::normalizeBreaks($this->html2text($message, $advanced)); + if (!$this->alternativeExists()) { + $this->AltBody = 'This is an HTML-only message. To view it, activate HTML in your email application.' + . static::$LE; + } + + return $this->Body; + } + + /** + * Convert an HTML string into plain text. + * This is used by msgHTML(). + * Note - older versions of this function used a bundled advanced converter + * which was removed for license reasons in #232. + * Example usage: + * + * ```php + * // Use default conversion + * $plain = $mail->html2text($html); + * // Use your own custom converter + * $plain = $mail->html2text($html, function($html) { + * $converter = new MyHtml2text($html); + * return $converter->get_text(); + * }); + * ``` + * + * @param string $html The HTML text to convert + * @param bool|callable $advanced Any boolean value to use the internal converter, + * or provide your own callable for custom conversion + * + * @return string + */ + public function html2text($html, $advanced = false) + { + if (is_callable($advanced)) { + return call_user_func($advanced, $html); + } + + return html_entity_decode( + trim(strip_tags(preg_replace('/<(head|title|style|script)[^>]*>.*?<\/\\1>/si', '', $html))), + ENT_QUOTES, + $this->CharSet + ); + } + + /** + * Get the MIME type for a file extension. + * + * @param string $ext File extension + * + * @return string MIME type of file + */ + public static function _mime_types($ext = '') + { + $mimes = [ + 'xl' => 'application/excel', + 'js' => 'application/javascript', + 'hqx' => 'application/mac-binhex40', + 'cpt' => 'application/mac-compactpro', + 'bin' => 'application/macbinary', + 'doc' => 'application/msword', + 'word' => 'application/msword', + 'xlsx' => 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet', + 'xltx' => 'application/vnd.openxmlformats-officedocument.spreadsheetml.template', + 'potx' => 'application/vnd.openxmlformats-officedocument.presentationml.template', + 'ppsx' => 'application/vnd.openxmlformats-officedocument.presentationml.slideshow', + 'pptx' => 'application/vnd.openxmlformats-officedocument.presentationml.presentation', + 'sldx' => 'application/vnd.openxmlformats-officedocument.presentationml.slide', + 'docx' => 'application/vnd.openxmlformats-officedocument.wordprocessingml.document', + 'dotx' => 'application/vnd.openxmlformats-officedocument.wordprocessingml.template', + 'xlam' => 'application/vnd.ms-excel.addin.macroEnabled.12', + 'xlsb' => 'application/vnd.ms-excel.sheet.binary.macroEnabled.12', + 'class' => 'application/octet-stream', + 'dll' => 'application/octet-stream', + 'dms' => 'application/octet-stream', + 'exe' => 'application/octet-stream', + 'lha' => 'application/octet-stream', + 'lzh' => 'application/octet-stream', + 'psd' => 'application/octet-stream', + 'sea' => 'application/octet-stream', + 'so' => 'application/octet-stream', + 'oda' => 'application/oda', + 'pdf' => 'application/pdf', + 'ai' => 'application/postscript', + 'eps' => 'application/postscript', + 'ps' => 'application/postscript', + 'smi' => 'application/smil', + 'smil' => 'application/smil', + 'mif' => 'application/vnd.mif', + 'xls' => 'application/vnd.ms-excel', + 'ppt' => 'application/vnd.ms-powerpoint', + 'wbxml' => 'application/vnd.wap.wbxml', + 'wmlc' => 'application/vnd.wap.wmlc', + 'dcr' => 'application/x-director', + 'dir' => 'application/x-director', + 'dxr' => 'application/x-director', + 'dvi' => 'application/x-dvi', + 'gtar' => 'application/x-gtar', + 'php3' => 'application/x-httpd-php', + 'php4' => 'application/x-httpd-php', + 'php' => 'application/x-httpd-php', + 'phtml' => 'application/x-httpd-php', + 'phps' => 'application/x-httpd-php-source', + 'swf' => 'application/x-shockwave-flash', + 'sit' => 'application/x-stuffit', + 'tar' => 'application/x-tar', + 'tgz' => 'application/x-tar', + 'xht' => 'application/xhtml+xml', + 'xhtml' => 'application/xhtml+xml', + 'zip' => 'application/zip', + 'mid' => 'audio/midi', + 'midi' => 'audio/midi', + 'mp2' => 'audio/mpeg', + 'mp3' => 'audio/mpeg', + 'm4a' => 'audio/mp4', + 'mpga' => 'audio/mpeg', + 'aif' => 'audio/x-aiff', + 'aifc' => 'audio/x-aiff', + 'aiff' => 'audio/x-aiff', + 'ram' => 'audio/x-pn-realaudio', + 'rm' => 'audio/x-pn-realaudio', + 'rpm' => 'audio/x-pn-realaudio-plugin', + 'ra' => 'audio/x-realaudio', + 'wav' => 'audio/x-wav', + 'mka' => 'audio/x-matroska', + 'bmp' => 'image/bmp', + 'gif' => 'image/gif', + 'jpeg' => 'image/jpeg', + 'jpe' => 'image/jpeg', + 'jpg' => 'image/jpeg', + 'png' => 'image/png', + 'tiff' => 'image/tiff', + 'tif' => 'image/tiff', + 'webp' => 'image/webp', + 'heif' => 'image/heif', + 'heifs' => 'image/heif-sequence', + 'heic' => 'image/heic', + 'heics' => 'image/heic-sequence', + 'eml' => 'message/rfc822', + 'css' => 'text/css', + 'html' => 'text/html', + 'htm' => 'text/html', + 'shtml' => 'text/html', + 'log' => 'text/plain', + 'text' => 'text/plain', + 'txt' => 'text/plain', + 'rtx' => 'text/richtext', + 'rtf' => 'text/rtf', + 'vcf' => 'text/vcard', + 'vcard' => 'text/vcard', + 'ics' => 'text/calendar', + 'xml' => 'text/xml', + 'xsl' => 'text/xml', + 'wmv' => 'video/x-ms-wmv', + 'mpeg' => 'video/mpeg', + 'mpe' => 'video/mpeg', + 'mpg' => 'video/mpeg', + 'mp4' => 'video/mp4', + 'm4v' => 'video/mp4', + 'mov' => 'video/quicktime', + 'qt' => 'video/quicktime', + 'rv' => 'video/vnd.rn-realvideo', + 'avi' => 'video/x-msvideo', + 'movie' => 'video/x-sgi-movie', + 'webm' => 'video/webm', + 'mkv' => 'video/x-matroska', + ]; + $ext = strtolower($ext); + if (array_key_exists($ext, $mimes)) { + return $mimes[$ext]; + } + + return 'application/octet-stream'; + } + + /** + * Map a file name to a MIME type. + * Defaults to 'application/octet-stream', i.e.. arbitrary binary data. + * + * @param string $filename A file name or full path, does not need to exist as a file + * + * @return string + */ + public static function filenameToType($filename) + { + // In case the path is a URL, strip any query string before getting extension + $qpos = strpos($filename, '?'); + if (false !== $qpos) { + $filename = substr($filename, 0, $qpos); + } + $ext = static::mb_pathinfo($filename, PATHINFO_EXTENSION); + + return static::_mime_types($ext); + } + + /** + * Multi-byte-safe pathinfo replacement. + * Drop-in replacement for pathinfo(), but multibyte- and cross-platform-safe. + * + * @see http://www.php.net/manual/en/function.pathinfo.php#107461 + * + * @param string $path A filename or path, does not need to exist as a file + * @param int|string $options Either a PATHINFO_* constant, + * or a string name to return only the specified piece + * + * @return string|array + */ + public static function mb_pathinfo($path, $options = null) + { + $ret = ['dirname' => '', 'basename' => '', 'extension' => '', 'filename' => '']; + $pathinfo = []; + if (preg_match('#^(.*?)[\\\\/]*(([^/\\\\]*?)(\.([^.\\\\/]+?)|))[\\\\/.]*$#m', $path, $pathinfo)) { + if (array_key_exists(1, $pathinfo)) { + $ret['dirname'] = $pathinfo[1]; + } + if (array_key_exists(2, $pathinfo)) { + $ret['basename'] = $pathinfo[2]; + } + if (array_key_exists(5, $pathinfo)) { + $ret['extension'] = $pathinfo[5]; + } + if (array_key_exists(3, $pathinfo)) { + $ret['filename'] = $pathinfo[3]; + } + } + switch ($options) { + case PATHINFO_DIRNAME: + case 'dirname': + return $ret['dirname']; + case PATHINFO_BASENAME: + case 'basename': + return $ret['basename']; + case PATHINFO_EXTENSION: + case 'extension': + return $ret['extension']; + case PATHINFO_FILENAME: + case 'filename': + return $ret['filename']; + default: + return $ret; + } + } + + /** + * Set or reset instance properties. + * You should avoid this function - it's more verbose, less efficient, more error-prone and + * harder to debug than setting properties directly. + * Usage Example: + * `$mail->set('SMTPSecure', static::ENCRYPTION_STARTTLS);` + * is the same as: + * `$mail->SMTPSecure = static::ENCRYPTION_STARTTLS;`. + * + * @param string $name The property name to set + * @param mixed $value The value to set the property to + * + * @return bool + */ + public function set($name, $value = '') + { + if (property_exists($this, $name)) { + $this->$name = $value; + + return true; + } + $this->setError($this->lang('variable_set') . $name); + + return false; + } + + /** + * Strip newlines to prevent header injection. + * + * @param string $str + * + * @return string + */ + public function secureHeader($str) + { + return trim(str_replace(["\r", "\n"], '', $str)); + } + + /** + * Normalize line breaks in a string. + * Converts UNIX LF, Mac CR and Windows CRLF line breaks into a single line break format. + * Defaults to CRLF (for message bodies) and preserves consecutive breaks. + * + * @param string $text + * @param string $breaktype What kind of line break to use; defaults to static::$LE + * + * @return string + */ + public static function normalizeBreaks($text, $breaktype = null) + { + if (null === $breaktype) { + $breaktype = static::$LE; + } + // Normalise to \n + $text = str_replace([self::CRLF, "\r"], "\n", $text); + // Now convert LE as needed + if ("\n" !== $breaktype) { + $text = str_replace("\n", $breaktype, $text); + } + + return $text; + } + + /** + * Remove trailing breaks from a string. + * + * @param string $text + * + * @return string The text to remove breaks from + */ + public static function stripTrailingWSP($text) + { + return rtrim($text, " \r\n\t"); + } + + /** + * Return the current line break format string. + * + * @return string + */ + public static function getLE() + { + return static::$LE; + } + + /** + * Set the line break format string, e.g. "\r\n". + * + * @param string $le + */ + protected static function setLE($le) + { + static::$LE = $le; + } + + /** + * Set the public and private key files and password for S/MIME signing. + * + * @param string $cert_filename + * @param string $key_filename + * @param string $key_pass Password for private key + * @param string $extracerts_filename Optional path to chain certificate + */ + public function sign($cert_filename, $key_filename, $key_pass, $extracerts_filename = '') + { + $this->sign_cert_file = $cert_filename; + $this->sign_key_file = $key_filename; + $this->sign_key_pass = $key_pass; + $this->sign_extracerts_file = $extracerts_filename; + } + + /** + * Quoted-Printable-encode a DKIM header. + * + * @param string $txt + * + * @return string + */ + public function DKIM_QP($txt) + { + $line = ''; + $len = strlen($txt); + for ($i = 0; $i < $len; ++$i) { + $ord = ord($txt[$i]); + if (((0x21 <= $ord) && ($ord <= 0x3A)) || $ord === 0x3C || ((0x3E <= $ord) && ($ord <= 0x7E))) { + $line .= $txt[$i]; + } else { + $line .= '=' . sprintf('%02X', $ord); + } + } + + return $line; + } + + /** + * Generate a DKIM signature. + * + * @param string $signHeader + * + * @throws Exception + * + * @return string The DKIM signature value + */ + public function DKIM_Sign($signHeader) + { + if (!defined('PKCS7_TEXT')) { + if ($this->exceptions) { + throw new Exception($this->lang('extension_missing') . 'openssl'); + } + + return ''; + } + $privKeyStr = !empty($this->DKIM_private_string) ? + $this->DKIM_private_string : + file_get_contents($this->DKIM_private); + if ('' !== $this->DKIM_passphrase) { + $privKey = openssl_pkey_get_private($privKeyStr, $this->DKIM_passphrase); + } else { + $privKey = openssl_pkey_get_private($privKeyStr); + } + if (openssl_sign($signHeader, $signature, $privKey, 'sha256WithRSAEncryption')) { + openssl_pkey_free($privKey); + + return base64_encode($signature); + } + openssl_pkey_free($privKey); + + return ''; + } + + /** + * Generate a DKIM canonicalization header. + * Uses the 'relaxed' algorithm from RFC6376 section 3.4.2. + * Canonicalized headers should *always* use CRLF, regardless of mailer setting. + * + * @see https://tools.ietf.org/html/rfc6376#section-3.4.2 + * + * @param string $signHeader Header + * + * @return string + */ + public function DKIM_HeaderC($signHeader) + { + //Normalize breaks to CRLF (regardless of the mailer) + $signHeader = static::normalizeBreaks($signHeader, self::CRLF); + //Unfold header lines + //Note PCRE \s is too broad a definition of whitespace; RFC5322 defines it as `[ \t]` + //@see https://tools.ietf.org/html/rfc5322#section-2.2 + //That means this may break if you do something daft like put vertical tabs in your headers. + $signHeader = preg_replace('/\r\n[ \t]+/', ' ', $signHeader); + //Break headers out into an array + $lines = explode(self::CRLF, $signHeader); + foreach ($lines as $key => $line) { + //If the header is missing a :, skip it as it's invalid + //This is likely to happen because the explode() above will also split + //on the trailing LE, leaving an empty line + if (strpos($line, ':') === false) { + continue; + } + list($heading, $value) = explode(':', $line, 2); + //Lower-case header name + $heading = strtolower($heading); + //Collapse white space within the value, also convert WSP to space + $value = preg_replace('/[ \t]+/', ' ', $value); + //RFC6376 is slightly unclear here - it says to delete space at the *end* of each value + //But then says to delete space before and after the colon. + //Net result is the same as trimming both ends of the value. + //By elimination, the same applies to the field name + $lines[$key] = trim($heading, " \t") . ':' . trim($value, " \t"); + } + + return implode(self::CRLF, $lines); + } + + /** + * Generate a DKIM canonicalization body. + * Uses the 'simple' algorithm from RFC6376 section 3.4.3. + * Canonicalized bodies should *always* use CRLF, regardless of mailer setting. + * + * @see https://tools.ietf.org/html/rfc6376#section-3.4.3 + * + * @param string $body Message Body + * + * @return string + */ + public function DKIM_BodyC($body) + { + if (empty($body)) { + return self::CRLF; + } + // Normalize line endings to CRLF + $body = static::normalizeBreaks($body, self::CRLF); + + //Reduce multiple trailing line breaks to a single one + return static::stripTrailingWSP($body) . self::CRLF; + } + + /** + * Create the DKIM header and body in a new message header. + * + * @param string $headers_line Header lines + * @param string $subject Subject + * @param string $body Body + * + * @throws Exception + * + * @return string + */ + public function DKIM_Add($headers_line, $subject, $body) + { + $DKIMsignatureType = 'rsa-sha256'; // Signature & hash algorithms + $DKIMcanonicalization = 'relaxed/simple'; // Canonicalization methods of header & body + $DKIMquery = 'dns/txt'; // Query method + $DKIMtime = time(); + //Always sign these headers without being asked + //Recommended list from https://tools.ietf.org/html/rfc6376#section-5.4.1 + $autoSignHeaders = [ + 'from', + 'to', + 'cc', + 'date', + 'subject', + 'reply-to', + 'message-id', + 'content-type', + 'mime-version', + 'x-mailer', + ]; + if (stripos($headers_line, 'Subject') === false) { + $headers_line .= 'Subject: ' . $subject . static::$LE; + } + $headerLines = explode(static::$LE, $headers_line); + $currentHeaderLabel = ''; + $currentHeaderValue = ''; + $parsedHeaders = []; + $headerLineIndex = 0; + $headerLineCount = count($headerLines); + foreach ($headerLines as $headerLine) { + $matches = []; + if (preg_match('/^([^ \t]*?)(?::[ \t]*)(.*)$/', $headerLine, $matches)) { + if ($currentHeaderLabel !== '') { + //We were previously in another header; This is the start of a new header, so save the previous one + $parsedHeaders[] = ['label' => $currentHeaderLabel, 'value' => $currentHeaderValue]; + } + $currentHeaderLabel = $matches[1]; + $currentHeaderValue = $matches[2]; + } elseif (preg_match('/^[ \t]+(.*)$/', $headerLine, $matches)) { + //This is a folded continuation of the current header, so unfold it + $currentHeaderValue .= ' ' . $matches[1]; + } + ++$headerLineIndex; + if ($headerLineIndex >= $headerLineCount) { + //This was the last line, so finish off this header + $parsedHeaders[] = ['label' => $currentHeaderLabel, 'value' => $currentHeaderValue]; + } + } + $copiedHeaders = []; + $headersToSignKeys = []; + $headersToSign = []; + foreach ($parsedHeaders as $header) { + //Is this header one that must be included in the DKIM signature? + if (in_array(strtolower($header['label']), $autoSignHeaders, true)) { + $headersToSignKeys[] = $header['label']; + $headersToSign[] = $header['label'] . ': ' . $header['value']; + if ($this->DKIM_copyHeaderFields) { + $copiedHeaders[] = $header['label'] . ':' . //Note no space after this, as per RFC + str_replace('|', '=7C', $this->DKIM_QP($header['value'])); + } + continue; + } + //Is this an extra custom header we've been asked to sign? + if (in_array($header['label'], $this->DKIM_extraHeaders, true)) { + //Find its value in custom headers + foreach ($this->CustomHeader as $customHeader) { + if ($customHeader[0] === $header['label']) { + $headersToSignKeys[] = $header['label']; + $headersToSign[] = $header['label'] . ': ' . $header['value']; + if ($this->DKIM_copyHeaderFields) { + $copiedHeaders[] = $header['label'] . ':' . //Note no space after this, as per RFC + str_replace('|', '=7C', $this->DKIM_QP($header['value'])); + } + //Skip straight to the next header + continue 2; + } + } + } + } + $copiedHeaderFields = ''; + if ($this->DKIM_copyHeaderFields && count($copiedHeaders) > 0) { + //Assemble a DKIM 'z' tag + $copiedHeaderFields = ' z='; + $first = true; + foreach ($copiedHeaders as $copiedHeader) { + if (!$first) { + $copiedHeaderFields .= static::$LE . ' |'; + } + //Fold long values + if (strlen($copiedHeader) > self::STD_LINE_LENGTH - 3) { + $copiedHeaderFields .= substr( + chunk_split($copiedHeader, self::STD_LINE_LENGTH - 3, static::$LE . self::FWS), + 0, + -strlen(static::$LE . self::FWS) + ); + } else { + $copiedHeaderFields .= $copiedHeader; + } + $first = false; + } + $copiedHeaderFields .= ';' . static::$LE; + } + $headerKeys = ' h=' . implode(':', $headersToSignKeys) . ';' . static::$LE; + $headerValues = implode(static::$LE, $headersToSign); + $body = $this->DKIM_BodyC($body); + $DKIMb64 = base64_encode(pack('H*', hash('sha256', $body))); // Base64 of packed binary SHA-256 hash of body + $ident = ''; + if ('' !== $this->DKIM_identity) { + $ident = ' i=' . $this->DKIM_identity . ';' . static::$LE; + } + //The DKIM-Signature header is included in the signature *except for* the value of the `b` tag + //which is appended after calculating the signature + //https://tools.ietf.org/html/rfc6376#section-3.5 + $dkimSignatureHeader = 'DKIM-Signature: v=1;' . + ' d=' . $this->DKIM_domain . ';' . + ' s=' . $this->DKIM_selector . ';' . static::$LE . + ' a=' . $DKIMsignatureType . ';' . + ' q=' . $DKIMquery . ';' . + ' t=' . $DKIMtime . ';' . + ' c=' . $DKIMcanonicalization . ';' . static::$LE . + $headerKeys . + $ident . + $copiedHeaderFields . + ' bh=' . $DKIMb64 . ';' . static::$LE . + ' b='; + //Canonicalize the set of headers + $canonicalizedHeaders = $this->DKIM_HeaderC( + $headerValues . static::$LE . $dkimSignatureHeader + ); + $signature = $this->DKIM_Sign($canonicalizedHeaders); + $signature = trim(chunk_split($signature, self::STD_LINE_LENGTH - 3, static::$LE . self::FWS)); + + return static::normalizeBreaks($dkimSignatureHeader . $signature); + } + + /** + * Detect if a string contains a line longer than the maximum line length + * allowed by RFC 2822 section 2.1.1. + * + * @param string $str + * + * @return bool + */ + public static function hasLineLongerThanMax($str) + { + return (bool) preg_match('/^(.{' . (self::MAX_LINE_LENGTH + strlen(static::$LE)) . ',})/m', $str); + } + + /** + * If a string contains any "special" characters, double-quote the name, + * and escape any double quotes with a backslash. + * + * @param string $str + * + * @return string + * + * @see RFC822 3.4.1 + */ + public static function quotedString($str) + { + if (preg_match('/[ ()<>@,;:"\/\[\]?=]/', $str)) { + //If the string contains any of these chars, it must be double-quoted + //and any double quotes must be escaped with a backslash + return '"' . str_replace('"', '\\"', $str) . '"'; + } + + //Return the string untouched, it doesn't need quoting + return $str; + } + + /** + * Allows for public read access to 'to' property. + * Before the send() call, queued addresses (i.e. with IDN) are not yet included. + * + * @return array + */ + public function getToAddresses() + { + return $this->to; + } + + /** + * Allows for public read access to 'cc' property. + * Before the send() call, queued addresses (i.e. with IDN) are not yet included. + * + * @return array + */ + public function getCcAddresses() + { + return $this->cc; + } + + /** + * Allows for public read access to 'bcc' property. + * Before the send() call, queued addresses (i.e. with IDN) are not yet included. + * + * @return array + */ + public function getBccAddresses() + { + return $this->bcc; + } + + /** + * Allows for public read access to 'ReplyTo' property. + * Before the send() call, queued addresses (i.e. with IDN) are not yet included. + * + * @return array + */ + public function getReplyToAddresses() + { + return $this->ReplyTo; + } + + /** + * Allows for public read access to 'all_recipients' property. + * Before the send() call, queued addresses (i.e. with IDN) are not yet included. + * + * @return array + */ + public function getAllRecipientAddresses() + { + return $this->all_recipients; + } + + /** + * Perform a callback. + * + * @param bool $isSent + * @param array $to + * @param array $cc + * @param array $bcc + * @param string $subject + * @param string $body + * @param string $from + * @param array $extra + */ + protected function doCallback($isSent, $to, $cc, $bcc, $subject, $body, $from, $extra) + { + if (!empty($this->action_function) && is_callable($this->action_function)) { + call_user_func($this->action_function, $isSent, $to, $cc, $bcc, $subject, $body, $from, $extra); + } + } + + /** + * Get the OAuth instance. + * + * @return OAuth + */ + public function getOAuth() + { + return $this->oauth; + } + + /** + * Set an OAuth instance. + */ + public function setOAuth(OAuth $oauth) + { + $this->oauth = $oauth; + } +} diff --git a/phpmailer/POP3.php b/phpmailer/POP3.php new file mode 100644 index 0000000..9a3b07c --- /dev/null +++ b/phpmailer/POP3.php @@ -0,0 +1,421 @@ + + * @author Jim Jagielski (jimjag) + * @author Andy Prevost (codeworxtech) + * @author Brent R. Matzelle (original founder) + * @copyright 2012 - 2019 Marcus Bointon + * @copyright 2010 - 2012 Jim Jagielski + * @copyright 2004 - 2009 Andy Prevost + * @license http://www.gnu.org/copyleft/lesser.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 POP-Before-SMTP Authentication Class. + * Specifically for PHPMailer to use for RFC1939 POP-before-SMTP authentication. + * 1) This class does not support APOP authentication. + * 2) Opening and closing lots of POP3 connections can be quite slow. If you need + * to send a batch of emails then just perform the authentication once at the start, + * and then loop through your mail sending script. Providing this process doesn't + * take longer than the verification period lasts on your POP3 server, you should be fine. + * 3) This is really ancient technology; you should only need to use it to talk to very old systems. + * 4) This POP3 class is deliberately lightweight and incomplete, implementing just + * enough to do authentication. + * If you want a more complete class there are other POP3 classes for PHP available. + * + * @author Richard Davey (original author) + * @author Marcus Bointon (Synchro/coolbru) + * @author Jim Jagielski (jimjag) + * @author Andy Prevost (codeworxtech) + */ +class POP3 +{ + /** + * The POP3 PHPMailer Version number. + * + * @var string + */ + const VERSION = '6.1.7'; + + /** + * Default POP3 port number. + * + * @var int + */ + const DEFAULT_PORT = 110; + + /** + * Default timeout in seconds. + * + * @var int + */ + const DEFAULT_TIMEOUT = 30; + + /** + * Debug display level. + * Options: 0 = no, 1+ = yes. + * + * @var int + */ + public $do_debug = 0; + + /** + * POP3 mail server hostname. + * + * @var string + */ + public $host; + + /** + * POP3 port number. + * + * @var int + */ + public $port; + + /** + * POP3 Timeout Value in seconds. + * + * @var int + */ + public $tval; + + /** + * POP3 username. + * + * @var string + */ + public $username; + + /** + * POP3 password. + * + * @var string + */ + public $password; + + /** + * Resource handle for the POP3 connection socket. + * + * @var resource + */ + protected $pop_conn; + + /** + * Are we connected? + * + * @var bool + */ + protected $connected = false; + + /** + * Error container. + * + * @var array + */ + protected $errors = []; + + /** + * Line break constant. + */ + const LE = "\r\n"; + + /** + * Simple static wrapper for all-in-one POP before SMTP. + * + * @param string $host The hostname to connect to + * @param int|bool $port The port number to connect to + * @param int|bool $timeout The timeout value + * @param string $username + * @param string $password + * @param int $debug_level + * + * @return bool + */ + public static function popBeforeSmtp( + $host, + $port = false, + $timeout = false, + $username = '', + $password = '', + $debug_level = 0 + ) { + $pop = new self(); + + return $pop->authorise($host, $port, $timeout, $username, $password, $debug_level); + } + + /** + * Authenticate with a POP3 server. + * A connect, login, disconnect sequence + * appropriate for POP-before SMTP authorisation. + * + * @param string $host The hostname to connect to + * @param int|bool $port The port number to connect to + * @param int|bool $timeout The timeout value + * @param string $username + * @param string $password + * @param int $debug_level + * + * @return bool + */ + public function authorise($host, $port = false, $timeout = false, $username = '', $password = '', $debug_level = 0) + { + $this->host = $host; + // If no port value provided, use default + if (false === $port) { + $this->port = static::DEFAULT_PORT; + } else { + $this->port = (int) $port; + } + // If no timeout value provided, use default + if (false === $timeout) { + $this->tval = static::DEFAULT_TIMEOUT; + } else { + $this->tval = (int) $timeout; + } + $this->do_debug = $debug_level; + $this->username = $username; + $this->password = $password; + // Reset the error log + $this->errors = []; + // connect + $result = $this->connect($this->host, $this->port, $this->tval); + if ($result) { + $login_result = $this->login($this->username, $this->password); + if ($login_result) { + $this->disconnect(); + + return true; + } + } + // We need to disconnect regardless of whether the login succeeded + $this->disconnect(); + + return false; + } + + /** + * Connect to a POP3 server. + * + * @param string $host + * @param int|bool $port + * @param int $tval + * + * @return bool + */ + public function connect($host, $port = false, $tval = 30) + { + // Are we already connected? + if ($this->connected) { + return true; + } + + //On Windows this will raise a PHP Warning error if the hostname doesn't exist. + //Rather than suppress it with @fsockopen, capture it cleanly instead + set_error_handler([$this, 'catchWarning']); + + if (false === $port) { + $port = static::DEFAULT_PORT; + } + + // connect to the POP3 server + $errno = 0; + $errstr = ''; + $this->pop_conn = fsockopen( + $host, // POP3 Host + $port, // Port # + $errno, // Error Number + $errstr, // Error Message + $tval + ); // Timeout (seconds) + // Restore the error handler + restore_error_handler(); + + // Did we connect? + if (false === $this->pop_conn) { + // It would appear not... + $this->setError( + "Failed to connect to server $host on port $port. errno: $errno; errstr: $errstr" + ); + + return false; + } + + // Increase the stream time-out + stream_set_timeout($this->pop_conn, $tval, 0); + + // Get the POP3 server response + $pop3_response = $this->getResponse(); + // Check for the +OK + if ($this->checkResponse($pop3_response)) { + // The connection is established and the POP3 server is talking + $this->connected = true; + + return true; + } + + return false; + } + + /** + * Log in to the POP3 server. + * Does not support APOP (RFC 2828, 4949). + * + * @param string $username + * @param string $password + * + * @return bool + */ + public function login($username = '', $password = '') + { + if (!$this->connected) { + $this->setError('Not connected to POP3 server'); + } + if (empty($username)) { + $username = $this->username; + } + if (empty($password)) { + $password = $this->password; + } + + // Send the Username + $this->sendString("USER $username" . static::LE); + $pop3_response = $this->getResponse(); + if ($this->checkResponse($pop3_response)) { + // Send the Password + $this->sendString("PASS $password" . static::LE); + $pop3_response = $this->getResponse(); + if ($this->checkResponse($pop3_response)) { + return true; + } + } + + return false; + } + + /** + * Disconnect from the POP3 server. + */ + public function disconnect() + { + $this->sendString('QUIT'); + //The QUIT command may cause the daemon to exit, which will kill our connection + //So ignore errors here + try { + @fclose($this->pop_conn); + } catch (Exception $e) { + //Do nothing + } + } + + /** + * Get a response from the POP3 server. + * + * @param int $size The maximum number of bytes to retrieve + * + * @return string + */ + protected function getResponse($size = 128) + { + $response = fgets($this->pop_conn, $size); + if ($this->do_debug >= 1) { + echo 'Server -> Client: ', $response; + } + + return $response; + } + + /** + * Send raw data to the POP3 server. + * + * @param string $string + * + * @return int + */ + protected function sendString($string) + { + if ($this->pop_conn) { + if ($this->do_debug >= 2) { //Show client messages when debug >= 2 + echo 'Client -> Server: ', $string; + } + + return fwrite($this->pop_conn, $string, strlen($string)); + } + + return 0; + } + + /** + * Checks the POP3 server response. + * Looks for for +OK or -ERR. + * + * @param string $string + * + * @return bool + */ + protected function checkResponse($string) + { + if (strpos($string, '+OK') !== 0) { + $this->setError("Server reported an error: $string"); + + return false; + } + + return true; + } + + /** + * Add an error to the internal error store. + * Also display debug output if it's enabled. + * + * @param string $error + */ + protected function setError($error) + { + $this->errors[] = $error; + if ($this->do_debug >= 1) { + echo '
';
+            foreach ($this->errors as $e) {
+                print_r($e);
+            }
+            echo '
'; + } + } + + /** + * Get an array of error messages, if any. + * + * @return array + */ + public function getErrors() + { + return $this->errors; + } + + /** + * POP3 connection error handler. + * + * @param int $errno + * @param string $errstr + * @param string $errfile + * @param int $errline + */ + protected function catchWarning($errno, $errstr, $errfile, $errline) + { + $this->setError( + 'Connecting to the POP3 server raised a PHP warning:' . + "errno: $errno errstr: $errstr; errfile: $errfile; errline: $errline" + ); + } +} diff --git a/phpmailer/SMTP.php b/phpmailer/SMTP.php new file mode 100644 index 0000000..6b0b73d --- /dev/null +++ b/phpmailer/SMTP.php @@ -0,0 +1,1426 @@ + + * @author Jim Jagielski (jimjag) + * @author Andy Prevost (codeworxtech) + * @author Brent R. Matzelle (original founder) + * @copyright 2012 - 2019 Marcus Bointon + * @copyright 2010 - 2012 Jim Jagielski + * @copyright 2004 - 2009 Andy Prevost + * @license http://www.gnu.org/copyleft/lesser.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 RFC821 SMTP email transport class. + * Implements RFC 821 SMTP commands and provides some utility methods for sending mail to an SMTP server. + * + * @author Chris Ryan + * @author Marcus Bointon + */ +class SMTP +{ + /** + * The PHPMailer SMTP version number. + * + * @var string + */ + const VERSION = '6.1.7'; + + /** + * SMTP line break constant. + * + * @var string + */ + const LE = "\r\n"; + + /** + * The SMTP port to use if one is not specified. + * + * @var int + */ + const DEFAULT_PORT = 25; + + /** + * The maximum line length allowed by RFC 5321 section 4.5.3.1.6, + * *excluding* a trailing CRLF break. + * + * @see https://tools.ietf.org/html/rfc5321#section-4.5.3.1.6 + * + * @var int + */ + const MAX_LINE_LENGTH = 998; + + /** + * The maximum line length allowed for replies in RFC 5321 section 4.5.3.1.5, + * *including* a trailing CRLF line break. + * + * @see https://tools.ietf.org/html/rfc5321#section-4.5.3.1.5 + * + * @var int + */ + const MAX_REPLY_LENGTH = 512; + + /** + * Debug level for no output. + * + * @var int + */ + const DEBUG_OFF = 0; + + /** + * Debug level to show client -> server messages. + * + * @var int + */ + const DEBUG_CLIENT = 1; + + /** + * Debug level to show client -> server and server -> client messages. + * + * @var int + */ + const DEBUG_SERVER = 2; + + /** + * Debug level to show connection status, client -> server and server -> client messages. + * + * @var int + */ + const DEBUG_CONNECTION = 3; + + /** + * Debug level to show all messages. + * + * @var int + */ + const DEBUG_LOWLEVEL = 4; + + /** + * Debug output level. + * Options: + * * self::DEBUG_OFF (`0`) No debug output, default + * * self::DEBUG_CLIENT (`1`) Client commands + * * self::DEBUG_SERVER (`2`) Client commands and server responses + * * self::DEBUG_CONNECTION (`3`) As DEBUG_SERVER plus connection status + * * self::DEBUG_LOWLEVEL (`4`) Low-level data output, all messages. + * + * @var int + */ + public $do_debug = self::DEBUG_OFF; + + /** + * 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 + * Alternatively, you can provide a callable expecting two params: a message string and the debug level: + * + * ```php + * $smtp->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; + * ``` + * + * @var string|callable|\Psr\Log\LoggerInterface + */ + public $Debugoutput = 'echo'; + + /** + * Whether to use VERP. + * + * @see http://en.wikipedia.org/wiki/Variable_envelope_return_path + * @see http://www.postfix.org/VERP_README.html Info on VERP + * + * @var bool + */ + public $do_verp = false; + + /** + * The timeout value for connection, in seconds. + * Default of 5 minutes (300sec) is from RFC2821 section 4.5.3.2. + * This needs to be quite high to function correctly with hosts using greetdelay as an anti-spam measure. + * + * @see http://tools.ietf.org/html/rfc2821#section-4.5.3.2 + * + * @var int + */ + public $Timeout = 300; + + /** + * How long to wait for commands to complete, in seconds. + * Default of 5 minutes (300sec) is from RFC2821 section 4.5.3.2. + * + * @var int + */ + public $Timelimit = 300; + + /** + * Patterns to extract an SMTP transaction id from reply to a DATA command. + * The first capture group in each regex will be used as the ID. + * MS ESMTP returns the message ID, which may not be correct for internal tracking. + * + * @var string[] + */ + protected $smtp_transaction_id_patterns = [ + 'exim' => '/[\d]{3} OK id=(.*)/', + 'sendmail' => '/[\d]{3} 2.0.0 (.*) Message/', + 'postfix' => '/[\d]{3} 2.0.0 Ok: queued as (.*)/', + 'Microsoft_ESMTP' => '/[0-9]{3} 2.[\d].0 (.*)@(?:.*) Queued mail for delivery/', + 'Amazon_SES' => '/[\d]{3} Ok (.*)/', + 'SendGrid' => '/[\d]{3} Ok: queued as (.*)/', + 'CampaignMonitor' => '/[\d]{3} 2.0.0 OK:([a-zA-Z\d]{48})/', + ]; + + /** + * The last transaction ID issued in response to a DATA command, + * if one was detected. + * + * @var string|bool|null + */ + protected $last_smtp_transaction_id; + + /** + * The socket for the server connection. + * + * @var ?resource + */ + protected $smtp_conn; + + /** + * Error information, if any, for the last SMTP command. + * + * @var array + */ + protected $error = [ + 'error' => '', + 'detail' => '', + 'smtp_code' => '', + 'smtp_code_ex' => '', + ]; + + /** + * The reply the server sent to us for HELO. + * If null, no HELO string has yet been received. + * + * @var string|null + */ + protected $helo_rply; + + /** + * The set of SMTP extensions sent in reply to EHLO command. + * Indexes of the array are extension names. + * Value at index 'HELO' or 'EHLO' (according to command that was sent) + * represents the server name. In case of HELO it is the only element of the array. + * Other values can be boolean TRUE or an array containing extension options. + * If null, no HELO/EHLO string has yet been received. + * + * @var array|null + */ + protected $server_caps; + + /** + * The most recent reply received from the server. + * + * @var string + */ + protected $last_reply = ''; + + /** + * Output debugging info via a user-selected method. + * + * @param string $str Debug string to output + * @param int $level The debug level of this message; see DEBUG_* constants + * + * @see SMTP::$Debugoutput + * @see SMTP::$do_debug + */ + protected function edebug($str, $level = 0) + { + if ($level > $this->do_debug) { + return; + } + //Is this a PSR-3 logger? + if ($this->Debugoutput instanceof \Psr\Log\LoggerInterface) { + $this->Debugoutput->debug($str); + + 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, $level); + + return; + } + switch ($this->Debugoutput) { + case 'error_log': + //Don't output, just log + error_log($str); + break; + case 'html': + //Cleans up output a bit for a better looking, HTML-safe output + echo gmdate('Y-m-d H:i:s'), ' ', 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"; + } + } + + /** + * Connect to an SMTP server. + * + * @param string $host SMTP server IP or host name + * @param int $port The port number to connect to + * @param int $timeout How long to wait for the connection to open + * @param array $options An array of options for stream_context_create() + * + * @return bool + */ + public function connect($host, $port = null, $timeout = 30, $options = []) + { + // Clear errors to avoid confusion + $this->setError(''); + // Make sure we are __not__ connected + if ($this->connected()) { + // Already connected, generate error + $this->setError('Already connected to a server'); + + return false; + } + if (empty($port)) { + $port = self::DEFAULT_PORT; + } + // Connect to the SMTP server + $this->edebug( + "Connection: opening to $host:$port, timeout=$timeout, options=" . + (count($options) > 0 ? var_export($options, true) : 'array()'), + self::DEBUG_CONNECTION + ); + + $this->smtp_conn = $this->getSMTPConnection($host, $port, $timeout, $options); + + if ($this->smtp_conn === false) { + //Error info already set inside `getSMTPConnection()` + return false; + } + + $this->edebug('Connection: opened', self::DEBUG_CONNECTION); + + // Get any announcement + $this->last_reply = $this->get_lines(); + $this->edebug('SERVER -> CLIENT: ' . $this->last_reply, self::DEBUG_SERVER); + + return true; + } + + /** + * Create connection to the SMTP server. + * + * @param string $host SMTP server IP or host name + * @param int $port The port number to connect to + * @param int $timeout How long to wait for the connection to open + * @param array $options An array of options for stream_context_create() + * + * @return false|resource + */ + protected function getSMTPConnection($host, $port = null, $timeout = 30, $options = []) + { + static $streamok; + //This is enabled by default since 5.0.0 but some providers disable it + //Check this once and cache the result + if (null === $streamok) { + $streamok = function_exists('stream_socket_client'); + } + + $errno = 0; + $errstr = ''; + if ($streamok) { + $socket_context = stream_context_create($options); + set_error_handler([$this, 'errorHandler']); + $connection = stream_socket_client( + $host . ':' . $port, + $errno, + $errstr, + $timeout, + STREAM_CLIENT_CONNECT, + $socket_context + ); + restore_error_handler(); + } else { + //Fall back to fsockopen which should work in more places, but is missing some features + $this->edebug( + 'Connection: stream_socket_client not available, falling back to fsockopen', + self::DEBUG_CONNECTION + ); + set_error_handler([$this, 'errorHandler']); + $connection = fsockopen( + $host, + $port, + $errno, + $errstr, + $timeout + ); + restore_error_handler(); + } + + // Verify we connected properly + if (!is_resource($connection)) { + $this->setError( + 'Failed to connect to server', + '', + (string) $errno, + $errstr + ); + $this->edebug( + 'SMTP ERROR: ' . $this->error['error'] + . ": $errstr ($errno)", + self::DEBUG_CLIENT + ); + + return false; + } + + // SMTP server can take longer to respond, give longer timeout for first read + // Windows does not have support for this timeout function + if (strpos(PHP_OS, 'WIN') !== 0) { + $max = (int)ini_get('max_execution_time'); + // Don't bother if unlimited + if (0 !== $max && $timeout > $max) { + @set_time_limit($timeout); + } + stream_set_timeout($connection, $timeout, 0); + } + + return $connection; + } + + /** + * Initiate a TLS (encrypted) session. + * + * @return bool + */ + public function startTLS() + { + if (!$this->sendCommand('STARTTLS', 'STARTTLS', 220)) { + return false; + } + + //Allow the best TLS version(s) we can + $crypto_method = STREAM_CRYPTO_METHOD_TLS_CLIENT; + + //PHP 5.6.7 dropped inclusion of TLS 1.1 and 1.2 in STREAM_CRYPTO_METHOD_TLS_CLIENT + //so add them back in manually if we can + if (defined('STREAM_CRYPTO_METHOD_TLSv1_2_CLIENT')) { + $crypto_method |= STREAM_CRYPTO_METHOD_TLSv1_2_CLIENT; + $crypto_method |= STREAM_CRYPTO_METHOD_TLSv1_1_CLIENT; + } + + // Begin encrypted connection + set_error_handler([$this, 'errorHandler']); + $crypto_ok = stream_socket_enable_crypto( + $this->smtp_conn, + true, + $crypto_method + ); + restore_error_handler(); + + return (bool) $crypto_ok; + } + + /** + * Perform SMTP authentication. + * Must be run after hello(). + * + * @see hello() + * + * @param string $username The user name + * @param string $password The password + * @param string $authtype The auth type (CRAM-MD5, PLAIN, LOGIN, XOAUTH2) + * @param OAuth $OAuth An optional OAuth instance for XOAUTH2 authentication + * + * @return bool True if successfully authenticated + */ + public function authenticate( + $username, + $password, + $authtype = null, + $OAuth = null + ) { + if (!$this->server_caps) { + $this->setError('Authentication is not allowed before HELO/EHLO'); + + return false; + } + + if (array_key_exists('EHLO', $this->server_caps)) { + // SMTP extensions are available; try to find a proper authentication method + if (!array_key_exists('AUTH', $this->server_caps)) { + $this->setError('Authentication is not allowed at this stage'); + // 'at this stage' means that auth may be allowed after the stage changes + // e.g. after STARTTLS + + return false; + } + + $this->edebug('Auth method requested: ' . ($authtype ?: 'UNSPECIFIED'), self::DEBUG_LOWLEVEL); + $this->edebug( + 'Auth methods available on the server: ' . implode(',', $this->server_caps['AUTH']), + self::DEBUG_LOWLEVEL + ); + + //If we have requested a specific auth type, check the server supports it before trying others + if (null !== $authtype && !in_array($authtype, $this->server_caps['AUTH'], true)) { + $this->edebug('Requested auth method not available: ' . $authtype, self::DEBUG_LOWLEVEL); + $authtype = null; + } + + if (empty($authtype)) { + //If no auth mechanism is specified, attempt to use these, in this order + //Try CRAM-MD5 first as it's more secure than the others + foreach (['CRAM-MD5', 'LOGIN', 'PLAIN', 'XOAUTH2'] as $method) { + if (in_array($method, $this->server_caps['AUTH'], true)) { + $authtype = $method; + break; + } + } + if (empty($authtype)) { + $this->setError('No supported authentication methods found'); + + return false; + } + $this->edebug('Auth method selected: ' . $authtype, self::DEBUG_LOWLEVEL); + } + + if (!in_array($authtype, $this->server_caps['AUTH'], true)) { + $this->setError("The requested authentication method \"$authtype\" is not supported by the server"); + + return false; + } + } elseif (empty($authtype)) { + $authtype = 'LOGIN'; + } + switch ($authtype) { + case 'PLAIN': + // Start authentication + if (!$this->sendCommand('AUTH', 'AUTH PLAIN', 334)) { + return false; + } + // Send encoded username and password + if (!$this->sendCommand( + 'User & Password', + base64_encode("\0" . $username . "\0" . $password), + 235 + ) + ) { + return false; + } + break; + case 'LOGIN': + // Start authentication + if (!$this->sendCommand('AUTH', 'AUTH LOGIN', 334)) { + return false; + } + if (!$this->sendCommand('Username', base64_encode($username), 334)) { + return false; + } + if (!$this->sendCommand('Password', base64_encode($password), 235)) { + return false; + } + break; + case 'CRAM-MD5': + // Start authentication + if (!$this->sendCommand('AUTH CRAM-MD5', 'AUTH CRAM-MD5', 334)) { + return false; + } + // Get the challenge + $challenge = base64_decode(substr($this->last_reply, 4)); + + // Build the response + $response = $username . ' ' . $this->hmac($challenge, $password); + + // send encoded credentials + return $this->sendCommand('Username', base64_encode($response), 235); + case 'XOAUTH2': + //The OAuth instance must be set up prior to requesting auth. + if (null === $OAuth) { + return false; + } + $oauth = $OAuth->getOauth64(); + + // Start authentication + if (!$this->sendCommand('AUTH', 'AUTH XOAUTH2 ' . $oauth, 235)) { + return false; + } + break; + default: + $this->setError("Authentication method \"$authtype\" is not supported"); + + return false; + } + + return true; + } + + /** + * Calculate an MD5 HMAC hash. + * Works like hash_hmac('md5', $data, $key) + * in case that function is not available. + * + * @param string $data The data to hash + * @param string $key The key to hash with + * + * @return string + */ + protected function hmac($data, $key) + { + if (function_exists('hash_hmac')) { + return hash_hmac('md5', $data, $key); + } + + // The following borrowed from + // http://php.net/manual/en/function.mhash.php#27225 + + // RFC 2104 HMAC implementation for php. + // Creates an md5 HMAC. + // Eliminates the need to install mhash to compute a HMAC + // by Lance Rushing + + $bytelen = 64; // byte length for md5 + if (strlen($key) > $bytelen) { + $key = pack('H*', md5($key)); + } + $key = str_pad($key, $bytelen, chr(0x00)); + $ipad = str_pad('', $bytelen, chr(0x36)); + $opad = str_pad('', $bytelen, chr(0x5c)); + $k_ipad = $key ^ $ipad; + $k_opad = $key ^ $opad; + + return md5($k_opad . pack('H*', md5($k_ipad . $data))); + } + + /** + * Check connection state. + * + * @return bool True if connected + */ + public function connected() + { + if (is_resource($this->smtp_conn)) { + $sock_status = stream_get_meta_data($this->smtp_conn); + if ($sock_status['eof']) { + // The socket is valid but we are not connected + $this->edebug( + 'SMTP NOTICE: EOF caught while checking if connected', + self::DEBUG_CLIENT + ); + $this->close(); + + return false; + } + + return true; // everything looks good + } + + return false; + } + + /** + * Close the socket and clean up the state of the class. + * Don't use this function without first trying to use QUIT. + * + * @see quit() + */ + public function close() + { + $this->setError(''); + $this->server_caps = null; + $this->helo_rply = null; + if (is_resource($this->smtp_conn)) { + // close the connection and cleanup + fclose($this->smtp_conn); + $this->smtp_conn = null; //Makes for cleaner serialization + $this->edebug('Connection: closed', self::DEBUG_CONNECTION); + } + } + + /** + * Send an SMTP DATA command. + * Issues a data command and sends the msg_data to the server, + * finializing the mail transaction. $msg_data is the message + * that is to be send with the headers. Each header needs to be + * on a single line followed by a with the message headers + * and the message body being separated by an additional . + * Implements RFC 821: DATA . + * + * @param string $msg_data Message data to send + * + * @return bool + */ + public function data($msg_data) + { + //This will use the standard timelimit + if (!$this->sendCommand('DATA', 'DATA', 354)) { + return false; + } + + /* The server is ready to accept data! + * According to rfc821 we should not send more than 1000 characters on a single line (including the LE) + * so we will break the data up into lines by \r and/or \n then if needed we will break each of those into + * smaller lines to fit within the limit. + * We will also look for lines that start with a '.' and prepend an additional '.'. + * NOTE: this does not count towards line-length limit. + */ + + // Normalize line breaks before exploding + $lines = explode("\n", str_replace(["\r\n", "\r"], "\n", $msg_data)); + + /* To distinguish between a complete RFC822 message and a plain message body, we check if the first field + * of the first line (':' separated) does not contain a space then it _should_ be a header and we will + * process all lines before a blank line as headers. + */ + + $field = substr($lines[0], 0, strpos($lines[0], ':')); + $in_headers = false; + if (!empty($field) && strpos($field, ' ') === false) { + $in_headers = true; + } + + foreach ($lines as $line) { + $lines_out = []; + if ($in_headers && $line === '') { + $in_headers = false; + } + //Break this line up into several smaller lines if it's too long + //Micro-optimisation: isset($str[$len]) is faster than (strlen($str) > $len), + while (isset($line[self::MAX_LINE_LENGTH])) { + //Working backwards, try to find a space within the last MAX_LINE_LENGTH chars of the line to break on + //so as to avoid breaking in the middle of a word + $pos = strrpos(substr($line, 0, self::MAX_LINE_LENGTH), ' '); + //Deliberately matches both false and 0 + if (!$pos) { + //No nice break found, add a hard break + $pos = self::MAX_LINE_LENGTH - 1; + $lines_out[] = substr($line, 0, $pos); + $line = substr($line, $pos); + } else { + //Break at the found point + $lines_out[] = substr($line, 0, $pos); + //Move along by the amount we dealt with + $line = substr($line, $pos + 1); + } + //If processing headers add a LWSP-char to the front of new line RFC822 section 3.1.1 + if ($in_headers) { + $line = "\t" . $line; + } + } + $lines_out[] = $line; + + //Send the lines to the server + foreach ($lines_out as $line_out) { + //RFC2821 section 4.5.2 + if (!empty($line_out) && $line_out[0] === '.') { + $line_out = '.' . $line_out; + } + $this->client_send($line_out . static::LE, 'DATA'); + } + } + + //Message data has been sent, complete the command + //Increase timelimit for end of DATA command + $savetimelimit = $this->Timelimit; + $this->Timelimit *= 2; + $result = $this->sendCommand('DATA END', '.', 250); + $this->recordLastTransactionID(); + //Restore timelimit + $this->Timelimit = $savetimelimit; + + return $result; + } + + /** + * Send an SMTP HELO or EHLO command. + * Used to identify the sending server to the receiving server. + * This makes sure that client and server are in a known state. + * Implements RFC 821: HELO + * and RFC 2821 EHLO. + * + * @param string $host The host name or IP to connect to + * + * @return bool + */ + public function hello($host = '') + { + //Try extended hello first (RFC 2821) + return $this->sendHello('EHLO', $host) or $this->sendHello('HELO', $host); + } + + /** + * Send an SMTP HELO or EHLO command. + * Low-level implementation used by hello(). + * + * @param string $hello The HELO string + * @param string $host The hostname to say we are + * + * @return bool + * + * @see hello() + */ + protected function sendHello($hello, $host) + { + $noerror = $this->sendCommand($hello, $hello . ' ' . $host, 250); + $this->helo_rply = $this->last_reply; + if ($noerror) { + $this->parseHelloFields($hello); + } else { + $this->server_caps = null; + } + + return $noerror; + } + + /** + * Parse a reply to HELO/EHLO command to discover server extensions. + * In case of HELO, the only parameter that can be discovered is a server name. + * + * @param string $type `HELO` or `EHLO` + */ + protected function parseHelloFields($type) + { + $this->server_caps = []; + $lines = explode("\n", $this->helo_rply); + + foreach ($lines as $n => $s) { + //First 4 chars contain response code followed by - or space + $s = trim(substr($s, 4)); + if (empty($s)) { + continue; + } + $fields = explode(' ', $s); + if (!empty($fields)) { + if (!$n) { + $name = $type; + $fields = $fields[0]; + } else { + $name = array_shift($fields); + switch ($name) { + case 'SIZE': + $fields = ($fields ? $fields[0] : 0); + break; + case 'AUTH': + if (!is_array($fields)) { + $fields = []; + } + break; + default: + $fields = true; + } + } + $this->server_caps[$name] = $fields; + } + } + } + + /** + * Send an SMTP MAIL command. + * Starts a mail transaction from the email address specified in + * $from. Returns true if successful or false otherwise. If True + * the mail transaction is started and then one or more recipient + * commands may be called followed by a data command. + * Implements RFC 821: MAIL FROM: . + * + * @param string $from Source address of this message + * + * @return bool + */ + public function mail($from) + { + $useVerp = ($this->do_verp ? ' XVERP' : ''); + + return $this->sendCommand( + 'MAIL FROM', + 'MAIL FROM:<' . $from . '>' . $useVerp, + 250 + ); + } + + /** + * Send an SMTP QUIT command. + * Closes the socket if there is no error or the $close_on_error argument is true. + * Implements from RFC 821: QUIT . + * + * @param bool $close_on_error Should the connection close if an error occurs? + * + * @return bool + */ + public function quit($close_on_error = true) + { + $noerror = $this->sendCommand('QUIT', 'QUIT', 221); + $err = $this->error; //Save any error + if ($noerror || $close_on_error) { + $this->close(); + $this->error = $err; //Restore any error from the quit command + } + + return $noerror; + } + + /** + * Send an SMTP RCPT command. + * Sets the TO argument to $toaddr. + * Returns true if the recipient was accepted false if it was rejected. + * Implements from RFC 821: RCPT TO: . + * + * @param string $address The address the message is being sent to + * @param string $dsn Comma separated list of DSN notifications. NEVER, SUCCESS, FAILURE + * or DELAY. If you specify NEVER all other notifications are ignored. + * + * @return bool + */ + public function recipient($address, $dsn = '') + { + if (empty($dsn)) { + $rcpt = 'RCPT TO:<' . $address . '>'; + } else { + $dsn = strtoupper($dsn); + $notify = []; + + if (strpos($dsn, 'NEVER') !== false) { + $notify[] = 'NEVER'; + } else { + foreach (['SUCCESS', 'FAILURE', 'DELAY'] as $value) { + if (strpos($dsn, $value) !== false) { + $notify[] = $value; + } + } + } + + $rcpt = 'RCPT TO:<' . $address . '> NOTIFY=' . implode(',', $notify); + } + + return $this->sendCommand( + 'RCPT TO', + $rcpt, + [250, 251] + ); + } + + /** + * Send an SMTP RSET command. + * Abort any transaction that is currently in progress. + * Implements RFC 821: RSET . + * + * @return bool True on success + */ + public function reset() + { + return $this->sendCommand('RSET', 'RSET', 250); + } + + /** + * Send a command to an SMTP server and check its return code. + * + * @param string $command The command name - not sent to the server + * @param string $commandstring The actual command to send + * @param int|array $expect One or more expected integer success codes + * + * @return bool True on success + */ + protected function sendCommand($command, $commandstring, $expect) + { + if (!$this->connected()) { + $this->setError("Called $command without being connected"); + + return false; + } + //Reject line breaks in all commands + if ((strpos($commandstring, "\n") !== false) || (strpos($commandstring, "\r") !== false)) { + $this->setError("Command '$command' contained line breaks"); + + return false; + } + $this->client_send($commandstring . static::LE, $command); + + $this->last_reply = $this->get_lines(); + // Fetch SMTP code and possible error code explanation + $matches = []; + if (preg_match('/^([\d]{3})[ -](?:([\d]\\.[\d]\\.[\d]{1,2}) )?/', $this->last_reply, $matches)) { + $code = (int) $matches[1]; + $code_ex = (count($matches) > 2 ? $matches[2] : null); + // Cut off error code from each response line + $detail = preg_replace( + "/{$code}[ -]" . + ($code_ex ? str_replace('.', '\\.', $code_ex) . ' ' : '') . '/m', + '', + $this->last_reply + ); + } else { + // Fall back to simple parsing if regex fails + $code = (int) substr($this->last_reply, 0, 3); + $code_ex = null; + $detail = substr($this->last_reply, 4); + } + + $this->edebug('SERVER -> CLIENT: ' . $this->last_reply, self::DEBUG_SERVER); + + if (!in_array($code, (array) $expect, true)) { + $this->setError( + "$command command failed", + $detail, + $code, + $code_ex + ); + $this->edebug( + 'SMTP ERROR: ' . $this->error['error'] . ': ' . $this->last_reply, + self::DEBUG_CLIENT + ); + + return false; + } + + $this->setError(''); + + return true; + } + + /** + * Send an SMTP SAML command. + * Starts a mail transaction from the email address specified in $from. + * Returns true if successful or false otherwise. If True + * the mail transaction is started and then one or more recipient + * commands may be called followed by a data command. This command + * will send the message to the users terminal if they are logged + * in and send them an email. + * Implements RFC 821: SAML FROM: . + * + * @param string $from The address the message is from + * + * @return bool + */ + public function sendAndMail($from) + { + return $this->sendCommand('SAML', "SAML FROM:$from", 250); + } + + /** + * Send an SMTP VRFY command. + * + * @param string $name The name to verify + * + * @return bool + */ + public function verify($name) + { + return $this->sendCommand('VRFY', "VRFY $name", [250, 251]); + } + + /** + * Send an SMTP NOOP command. + * Used to keep keep-alives alive, doesn't actually do anything. + * + * @return bool + */ + public function noop() + { + return $this->sendCommand('NOOP', 'NOOP', 250); + } + + /** + * Send an SMTP TURN command. + * This is an optional command for SMTP that this class does not support. + * This method is here to make the RFC821 Definition complete for this class + * and _may_ be implemented in future. + * Implements from RFC 821: TURN . + * + * @return bool + */ + public function turn() + { + $this->setError('The SMTP TURN command is not implemented'); + $this->edebug('SMTP NOTICE: ' . $this->error['error'], self::DEBUG_CLIENT); + + return false; + } + + /** + * Send raw data to the server. + * + * @param string $data The data to send + * @param string $command Optionally, the command this is part of, used only for controlling debug output + * + * @return int|bool The number of bytes sent to the server or false on error + */ + public function client_send($data, $command = '') + { + //If SMTP transcripts are left enabled, or debug output is posted online + //it can leak credentials, so hide credentials in all but lowest level + if (self::DEBUG_LOWLEVEL > $this->do_debug && + in_array($command, ['User & Password', 'Username', 'Password'], true)) { + $this->edebug('CLIENT -> SERVER: [credentials hidden]', self::DEBUG_CLIENT); + } else { + $this->edebug('CLIENT -> SERVER: ' . $data, self::DEBUG_CLIENT); + } + set_error_handler([$this, 'errorHandler']); + $result = fwrite($this->smtp_conn, $data); + restore_error_handler(); + + return $result; + } + + /** + * Get the latest error. + * + * @return array + */ + public function getError() + { + return $this->error; + } + + /** + * Get SMTP extensions available on the server. + * + * @return array|null + */ + public function getServerExtList() + { + return $this->server_caps; + } + + /** + * Get metadata about the SMTP server from its HELO/EHLO response. + * The method works in three ways, dependent on argument value and current state: + * 1. HELO/EHLO has not been sent - returns null and populates $this->error. + * 2. HELO has been sent - + * $name == 'HELO': returns server name + * $name == 'EHLO': returns boolean false + * $name == any other string: returns null and populates $this->error + * 3. EHLO has been sent - + * $name == 'HELO'|'EHLO': returns the server name + * $name == any other string: if extension $name exists, returns True + * or its options (e.g. AUTH mechanisms supported). Otherwise returns False. + * + * @param string $name Name of SMTP extension or 'HELO'|'EHLO' + * + * @return string|bool|null + */ + public function getServerExt($name) + { + if (!$this->server_caps) { + $this->setError('No HELO/EHLO was sent'); + + return; + } + + if (!array_key_exists($name, $this->server_caps)) { + if ('HELO' === $name) { + return $this->server_caps['EHLO']; + } + if ('EHLO' === $name || array_key_exists('EHLO', $this->server_caps)) { + return false; + } + $this->setError('HELO handshake was used; No information about server extensions available'); + + return; + } + + return $this->server_caps[$name]; + } + + /** + * Get the last reply from the server. + * + * @return string + */ + public function getLastReply() + { + return $this->last_reply; + } + + /** + * Read the SMTP server's response. + * Either before eof or socket timeout occurs on the operation. + * With SMTP we can tell if we have more lines to read if the + * 4th character is '-' symbol. If it is a space then we don't + * need to read anything else. + * + * @return string + */ + protected function get_lines() + { + // If the connection is bad, give up straight away + if (!is_resource($this->smtp_conn)) { + return ''; + } + $data = ''; + $endtime = 0; + stream_set_timeout($this->smtp_conn, $this->Timeout); + if ($this->Timelimit > 0) { + $endtime = time() + $this->Timelimit; + } + $selR = [$this->smtp_conn]; + $selW = null; + while (is_resource($this->smtp_conn) && !feof($this->smtp_conn)) { + //Must pass vars in here as params are by reference + //solution for signals inspired by https://github.com/symfony/symfony/pull/6540 + set_error_handler([$this, 'errorHandler']); + $n = stream_select($selR, $selW, $selW, $this->Timelimit); + restore_error_handler(); + + if ($n === false) { + $message = $this->getError()['detail']; + + $this->edebug( + 'SMTP -> get_lines(): select failed (' . $message . ')', + self::DEBUG_LOWLEVEL + ); + + //stream_select returns false when the `select` system call is interrupted by an incoming signal, try the select again + if (stripos($message, 'interrupted system call') !== false) { + $this->edebug( + 'SMTP -> get_lines(): retrying stream_select', + self::DEBUG_LOWLEVEL + ); + $this->setError(''); + continue; + } + + break; + } + + if (!$n) { + $this->edebug( + 'SMTP -> get_lines(): select timed-out in (' . $this->Timelimit . ' sec)', + self::DEBUG_LOWLEVEL + ); + break; + } + + //Deliberate noise suppression - errors are handled afterwards + $str = @fgets($this->smtp_conn, self::MAX_REPLY_LENGTH); + $this->edebug('SMTP INBOUND: "' . trim($str) . '"', self::DEBUG_LOWLEVEL); + $data .= $str; + // If response is only 3 chars (not valid, but RFC5321 S4.2 says it must be handled), + // or 4th character is a space or a line break char, we are done reading, break the loop. + // String array access is a significant micro-optimisation over strlen + if (!isset($str[3]) || $str[3] === ' ' || $str[3] === "\r" || $str[3] === "\n") { + break; + } + // Timed-out? Log and break + $info = stream_get_meta_data($this->smtp_conn); + if ($info['timed_out']) { + $this->edebug( + 'SMTP -> get_lines(): stream timed-out (' . $this->Timeout . ' sec)', + self::DEBUG_LOWLEVEL + ); + break; + } + // Now check if reads took too long + if ($endtime && time() > $endtime) { + $this->edebug( + 'SMTP -> get_lines(): timelimit reached (' . + $this->Timelimit . ' sec)', + self::DEBUG_LOWLEVEL + ); + break; + } + } + + return $data; + } + + /** + * Enable or disable VERP address generation. + * + * @param bool $enabled + */ + public function setVerp($enabled = false) + { + $this->do_verp = $enabled; + } + + /** + * Get VERP address generation mode. + * + * @return bool + */ + public function getVerp() + { + return $this->do_verp; + } + + /** + * Set error messages and codes. + * + * @param string $message The error message + * @param string $detail Further detail on the error + * @param string $smtp_code An associated SMTP error code + * @param string $smtp_code_ex Extended SMTP code + */ + protected function setError($message, $detail = '', $smtp_code = '', $smtp_code_ex = '') + { + $this->error = [ + 'error' => $message, + 'detail' => $detail, + 'smtp_code' => $smtp_code, + 'smtp_code_ex' => $smtp_code_ex, + ]; + } + + /** + * Set debug output method. + * + * @param string|callable $method The name of the mechanism to use for debugging output, or a callable to handle it + */ + public function setDebugOutput($method = 'echo') + { + $this->Debugoutput = $method; + } + + /** + * Get debug output method. + * + * @return string + */ + public function getDebugOutput() + { + return $this->Debugoutput; + } + + /** + * Set debug output level. + * + * @param int $level + */ + public function setDebugLevel($level = 0) + { + $this->do_debug = $level; + } + + /** + * Get debug output level. + * + * @return int + */ + public function getDebugLevel() + { + return $this->do_debug; + } + + /** + * Set SMTP timeout. + * + * @param int $timeout The timeout duration in seconds + */ + public function setTimeout($timeout = 0) + { + $this->Timeout = $timeout; + } + + /** + * Get SMTP timeout. + * + * @return int + */ + public function getTimeout() + { + return $this->Timeout; + } + + /** + * Reports an error number and string. + * + * @param int $errno The error number returned by PHP + * @param string $errmsg The error message returned by PHP + * @param string $errfile The file the error occurred in + * @param int $errline The line number the error occurred on + */ + protected function errorHandler($errno, $errmsg, $errfile = '', $errline = 0) + { + $notice = 'Connection failed.'; + $this->setError( + $notice, + $errmsg, + (string) $errno + ); + $this->edebug( + "$notice Error #$errno: $errmsg [$errfile line $errline]", + self::DEBUG_CONNECTION + ); + } + + /** + * Extract and return the ID of the last SMTP transaction based on + * a list of patterns provided in SMTP::$smtp_transaction_id_patterns. + * Relies on the host providing the ID in response to a DATA command. + * If no reply has been received yet, it will return null. + * If no pattern was matched, it will return false. + * + * @return bool|string|null + */ + protected function recordLastTransactionID() + { + $reply = $this->getLastReply(); + + if (empty($reply)) { + $this->last_smtp_transaction_id = null; + } else { + $this->last_smtp_transaction_id = false; + foreach ($this->smtp_transaction_id_patterns as $smtp_transaction_id_pattern) { + $matches = []; + if (preg_match($smtp_transaction_id_pattern, $reply, $matches)) { + $this->last_smtp_transaction_id = trim($matches[1]); + break; + } + } + } + + return $this->last_smtp_transaction_id; + } + + /** + * Get the queue/transaction ID of the last SMTP transaction + * If no reply has been received yet, it will return null. + * If no pattern was matched, it will return false. + * + * @return bool|string|null + * + * @see recordLastTransactionID() + */ + public function getLastTransactionID() + { + return $this->last_smtp_transaction_id; + } +} diff --git a/privacy.php b/privacy.php new file mode 100644 index 0000000..faf323c --- /dev/null +++ b/privacy.php @@ -0,0 +1,56 @@ + + + + + + + + + + About Us - Chapalang Clothes + + + + + + + + + + + +
+
+ +
+

Privacy Policy

+
+

Your privacy is important to us. It is Chapalang Clothes' policy to respect your privacy regarding any information we may collect from you across our website, http://chapalangclothes.com, and other sites we own and operate. + + We only ask for personal information when we truly need it to provide a service to you. We collect it by fair and lawful means, with your knowledge and consent. We also let you know why we’re collecting it and how it will be used. + + We only retain collected information for as long as necessary to provide you with your requested service. What data we store, we’ll protect within commercially acceptable means to prevent loss and theft, as well as unauthorised access, disclosure, copying, use or modification. + + We don’t share any personally identifying information publicly or with third-parties, except when required to by law. + Our website may link to external sites that are not operated by us. Please be aware that we have no control over the content and practices of these sites, and cannot accept responsibility or liability for their respective privacy policies. + + You are free to refuse our request for your personal information, with the understanding that we may be unable to provide you with some of your desired services. + + Your continued use of our website will be regarded as acceptance of our practices around privacy and personal information. If you have any questions about how we handle user data and personal information, feel free to contact us. + + This policy is effective as of 29 November 2020.

+
+
+ + + + + + + + + + + + + \ No newline at end of file diff --git a/product_approval.php b/product_approval.php new file mode 100644 index 0000000..ffe1f10 --- /dev/null +++ b/product_approval.php @@ -0,0 +1,12 @@ + \ No newline at end of file diff --git a/product_refund.php b/product_refund.php new file mode 100644 index 0000000..e626e6b --- /dev/null +++ b/product_refund.php @@ -0,0 +1,13 @@ + \ No newline at end of file diff --git a/purchase_history.php b/purchase_history.php new file mode 100644 index 0000000..d070503 --- /dev/null +++ b/purchase_history.php @@ -0,0 +1,207 @@ + + + + + + + + + + + + User Profile + + + + + + + + + + + + + + + +
+
+ +
+ +
+ +
+
+
My Purchases
+ Successfully applied for refund!
'; + break; + case 'refund_fail': + echo '
Failed to apply for refund!
'; + break; + } + } + + $orders = mysqlidb::fetchAllRows("SELECT productorder.OrderId,productorder.PurchaseDate,productorder.OrderTotal,orderitem.ProductId,orderitem.ProductQuantity,orderitem.OrderStatus,product.ProductPrice,product.ProductName FROM productorder INNER JOIN orderitem ON orderitem.OrderId = productorder.OrderId INNER JOIN product ON orderitem.ProductId = product.ProductId WHERE UserId=$userId ORDER BY PurchaseDate DESC, OrderId DESC "); + + + if (count($orders) > 0) { + $prevOrderId = -1; + for ($i = 0; $i < count($orders); $i++) { + if ($orders[$i]["OrderId"] != $prevOrderId) { + echo "
+
+
Ordered on {$orders[$i]['PurchaseDate']}
+
+
"; + } + $prevOrderId = $orders[$i]['OrderId']; + echo "
+ +
close{$orders[$i]['ProductQuantity']}
+
RM " . number_format($orders[$i]['ProductPrice'], 2) . "
+
"; + + echo "Report"; + + if ($orders[$i]['OrderStatus'] != "Refund") { + if ($orders[$i]['OrderStatus'] != "RefundPending") { + echo "Refund"; + } else { + echo "
Pending Refund
"; + } + } else { + echo "
Refunded
"; + } + + echo "
+
"; + + if ($i + 1 <= count($orders) - 1) { + if ($orders[$i + 1]["OrderId"] != $prevOrderId) { + echo "
+
+
View Details
+
Total
+
RM " . number_format($orders[$i]['OrderTotal'], 2) . "
+
+
"; + } + } else { + echo "
+
+
View Details
+
Total
+
RM " . number_format($orders[$i]['OrderTotal'], 2) . "
+
+
"; + } + } + } else { + echo "

No purchases to show.

"; + } + + + ?> + +
+ + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/refund_submission.php b/refund_submission.php new file mode 100644 index 0000000..ce5e1cc --- /dev/null +++ b/refund_submission.php @@ -0,0 +1,33 @@ + + alert('Image already exists! Please choose another image'); + window.location.href = 'purchase_history.php?message=refund_fail' + "; + } +} diff --git a/refundapplication.php b/refundapplication.php new file mode 100644 index 0000000..4f0446e --- /dev/null +++ b/refundapplication.php @@ -0,0 +1,98 @@ + + + + + + + + + + + + + + Refund Application + + + + + + +
+
+ + +
+
+
+
+ " + ?> +
+
+
Product Name :
+
Shop Name :
+
Quantity :
+
Price :
+
Date Purchased :
+
+
+ +
+
+
+ +
+ +
+ + +
+
+ + + +
+
+ +
+ +
+ + + + + +
+ + + + + + \ No newline at end of file diff --git a/register.php b/register.php new file mode 100644 index 0000000..9b8caa0 --- /dev/null +++ b/register.php @@ -0,0 +1,53 @@ + + alert("User has been registered. You will now be redirected to the login page"); + window.location.href = "./loginpage.php" + '; + } + } elseif ($_POST["userrole"] === "vendor" && is_null($vendorname) && is_null($vendoremail)) { + + if (!mysqli_query($con, $vendor)) { + die('Error: ' . mysqli_error($con)); + } else { + echo ''; + } + } else { + echo ''; + } +} else { + echo ''; +} +mysqli_close($con); diff --git a/registerpage.php b/registerpage.php new file mode 100644 index 0000000..5a09781 --- /dev/null +++ b/registerpage.php @@ -0,0 +1,61 @@ + + + + + + + + Sign Up Page + + + + +
+ + + +
+ +
+ + +
+ + + + \ No newline at end of file diff --git a/report.php b/report.php new file mode 100644 index 0000000..3d5d3fc --- /dev/null +++ b/report.php @@ -0,0 +1,17 @@ +alert('User with that email does not exist!'); window.location.href='forgotpassword.php'"; + die(); + } +} else if (strtolower($userrole) == "vendor") { + if ($vendor = mysqlidb::fetchRow("SELECT * FROM vendor WHERE Email='$email'")) { + $vendorid = $vendor["VendorId"]; + $email = $vendor["Email"]; + $username = $vendor["Username"]; + $newpassword = random_password(); + mysqlidb::query("UPDATE vendor SET `Password`=sha1('$newpassword') WHERE VendorId=$vendorid"); + } else { + echo ""; + die(); + } +} + + +use PHPMailer\PHPMailer\PHPMailer; +use PHPMailer\PHPMailer\Exception; + +require 'phpmailer/Exception.php'; +require 'phpmailer/PHPMailer.php'; +require 'phpmailer/SMTP.php'; + +$mail = new PHPMailer(true); + +try { + //Server settings + $mail->SMTPDebug = 0; // Enable verbose debug output + $mail->isSMTP(); // Send using SMTP + $mail->Host = 'smtp.gmail.com'; // Set the SMTP server to send through + $mail->SMTPAuth = true; // Enable SMTP authentication + $mail->Username = 'EMAILHERE'; // SMTP username + $mail->Password = 'PASSWORDHERE'; // SMTP password + $mail->SMTPSecure = PHPMailer::ENCRYPTION_STARTTLS; // Enable TLS encryption; `PHPMailer::ENCRYPTION_SMTPS` encouraged + $mail->Port = 587; // TCP port to connect to, use 465 for `PHPMailer::ENCRYPTION_SMTPS` above + + //Recipients + $mail->setFrom('noreply@chapalang.com.my', 'Chapalang Clothes'); + $mail->addAddress($email); // Add a recipient + + // Content + $mail->isHTML(true); + $mail->Subject = 'Your Chapalang Account: Reset Password'; + $mail->Body = "

You have requested a password reset.

+

Your username is: $username

+

Your new password is: $newpassword

"; + + $mail->send(); + + echo ""; + die(); +} catch (Exception $e) { + echo ""; + die(); +} diff --git a/review.php b/review.php new file mode 100644 index 0000000..f747ad4 --- /dev/null +++ b/review.php @@ -0,0 +1,28 @@ +1&&(a-=1)),[360*a,100*r,100*u]},a.rgb.hwb=function(t){var e=t[0],n=t[1],i=t[2];return[a.rgb.hsl(t)[0],100*(1/255*Math.min(e,Math.min(n,i))),100*(i=1-1/255*Math.max(e,Math.max(n,i)))]},a.rgb.cmyk=function(t){var e,n=t[0]/255,i=t[1]/255,a=t[2]/255;return[100*((1-n-(e=Math.min(1-n,1-i,1-a)))/(1-e)||0),100*((1-i-e)/(1-e)||0),100*((1-a-e)/(1-e)||0),100*e]},a.rgb.keyword=function(t){var i=n[t];if(i)return i;var a,r,o,s=1/0;for(var l in e)if(e.hasOwnProperty(l)){var u=e[l],d=(r=t,o=u,Math.pow(r[0]-o[0],2)+Math.pow(r[1]-o[1],2)+Math.pow(r[2]-o[2],2));d.04045?Math.pow((e+.055)/1.055,2.4):e/12.92)+.3576*(n=n>.04045?Math.pow((n+.055)/1.055,2.4):n/12.92)+.1805*(i=i>.04045?Math.pow((i+.055)/1.055,2.4):i/12.92)),100*(.2126*e+.7152*n+.0722*i),100*(.0193*e+.1192*n+.9505*i)]},a.rgb.lab=function(t){var e=a.rgb.xyz(t),n=e[0],i=e[1],r=e[2];return i/=100,r/=108.883,n=(n/=95.047)>.008856?Math.pow(n,1/3):7.787*n+16/116,[116*(i=i>.008856?Math.pow(i,1/3):7.787*i+16/116)-16,500*(n-i),200*(i-(r=r>.008856?Math.pow(r,1/3):7.787*r+16/116))]},a.hsl.rgb=function(t){var e,n,i,a,r,o=t[0]/360,s=t[1]/100,l=t[2]/100;if(0===s)return[r=255*l,r,r];e=2*l-(n=l<.5?l*(1+s):l+s-l*s),a=[0,0,0];for(var u=0;u<3;u++)(i=o+1/3*-(u-1))<0&&i++,i>1&&i--,r=6*i<1?e+6*(n-e)*i:2*i<1?n:3*i<2?e+(n-e)*(2/3-i)*6:e,a[u]=255*r;return a},a.hsl.hsv=function(t){var e=t[0],n=t[1]/100,i=t[2]/100,a=n,r=Math.max(i,.01);return n*=(i*=2)<=1?i:2-i,a*=r<=1?r:2-r,[e,100*(0===i?2*a/(r+a):2*n/(i+n)),100*((i+n)/2)]},a.hsv.rgb=function(t){var e=t[0]/60,n=t[1]/100,i=t[2]/100,a=Math.floor(e)%6,r=e-Math.floor(e),o=255*i*(1-n),s=255*i*(1-n*r),l=255*i*(1-n*(1-r));switch(i*=255,a){case 0:return[i,l,o];case 1:return[s,i,o];case 2:return[o,i,l];case 3:return[o,s,i];case 4:return[l,o,i];case 5:return[i,o,s]}},a.hsv.hsl=function(t){var e,n,i,a=t[0],r=t[1]/100,o=t[2]/100,s=Math.max(o,.01);return i=(2-r)*o,n=r*s,[a,100*(n=(n/=(e=(2-r)*s)<=1?e:2-e)||0),100*(i/=2)]},a.hwb.rgb=function(t){var e,n,i,a,r,o,s,l=t[0]/360,u=t[1]/100,d=t[2]/100,h=u+d;switch(h>1&&(u/=h,d/=h),i=6*l-(e=Math.floor(6*l)),0!=(1&e)&&(i=1-i),a=u+i*((n=1-d)-u),e){default:case 6:case 0:r=n,o=a,s=u;break;case 1:r=a,o=n,s=u;break;case 2:r=u,o=n,s=a;break;case 3:r=u,o=a,s=n;break;case 4:r=a,o=u,s=n;break;case 5:r=n,o=u,s=a}return[255*r,255*o,255*s]},a.cmyk.rgb=function(t){var e=t[0]/100,n=t[1]/100,i=t[2]/100,a=t[3]/100;return[255*(1-Math.min(1,e*(1-a)+a)),255*(1-Math.min(1,n*(1-a)+a)),255*(1-Math.min(1,i*(1-a)+a))]},a.xyz.rgb=function(t){var e,n,i,a=t[0]/100,r=t[1]/100,o=t[2]/100;return n=-.9689*a+1.8758*r+.0415*o,i=.0557*a+-.204*r+1.057*o,e=(e=3.2406*a+-1.5372*r+-.4986*o)>.0031308?1.055*Math.pow(e,1/2.4)-.055:12.92*e,n=n>.0031308?1.055*Math.pow(n,1/2.4)-.055:12.92*n,i=i>.0031308?1.055*Math.pow(i,1/2.4)-.055:12.92*i,[255*(e=Math.min(Math.max(0,e),1)),255*(n=Math.min(Math.max(0,n),1)),255*(i=Math.min(Math.max(0,i),1))]},a.xyz.lab=function(t){var e=t[0],n=t[1],i=t[2];return n/=100,i/=108.883,e=(e/=95.047)>.008856?Math.pow(e,1/3):7.787*e+16/116,[116*(n=n>.008856?Math.pow(n,1/3):7.787*n+16/116)-16,500*(e-n),200*(n-(i=i>.008856?Math.pow(i,1/3):7.787*i+16/116))]},a.lab.xyz=function(t){var e,n,i,a=t[0];e=t[1]/500+(n=(a+16)/116),i=n-t[2]/200;var r=Math.pow(n,3),o=Math.pow(e,3),s=Math.pow(i,3);return n=r>.008856?r:(n-16/116)/7.787,e=o>.008856?o:(e-16/116)/7.787,i=s>.008856?s:(i-16/116)/7.787,[e*=95.047,n*=100,i*=108.883]},a.lab.lch=function(t){var e,n=t[0],i=t[1],a=t[2];return(e=360*Math.atan2(a,i)/2/Math.PI)<0&&(e+=360),[n,Math.sqrt(i*i+a*a),e]},a.lch.lab=function(t){var e,n=t[0],i=t[1];return e=t[2]/360*2*Math.PI,[n,i*Math.cos(e),i*Math.sin(e)]},a.rgb.ansi16=function(t){var e=t[0],n=t[1],i=t[2],r=1 in arguments?arguments[1]:a.rgb.hsv(t)[2];if(0===(r=Math.round(r/50)))return 30;var o=30+(Math.round(i/255)<<2|Math.round(n/255)<<1|Math.round(e/255));return 2===r&&(o+=60),o},a.hsv.ansi16=function(t){return a.rgb.ansi16(a.hsv.rgb(t),t[2])},a.rgb.ansi256=function(t){var e=t[0],n=t[1],i=t[2];return e===n&&n===i?e<8?16:e>248?231:Math.round((e-8)/247*24)+232:16+36*Math.round(e/255*5)+6*Math.round(n/255*5)+Math.round(i/255*5)},a.ansi16.rgb=function(t){var e=t%10;if(0===e||7===e)return t>50&&(e+=3.5),[e=e/10.5*255,e,e];var n=.5*(1+~~(t>50));return[(1&e)*n*255,(e>>1&1)*n*255,(e>>2&1)*n*255]},a.ansi256.rgb=function(t){if(t>=232){var e=10*(t-232)+8;return[e,e,e]}var n;return t-=16,[Math.floor(t/36)/5*255,Math.floor((n=t%36)/6)/5*255,n%6/5*255]},a.rgb.hex=function(t){var e=(((255&Math.round(t[0]))<<16)+((255&Math.round(t[1]))<<8)+(255&Math.round(t[2]))).toString(16).toUpperCase();return"000000".substring(e.length)+e},a.hex.rgb=function(t){var e=t.toString(16).match(/[a-f0-9]{6}|[a-f0-9]{3}/i);if(!e)return[0,0,0];var n=e[0];3===e[0].length&&(n=n.split("").map((function(t){return t+t})).join(""));var i=parseInt(n,16);return[i>>16&255,i>>8&255,255&i]},a.rgb.hcg=function(t){var e,n=t[0]/255,i=t[1]/255,a=t[2]/255,r=Math.max(Math.max(n,i),a),o=Math.min(Math.min(n,i),a),s=r-o;return e=s<=0?0:r===n?(i-a)/s%6:r===i?2+(a-n)/s:4+(n-i)/s+4,e/=6,[360*(e%=1),100*s,100*(s<1?o/(1-s):0)]},a.hsl.hcg=function(t){var e=t[1]/100,n=t[2]/100,i=1,a=0;return(i=n<.5?2*e*n:2*e*(1-n))<1&&(a=(n-.5*i)/(1-i)),[t[0],100*i,100*a]},a.hsv.hcg=function(t){var e=t[1]/100,n=t[2]/100,i=e*n,a=0;return i<1&&(a=(n-i)/(1-i)),[t[0],100*i,100*a]},a.hcg.rgb=function(t){var e=t[0]/360,n=t[1]/100,i=t[2]/100;if(0===n)return[255*i,255*i,255*i];var a,r=[0,0,0],o=e%1*6,s=o%1,l=1-s;switch(Math.floor(o)){case 0:r[0]=1,r[1]=s,r[2]=0;break;case 1:r[0]=l,r[1]=1,r[2]=0;break;case 2:r[0]=0,r[1]=1,r[2]=s;break;case 3:r[0]=0,r[1]=l,r[2]=1;break;case 4:r[0]=s,r[1]=0,r[2]=1;break;default:r[0]=1,r[1]=0,r[2]=l}return a=(1-n)*i,[255*(n*r[0]+a),255*(n*r[1]+a),255*(n*r[2]+a)]},a.hcg.hsv=function(t){var e=t[1]/100,n=e+t[2]/100*(1-e),i=0;return n>0&&(i=e/n),[t[0],100*i,100*n]},a.hcg.hsl=function(t){var e=t[1]/100,n=t[2]/100*(1-e)+.5*e,i=0;return n>0&&n<.5?i=e/(2*n):n>=.5&&n<1&&(i=e/(2*(1-n))),[t[0],100*i,100*n]},a.hcg.hwb=function(t){var e=t[1]/100,n=e+t[2]/100*(1-e);return[t[0],100*(n-e),100*(1-n)]},a.hwb.hcg=function(t){var e=t[1]/100,n=1-t[2]/100,i=n-e,a=0;return i<1&&(a=(n-i)/(1-i)),[t[0],100*i,100*a]},a.apple.rgb=function(t){return[t[0]/65535*255,t[1]/65535*255,t[2]/65535*255]},a.rgb.apple=function(t){return[t[0]/255*65535,t[1]/255*65535,t[2]/255*65535]},a.gray.rgb=function(t){return[t[0]/100*255,t[0]/100*255,t[0]/100*255]},a.gray.hsl=a.gray.hsv=function(t){return[0,0,t[0]]},a.gray.hwb=function(t){return[0,100,t[0]]},a.gray.cmyk=function(t){return[0,0,0,t[0]]},a.gray.lab=function(t){return[t[0],0,0]},a.gray.hex=function(t){var e=255&Math.round(t[0]/100*255),n=((e<<16)+(e<<8)+e).toString(16).toUpperCase();return"000000".substring(n.length)+n},a.rgb.gray=function(t){return[(t[0]+t[1]+t[2])/3/255*100]}}));n.rgb,n.hsl,n.hsv,n.hwb,n.cmyk,n.xyz,n.lab,n.lch,n.hex,n.keyword,n.ansi16,n.ansi256,n.hcg,n.apple,n.gray;function i(t){var e=function(){for(var t={},e=Object.keys(n),i=e.length,a=0;a1&&(e=Array.prototype.slice.call(arguments));var n=t(e);if("object"==typeof n)for(var i=n.length,a=0;a1&&(e=Array.prototype.slice.call(arguments)),t(e))};return"conversion"in t&&(e.conversion=t.conversion),e}(i)}))}));var s=o,l={aliceblue:[240,248,255],antiquewhite:[250,235,215],aqua:[0,255,255],aquamarine:[127,255,212],azure:[240,255,255],beige:[245,245,220],bisque:[255,228,196],black:[0,0,0],blanchedalmond:[255,235,205],blue:[0,0,255],blueviolet:[138,43,226],brown:[165,42,42],burlywood:[222,184,135],cadetblue:[95,158,160],chartreuse:[127,255,0],chocolate:[210,105,30],coral:[255,127,80],cornflowerblue:[100,149,237],cornsilk:[255,248,220],crimson:[220,20,60],cyan:[0,255,255],darkblue:[0,0,139],darkcyan:[0,139,139],darkgoldenrod:[184,134,11],darkgray:[169,169,169],darkgreen:[0,100,0],darkgrey:[169,169,169],darkkhaki:[189,183,107],darkmagenta:[139,0,139],darkolivegreen:[85,107,47],darkorange:[255,140,0],darkorchid:[153,50,204],darkred:[139,0,0],darksalmon:[233,150,122],darkseagreen:[143,188,143],darkslateblue:[72,61,139],darkslategray:[47,79,79],darkslategrey:[47,79,79],darkturquoise:[0,206,209],darkviolet:[148,0,211],deeppink:[255,20,147],deepskyblue:[0,191,255],dimgray:[105,105,105],dimgrey:[105,105,105],dodgerblue:[30,144,255],firebrick:[178,34,34],floralwhite:[255,250,240],forestgreen:[34,139,34],fuchsia:[255,0,255],gainsboro:[220,220,220],ghostwhite:[248,248,255],gold:[255,215,0],goldenrod:[218,165,32],gray:[128,128,128],green:[0,128,0],greenyellow:[173,255,47],grey:[128,128,128],honeydew:[240,255,240],hotpink:[255,105,180],indianred:[205,92,92],indigo:[75,0,130],ivory:[255,255,240],khaki:[240,230,140],lavender:[230,230,250],lavenderblush:[255,240,245],lawngreen:[124,252,0],lemonchiffon:[255,250,205],lightblue:[173,216,230],lightcoral:[240,128,128],lightcyan:[224,255,255],lightgoldenrodyellow:[250,250,210],lightgray:[211,211,211],lightgreen:[144,238,144],lightgrey:[211,211,211],lightpink:[255,182,193],lightsalmon:[255,160,122],lightseagreen:[32,178,170],lightskyblue:[135,206,250],lightslategray:[119,136,153],lightslategrey:[119,136,153],lightsteelblue:[176,196,222],lightyellow:[255,255,224],lime:[0,255,0],limegreen:[50,205,50],linen:[250,240,230],magenta:[255,0,255],maroon:[128,0,0],mediumaquamarine:[102,205,170],mediumblue:[0,0,205],mediumorchid:[186,85,211],mediumpurple:[147,112,219],mediumseagreen:[60,179,113],mediumslateblue:[123,104,238],mediumspringgreen:[0,250,154],mediumturquoise:[72,209,204],mediumvioletred:[199,21,133],midnightblue:[25,25,112],mintcream:[245,255,250],mistyrose:[255,228,225],moccasin:[255,228,181],navajowhite:[255,222,173],navy:[0,0,128],oldlace:[253,245,230],olive:[128,128,0],olivedrab:[107,142,35],orange:[255,165,0],orangered:[255,69,0],orchid:[218,112,214],palegoldenrod:[238,232,170],palegreen:[152,251,152],paleturquoise:[175,238,238],palevioletred:[219,112,147],papayawhip:[255,239,213],peachpuff:[255,218,185],peru:[205,133,63],pink:[255,192,203],plum:[221,160,221],powderblue:[176,224,230],purple:[128,0,128],rebeccapurple:[102,51,153],red:[255,0,0],rosybrown:[188,143,143],royalblue:[65,105,225],saddlebrown:[139,69,19],salmon:[250,128,114],sandybrown:[244,164,96],seagreen:[46,139,87],seashell:[255,245,238],sienna:[160,82,45],silver:[192,192,192],skyblue:[135,206,235],slateblue:[106,90,205],slategray:[112,128,144],slategrey:[112,128,144],snow:[255,250,250],springgreen:[0,255,127],steelblue:[70,130,180],tan:[210,180,140],teal:[0,128,128],thistle:[216,191,216],tomato:[255,99,71],turquoise:[64,224,208],violet:[238,130,238],wheat:[245,222,179],white:[255,255,255],whitesmoke:[245,245,245],yellow:[255,255,0],yellowgreen:[154,205,50]},u={getRgba:d,getHsla:h,getRgb:function(t){var e=d(t);return e&&e.slice(0,3)},getHsl:function(t){var e=h(t);return e&&e.slice(0,3)},getHwb:c,getAlpha:function(t){var e=d(t);if(e)return e[3];if(e=h(t))return e[3];if(e=c(t))return e[3]},hexString:function(t,e){e=void 0!==e&&3===t.length?e:t[3];return"#"+v(t[0])+v(t[1])+v(t[2])+(e>=0&&e<1?v(Math.round(255*e)):"")},rgbString:function(t,e){if(e<1||t[3]&&t[3]<1)return f(t,e);return"rgb("+t[0]+", "+t[1]+", "+t[2]+")"},rgbaString:f,percentString:function(t,e){if(e<1||t[3]&&t[3]<1)return g(t,e);var n=Math.round(t[0]/255*100),i=Math.round(t[1]/255*100),a=Math.round(t[2]/255*100);return"rgb("+n+"%, "+i+"%, "+a+"%)"},percentaString:g,hslString:function(t,e){if(e<1||t[3]&&t[3]<1)return p(t,e);return"hsl("+t[0]+", "+t[1]+"%, "+t[2]+"%)"},hslaString:p,hwbString:function(t,e){void 0===e&&(e=void 0!==t[3]?t[3]:1);return"hwb("+t[0]+", "+t[1]+"%, "+t[2]+"%"+(void 0!==e&&1!==e?", "+e:"")+")"},keyword:function(t){return b[t.slice(0,3)]}};function d(t){if(t){var e=[0,0,0],n=1,i=t.match(/^#([a-fA-F0-9]{3,4})$/i),a="";if(i){a=(i=i[1])[3];for(var r=0;rn?(e+.05)/(n+.05):(n+.05)/(e+.05)},level:function(t){var e=this.contrast(t);return e>=7.1?"AAA":e>=4.5?"AA":""},dark:function(){var t=this.values.rgb;return(299*t[0]+587*t[1]+114*t[2])/1e3<128},light:function(){return!this.dark()},negate:function(){for(var t=[],e=0;e<3;e++)t[e]=255-this.values.rgb[e];return this.setValues("rgb",t),this},lighten:function(t){var e=this.values.hsl;return e[2]+=e[2]*t,this.setValues("hsl",e),this},darken:function(t){var e=this.values.hsl;return e[2]-=e[2]*t,this.setValues("hsl",e),this},saturate:function(t){var e=this.values.hsl;return e[1]+=e[1]*t,this.setValues("hsl",e),this},desaturate:function(t){var e=this.values.hsl;return e[1]-=e[1]*t,this.setValues("hsl",e),this},whiten:function(t){var e=this.values.hwb;return e[1]+=e[1]*t,this.setValues("hwb",e),this},blacken:function(t){var e=this.values.hwb;return e[2]+=e[2]*t,this.setValues("hwb",e),this},greyscale:function(){var t=this.values.rgb,e=.3*t[0]+.59*t[1]+.11*t[2];return this.setValues("rgb",[e,e,e]),this},clearer:function(t){var e=this.values.alpha;return this.setValues("alpha",e-e*t),this},opaquer:function(t){var e=this.values.alpha;return this.setValues("alpha",e+e*t),this},rotate:function(t){var e=this.values.hsl,n=(e[0]+t)%360;return e[0]=n<0?360+n:n,this.setValues("hsl",e),this},mix:function(t,e){var n=t,i=void 0===e?.5:e,a=2*i-1,r=this.alpha()-n.alpha(),o=((a*r==-1?a:(a+r)/(1+a*r))+1)/2,s=1-o;return this.rgb(o*this.red()+s*n.red(),o*this.green()+s*n.green(),o*this.blue()+s*n.blue()).alpha(this.alpha()*i+n.alpha()*(1-i))},toJSON:function(){return this.rgb()},clone:function(){var t,e,n=new y,i=this.values,a=n.values;for(var r in i)i.hasOwnProperty(r)&&(t=i[r],"[object Array]"===(e={}.toString.call(t))?a[r]=t.slice(0):"[object Number]"===e?a[r]=t:console.error("unexpected color value:",t));return n}},y.prototype.spaces={rgb:["red","green","blue"],hsl:["hue","saturation","lightness"],hsv:["hue","saturation","value"],hwb:["hue","whiteness","blackness"],cmyk:["cyan","magenta","yellow","black"]},y.prototype.maxes={rgb:[255,255,255],hsl:[360,100,100],hsv:[360,100,100],hwb:[360,100,100],cmyk:[100,100,100,100]},y.prototype.getValues=function(t){for(var e=this.values,n={},i=0;i=0;a--)e.call(n,t[a],a);else for(a=0;a=1?t:-(Math.sqrt(1-t*t)-1)},easeOutCirc:function(t){return Math.sqrt(1-(t-=1)*t)},easeInOutCirc:function(t){return(t/=.5)<1?-.5*(Math.sqrt(1-t*t)-1):.5*(Math.sqrt(1-(t-=2)*t)+1)},easeInElastic:function(t){var e=1.70158,n=0,i=1;return 0===t?0:1===t?1:(n||(n=.3),i<1?(i=1,e=n/4):e=n/(2*Math.PI)*Math.asin(1/i),-i*Math.pow(2,10*(t-=1))*Math.sin((t-e)*(2*Math.PI)/n))},easeOutElastic:function(t){var e=1.70158,n=0,i=1;return 0===t?0:1===t?1:(n||(n=.3),i<1?(i=1,e=n/4):e=n/(2*Math.PI)*Math.asin(1/i),i*Math.pow(2,-10*t)*Math.sin((t-e)*(2*Math.PI)/n)+1)},easeInOutElastic:function(t){var e=1.70158,n=0,i=1;return 0===t?0:2==(t/=.5)?1:(n||(n=.45),i<1?(i=1,e=n/4):e=n/(2*Math.PI)*Math.asin(1/i),t<1?i*Math.pow(2,10*(t-=1))*Math.sin((t-e)*(2*Math.PI)/n)*-.5:i*Math.pow(2,-10*(t-=1))*Math.sin((t-e)*(2*Math.PI)/n)*.5+1)},easeInBack:function(t){var e=1.70158;return t*t*((e+1)*t-e)},easeOutBack:function(t){var e=1.70158;return(t-=1)*t*((e+1)*t+e)+1},easeInOutBack:function(t){var e=1.70158;return(t/=.5)<1?t*t*((1+(e*=1.525))*t-e)*.5:.5*((t-=2)*t*((1+(e*=1.525))*t+e)+2)},easeInBounce:function(t){return 1-C.easeOutBounce(1-t)},easeOutBounce:function(t){return t<1/2.75?7.5625*t*t:t<2/2.75?7.5625*(t-=1.5/2.75)*t+.75:t<2.5/2.75?7.5625*(t-=2.25/2.75)*t+.9375:7.5625*(t-=2.625/2.75)*t+.984375},easeInOutBounce:function(t){return t<.5?.5*C.easeInBounce(2*t):.5*C.easeOutBounce(2*t-1)+.5}},P={effects:C};S.easingEffects=C;var A=Math.PI,D=A/180,T=2*A,I=A/2,F=A/4,O=2*A/3,L={clear:function(t){t.ctx.clearRect(0,0,t.width,t.height)},roundedRect:function(t,e,n,i,a,r){if(r){var o=Math.min(r,a/2,i/2),s=e+o,l=n+o,u=e+i-o,d=n+a-o;t.moveTo(e,l),se.left-1e-6&&t.xe.top-1e-6&&t.y0&&this.requestAnimationFrame()},advance:function(){for(var t,e,n,i,a=this.animations,r=0;r=n?(H.callback(t.onAnimationComplete,[t],e),e.animating=!1,a.splice(r,1)):++r}},Q=H.options.resolve,tt=["push","pop","shift","splice","unshift"];function et(t,e){var n=t._chartjs;if(n){var i=n.listeners,a=i.indexOf(e);-1!==a&&i.splice(a,1),i.length>0||(tt.forEach((function(e){delete t[e]})),delete t._chartjs)}}var nt=function(t,e){this.initialize(t,e)};H.extend(nt.prototype,{datasetElementType:null,dataElementType:null,_datasetElementOptions:["backgroundColor","borderCapStyle","borderColor","borderDash","borderDashOffset","borderJoinStyle","borderWidth"],_dataElementOptions:["backgroundColor","borderColor","borderWidth","pointStyle"],initialize:function(t,e){var n=this;n.chart=t,n.index=e,n.linkScales(),n.addElements(),n._type=n.getMeta().type},updateIndex:function(t){this.index=t},linkScales:function(){var t=this.getMeta(),e=this.chart,n=e.scales,i=this.getDataset(),a=e.options.scales;null!==t.xAxisID&&t.xAxisID in n&&!i.xAxisID||(t.xAxisID=i.xAxisID||a.xAxes[0].id),null!==t.yAxisID&&t.yAxisID in n&&!i.yAxisID||(t.yAxisID=i.yAxisID||a.yAxes[0].id)},getDataset:function(){return this.chart.data.datasets[this.index]},getMeta:function(){return this.chart.getDatasetMeta(this.index)},getScaleForId:function(t){return this.chart.scales[t]},_getValueScaleId:function(){return this.getMeta().yAxisID},_getIndexScaleId:function(){return this.getMeta().xAxisID},_getValueScale:function(){return this.getScaleForId(this._getValueScaleId())},_getIndexScale:function(){return this.getScaleForId(this._getIndexScaleId())},reset:function(){this._update(!0)},destroy:function(){this._data&&et(this._data,this)},createMetaDataset:function(){var t=this.datasetElementType;return t&&new t({_chart:this.chart,_datasetIndex:this.index})},createMetaData:function(t){var e=this.dataElementType;return e&&new e({_chart:this.chart,_datasetIndex:this.index,_index:t})},addElements:function(){var t,e,n=this.getMeta(),i=this.getDataset().data||[],a=n.data;for(t=0,e=i.length;tn&&this.insertElements(n,i-n)},insertElements:function(t,e){for(var n=0;na?(r=a/e.innerRadius,t.arc(o,s,e.innerRadius-a,i+r,n-r,!0)):t.arc(o,s,a,i+Math.PI/2,n-Math.PI/2),t.closePath(),t.clip()}function ot(t,e,n){var i="inner"===e.borderAlign;i?(t.lineWidth=2*e.borderWidth,t.lineJoin="round"):(t.lineWidth=e.borderWidth,t.lineJoin="bevel"),n.fullCircles&&function(t,e,n,i){var a,r=n.endAngle;for(i&&(n.endAngle=n.startAngle+at,rt(t,n),n.endAngle=r,n.endAngle===n.startAngle&&n.fullCircles&&(n.endAngle+=at,n.fullCircles--)),t.beginPath(),t.arc(n.x,n.y,n.innerRadius,n.startAngle+at,n.startAngle,!0),a=0;as;)a-=at;for(;a=o&&a<=s,u=r>=n.innerRadius&&r<=n.outerRadius;return l&&u}return!1},getCenterPoint:function(){var t=this._view,e=(t.startAngle+t.endAngle)/2,n=(t.innerRadius+t.outerRadius)/2;return{x:t.x+Math.cos(e)*n,y:t.y+Math.sin(e)*n}},getArea:function(){var t=this._view;return Math.PI*((t.endAngle-t.startAngle)/(2*Math.PI))*(Math.pow(t.outerRadius,2)-Math.pow(t.innerRadius,2))},tooltipPosition:function(){var t=this._view,e=t.startAngle+(t.endAngle-t.startAngle)/2,n=(t.outerRadius-t.innerRadius)/2+t.innerRadius;return{x:t.x+Math.cos(e)*n,y:t.y+Math.sin(e)*n}},draw:function(){var t,e=this._chart.ctx,n=this._view,i="inner"===n.borderAlign?.33:0,a={x:n.x,y:n.y,innerRadius:n.innerRadius,outerRadius:Math.max(n.outerRadius-i,0),pixelMargin:i,startAngle:n.startAngle,endAngle:n.endAngle,fullCircles:Math.floor(n.circumference/at)};if(e.save(),e.fillStyle=n.backgroundColor,e.strokeStyle=n.borderColor,a.fullCircles){for(a.endAngle=a.startAngle+at,e.beginPath(),e.arc(a.x,a.y,a.outerRadius,a.startAngle,a.endAngle),e.arc(a.x,a.y,a.innerRadius,a.endAngle,a.startAngle,!0),e.closePath(),t=0;tt.x&&(e=bt(e,"left","right")):t.basen?n:i,r:l.right||a<0?0:a>e?e:a,b:l.bottom||r<0?0:r>n?n:r,l:l.left||o<0?0:o>e?e:o}}function yt(t,e,n){var i=null===e,a=null===n,r=!(!t||i&&a)&&vt(t);return r&&(i||e>=r.left&&e<=r.right)&&(a||n>=r.top&&n<=r.bottom)}N._set("global",{elements:{rectangle:{backgroundColor:pt,borderColor:pt,borderSkipped:"bottom",borderWidth:0}}});var _t=K.extend({_type:"rectangle",draw:function(){var t=this._chart.ctx,e=this._view,n=function(t){var e=vt(t),n=e.right-e.left,i=e.bottom-e.top,a=xt(t,n/2,i/2);return{outer:{x:e.left,y:e.top,w:n,h:i},inner:{x:e.left+a.l,y:e.top+a.t,w:n-a.l-a.r,h:i-a.t-a.b}}}(e),i=n.outer,a=n.inner;t.fillStyle=e.backgroundColor,t.fillRect(i.x,i.y,i.w,i.h),i.w===a.w&&i.h===a.h||(t.save(),t.beginPath(),t.rect(i.x,i.y,i.w,i.h),t.clip(),t.fillStyle=e.borderColor,t.rect(a.x,a.y,a.w,a.h),t.fill("evenodd"),t.restore())},height:function(){var t=this._view;return t.base-t.y},inRange:function(t,e){return yt(this._view,t,e)},inLabelRange:function(t,e){var n=this._view;return mt(n)?yt(n,t,null):yt(n,null,e)},inXRange:function(t){return yt(this._view,t,null)},inYRange:function(t){return yt(this._view,null,t)},getCenterPoint:function(){var t,e,n=this._view;return mt(n)?(t=n.x,e=(n.y+n.base)/2):(t=(n.x+n.base)/2,e=n.y),{x:t,y:e}},getArea:function(){var t=this._view;return mt(t)?t.width*Math.abs(t.y-t.base):t.height*Math.abs(t.x-t.base)},tooltipPosition:function(){var t=this._view;return{x:t.x,y:t.y}}}),kt={},wt=st,Mt=dt,St=gt,Ct=_t;kt.Arc=wt,kt.Line=Mt,kt.Point=St,kt.Rectangle=Ct;var Pt=H._deprecated,At=H.valueOrDefault;function Dt(t,e,n){var i,a,r=n.barThickness,o=e.stackCount,s=e.pixels[t],l=H.isNullOrUndef(r)?function(t,e){var n,i,a,r,o=t._length;for(a=1,r=e.length;a0?Math.min(o,Math.abs(i-n)):o,n=i;return o}(e.scale,e.pixels):-1;return H.isNullOrUndef(r)?(i=l*n.categoryPercentage,a=n.barPercentage):(i=r*o,a=1),{chunk:i/o,ratio:a,start:s-i/2}}N._set("bar",{hover:{mode:"label"},scales:{xAxes:[{type:"category",offset:!0,gridLines:{offsetGridLines:!0}}],yAxes:[{type:"linear"}]}}),N._set("global",{datasets:{bar:{categoryPercentage:.8,barPercentage:.9}}});var Tt=it.extend({dataElementType:kt.Rectangle,_dataElementOptions:["backgroundColor","borderColor","borderSkipped","borderWidth","barPercentage","barThickness","categoryPercentage","maxBarThickness","minBarLength"],initialize:function(){var t,e,n=this;it.prototype.initialize.apply(n,arguments),(t=n.getMeta()).stack=n.getDataset().stack,t.bar=!0,e=n._getIndexScale().options,Pt("bar chart",e.barPercentage,"scales.[x/y]Axes.barPercentage","dataset.barPercentage"),Pt("bar chart",e.barThickness,"scales.[x/y]Axes.barThickness","dataset.barThickness"),Pt("bar chart",e.categoryPercentage,"scales.[x/y]Axes.categoryPercentage","dataset.categoryPercentage"),Pt("bar chart",n._getValueScale().options.minBarLength,"scales.[x/y]Axes.minBarLength","dataset.minBarLength"),Pt("bar chart",e.maxBarThickness,"scales.[x/y]Axes.maxBarThickness","dataset.maxBarThickness")},update:function(t){var e,n,i=this.getMeta().data;for(this._ruler=this.getRuler(),e=0,n=i.length;e=0&&p.min>=0?p.min:p.max,y=void 0===p.start?p.end:p.max>=0&&p.min>=0?p.max-p.min:p.min-p.max,_=g.length;if(v||void 0===v&&void 0!==b)for(i=0;i<_&&(a=g[i]).index!==t;++i)a.stack===b&&(r=void 0===(u=h._parseValue(f[a.index].data[e])).start?u.end:u.min>=0&&u.max>=0?u.max:u.min,(p.min<0&&r<0||p.max>=0&&r>0)&&(x+=r));return o=h.getPixelForValue(x),l=(s=h.getPixelForValue(x+y))-o,void 0!==m&&Math.abs(l)=0&&!c||y<0&&c?o-m:o+m),{size:l,base:o,head:s,center:s+l/2}},calculateBarIndexPixels:function(t,e,n,i){var a="flex"===i.barThickness?function(t,e,n){var i,a=e.pixels,r=a[t],o=t>0?a[t-1]:null,s=t=Rt?-zt:b<-Rt?zt:0)+m,y=Math.cos(b),_=Math.sin(b),k=Math.cos(x),w=Math.sin(x),M=b<=0&&x>=0||x>=zt,S=b<=Nt&&x>=Nt||x>=zt+Nt,C=b<=-Nt&&x>=-Nt||x>=Rt+Nt,P=b===-Rt||x>=Rt?-1:Math.min(y,y*p,k,k*p),A=C?-1:Math.min(_,_*p,w,w*p),D=M?1:Math.max(y,y*p,k,k*p),T=S?1:Math.max(_,_*p,w,w*p);u=(D-P)/2,d=(T-A)/2,h=-(D+P)/2,c=-(T+A)/2}for(i=0,a=g.length;i0&&!isNaN(t)?zt*(Math.abs(t)/e):0},getMaxBorderWidth:function(t){var e,n,i,a,r,o,s,l,u=0,d=this.chart;if(!t)for(e=0,n=d.data.datasets.length;e(u=s>u?s:u)?l:u);return u},setHoverStyle:function(t){var e=t._model,n=t._options,i=H.getHoverColor;t.$previousStyle={backgroundColor:e.backgroundColor,borderColor:e.borderColor,borderWidth:e.borderWidth},e.backgroundColor=Lt(n.hoverBackgroundColor,i(n.backgroundColor)),e.borderColor=Lt(n.hoverBorderColor,i(n.borderColor)),e.borderWidth=Lt(n.hoverBorderWidth,n.borderWidth)},_getRingWeightOffset:function(t){for(var e=0,n=0;n0&&Ht(l[t-1]._model,s)&&(n.controlPointPreviousX=u(n.controlPointPreviousX,s.left,s.right),n.controlPointPreviousY=u(n.controlPointPreviousY,s.top,s.bottom)),t0&&(r=t.getDatasetMeta(r[0]._datasetIndex).data),r},"x-axis":function(t,e){return ae(t,e,{intersect:!1})},point:function(t,e){return ee(t,Qt(e,t))},nearest:function(t,e,n){var i=Qt(e,t);n.axis=n.axis||"xy";var a=ie(n.axis);return ne(t,i,n.intersect,a)},x:function(t,e,n){var i=Qt(e,t),a=[],r=!1;return te(t,(function(t){t.inXRange(i.x)&&a.push(t),t.inRange(i.x,i.y)&&(r=!0)})),n.intersect&&!r&&(a=[]),a},y:function(t,e,n){var i=Qt(e,t),a=[],r=!1;return te(t,(function(t){t.inYRange(i.y)&&a.push(t),t.inRange(i.x,i.y)&&(r=!0)})),n.intersect&&!r&&(a=[]),a}}},oe=H.extend;function se(t,e){return H.where(t,(function(t){return t.pos===e}))}function le(t,e){return t.sort((function(t,n){var i=e?n:t,a=e?t:n;return i.weight===a.weight?i.index-a.index:i.weight-a.weight}))}function ue(t,e,n,i){return Math.max(t[n],e[n])+Math.max(t[i],e[i])}function de(t,e,n){var i,a,r=n.box,o=t.maxPadding;if(n.size&&(t[n.pos]-=n.size),n.size=n.horizontal?r.height:r.width,t[n.pos]+=n.size,r.getPadding){var s=r.getPadding();o.top=Math.max(o.top,s.top),o.left=Math.max(o.left,s.left),o.bottom=Math.max(o.bottom,s.bottom),o.right=Math.max(o.right,s.right)}if(i=e.outerWidth-ue(o,t,"left","right"),a=e.outerHeight-ue(o,t,"top","bottom"),i!==t.w||a!==t.h){t.w=i,t.h=a;var l=n.horizontal?[i,t.w]:[a,t.h];return!(l[0]===l[1]||isNaN(l[0])&&isNaN(l[1]))}}function he(t,e){var n=e.maxPadding;function i(t){var i={left:0,top:0,right:0,bottom:0};return t.forEach((function(t){i[t]=Math.max(e[t],n[t])})),i}return i(t?["left","right"]:["top","bottom"])}function ce(t,e,n){var i,a,r,o,s,l,u=[];for(i=0,a=t.length;idiv{position:absolute;width:1000000px;height:1000000px;left:0;top:0}.chartjs-size-monitor-shrink>div{position:absolute;width:200%;height:200%;left:0;top:0}"}))&&ge.default||ge,ve="$chartjs",be="chartjs-size-monitor",xe="chartjs-render-monitor",ye="chartjs-render-animation",_e=["animationstart","webkitAnimationStart"],ke={touchstart:"mousedown",touchmove:"mousemove",touchend:"mouseup",pointerenter:"mouseenter",pointerdown:"mousedown",pointermove:"mousemove",pointerup:"mouseup",pointerleave:"mouseout",pointerout:"mouseout"};function we(t,e){var n=H.getStyle(t,e),i=n&&n.match(/^(\d+)(\.\d+)?px$/);return i?Number(i[1]):void 0}var Me=!!function(){var t=!1;try{var e=Object.defineProperty({},"passive",{get:function(){t=!0}});window.addEventListener("e",null,e)}catch(t){}return t}()&&{passive:!0};function Se(t,e,n){t.addEventListener(e,n,Me)}function Ce(t,e,n){t.removeEventListener(e,n,Me)}function Pe(t,e,n,i,a){return{type:t,chart:e,native:a||null,x:void 0!==n?n:null,y:void 0!==i?i:null}}function Ae(t){var e=document.createElement("div");return e.className=t||"",e}function De(t,e,n){var i,a,r,o,s=t[ve]||(t[ve]={}),l=s.resizer=function(t){var e=Ae(be),n=Ae(be+"-expand"),i=Ae(be+"-shrink");n.appendChild(Ae()),i.appendChild(Ae()),e.appendChild(n),e.appendChild(i),e._reset=function(){n.scrollLeft=1e6,n.scrollTop=1e6,i.scrollLeft=1e6,i.scrollTop=1e6};var a=function(){e._reset(),t()};return Se(n,"scroll",a.bind(n,"expand")),Se(i,"scroll",a.bind(i,"shrink")),e}((i=function(){if(s.resizer){var i=n.options.maintainAspectRatio&&t.parentNode,a=i?i.clientWidth:0;e(Pe("resize",n)),i&&i.clientWidth0){var r=t[0];r.label?n=r.label:r.xLabel?n=r.xLabel:a>0&&r.index-1?t.split("\n"):t}function Ve(t){var e=N.global;return{xPadding:t.xPadding,yPadding:t.yPadding,xAlign:t.xAlign,yAlign:t.yAlign,rtl:t.rtl,textDirection:t.textDirection,bodyFontColor:t.bodyFontColor,_bodyFontFamily:ze(t.bodyFontFamily,e.defaultFontFamily),_bodyFontStyle:ze(t.bodyFontStyle,e.defaultFontStyle),_bodyAlign:t.bodyAlign,bodyFontSize:ze(t.bodyFontSize,e.defaultFontSize),bodySpacing:t.bodySpacing,titleFontColor:t.titleFontColor,_titleFontFamily:ze(t.titleFontFamily,e.defaultFontFamily),_titleFontStyle:ze(t.titleFontStyle,e.defaultFontStyle),titleFontSize:ze(t.titleFontSize,e.defaultFontSize),_titleAlign:t.titleAlign,titleSpacing:t.titleSpacing,titleMarginBottom:t.titleMarginBottom,footerFontColor:t.footerFontColor,_footerFontFamily:ze(t.footerFontFamily,e.defaultFontFamily),_footerFontStyle:ze(t.footerFontStyle,e.defaultFontStyle),footerFontSize:ze(t.footerFontSize,e.defaultFontSize),_footerAlign:t.footerAlign,footerSpacing:t.footerSpacing,footerMarginTop:t.footerMarginTop,caretSize:t.caretSize,cornerRadius:t.cornerRadius,backgroundColor:t.backgroundColor,opacity:0,legendColorBackground:t.multiKeyBackground,displayColors:t.displayColors,borderColor:t.borderColor,borderWidth:t.borderWidth}}function He(t,e){return"center"===e?t.x+t.width/2:"right"===e?t.x+t.width-t.xPadding:t.x+t.xPadding}function je(t){return Ee([],We(t))}var qe=K.extend({initialize:function(){this._model=Ve(this._options),this._lastActive=[]},getTitle:function(){var t=this,e=t._options,n=e.callbacks,i=n.beforeTitle.apply(t,arguments),a=n.title.apply(t,arguments),r=n.afterTitle.apply(t,arguments),o=[];return o=Ee(o,We(i)),o=Ee(o,We(a)),o=Ee(o,We(r))},getBeforeBody:function(){return je(this._options.callbacks.beforeBody.apply(this,arguments))},getBody:function(t,e){var n=this,i=n._options.callbacks,a=[];return H.each(t,(function(t){var r={before:[],lines:[],after:[]};Ee(r.before,We(i.beforeLabel.call(n,t,e))),Ee(r.lines,i.label.call(n,t,e)),Ee(r.after,We(i.afterLabel.call(n,t,e))),a.push(r)})),a},getAfterBody:function(){return je(this._options.callbacks.afterBody.apply(this,arguments))},getFooter:function(){var t=this,e=t._options.callbacks,n=e.beforeFooter.apply(t,arguments),i=e.footer.apply(t,arguments),a=e.afterFooter.apply(t,arguments),r=[];return r=Ee(r,We(n)),r=Ee(r,We(i)),r=Ee(r,We(a))},update:function(t){var e,n,i,a,r,o,s,l,u,d,h=this,c=h._options,f=h._model,g=h._model=Ve(c),p=h._active,m=h._data,v={xAlign:f.xAlign,yAlign:f.yAlign},b={x:f.x,y:f.y},x={width:f.width,height:f.height},y={x:f.caretX,y:f.caretY};if(p.length){g.opacity=1;var _=[],k=[];y=Be[c.position].call(h,p,h._eventPosition);var w=[];for(e=0,n=p.length;ei.width&&(a=i.width-e.width),a<0&&(a=0)),"top"===d?r+=h:r-="bottom"===d?e.height+h:e.height/2,"center"===d?"left"===u?a+=h:"right"===u&&(a-=h):"left"===u?a-=c:"right"===u&&(a+=c),{x:a,y:r}}(g,x,v=function(t,e){var n,i,a,r,o,s=t._model,l=t._chart,u=t._chart.chartArea,d="center",h="center";s.yl.height-e.height&&(h="bottom");var c=(u.left+u.right)/2,f=(u.top+u.bottom)/2;"center"===h?(n=function(t){return t<=c},i=function(t){return t>c}):(n=function(t){return t<=e.width/2},i=function(t){return t>=l.width-e.width/2}),a=function(t){return t+e.width+s.caretSize+s.caretPadding>l.width},r=function(t){return t-e.width-s.caretSize-s.caretPadding<0},o=function(t){return t<=f?"top":"bottom"},n(s.x)?(d="left",a(s.x)&&(d="center",h=o(s.y))):i(s.x)&&(d="right",r(s.x)&&(d="center",h=o(s.y)));var g=t._options;return{xAlign:g.xAlign?g.xAlign:d,yAlign:g.yAlign?g.yAlign:h}}(this,x),h._chart)}else g.opacity=0;return g.xAlign=v.xAlign,g.yAlign=v.yAlign,g.x=b.x,g.y=b.y,g.width=x.width,g.height=x.height,g.caretX=y.x,g.caretY=y.y,h._model=g,t&&c.custom&&c.custom.call(h,g),h},drawCaret:function(t,e){var n=this._chart.ctx,i=this._view,a=this.getCaretPosition(t,e,i);n.lineTo(a.x1,a.y1),n.lineTo(a.x2,a.y2),n.lineTo(a.x3,a.y3)},getCaretPosition:function(t,e,n){var i,a,r,o,s,l,u=n.caretSize,d=n.cornerRadius,h=n.xAlign,c=n.yAlign,f=t.x,g=t.y,p=e.width,m=e.height;if("center"===c)s=g+m/2,"left"===h?(a=(i=f)-u,r=i,o=s+u,l=s-u):(a=(i=f+p)+u,r=i,o=s-u,l=s+u);else if("left"===h?(i=(a=f+d+u)-u,r=a+u):"right"===h?(i=(a=f+p-d-u)-u,r=a+u):(i=(a=n.caretX)-u,r=a+u),"top"===c)s=(o=g)-u,l=o;else{s=(o=g+m)+u,l=o;var v=r;r=i,i=v}return{x1:i,x2:a,x3:r,y1:o,y2:s,y3:l}},drawTitle:function(t,e,n){var i,a,r,o=e.title,s=o.length;if(s){var l=Ne(e.rtl,e.x,e.width);for(t.x=He(e,e._titleAlign),n.textAlign=l.textAlign(e._titleAlign),n.textBaseline="middle",i=e.titleFontSize,a=e.titleSpacing,n.fillStyle=e.titleFontColor,n.font=H.fontString(i,e._titleFontStyle,e._titleFontFamily),r=0;r0&&n.stroke()},draw:function(){var t=this._chart.ctx,e=this._view;if(0!==e.opacity){var n={width:e.width,height:e.height},i={x:e.x,y:e.y},a=Math.abs(e.opacity<.001)?0:e.opacity,r=e.title.length||e.beforeBody.length||e.body.length||e.afterBody.length||e.footer.length;this._options.enabled&&r&&(t.save(),t.globalAlpha=a,this.drawBackground(i,e,t,n),i.y+=e.yPadding,H.rtl.overrideTextDirection(t,e.textDirection),this.drawTitle(i,e,t),this.drawBody(i,e,t),this.drawFooter(i,e,t),H.rtl.restoreTextDirection(t,e.textDirection),t.restore())}},handleEvent:function(t){var e,n=this,i=n._options;return n._lastActive=n._lastActive||[],"mouseout"===t.type?n._active=[]:(n._active=n._chart.getElementsAtEventForMode(t,i.mode,i),i.reverse&&n._active.reverse()),(e=!H.arrayEquals(n._active,n._lastActive))&&(n._lastActive=n._active,(i.enabled||i.custom)&&(n._eventPosition={x:t.x,y:t.y},n.update(!0),n.pivot())),e}}),Ue=Be,Ye=qe;Ye.positioners=Ue;var Ge=H.valueOrDefault;function Xe(){return H.merge(Object.create(null),[].slice.call(arguments),{merger:function(t,e,n,i){if("xAxes"===t||"yAxes"===t){var a,r,o,s=n[t].length;for(e[t]||(e[t]=[]),a=0;a=e[t].length&&e[t].push({}),!e[t][a].type||o.type&&o.type!==e[t][a].type?H.merge(e[t][a],[Re.getScaleDefaults(r),o]):H.merge(e[t][a],o)}else H._merger(t,e,n,i)}})}function Ke(){return H.merge(Object.create(null),[].slice.call(arguments),{merger:function(t,e,n,i){var a=e[t]||Object.create(null),r=n[t];"scales"===t?e[t]=Xe(a,r):"scale"===t?e[t]=H.merge(a,[Re.getScaleDefaults(r.type),r]):H._merger(t,e,n,i)}})}function Ze(t){var e=t.options;H.each(t.scales,(function(e){pe.removeBox(t,e)})),e=Ke(N.global,N[t.config.type],e),t.options=t.config.options=e,t.ensureScalesHaveIDs(),t.buildOrUpdateScales(),t.tooltip._options=e.tooltips,t.tooltip.initialize()}function $e(t,e,n){var i,a=function(t){return t.id===i};do{i=e+n++}while(H.findIndex(t,a)>=0);return i}function Je(t){return"top"===t||"bottom"===t}function Qe(t,e){return function(n,i){return n[t]===i[t]?n[e]-i[e]:n[t]-i[t]}}N._set("global",{elements:{},events:["mousemove","mouseout","click","touchstart","touchmove"],hover:{onHover:null,mode:"nearest",intersect:!0,animationDuration:400},onClick:null,maintainAspectRatio:!0,responsive:!0,responsiveAnimationDuration:0});var tn=function(t,e){return this.construct(t,e),this};H.extend(tn.prototype,{construct:function(t,e){var n=this;e=function(t){var e=(t=t||Object.create(null)).data=t.data||{};return e.datasets=e.datasets||[],e.labels=e.labels||[],t.options=Ke(N.global,N[t.type],t.options||{}),t}(e);var i=Oe.acquireContext(t,e),a=i&&i.canvas,r=a&&a.height,o=a&&a.width;n.id=H.uid(),n.ctx=i,n.canvas=a,n.config=e,n.width=o,n.height=r,n.aspectRatio=r?o/r:null,n.options=e.options,n._bufferedRender=!1,n._layers=[],n.chart=n,n.controller=n,tn.instances[n.id]=n,Object.defineProperty(n,"data",{get:function(){return n.config.data},set:function(t){n.config.data=t}}),i&&a?(n.initialize(),n.update()):console.error("Failed to create chart: can't acquire context from the given item")},initialize:function(){var t=this;return Le.notify(t,"beforeInit"),H.retinaScale(t,t.options.devicePixelRatio),t.bindEvents(),t.options.responsive&&t.resize(!0),t.initToolTip(),Le.notify(t,"afterInit"),t},clear:function(){return H.canvas.clear(this),this},stop:function(){return J.cancelAnimation(this),this},resize:function(t){var e=this,n=e.options,i=e.canvas,a=n.maintainAspectRatio&&e.aspectRatio||null,r=Math.max(0,Math.floor(H.getMaximumWidth(i))),o=Math.max(0,Math.floor(a?r/a:H.getMaximumHeight(i)));if((e.width!==r||e.height!==o)&&(i.width=e.width=r,i.height=e.height=o,i.style.width=r+"px",i.style.height=o+"px",H.retinaScale(e,n.devicePixelRatio),!t)){var s={width:r,height:o};Le.notify(e,"resize",[s]),n.onResize&&n.onResize(e,s),e.stop(),e.update({duration:n.responsiveAnimationDuration})}},ensureScalesHaveIDs:function(){var t=this.options,e=t.scales||{},n=t.scale;H.each(e.xAxes,(function(t,n){t.id||(t.id=$e(e.xAxes,"x-axis-",n))})),H.each(e.yAxes,(function(t,n){t.id||(t.id=$e(e.yAxes,"y-axis-",n))})),n&&(n.id=n.id||"scale")},buildOrUpdateScales:function(){var t=this,e=t.options,n=t.scales||{},i=[],a=Object.keys(n).reduce((function(t,e){return t[e]=!1,t}),{});e.scales&&(i=i.concat((e.scales.xAxes||[]).map((function(t){return{options:t,dtype:"category",dposition:"bottom"}})),(e.scales.yAxes||[]).map((function(t){return{options:t,dtype:"linear",dposition:"left"}})))),e.scale&&i.push({options:e.scale,dtype:"radialLinear",isDefault:!0,dposition:"chartArea"}),H.each(i,(function(e){var i=e.options,r=i.id,o=Ge(i.type,e.dtype);Je(i.position)!==Je(e.dposition)&&(i.position=e.dposition),a[r]=!0;var s=null;if(r in n&&n[r].type===o)(s=n[r]).options=i,s.ctx=t.ctx,s.chart=t;else{var l=Re.getScaleConstructor(o);if(!l)return;s=new l({id:r,type:o,options:i,ctx:t.ctx,chart:t}),n[s.id]=s}s.mergeTicksOptions(),e.isDefault&&(t.scale=s)})),H.each(a,(function(t,e){t||delete n[e]})),t.scales=n,Re.addScalesToLayout(this)},buildOrUpdateControllers:function(){var t,e,n=this,i=[],a=n.data.datasets;for(t=0,e=a.length;t=0;--n)this.drawDataset(e[n],t);Le.notify(this,"afterDatasetsDraw",[t])}},drawDataset:function(t,e){var n={meta:t,index:t.index,easingValue:e};!1!==Le.notify(this,"beforeDatasetDraw",[n])&&(t.controller.draw(e),Le.notify(this,"afterDatasetDraw",[n]))},_drawTooltip:function(t){var e=this.tooltip,n={tooltip:e,easingValue:t};!1!==Le.notify(this,"beforeTooltipDraw",[n])&&(e.draw(),Le.notify(this,"afterTooltipDraw",[n]))},getElementAtEvent:function(t){return re.modes.single(this,t)},getElementsAtEvent:function(t){return re.modes.label(this,t,{intersect:!0})},getElementsAtXAxis:function(t){return re.modes["x-axis"](this,t,{intersect:!0})},getElementsAtEventForMode:function(t,e,n){var i=re.modes[e];return"function"==typeof i?i(this,t,n):[]},getDatasetAtEvent:function(t){return re.modes.dataset(this,t,{intersect:!0})},getDatasetMeta:function(t){var e=this.data.datasets[t];e._meta||(e._meta={});var n=e._meta[this.id];return n||(n=e._meta[this.id]={type:null,data:[],dataset:null,controller:null,hidden:null,xAxisID:null,yAxisID:null,order:e.order||0,index:t}),n},getVisibleDatasetCount:function(){for(var t=0,e=0,n=this.data.datasets.length;e3?n[2]-n[1]:n[1]-n[0];Math.abs(i)>1&&t!==Math.floor(t)&&(i=t-Math.floor(t));var a=H.log10(Math.abs(i)),r="";if(0!==t)if(Math.max(Math.abs(n[0]),Math.abs(n[n.length-1]))<1e-4){var o=H.log10(Math.abs(t)),s=Math.floor(o)-Math.floor(a);s=Math.max(Math.min(s,20),0),r=t.toExponential(s)}else{var l=-1*Math.floor(a);l=Math.max(Math.min(l,20),0),r=t.toFixed(l)}else r="0";return r},logarithmic:function(t,e,n){var i=t/Math.pow(10,Math.floor(H.log10(t)));return 0===t?"0":1===i||2===i||5===i||0===e||e===n.length-1?t.toExponential():""}}},sn=H.isArray,ln=H.isNullOrUndef,un=H.valueOrDefault,dn=H.valueAtIndexOrDefault;function hn(t,e,n){var i,a=t.getTicks().length,r=Math.min(e,a-1),o=t.getPixelForTick(r),s=t._startPixel,l=t._endPixel;if(!(n&&(i=1===a?Math.max(o-s,l-o):0===e?(t.getPixelForTick(1)-o)/2:(o-t.getPixelForTick(r-1))/2,(o+=rl+1e-6)))return o}function cn(t,e,n,i){var a,r,o,s,l,u,d,h,c,f,g,p,m,v=n.length,b=[],x=[],y=[],_=0,k=0;for(a=0;ae){for(n=0;n=c||d<=1||!s.isHorizontal()?s.labelRotation=h:(e=(t=s._getLabelSizes()).widest.width,n=t.highest.height-t.highest.offset,i=Math.min(s.maxWidth,s.chart.width-e),e+6>(a=l.offset?s.maxWidth/d:i/(d-1))&&(a=i/(d-(l.offset?.5:1)),r=s.maxHeight-fn(l.gridLines)-u.padding-gn(l.scaleLabel),o=Math.sqrt(e*e+n*n),f=H.toDegrees(Math.min(Math.asin(Math.min((t.highest.height+6)/a,1)),Math.asin(Math.min(r/o,1))-Math.asin(n/o))),f=Math.max(h,Math.min(c,f))),s.labelRotation=f)},afterCalculateTickRotation:function(){H.callback(this.options.afterCalculateTickRotation,[this])},beforeFit:function(){H.callback(this.options.beforeFit,[this])},fit:function(){var t=this,e=t.minSize={width:0,height:0},n=t.chart,i=t.options,a=i.ticks,r=i.scaleLabel,o=i.gridLines,s=t._isVisible(),l="bottom"===i.position,u=t.isHorizontal();if(u?e.width=t.maxWidth:s&&(e.width=fn(o)+gn(r)),u?s&&(e.height=fn(o)+gn(r)):e.height=t.maxHeight,a.display&&s){var d=mn(a),h=t._getLabelSizes(),c=h.first,f=h.last,g=h.widest,p=h.highest,m=.4*d.minor.lineHeight,v=a.padding;if(u){var b=0!==t.labelRotation,x=H.toRadians(t.labelRotation),y=Math.cos(x),_=Math.sin(x),k=_*g.width+y*(p.height-(b?p.offset:0))+(b?0:m);e.height=Math.min(t.maxHeight,e.height+k+v);var w,M,S=t.getPixelForTick(0)-t.left,C=t.right-t.getPixelForTick(t.getTicks().length-1);b?(w=l?y*c.width+_*c.offset:_*(c.height-c.offset),M=l?_*(f.height-f.offset):y*f.width+_*f.offset):(w=c.width/2,M=f.width/2),t.paddingLeft=Math.max((w-S)*t.width/(t.width-S),0)+3,t.paddingRight=Math.max((M-C)*t.width/(t.width-C),0)+3}else{var P=a.mirror?0:g.width+v+m;e.width=Math.min(t.maxWidth,e.width+P),t.paddingTop=c.height/2,t.paddingBottom=f.height/2}}t.handleMargins(),u?(t.width=t._length=n.width-t.margins.left-t.margins.right,t.height=e.height):(t.width=e.width,t.height=t._length=n.height-t.margins.top-t.margins.bottom)},handleMargins:function(){var t=this;t.margins&&(t.margins.left=Math.max(t.paddingLeft,t.margins.left),t.margins.top=Math.max(t.paddingTop,t.margins.top),t.margins.right=Math.max(t.paddingRight,t.margins.right),t.margins.bottom=Math.max(t.paddingBottom,t.margins.bottom))},afterFit:function(){H.callback(this.options.afterFit,[this])},isHorizontal:function(){var t=this.options.position;return"top"===t||"bottom"===t},isFullWidth:function(){return this.options.fullWidth},getRightValue:function(t){if(ln(t))return NaN;if(("number"==typeof t||t instanceof Number)&&!isFinite(t))return NaN;if(t)if(this.isHorizontal()){if(void 0!==t.x)return this.getRightValue(t.x)}else if(void 0!==t.y)return this.getRightValue(t.y);return t},_convertTicksToLabels:function(t){var e,n,i,a=this;for(a.ticks=t.map((function(t){return t.value})),a.beforeTickToLabelConversion(),e=a.convertTicksToLabels(t)||a.ticks,a.afterTickToLabelConversion(),n=0,i=t.length;nn-1?null:this.getPixelForDecimal(t*i+(e?i/2:0))},getPixelForDecimal:function(t){return this._reversePixels&&(t=1-t),this._startPixel+t*this._length},getDecimalForPixel:function(t){var e=(t-this._startPixel)/this._length;return this._reversePixels?1-e:e},getBasePixel:function(){return this.getPixelForValue(this.getBaseValue())},getBaseValue:function(){var t=this.min,e=this.max;return this.beginAtZero?0:t<0&&e<0?e:t>0&&e>0?t:0},_autoSkip:function(t){var e,n,i,a,r=this.options.ticks,o=this._length,s=r.maxTicksLimit||o/this._tickSize()+1,l=r.major.enabled?function(t){var e,n,i=[];for(e=0,n=t.length;es)return function(t,e,n){var i,a,r=0,o=e[0];for(n=Math.ceil(n),i=0;iu)return r;return Math.max(u,1)}(l,t,0,s),u>0){for(e=0,n=u-1;e1?(h-d)/(u-1):null,bn(t,i,H.isNullOrUndef(a)?0:d-a,d),bn(t,i,h,H.isNullOrUndef(a)?t.length:h+a),vn(t)}return bn(t,i),vn(t)},_tickSize:function(){var t=this.options.ticks,e=H.toRadians(this.labelRotation),n=Math.abs(Math.cos(e)),i=Math.abs(Math.sin(e)),a=this._getLabelSizes(),r=t.autoSkipPadding||0,o=a?a.widest.width+r:0,s=a?a.highest.height+r:0;return this.isHorizontal()?s*n>o*i?o/n:s/i:s*i=0&&(o=t),void 0!==r&&(t=n.indexOf(r))>=0&&(s=t),e.minIndex=o,e.maxIndex=s,e.min=n[o],e.max=n[s]},buildTicks:function(){var t=this._getLabels(),e=this.minIndex,n=this.maxIndex;this.ticks=0===e&&n===t.length-1?t:t.slice(e,n+1)},getLabelForIndex:function(t,e){var n=this.chart;return n.getDatasetMeta(e).controller._getValueScaleId()===this.id?this.getRightValue(n.data.datasets[e].data[t]):this._getLabels()[t]},_configure:function(){var t=this,e=t.options.offset,n=t.ticks;yn.prototype._configure.call(t),t.isHorizontal()||(t._reversePixels=!t._reversePixels),n&&(t._startValue=t.minIndex-(e?.5:0),t._valueRange=Math.max(n.length-(e?0:1),1))},getPixelForValue:function(t,e,n){var i,a,r,o=this;return _n(e)||_n(n)||(t=o.chart.data.datasets[n].data[e]),_n(t)||(i=o.isHorizontal()?t.x:t.y),(void 0!==i||void 0!==t&&isNaN(e))&&(a=o._getLabels(),t=H.valueOrDefault(i,t),e=-1!==(r=a.indexOf(t))?r:e,isNaN(e)&&(e=t)),o.getPixelForDecimal((e-o._startValue)/o._valueRange)},getPixelForTick:function(t){var e=this.ticks;return t<0||t>e.length-1?null:this.getPixelForValue(e[t],t+this.minIndex)},getValueForPixel:function(t){var e=Math.round(this._startValue+this.getDecimalForPixel(t)*this._valueRange);return Math.min(Math.max(e,0),this.ticks.length-1)},getBasePixel:function(){return this.bottom}}),wn={position:"bottom"};kn._defaults=wn;var Mn=H.noop,Sn=H.isNullOrUndef;var Cn=yn.extend({getRightValue:function(t){return"string"==typeof t?+t:yn.prototype.getRightValue.call(this,t)},handleTickRangeOptions:function(){var t=this,e=t.options.ticks;if(e.beginAtZero){var n=H.sign(t.min),i=H.sign(t.max);n<0&&i<0?t.max=0:n>0&&i>0&&(t.min=0)}var a=void 0!==e.min||void 0!==e.suggestedMin,r=void 0!==e.max||void 0!==e.suggestedMax;void 0!==e.min?t.min=e.min:void 0!==e.suggestedMin&&(null===t.min?t.min=e.suggestedMin:t.min=Math.min(t.min,e.suggestedMin)),void 0!==e.max?t.max=e.max:void 0!==e.suggestedMax&&(null===t.max?t.max=e.suggestedMax:t.max=Math.max(t.max,e.suggestedMax)),a!==r&&t.min>=t.max&&(a?t.max=t.min+1:t.min=t.max-1),t.min===t.max&&(t.max++,e.beginAtZero||t.min--)},getTickLimit:function(){var t,e=this.options.ticks,n=e.stepSize,i=e.maxTicksLimit;return n?t=Math.ceil(this.max/n)-Math.floor(this.min/n)+1:(t=this._computeTickLimit(),i=i||11),i&&(t=Math.min(i,t)),t},_computeTickLimit:function(){return Number.POSITIVE_INFINITY},handleDirectionalChanges:Mn,buildTicks:function(){var t=this,e=t.options.ticks,n=t.getTickLimit(),i={maxTicks:n=Math.max(2,n),min:e.min,max:e.max,precision:e.precision,stepSize:H.valueOrDefault(e.fixedStepSize,e.stepSize)},a=t.ticks=function(t,e){var n,i,a,r,o=[],s=t.stepSize,l=s||1,u=t.maxTicks-1,d=t.min,h=t.max,c=t.precision,f=e.min,g=e.max,p=H.niceNum((g-f)/u/l)*l;if(p<1e-14&&Sn(d)&&Sn(h))return[f,g];(r=Math.ceil(g/p)-Math.floor(f/p))>u&&(p=H.niceNum(r*p/u/l)*l),s||Sn(c)?n=Math.pow(10,H._decimalPlaces(p)):(n=Math.pow(10,c),p=Math.ceil(p*n)/n),i=Math.floor(f/p)*p,a=Math.ceil(g/p)*p,s&&(!Sn(d)&&H.almostWhole(d/p,p/1e3)&&(i=d),!Sn(h)&&H.almostWhole(h/p,p/1e3)&&(a=h)),r=(a-i)/p,r=H.almostEquals(r,Math.round(r),p/1e3)?Math.round(r):Math.ceil(r),i=Math.round(i*n)/n,a=Math.round(a*n)/n,o.push(Sn(d)?i:d);for(var m=1;me.length-1?null:this.getPixelForValue(e[t])}}),In=Pn;Tn._defaults=In;var Fn=H.valueOrDefault,On=H.math.log10;var Ln={position:"left",ticks:{callback:on.formatters.logarithmic}};function Rn(t,e){return H.isFinite(t)&&t>=0?t:e}var zn=yn.extend({determineDataLimits:function(){var t,e,n,i,a,r,o=this,s=o.options,l=o.chart,u=l.data.datasets,d=o.isHorizontal();function h(t){return d?t.xAxisID===o.id:t.yAxisID===o.id}o.min=Number.POSITIVE_INFINITY,o.max=Number.NEGATIVE_INFINITY,o.minNotZero=Number.POSITIVE_INFINITY;var c=s.stacked;if(void 0===c)for(t=0;t0){var e=H.min(t),n=H.max(t);o.min=Math.min(o.min,e),o.max=Math.max(o.max,n)}}))}else for(t=0;t0?t.minNotZero=t.min:t.max<1?t.minNotZero=Math.pow(10,Math.floor(On(t.max))):t.minNotZero=1)},buildTicks:function(){var t=this,e=t.options.ticks,n=!t.isHorizontal(),i={min:Rn(e.min),max:Rn(e.max)},a=t.ticks=function(t,e){var n,i,a=[],r=Fn(t.min,Math.pow(10,Math.floor(On(e.min)))),o=Math.floor(On(e.max)),s=Math.ceil(e.max/Math.pow(10,o));0===r?(n=Math.floor(On(e.minNotZero)),i=Math.floor(e.minNotZero/Math.pow(10,n)),a.push(r),r=i*Math.pow(10,n)):(n=Math.floor(On(r)),i=Math.floor(r/Math.pow(10,n)));var l=n<0?Math.pow(10,Math.abs(n)):1;do{a.push(r),10===++i&&(i=1,l=++n>=0?1:l),r=Math.round(i*Math.pow(10,n)*l)/l}while(ne.length-1?null:this.getPixelForValue(e[t])},_getFirstTickValue:function(t){var e=Math.floor(On(t));return Math.floor(t/Math.pow(10,e))*Math.pow(10,e)},_configure:function(){var t=this,e=t.min,n=0;yn.prototype._configure.call(t),0===e&&(e=t._getFirstTickValue(t.minNotZero),n=Fn(t.options.ticks.fontSize,N.global.defaultFontSize)/t._length),t._startValue=On(e),t._valueOffset=n,t._valueRange=(On(t.max)-On(e))/(1-n)},getPixelForValue:function(t){var e=this,n=0;return(t=+e.getRightValue(t))>e.min&&t>0&&(n=(On(t)-e._startValue)/e._valueRange+e._valueOffset),e.getPixelForDecimal(n)},getValueForPixel:function(t){var e=this,n=e.getDecimalForPixel(t);return 0===n&&0===e.min?0:Math.pow(10,e._startValue+(n-e._valueOffset)*e._valueRange)}}),Nn=Ln;zn._defaults=Nn;var Bn=H.valueOrDefault,En=H.valueAtIndexOrDefault,Wn=H.options.resolve,Vn={display:!0,animate:!0,position:"chartArea",angleLines:{display:!0,color:"rgba(0,0,0,0.1)",lineWidth:1,borderDash:[],borderDashOffset:0},gridLines:{circular:!1},ticks:{showLabelBackdrop:!0,backdropColor:"rgba(255,255,255,0.75)",backdropPaddingY:2,backdropPaddingX:2,callback:on.formatters.linear},pointLabels:{display:!0,fontSize:10,callback:function(t){return t}}};function Hn(t){var e=t.ticks;return e.display&&t.display?Bn(e.fontSize,N.global.defaultFontSize)+2*e.backdropPaddingY:0}function jn(t,e,n,i,a){return t===i||t===a?{start:e-n/2,end:e+n/2}:ta?{start:e-n,end:e}:{start:e,end:e+n}}function qn(t){return 0===t||180===t?"center":t<180?"left":"right"}function Un(t,e,n,i){var a,r,o=n.y+i/2;if(H.isArray(e))for(a=0,r=e.length;a270||t<90)&&(n.y-=e.h)}function Gn(t){return H.isNumber(t)?t:0}var Xn=Cn.extend({setDimensions:function(){var t=this;t.width=t.maxWidth,t.height=t.maxHeight,t.paddingTop=Hn(t.options)/2,t.xCenter=Math.floor(t.width/2),t.yCenter=Math.floor((t.height-t.paddingTop)/2),t.drawingArea=Math.min(t.height-t.paddingTop,t.width)/2},determineDataLimits:function(){var t=this,e=t.chart,n=Number.POSITIVE_INFINITY,i=Number.NEGATIVE_INFINITY;H.each(e.data.datasets,(function(a,r){if(e.isDatasetVisible(r)){var o=e.getDatasetMeta(r);H.each(a.data,(function(e,a){var r=+t.getRightValue(e);isNaN(r)||o.data[a].hidden||(n=Math.min(r,n),i=Math.max(r,i))}))}})),t.min=n===Number.POSITIVE_INFINITY?0:n,t.max=i===Number.NEGATIVE_INFINITY?0:i,t.handleTickRangeOptions()},_computeTickLimit:function(){return Math.ceil(this.drawingArea/Hn(this.options))},convertTicksToLabels:function(){var t=this;Cn.prototype.convertTicksToLabels.call(t),t.pointLabels=t.chart.data.labels.map((function(){var e=H.callback(t.options.pointLabels.callback,arguments,t);return e||0===e?e:""}))},getLabelForIndex:function(t,e){return+this.getRightValue(this.chart.data.datasets[e].data[t])},fit:function(){var t=this.options;t.display&&t.pointLabels.display?function(t){var e,n,i,a=H.options._parseFont(t.options.pointLabels),r={l:0,r:t.width,t:0,b:t.height-t.paddingTop},o={};t.ctx.font=a.string,t._pointLabelSizes=[];var s,l,u,d=t.chart.data.labels.length;for(e=0;er.r&&(r.r=f.end,o.r=h),g.startr.b&&(r.b=g.end,o.b=h)}t.setReductions(t.drawingArea,r,o)}(this):this.setCenterPoint(0,0,0,0)},setReductions:function(t,e,n){var i=this,a=e.l/Math.sin(n.l),r=Math.max(e.r-i.width,0)/Math.sin(n.r),o=-e.t/Math.cos(n.t),s=-Math.max(e.b-(i.height-i.paddingTop),0)/Math.cos(n.b);a=Gn(a),r=Gn(r),o=Gn(o),s=Gn(s),i.drawingArea=Math.min(Math.floor(t-(a+r)/2),Math.floor(t-(o+s)/2)),i.setCenterPoint(a,r,o,s)},setCenterPoint:function(t,e,n,i){var a=this,r=a.width-e-a.drawingArea,o=t+a.drawingArea,s=n+a.drawingArea,l=a.height-a.paddingTop-i-a.drawingArea;a.xCenter=Math.floor((o+r)/2+a.left),a.yCenter=Math.floor((s+l)/2+a.top+a.paddingTop)},getIndexAngle:function(t){var e=this.chart,n=(t*(360/e.data.labels.length)+((e.options||{}).startAngle||0))%360;return(n<0?n+360:n)*Math.PI*2/360},getDistanceFromCenterForValue:function(t){var e=this;if(H.isNullOrUndef(t))return NaN;var n=e.drawingArea/(e.max-e.min);return e.options.ticks.reverse?(e.max-t)*n:(t-e.min)*n},getPointPosition:function(t,e){var n=this.getIndexAngle(t)-Math.PI/2;return{x:Math.cos(n)*e+this.xCenter,y:Math.sin(n)*e+this.yCenter}},getPointPositionForValue:function(t,e){return this.getPointPosition(t,this.getDistanceFromCenterForValue(e))},getBasePosition:function(t){var e=this.min,n=this.max;return this.getPointPositionForValue(t||0,this.beginAtZero?0:e<0&&n<0?n:e>0&&n>0?e:0)},_drawGrid:function(){var t,e,n,i=this,a=i.ctx,r=i.options,o=r.gridLines,s=r.angleLines,l=Bn(s.lineWidth,o.lineWidth),u=Bn(s.color,o.color);if(r.pointLabels.display&&function(t){var e=t.ctx,n=t.options,i=n.pointLabels,a=Hn(n),r=t.getDistanceFromCenterForValue(n.ticks.reverse?t.min:t.max),o=H.options._parseFont(i);e.save(),e.font=o.string,e.textBaseline="middle";for(var s=t.chart.data.labels.length-1;s>=0;s--){var l=0===s?a/2:0,u=t.getPointPosition(s,r+l+5),d=En(i.fontColor,s,N.global.defaultFontColor);e.fillStyle=d;var h=t.getIndexAngle(s),c=H.toDegrees(h);e.textAlign=qn(c),Yn(c,t._pointLabelSizes[s],u),Un(e,t.pointLabels[s],u,o.lineHeight)}e.restore()}(i),o.display&&H.each(i.ticks,(function(t,n){0!==n&&(e=i.getDistanceFromCenterForValue(i.ticksAsNumbers[n]),function(t,e,n,i){var a,r=t.ctx,o=e.circular,s=t.chart.data.labels.length,l=En(e.color,i-1),u=En(e.lineWidth,i-1);if((o||s)&&l&&u){if(r.save(),r.strokeStyle=l,r.lineWidth=u,r.setLineDash&&(r.setLineDash(e.borderDash||[]),r.lineDashOffset=e.borderDashOffset||0),r.beginPath(),o)r.arc(t.xCenter,t.yCenter,n,0,2*Math.PI);else{a=t.getPointPosition(0,n),r.moveTo(a.x,a.y);for(var d=1;d=0;t--)e=i.getDistanceFromCenterForValue(r.ticks.reverse?i.min:i.max),n=i.getPointPosition(t,e),a.beginPath(),a.moveTo(i.xCenter,i.yCenter),a.lineTo(n.x,n.y),a.stroke();a.restore()}},_drawLabels:function(){var t=this,e=t.ctx,n=t.options.ticks;if(n.display){var i,a,r=t.getIndexAngle(0),o=H.options._parseFont(n),s=Bn(n.fontColor,N.global.defaultFontColor);e.save(),e.font=o.string,e.translate(t.xCenter,t.yCenter),e.rotate(r),e.textAlign="center",e.textBaseline="middle",H.each(t.ticks,(function(r,l){(0!==l||n.reverse)&&(i=t.getDistanceFromCenterForValue(t.ticksAsNumbers[l]),n.showLabelBackdrop&&(a=e.measureText(r).width,e.fillStyle=n.backdropColor,e.fillRect(-a/2-n.backdropPaddingX,-i-o.size/2-n.backdropPaddingY,a+2*n.backdropPaddingX,o.size+2*n.backdropPaddingY)),e.fillStyle=s,e.fillText(r,0,-i))})),e.restore()}},_drawTitle:H.noop}),Kn=Vn;Xn._defaults=Kn;var Zn=H._deprecated,$n=H.options.resolve,Jn=H.valueOrDefault,Qn=Number.MIN_SAFE_INTEGER||-9007199254740991,ti=Number.MAX_SAFE_INTEGER||9007199254740991,ei={millisecond:{common:!0,size:1,steps:1e3},second:{common:!0,size:1e3,steps:60},minute:{common:!0,size:6e4,steps:60},hour:{common:!0,size:36e5,steps:24},day:{common:!0,size:864e5,steps:30},week:{common:!1,size:6048e5,steps:4},month:{common:!0,size:2628e6,steps:12},quarter:{common:!1,size:7884e6,steps:4},year:{common:!0,size:3154e7}},ni=Object.keys(ei);function ii(t,e){return t-e}function ai(t){return H.valueOrDefault(t.time.min,t.ticks.min)}function ri(t){return H.valueOrDefault(t.time.max,t.ticks.max)}function oi(t,e,n,i){var a=function(t,e,n){for(var i,a,r,o=0,s=t.length-1;o>=0&&o<=s;){if(a=t[(i=o+s>>1)-1]||null,r=t[i],!a)return{lo:null,hi:r};if(r[e]n))return{lo:a,hi:r};s=i-1}}return{lo:r,hi:null}}(t,e,n),r=a.lo?a.hi?a.lo:t[t.length-2]:t[0],o=a.lo?a.hi?a.hi:t[t.length-1]:t[1],s=o[e]-r[e],l=s?(n-r[e])/s:0,u=(o[i]-r[i])*l;return r[i]+u}function si(t,e){var n=t._adapter,i=t.options.time,a=i.parser,r=a||i.format,o=e;return"function"==typeof a&&(o=a(o)),H.isFinite(o)||(o="string"==typeof r?n.parse(o,r):n.parse(o)),null!==o?+o:(a||"function"!=typeof r||(o=r(e),H.isFinite(o)||(o=n.parse(o))),o)}function li(t,e){if(H.isNullOrUndef(e))return null;var n=t.options.time,i=si(t,t.getRightValue(e));return null===i?i:(n.round&&(i=+t._adapter.startOf(i,n.round)),i)}function ui(t,e,n,i){var a,r,o,s=ni.length;for(a=ni.indexOf(t);a=0&&(e[r].major=!0);return e}(t,r,o,n):r}var hi=yn.extend({initialize:function(){this.mergeTicksOptions(),yn.prototype.initialize.call(this)},update:function(){var t=this,e=t.options,n=e.time||(e.time={}),i=t._adapter=new rn._date(e.adapters.date);return Zn("time scale",n.format,"time.format","time.parser"),Zn("time scale",n.min,"time.min","ticks.min"),Zn("time scale",n.max,"time.max","ticks.max"),H.mergeIf(n.displayFormats,i.formats()),yn.prototype.update.apply(t,arguments)},getRightValue:function(t){return t&&void 0!==t.t&&(t=t.t),yn.prototype.getRightValue.call(this,t)},determineDataLimits:function(){var t,e,n,i,a,r,o,s=this,l=s.chart,u=s._adapter,d=s.options,h=d.time.unit||"day",c=ti,f=Qn,g=[],p=[],m=[],v=s._getLabels();for(t=0,n=v.length;t1?function(t){var e,n,i,a={},r=[];for(e=0,n=t.length;e1e5*u)throw e+" and "+n+" are too far apart with stepSize of "+u+" "+l;for(a=h;a=a&&n<=r&&d.push(n);return i.min=a,i.max=r,i._unit=l.unit||(s.autoSkip?ui(l.minUnit,i.min,i.max,h):function(t,e,n,i,a){var r,o;for(r=ni.length-1;r>=ni.indexOf(n);r--)if(o=ni[r],ei[o].common&&t._adapter.diff(a,i,o)>=e-1)return o;return ni[n?ni.indexOf(n):0]}(i,d.length,l.minUnit,i.min,i.max)),i._majorUnit=s.major.enabled&&"year"!==i._unit?function(t){for(var e=ni.indexOf(t)+1,n=ni.length;ee&&s=0&&t0?s:1}}),ci={position:"bottom",distribution:"linear",bounds:"data",adapters:{},time:{parser:!1,unit:!1,round:!1,displayFormat:!1,isoWeekday:!1,minUnit:"millisecond",displayFormats:{}},ticks:{autoSkip:!1,source:"auto",major:{enabled:!1}}};hi._defaults=ci;var fi={category:kn,linear:Tn,logarithmic:zn,radialLinear:Xn,time:hi},gi={datetime:"MMM D, YYYY, h:mm:ss a",millisecond:"h:mm:ss.SSS a",second:"h:mm:ss a",minute:"h:mm a",hour:"hA",day:"MMM D",week:"ll",month:"MMM YYYY",quarter:"[Q]Q - YYYY",year:"YYYY"};rn._date.override("function"==typeof t?{_id:"moment",formats:function(){return gi},parse:function(e,n){return"string"==typeof e&&"string"==typeof n?e=t(e,n):e instanceof t||(e=t(e)),e.isValid()?e.valueOf():null},format:function(e,n){return t(e).format(n)},add:function(e,n,i){return t(e).add(n,i).valueOf()},diff:function(e,n,i){return t(e).diff(t(n),i)},startOf:function(e,n,i){return e=t(e),"isoWeek"===n?e.isoWeekday(i).valueOf():e.startOf(n).valueOf()},endOf:function(e,n){return t(e).endOf(n).valueOf()},_create:function(e){return t(e)}}:{}),N._set("global",{plugins:{filler:{propagate:!0}}});var pi={dataset:function(t){var e=t.fill,n=t.chart,i=n.getDatasetMeta(e),a=i&&n.isDatasetVisible(e)&&i.dataset._children||[],r=a.length||0;return r?function(t,e){return e=n)&&i;switch(r){case"bottom":return"start";case"top":return"end";case"zero":return"origin";case"origin":case"start":case"end":return r;default:return!1}}function vi(t){return(t.el._scale||{}).getPointPositionForValue?function(t){var e,n,i,a,r,o=t.el._scale,s=o.options,l=o.chart.data.labels.length,u=t.fill,d=[];if(!l)return null;for(e=s.ticks.reverse?o.max:o.min,n=s.ticks.reverse?o.min:o.max,i=o.getPointPositionForValue(0,e),a=0;a0;--r)H.canvas.lineTo(t,n[r],n[r-1],!0);else for(o=n[0].cx,s=n[0].cy,l=Math.sqrt(Math.pow(n[0].x-o,2)+Math.pow(n[0].y-s,2)),r=a-1;r>0;--r)t.arc(o,s,l,n[r].angle,n[r-1].angle,!0)}}function ki(t,e,n,i,a,r){var o,s,l,u,d,h,c,f,g=e.length,p=i.spanGaps,m=[],v=[],b=0,x=0;for(t.beginPath(),o=0,s=g;o=0;--n)(e=l[n].$filler)&&e.visible&&(a=(i=e.el)._view,r=i._children||[],o=e.mapper,s=a.backgroundColor||N.global.defaultColor,o&&s&&r.length&&(H.canvas.clipArea(u,t.chartArea),ki(u,r,o,a,s,i._loop),H.canvas.unclipArea(u)))}},Mi=H.rtl.getRtlAdapter,Si=H.noop,Ci=H.valueOrDefault;function Pi(t,e){return t.usePointStyle&&t.boxWidth>e?e:t.boxWidth}N._set("global",{legend:{display:!0,position:"top",align:"center",fullWidth:!0,reverse:!1,weight:1e3,onClick:function(t,e){var n=e.datasetIndex,i=this.chart,a=i.getDatasetMeta(n);a.hidden=null===a.hidden?!i.data.datasets[n].hidden:null,i.update()},onHover:null,onLeave:null,labels:{boxWidth:40,padding:10,generateLabels:function(t){var e=t.data.datasets,n=t.options.legend||{},i=n.labels&&n.labels.usePointStyle;return t._getSortedDatasetMetas().map((function(n){var a=n.controller.getStyle(i?0:void 0);return{text:e[n.index].label,fillStyle:a.backgroundColor,hidden:!t.isDatasetVisible(n.index),lineCap:a.borderCapStyle,lineDash:a.borderDash,lineDashOffset:a.borderDashOffset,lineJoin:a.borderJoinStyle,lineWidth:a.borderWidth,strokeStyle:a.borderColor,pointStyle:a.pointStyle,rotation:a.rotation,datasetIndex:n.index}}),this)}}},legendCallback:function(t){var e,n,i,a=document.createElement("ul"),r=t.data.datasets;for(a.setAttribute("class",t.id+"-legend"),e=0,n=r.length;el.width)&&(h+=o+n.padding,d[d.length-(e>0?0:1)]=0),s[e]={left:0,top:0,width:i,height:o},d[d.length-1]+=i+n.padding})),l.height+=h}else{var c=n.padding,f=t.columnWidths=[],g=t.columnHeights=[],p=n.padding,m=0,v=0;H.each(t.legendItems,(function(t,e){var i=Pi(n,o)+o/2+a.measureText(t.text).width;e>0&&v+o+2*c>l.height&&(p+=m+n.padding,f.push(m),g.push(v),m=0,v=0),m=Math.max(m,i),v+=o+c,s[e]={left:0,top:0,width:i,height:o}})),p+=m,f.push(m),g.push(v),l.width+=p}t.width=l.width,t.height=l.height}else t.width=l.width=t.height=l.height=0},afterFit:Si,isHorizontal:function(){return"top"===this.options.position||"bottom"===this.options.position},draw:function(){var t=this,e=t.options,n=e.labels,i=N.global,a=i.defaultColor,r=i.elements.line,o=t.height,s=t.columnHeights,l=t.width,u=t.lineWidths;if(e.display){var d,h=Mi(e.rtl,t.left,t.minSize.width),c=t.ctx,f=Ci(n.fontColor,i.defaultFontColor),g=H.options._parseFont(n),p=g.size;c.textAlign=h.textAlign("left"),c.textBaseline="middle",c.lineWidth=.5,c.strokeStyle=f,c.fillStyle=f,c.font=g.string;var m=Pi(n,p),v=t.legendHitBoxes,b=function(t,i){switch(e.align){case"start":return n.padding;case"end":return t-i;default:return(t-i+n.padding)/2}},x=t.isHorizontal();d=x?{x:t.left+b(l,u[0]),y:t.top+n.padding,line:0}:{x:t.left+n.padding,y:t.top+b(o,s[0]),line:0},H.rtl.overrideTextDirection(t.ctx,e.textDirection);var y=p+n.padding;H.each(t.legendItems,(function(e,i){var f=c.measureText(e.text).width,g=m+p/2+f,_=d.x,k=d.y;h.setWidth(t.minSize.width),x?i>0&&_+g+n.padding>t.left+t.minSize.width&&(k=d.y+=y,d.line++,_=d.x=t.left+b(l,u[d.line])):i>0&&k+y>t.top+t.minSize.height&&(_=d.x=_+t.columnWidths[d.line]+n.padding,d.line++,k=d.y=t.top+b(o,s[d.line]));var w=h.x(_);!function(t,e,i){if(!(isNaN(m)||m<=0)){c.save();var o=Ci(i.lineWidth,r.borderWidth);if(c.fillStyle=Ci(i.fillStyle,a),c.lineCap=Ci(i.lineCap,r.borderCapStyle),c.lineDashOffset=Ci(i.lineDashOffset,r.borderDashOffset),c.lineJoin=Ci(i.lineJoin,r.borderJoinStyle),c.lineWidth=o,c.strokeStyle=Ci(i.strokeStyle,a),c.setLineDash&&c.setLineDash(Ci(i.lineDash,r.borderDash)),n&&n.usePointStyle){var s=m*Math.SQRT2/2,l=h.xPlus(t,m/2),u=e+p/2;H.canvas.drawPoint(c,i.pointStyle,s,l,u,i.rotation)}else c.fillRect(h.leftForLtr(t,m),e,m,p),0!==o&&c.strokeRect(h.leftForLtr(t,m),e,m,p);c.restore()}}(w,k,e),v[i].left=h.leftForLtr(w,v[i].width),v[i].top=k,function(t,e,n,i){var a=p/2,r=h.xPlus(t,m+a),o=e+a;c.fillText(n.text,r,o),n.hidden&&(c.beginPath(),c.lineWidth=2,c.moveTo(r,o),c.lineTo(h.xPlus(r,i),o),c.stroke())}(w,k,e,f),x?d.x+=g+n.padding:d.y+=y})),H.rtl.restoreTextDirection(t.ctx,e.textDirection)}},_getLegendItemAt:function(t,e){var n,i,a,r=this;if(t>=r.left&&t<=r.right&&e>=r.top&&e<=r.bottom)for(a=r.legendHitBoxes,n=0;n=(i=a[n]).left&&t<=i.left+i.width&&e>=i.top&&e<=i.top+i.height)return r.legendItems[n];return null},handleEvent:function(t){var e,n=this,i=n.options,a="mouseup"===t.type?"click":t.type;if("mousemove"===a){if(!i.onHover&&!i.onLeave)return}else{if("click"!==a)return;if(!i.onClick)return}e=n._getLegendItemAt(t.x,t.y),"click"===a?e&&i.onClick&&i.onClick.call(n,t.native,e):(i.onLeave&&e!==n._hoveredItem&&(n._hoveredItem&&i.onLeave.call(n,t.native,n._hoveredItem),n._hoveredItem=e),i.onHover&&e&&i.onHover.call(n,t.native,e))}});function Di(t,e){var n=new Ai({ctx:t.ctx,options:e,chart:t});pe.configure(t,n,e),pe.addBox(t,n),t.legend=n}var Ti={id:"legend",_element:Ai,beforeInit:function(t){var e=t.options.legend;e&&Di(t,e)},beforeUpdate:function(t){var e=t.options.legend,n=t.legend;e?(H.mergeIf(e,N.global.legend),n?(pe.configure(t,n,e),n.options=e):Di(t,e)):n&&(pe.removeBox(t,n),delete t.legend)},afterEvent:function(t,e){var n=t.legend;n&&n.handleEvent(e)}},Ii=H.noop;N._set("global",{title:{display:!1,fontStyle:"bold",fullWidth:!0,padding:10,position:"top",text:"",weight:2e3}});var Fi=K.extend({initialize:function(t){H.extend(this,t),this.legendHitBoxes=[]},beforeUpdate:Ii,update:function(t,e,n){var i=this;return i.beforeUpdate(),i.maxWidth=t,i.maxHeight=e,i.margins=n,i.beforeSetDimensions(),i.setDimensions(),i.afterSetDimensions(),i.beforeBuildLabels(),i.buildLabels(),i.afterBuildLabels(),i.beforeFit(),i.fit(),i.afterFit(),i.afterUpdate(),i.minSize},afterUpdate:Ii,beforeSetDimensions:Ii,setDimensions:function(){var t=this;t.isHorizontal()?(t.width=t.maxWidth,t.left=0,t.right=t.width):(t.height=t.maxHeight,t.top=0,t.bottom=t.height),t.paddingLeft=0,t.paddingTop=0,t.paddingRight=0,t.paddingBottom=0,t.minSize={width:0,height:0}},afterSetDimensions:Ii,beforeBuildLabels:Ii,buildLabels:Ii,afterBuildLabels:Ii,beforeFit:Ii,fit:function(){var t,e=this,n=e.options,i=e.minSize={},a=e.isHorizontal();n.display?(t=(H.isArray(n.text)?n.text.length:1)*H.options._parseFont(n).lineHeight+2*n.padding,e.width=i.width=a?e.maxWidth:t,e.height=i.height=a?t:e.maxHeight):e.width=i.width=e.height=i.height=0},afterFit:Ii,isHorizontal:function(){var t=this.options.position;return"top"===t||"bottom"===t},draw:function(){var t=this,e=t.ctx,n=t.options;if(n.display){var i,a,r,o=H.options._parseFont(n),s=o.lineHeight,l=s/2+n.padding,u=0,d=t.top,h=t.left,c=t.bottom,f=t.right;e.fillStyle=H.valueOrDefault(n.fontColor,N.global.defaultFontColor),e.font=o.string,t.isHorizontal()?(a=h+(f-h)/2,r=d+l,i=f-h):(a="left"===n.position?h+l:f-l,r=d+(c-d)/2,i=c-d,u=Math.PI*("left"===n.position?-.5:.5)),e.save(),e.translate(a,r),e.rotate(u),e.textAlign="center",e.textBaseline="middle";var g=n.text;if(H.isArray(g))for(var p=0,m=0;m=0;i--){var a=t[i];if(e(a))return a}},H.isNumber=function(t){return!isNaN(parseFloat(t))&&isFinite(t)},H.almostEquals=function(t,e,n){return Math.abs(t-e)=t},H.max=function(t){return t.reduce((function(t,e){return isNaN(e)?t:Math.max(t,e)}),Number.NEGATIVE_INFINITY)},H.min=function(t){return t.reduce((function(t,e){return isNaN(e)?t:Math.min(t,e)}),Number.POSITIVE_INFINITY)},H.sign=Math.sign?function(t){return Math.sign(t)}:function(t){return 0===(t=+t)||isNaN(t)?t:t>0?1:-1},H.toRadians=function(t){return t*(Math.PI/180)},H.toDegrees=function(t){return t*(180/Math.PI)},H._decimalPlaces=function(t){if(H.isFinite(t)){for(var e=1,n=0;Math.round(t*e)/e!==t;)e*=10,n++;return n}},H.getAngleFromPoint=function(t,e){var n=e.x-t.x,i=e.y-t.y,a=Math.sqrt(n*n+i*i),r=Math.atan2(i,n);return r<-.5*Math.PI&&(r+=2*Math.PI),{angle:r,distance:a}},H.distanceBetweenPoints=function(t,e){return Math.sqrt(Math.pow(e.x-t.x,2)+Math.pow(e.y-t.y,2))},H.aliasPixel=function(t){return t%2==0?0:.5},H._alignPixel=function(t,e,n){var i=t.currentDevicePixelRatio,a=n/2;return Math.round((e-a)*i)/i+a},H.splineCurve=function(t,e,n,i){var a=t.skip?e:t,r=e,o=n.skip?e:n,s=Math.sqrt(Math.pow(r.x-a.x,2)+Math.pow(r.y-a.y,2)),l=Math.sqrt(Math.pow(o.x-r.x,2)+Math.pow(o.y-r.y,2)),u=s/(s+l),d=l/(s+l),h=i*(u=isNaN(u)?0:u),c=i*(d=isNaN(d)?0:d);return{previous:{x:r.x-h*(o.x-a.x),y:r.y-h*(o.y-a.y)},next:{x:r.x+c*(o.x-a.x),y:r.y+c*(o.y-a.y)}}},H.EPSILON=Number.EPSILON||1e-14,H.splineCurveMonotone=function(t){var e,n,i,a,r,o,s,l,u,d=(t||[]).map((function(t){return{model:t._model,deltaK:0,mK:0}})),h=d.length;for(e=0;e0?d[e-1]:null,(a=e0?d[e-1]:null,a=e=t.length-1?t[0]:t[e+1]:e>=t.length-1?t[t.length-1]:t[e+1]},H.previousItem=function(t,e,n){return n?e<=0?t[t.length-1]:t[e-1]:e<=0?t[0]:t[e-1]},H.niceNum=function(t,e){var n=Math.floor(H.log10(t)),i=t/Math.pow(10,n);return(e?i<1.5?1:i<3?2:i<7?5:10:i<=1?1:i<=2?2:i<=5?5:10)*Math.pow(10,n)},H.requestAnimFrame="undefined"==typeof window?function(t){t()}:window.requestAnimationFrame||window.webkitRequestAnimationFrame||window.mozRequestAnimationFrame||window.oRequestAnimationFrame||window.msRequestAnimationFrame||function(t){return window.setTimeout(t,1e3/60)},H.getRelativePosition=function(t,e){var n,i,a=t.originalEvent||t,r=t.target||t.srcElement,o=r.getBoundingClientRect(),s=a.touches;s&&s.length>0?(n=s[0].clientX,i=s[0].clientY):(n=a.clientX,i=a.clientY);var l=parseFloat(H.getStyle(r,"padding-left")),u=parseFloat(H.getStyle(r,"padding-top")),d=parseFloat(H.getStyle(r,"padding-right")),h=parseFloat(H.getStyle(r,"padding-bottom")),c=o.right-o.left-l-d,f=o.bottom-o.top-u-h;return{x:n=Math.round((n-o.left-l)/c*r.width/e.currentDevicePixelRatio),y:i=Math.round((i-o.top-u)/f*r.height/e.currentDevicePixelRatio)}},H.getConstraintWidth=function(t){return n(t,"max-width","clientWidth")},H.getConstraintHeight=function(t){return n(t,"max-height","clientHeight")},H._calculatePadding=function(t,e,n){return(e=H.getStyle(t,e)).indexOf("%")>-1?n*parseInt(e,10)/100:parseInt(e,10)},H._getParentNode=function(t){var e=t.parentNode;return e&&"[object ShadowRoot]"===e.toString()&&(e=e.host),e},H.getMaximumWidth=function(t){var e=H._getParentNode(t);if(!e)return t.clientWidth;var n=e.clientWidth,i=n-H._calculatePadding(e,"padding-left",n)-H._calculatePadding(e,"padding-right",n),a=H.getConstraintWidth(t);return isNaN(a)?i:Math.min(i,a)},H.getMaximumHeight=function(t){var e=H._getParentNode(t);if(!e)return t.clientHeight;var n=e.clientHeight,i=n-H._calculatePadding(e,"padding-top",n)-H._calculatePadding(e,"padding-bottom",n),a=H.getConstraintHeight(t);return isNaN(a)?i:Math.min(i,a)},H.getStyle=function(t,e){return t.currentStyle?t.currentStyle[e]:document.defaultView.getComputedStyle(t,null).getPropertyValue(e)},H.retinaScale=function(t,e){var n=t.currentDevicePixelRatio=e||"undefined"!=typeof window&&window.devicePixelRatio||1;if(1!==n){var i=t.canvas,a=t.height,r=t.width;i.height=a*n,i.width=r*n,t.ctx.scale(n,n),i.style.height||i.style.width||(i.style.height=a+"px",i.style.width=r+"px")}},H.fontString=function(t,e,n){return e+" "+t+"px "+n},H.longestText=function(t,e,n,i){var a=(i=i||{}).data=i.data||{},r=i.garbageCollect=i.garbageCollect||[];i.font!==e&&(a=i.data={},r=i.garbageCollect=[],i.font=e),t.font=e;var o,s,l,u,d,h=0,c=n.length;for(o=0;on.length){for(o=0;oi&&(i=r),i},H.numberOfLabelLines=function(t){var e=1;return H.each(t,(function(t){H.isArray(t)&&t.length>e&&(e=t.length)})),e},H.color=_?function(t){return t instanceof CanvasGradient&&(t=N.global.defaultColor),_(t)}:function(t){return console.error("Color.js not found!"),t},H.getHoverColor=function(t){return t instanceof CanvasPattern||t instanceof CanvasGradient?t:H.color(t).saturate(.5).darken(.1).rgbString()}}(),en._adapters=rn,en.Animation=$,en.animationService=J,en.controllers=Jt,en.DatasetController=it,en.defaults=N,en.Element=K,en.elements=kt,en.Interaction=re,en.layouts=pe,en.platform=Oe,en.plugins=Le,en.Scale=yn,en.scaleService=Re,en.Ticks=on,en.Tooltip=Ye,en.helpers.each(fi,(function(t,e){en.scaleService.registerScaleType(e,t,t._defaults)})),Li)Li.hasOwnProperty(Bi)&&en.plugins.register(Li[Bi]);en.platform.initialize();var Ei=en;return"undefined"!=typeof window&&(window.Chart=en),en.Chart=en,en.Legend=Li.legend._element,en.Title=Li.title._element,en.pluginService=en.plugins,en.PluginBase=en.Element.extend({}),en.canvasHelpers=en.helpers.canvas,en.layoutService=en.layouts,en.LinearScaleBase=Cn,en.helpers.each(["Bar","Bubble","Doughnut","Line","PolarArea","Radar","Scatter"],(function(t){en[t]=function(e,n){return new en(e,en.helpers.merge(n||{},{type:t.charAt(0).toLowerCase()+t.slice(1)}))}})),Ei})); diff --git a/scripts/carousel.js b/scripts/carousel.js new file mode 100644 index 0000000..75d6c9f --- /dev/null +++ b/scripts/carousel.js @@ -0,0 +1,99 @@ +const carouselTrack = document.querySelector(".carousel-items"); +const carouselItems = Array.from(carouselTrack.children); +const carouselNav = document.querySelector(".carousel-navigation") +const carouselDetails = document.querySelectorAll(".carousel-item-details"); +const prevButton = document.querySelector(".carousel-btn-left"); +const nextButton = document.querySelector(".carousel-btn-right"); +const dotsNav = document.querySelector(".carousel-indicators"); +const carousel = document.querySelector(".carousel"); +const slideWidth = carousel.getBoundingClientRect().width; +var currentSlideIndex = 0; + +const initializeSlides = (slide, index) => { + slide.style.left = slideWidth * index + "px"; + var dot = document.createElement("div"); + if (index == 0) { + dot.setAttribute("class", "indicator selected"); + } else { + dot.setAttribute("class", "indicator"); + } + dotsNav.appendChild(dot); +}; +carouselItems.forEach(initializeSlides); + +const dots = Array.from(dotsNav.children); + +const slideMoveTo = (index) => { + if (index > -1 && index < carouselItems.length) { + const currentSlide = carouselTrack.querySelector(".carousel-active"); + const targetSlide = carouselTrack.children[index]; + currentSlide.classList.remove("carousel-active"); + targetSlide.classList.add("carousel-active"); + const amtToMove = targetSlide.style.left; + carouselTrack.style.transform = "translateX(-" + amtToMove + ")"; + currentSlideIndex = index; + updateDots(index); + } +}; + +const updateDots = (index) => { + const currentDot = document.querySelector(".indicator.selected"); + const targetDot = dots[index]; + currentDot.classList.remove("selected"); + targetDot.classList.add("selected"); +}; + +carousel.addEventListener("mouseenter", (e) => { + carouselNav.classList.remove("transparent"); + carouselDetails.forEach((element) => { + element.classList.remove("transparent"); + }) +}) + +carousel.addEventListener("mouseleave", (e) => { + carouselNav.classList.add("transparent"); + carouselDetails.forEach((element) => { + element.classList.add("transparent"); + }) +}) + +nextButton.addEventListener("click", (e) => { + if (currentSlideIndex + 1 > carouselItems.length - 1) { + currentSlideIndex = 0; + } else { + currentSlideIndex++; + } + slideMoveTo(currentSlideIndex); +}); + +prevButton.addEventListener("click", (e) => { + if (currentSlideIndex - 1 < 0) { + currentSlideIndex = carouselItems.length - 1; + } else { + currentSlideIndex--; + } + slideMoveTo(currentSlideIndex); +}); + +dotsNav.addEventListener("click", (e) => { + const targetDot = e.target.closest("div.indicator"); + + if (!targetDot) return; + + const targetIndex = dots.findIndex((dot) => dot === targetDot); + + slideMoveTo(targetIndex); +}); + +const carouselSpin = () => { + if (currentSlideIndex + 1 > carouselItems.length - 1) { + currentSlideIndex = 0; + } else { + currentSlideIndex++; + } + slideMoveTo(currentSlideIndex); + + setTimeout(carouselSpin, 8000); +}; + +setTimeout(carouselSpin, 8000); diff --git a/scripts/cart_page.js b/scripts/cart_page.js new file mode 100644 index 0000000..572609b --- /dev/null +++ b/scripts/cart_page.js @@ -0,0 +1,64 @@ +$(function () { + const $cardno = $("#card_no"); + const $cardexp = $("#card_exp"); + const $cardsec = $("#card_secret"); + const $cardhold = $("#card_holder"); + const $sendBtn = $("#send_transaction"); + + var $validCardNo = false; + var $validCardExp = false; + var $validCardSec = false; + var $validCardHold = false; + + $cardno.on("change paste keyup", e => { + if (!/^\d{16}$/g.test($cardno.val())) { + $cardno.addClass("invalid"); + $validCardNo = false; + } else { + $cardno.removeClass("invalid"); + $validCardNo = true; + } + checkValid(); + }); + + $cardexp.on("change paste keyup", e => { + if (!/^\d{2}\/\d{2}$/g.test($cardexp.val())) { + $cardexp.addClass("invalid"); + $validCardExp = false; + } else { + $cardexp.removeClass("invalid"); + $validCardExp = true; + } + checkValid(); + }); + + $cardsec.on("change paste keyup", e => { + if (!/^\d{3}$/g.test($cardsec.val())) { + $cardsec.addClass("invalid"); + $validCardSec = false; + } else { + $cardsec.removeClass("invalid"); + $validCardSec = true; + } + checkValid(); + }); + + $cardhold.on("change paste keyup", e => { + if (!$cardhold.val()) { + $cardhold.addClass("invalid"); + $validCardHold = false; + } else { + $cardhold.removeClass("invalid"); + $validCardHold = true; + } + checkValid(); + }); + + const checkValid = () => { + if ($validCardExp && $validCardHold && $validCardNo && $validCardSec) { + $sendBtn.prop("disabled", false); + } else { + $sendBtn.prop("disabled", true); + } + } +}); diff --git a/scripts/contact_page.js b/scripts/contact_page.js new file mode 100644 index 0000000..ea5f181 --- /dev/null +++ b/scripts/contact_page.js @@ -0,0 +1,45 @@ +const ValidateEmail = (email) => { + if (!/^[a-zA-Z0-9.!#$%&’*+/=?^_`{|}~-]+@[a-zA-Z0-9-]+(?:\.[a-zA-Z0-9-]+)*$/i.test(email)) { + return false; + } else { + return true; + } +} + +const ValidateContact = () => { + if (!$("#name").val()) { + $("#name").addClass("invalid"); + } else { + $("#name").removeClass("invalid"); + } + if (!$("#email").val() || !ValidateEmail($("#email").val())) { + $("#email").addClass("invalid"); + } else { + $("#email").removeClass("invalid"); + } + if (!$("#message").val()) { + $("#message").addClass("invalid"); + } else { + $("#message").removeClass("invalid"); + } +} + +$("#name").change(e => { + ValidateContact(); +}) + +$("#email").change(e => { + ValidateContact(); +}) + +$("#message").change(e => { + ValidateContact(); +}) + +$("#contact-form").submit(e => { + if (!$("#name").val() || !$("#email").val() || !$("#message").val()) { + e.preventDefault(); + + ValidateContact(); + } +}) \ No newline at end of file diff --git a/scripts/conversation_page.js b/scripts/conversation_page.js new file mode 100644 index 0000000..8815595 --- /dev/null +++ b/scripts/conversation_page.js @@ -0,0 +1,24 @@ + +var $inputValid = false; +const ValidateInput = () => { + if (!$('#conversationMessage').val() || + ($('#conversationMessage').val().length > 1024) || + ($('#conversationMessage').val().length < 5)) { + $('#conversationMessage').addClass('invalid'); + $inputValid = false; + } else { + $('#conversationMessage').removeClass('invalid'); + $inputValid = true; + } +} + +$('#conversationMessage').on('change paste keyup', e => { + ValidateInput(); +}) + +const SubmitMessage = (e) => { + ValidateInput(); + if ($inputValid) { + $('#messageform').submit(); + } +} \ No newline at end of file diff --git a/scripts/employee_add_banner.js b/scripts/employee_add_banner.js new file mode 100644 index 0000000..dbd01cb --- /dev/null +++ b/scripts/employee_add_banner.js @@ -0,0 +1,34 @@ +$(function () { + const LoadImage = (e) => { + if (e.files && e.files[0]) { + var reader = new FileReader(); + reader.onload = function (e) { + $('#image').attr('src', e.target.result); + }; + reader.readAsDataURL(e.files[0]); + } + } + + $("#bannerimage").change((e) => { + LoadImage(e.target); + }); + + const ValidateInput = () => { + if (!$("#bannername").val()) { + $("#bannername").addClass("invalid"); + } else { + $("#bannername").removeClass("invalid"); + } + + if (!$("#bannerdesc").val()) { + $("#bannerdesc").addClass("invalid"); + } else { + $("#bannerdesc").removeClass("invalid"); + } + } + + $("#bannername,#bannerdesc").on("change paste keyup", e => { + ValidateInput(); + }) +}); + diff --git a/scripts/employee_sidenav.js b/scripts/employee_sidenav.js new file mode 100644 index 0000000..9ecbc49 --- /dev/null +++ b/scripts/employee_sidenav.js @@ -0,0 +1,9 @@ +const employeeItem = document.querySelectorAll(".employee-sidenav-label a"); +const x = window.location.href.split("/"); +const pageHref = x[x.length - 1]; + +employeeItem.forEach(element => { + if (element.getAttribute("href") == pageHref) { + element.classList.add("active"); + } +}); \ No newline at end of file diff --git a/scripts/item_page.js b/scripts/item_page.js new file mode 100644 index 0000000..76e21f5 --- /dev/null +++ b/scripts/item_page.js @@ -0,0 +1,113 @@ +const smallProductImageContainer = document.querySelector(".product-image-list"); +const smallProductImages = Array.from(smallProductImageContainer.children); +const productImage = document.querySelector("div.product-image > img"); + +smallProductImageContainer.addEventListener("mouseover", (e) => { + const targetImageContainer = e.target.closest("div.product-image-small"); + + if (!targetImageContainer) return; + + const targetImage = targetImageContainer.querySelector("img"); + + if (!targetImage) return; + + smallProductImages.forEach((element) => { + element.classList.remove("active"); + }); + targetImageContainer.classList.add("active"); + + productImage.setAttribute("src", targetImage.getAttribute("src")); +}); + +const ProductQuantity = (amount) => { + const quantity = document.querySelector("#product-quantity"); + const instock = document.querySelector("#product-instock"); + var currentQuantity = parseInt(quantity.textContent); + var currentInstock = parseInt(instock.textContent); + if (currentQuantity + amount >= 1 && currentQuantity + amount <= currentInstock) { + quantity.textContent = currentQuantity + amount; + } +}; + +$("#add-review").click(() => { + $("#product-review-form").toggleClass("h-0"); +}); + +var allStars; +var $reviewRatingInput; + +const UpdateStars = (rating) => { + allStars.forEach(el => { + el.classList.remove("active"); + el.textContent = "star_outline"; + }); + + for (let i = 0; i <= rating - 1; i++) { + allStars[i].classList.add("active"); + allStars[i].textContent = "star"; + } +} + +const Initialize = () => { + if (document.querySelector(".review-rating-stars") != null) { + allStars = Array.from(document.querySelector(".review-rating-stars").children); + $reviewRatingInput = $("#review-rating"); + if ($reviewRatingInput != 0) { + UpdateStars($reviewRatingInput.val()); + } + } +} + +Initialize(); + +$(".review-rating-stars").mouseleave(e => { + if ($reviewRatingInput.val() == "0") { + UpdateStars(0); + } +}) + +$(".review-rating-stars > .material-icons").click(e => { + const ratingValue = allStars.findIndex(el => el === e.target) + 1; + $reviewRatingInput.val(ratingValue); + $(".review-rating-stars").removeClass('invalid'); + UpdateStars(ratingValue); +}); + +$(".review-rating-stars > .material-icons").hover(e => { + if ($reviewRatingInput.val() == "0") { + const childIndex = allStars.findIndex(el => el === e.target); + UpdateStars(childIndex + 1); + } +}); + +const ValidateReview = () => { + if (!$("#review-title").val()) { + $("#review-title").addClass("invalid"); + } else { + $("#review-title").removeClass("invalid"); + } + if (!$("#review-body").val()) { + $("#review-body").addClass("invalid"); + } else { + $("#review-body").removeClass("invalid"); + } +} + +$("#review-body").change(e => { + ValidateReview(); +}) + +$("#review-title").change(e => { + ValidateReview(); +}) + +$("#product-review-form").submit(e => { + if ($("#review-rating").val() <= 0 || !$("#review-title").val() || !$("#review-body").val()) { + e.preventDefault(); + + if ($("#review-rating").val() <= 0) { + $(".review-rating-stars").addClass('invalid'); + } + ValidateReview(); + } +}) diff --git a/scripts/jquery-3.5.1.min.js b/scripts/jquery-3.5.1.min.js new file mode 100644 index 0000000..b061403 --- /dev/null +++ b/scripts/jquery-3.5.1.min.js @@ -0,0 +1,2 @@ +/*! jQuery v3.5.1 | (c) JS Foundation and other contributors | jquery.org/license */ +!function(e,t){"use strict";"object"==typeof module&&"object"==typeof module.exports?module.exports=e.document?t(e,!0):function(e){if(!e.document)throw new Error("jQuery requires a window with a document");return t(e)}:t(e)}("undefined"!=typeof window?window:this,function(C,e){"use strict";var t=[],r=Object.getPrototypeOf,s=t.slice,g=t.flat?function(e){return t.flat.call(e)}:function(e){return t.concat.apply([],e)},u=t.push,i=t.indexOf,n={},o=n.toString,v=n.hasOwnProperty,a=v.toString,l=a.call(Object),y={},m=function(e){return"function"==typeof e&&"number"!=typeof e.nodeType},x=function(e){return null!=e&&e===e.window},E=C.document,c={type:!0,src:!0,nonce:!0,noModule:!0};function b(e,t,n){var r,i,o=(n=n||E).createElement("script");if(o.text=e,t)for(r in c)(i=t[r]||t.getAttribute&&t.getAttribute(r))&&o.setAttribute(r,i);n.head.appendChild(o).parentNode.removeChild(o)}function w(e){return null==e?e+"":"object"==typeof e||"function"==typeof e?n[o.call(e)]||"object":typeof e}var f="3.5.1",S=function(e,t){return new S.fn.init(e,t)};function p(e){var t=!!e&&"length"in e&&e.length,n=w(e);return!m(e)&&!x(e)&&("array"===n||0===t||"number"==typeof t&&0+~]|"+M+")"+M+"*"),U=new RegExp(M+"|>"),X=new RegExp(F),V=new RegExp("^"+I+"$"),G={ID:new RegExp("^#("+I+")"),CLASS:new RegExp("^\\.("+I+")"),TAG:new RegExp("^("+I+"|[*])"),ATTR:new RegExp("^"+W),PSEUDO:new RegExp("^"+F),CHILD:new RegExp("^:(only|first|last|nth|nth-last)-(child|of-type)(?:\\("+M+"*(even|odd|(([+-]|)(\\d*)n|)"+M+"*(?:([+-]|)"+M+"*(\\d+)|))"+M+"*\\)|)","i"),bool:new RegExp("^(?:"+R+")$","i"),needsContext:new RegExp("^"+M+"*[>+~]|:(even|odd|eq|gt|lt|nth|first|last)(?:\\("+M+"*((?:-\\d)?\\d*)"+M+"*\\)|)(?=[^-]|$)","i")},Y=/HTML$/i,Q=/^(?:input|select|textarea|button)$/i,J=/^h\d$/i,K=/^[^{]+\{\s*\[native \w/,Z=/^(?:#([\w-]+)|(\w+)|\.([\w-]+))$/,ee=/[+~]/,te=new RegExp("\\\\[\\da-fA-F]{1,6}"+M+"?|\\\\([^\\r\\n\\f])","g"),ne=function(e,t){var n="0x"+e.slice(1)-65536;return t||(n<0?String.fromCharCode(n+65536):String.fromCharCode(n>>10|55296,1023&n|56320))},re=/([\0-\x1f\x7f]|^-?\d)|^-$|[^\0-\x1f\x7f-\uFFFF\w-]/g,ie=function(e,t){return t?"\0"===e?"\ufffd":e.slice(0,-1)+"\\"+e.charCodeAt(e.length-1).toString(16)+" ":"\\"+e},oe=function(){T()},ae=be(function(e){return!0===e.disabled&&"fieldset"===e.nodeName.toLowerCase()},{dir:"parentNode",next:"legend"});try{H.apply(t=O.call(p.childNodes),p.childNodes),t[p.childNodes.length].nodeType}catch(e){H={apply:t.length?function(e,t){L.apply(e,O.call(t))}:function(e,t){var n=e.length,r=0;while(e[n++]=t[r++]);e.length=n-1}}}function se(t,e,n,r){var i,o,a,s,u,l,c,f=e&&e.ownerDocument,p=e?e.nodeType:9;if(n=n||[],"string"!=typeof t||!t||1!==p&&9!==p&&11!==p)return n;if(!r&&(T(e),e=e||C,E)){if(11!==p&&(u=Z.exec(t)))if(i=u[1]){if(9===p){if(!(a=e.getElementById(i)))return n;if(a.id===i)return n.push(a),n}else if(f&&(a=f.getElementById(i))&&y(e,a)&&a.id===i)return n.push(a),n}else{if(u[2])return H.apply(n,e.getElementsByTagName(t)),n;if((i=u[3])&&d.getElementsByClassName&&e.getElementsByClassName)return H.apply(n,e.getElementsByClassName(i)),n}if(d.qsa&&!N[t+" "]&&(!v||!v.test(t))&&(1!==p||"object"!==e.nodeName.toLowerCase())){if(c=t,f=e,1===p&&(U.test(t)||z.test(t))){(f=ee.test(t)&&ye(e.parentNode)||e)===e&&d.scope||((s=e.getAttribute("id"))?s=s.replace(re,ie):e.setAttribute("id",s=S)),o=(l=h(t)).length;while(o--)l[o]=(s?"#"+s:":scope")+" "+xe(l[o]);c=l.join(",")}try{return H.apply(n,f.querySelectorAll(c)),n}catch(e){N(t,!0)}finally{s===S&&e.removeAttribute("id")}}}return g(t.replace($,"$1"),e,n,r)}function ue(){var r=[];return function e(t,n){return r.push(t+" ")>b.cacheLength&&delete e[r.shift()],e[t+" "]=n}}function le(e){return e[S]=!0,e}function ce(e){var t=C.createElement("fieldset");try{return!!e(t)}catch(e){return!1}finally{t.parentNode&&t.parentNode.removeChild(t),t=null}}function fe(e,t){var n=e.split("|"),r=n.length;while(r--)b.attrHandle[n[r]]=t}function pe(e,t){var n=t&&e,r=n&&1===e.nodeType&&1===t.nodeType&&e.sourceIndex-t.sourceIndex;if(r)return r;if(n)while(n=n.nextSibling)if(n===t)return-1;return e?1:-1}function de(t){return function(e){return"input"===e.nodeName.toLowerCase()&&e.type===t}}function he(n){return function(e){var t=e.nodeName.toLowerCase();return("input"===t||"button"===t)&&e.type===n}}function ge(t){return function(e){return"form"in e?e.parentNode&&!1===e.disabled?"label"in e?"label"in e.parentNode?e.parentNode.disabled===t:e.disabled===t:e.isDisabled===t||e.isDisabled!==!t&&ae(e)===t:e.disabled===t:"label"in e&&e.disabled===t}}function ve(a){return le(function(o){return o=+o,le(function(e,t){var n,r=a([],e.length,o),i=r.length;while(i--)e[n=r[i]]&&(e[n]=!(t[n]=e[n]))})})}function ye(e){return e&&"undefined"!=typeof e.getElementsByTagName&&e}for(e in d=se.support={},i=se.isXML=function(e){var t=e.namespaceURI,n=(e.ownerDocument||e).documentElement;return!Y.test(t||n&&n.nodeName||"HTML")},T=se.setDocument=function(e){var t,n,r=e?e.ownerDocument||e:p;return r!=C&&9===r.nodeType&&r.documentElement&&(a=(C=r).documentElement,E=!i(C),p!=C&&(n=C.defaultView)&&n.top!==n&&(n.addEventListener?n.addEventListener("unload",oe,!1):n.attachEvent&&n.attachEvent("onunload",oe)),d.scope=ce(function(e){return a.appendChild(e).appendChild(C.createElement("div")),"undefined"!=typeof e.querySelectorAll&&!e.querySelectorAll(":scope fieldset div").length}),d.attributes=ce(function(e){return e.className="i",!e.getAttribute("className")}),d.getElementsByTagName=ce(function(e){return e.appendChild(C.createComment("")),!e.getElementsByTagName("*").length}),d.getElementsByClassName=K.test(C.getElementsByClassName),d.getById=ce(function(e){return a.appendChild(e).id=S,!C.getElementsByName||!C.getElementsByName(S).length}),d.getById?(b.filter.ID=function(e){var t=e.replace(te,ne);return function(e){return e.getAttribute("id")===t}},b.find.ID=function(e,t){if("undefined"!=typeof t.getElementById&&E){var n=t.getElementById(e);return n?[n]:[]}}):(b.filter.ID=function(e){var n=e.replace(te,ne);return function(e){var t="undefined"!=typeof e.getAttributeNode&&e.getAttributeNode("id");return t&&t.value===n}},b.find.ID=function(e,t){if("undefined"!=typeof t.getElementById&&E){var n,r,i,o=t.getElementById(e);if(o){if((n=o.getAttributeNode("id"))&&n.value===e)return[o];i=t.getElementsByName(e),r=0;while(o=i[r++])if((n=o.getAttributeNode("id"))&&n.value===e)return[o]}return[]}}),b.find.TAG=d.getElementsByTagName?function(e,t){return"undefined"!=typeof t.getElementsByTagName?t.getElementsByTagName(e):d.qsa?t.querySelectorAll(e):void 0}:function(e,t){var n,r=[],i=0,o=t.getElementsByTagName(e);if("*"===e){while(n=o[i++])1===n.nodeType&&r.push(n);return r}return o},b.find.CLASS=d.getElementsByClassName&&function(e,t){if("undefined"!=typeof t.getElementsByClassName&&E)return t.getElementsByClassName(e)},s=[],v=[],(d.qsa=K.test(C.querySelectorAll))&&(ce(function(e){var t;a.appendChild(e).innerHTML="",e.querySelectorAll("[msallowcapture^='']").length&&v.push("[*^$]="+M+"*(?:''|\"\")"),e.querySelectorAll("[selected]").length||v.push("\\["+M+"*(?:value|"+R+")"),e.querySelectorAll("[id~="+S+"-]").length||v.push("~="),(t=C.createElement("input")).setAttribute("name",""),e.appendChild(t),e.querySelectorAll("[name='']").length||v.push("\\["+M+"*name"+M+"*="+M+"*(?:''|\"\")"),e.querySelectorAll(":checked").length||v.push(":checked"),e.querySelectorAll("a#"+S+"+*").length||v.push(".#.+[+~]"),e.querySelectorAll("\\\f"),v.push("[\\r\\n\\f]")}),ce(function(e){e.innerHTML="";var t=C.createElement("input");t.setAttribute("type","hidden"),e.appendChild(t).setAttribute("name","D"),e.querySelectorAll("[name=d]").length&&v.push("name"+M+"*[*^$|!~]?="),2!==e.querySelectorAll(":enabled").length&&v.push(":enabled",":disabled"),a.appendChild(e).disabled=!0,2!==e.querySelectorAll(":disabled").length&&v.push(":enabled",":disabled"),e.querySelectorAll("*,:x"),v.push(",.*:")})),(d.matchesSelector=K.test(c=a.matches||a.webkitMatchesSelector||a.mozMatchesSelector||a.oMatchesSelector||a.msMatchesSelector))&&ce(function(e){d.disconnectedMatch=c.call(e,"*"),c.call(e,"[s!='']:x"),s.push("!=",F)}),v=v.length&&new RegExp(v.join("|")),s=s.length&&new RegExp(s.join("|")),t=K.test(a.compareDocumentPosition),y=t||K.test(a.contains)?function(e,t){var n=9===e.nodeType?e.documentElement:e,r=t&&t.parentNode;return e===r||!(!r||1!==r.nodeType||!(n.contains?n.contains(r):e.compareDocumentPosition&&16&e.compareDocumentPosition(r)))}:function(e,t){if(t)while(t=t.parentNode)if(t===e)return!0;return!1},D=t?function(e,t){if(e===t)return l=!0,0;var n=!e.compareDocumentPosition-!t.compareDocumentPosition;return n||(1&(n=(e.ownerDocument||e)==(t.ownerDocument||t)?e.compareDocumentPosition(t):1)||!d.sortDetached&&t.compareDocumentPosition(e)===n?e==C||e.ownerDocument==p&&y(p,e)?-1:t==C||t.ownerDocument==p&&y(p,t)?1:u?P(u,e)-P(u,t):0:4&n?-1:1)}:function(e,t){if(e===t)return l=!0,0;var n,r=0,i=e.parentNode,o=t.parentNode,a=[e],s=[t];if(!i||!o)return e==C?-1:t==C?1:i?-1:o?1:u?P(u,e)-P(u,t):0;if(i===o)return pe(e,t);n=e;while(n=n.parentNode)a.unshift(n);n=t;while(n=n.parentNode)s.unshift(n);while(a[r]===s[r])r++;return r?pe(a[r],s[r]):a[r]==p?-1:s[r]==p?1:0}),C},se.matches=function(e,t){return se(e,null,null,t)},se.matchesSelector=function(e,t){if(T(e),d.matchesSelector&&E&&!N[t+" "]&&(!s||!s.test(t))&&(!v||!v.test(t)))try{var n=c.call(e,t);if(n||d.disconnectedMatch||e.document&&11!==e.document.nodeType)return n}catch(e){N(t,!0)}return 0":{dir:"parentNode",first:!0}," ":{dir:"parentNode"},"+":{dir:"previousSibling",first:!0},"~":{dir:"previousSibling"}},preFilter:{ATTR:function(e){return e[1]=e[1].replace(te,ne),e[3]=(e[3]||e[4]||e[5]||"").replace(te,ne),"~="===e[2]&&(e[3]=" "+e[3]+" "),e.slice(0,4)},CHILD:function(e){return e[1]=e[1].toLowerCase(),"nth"===e[1].slice(0,3)?(e[3]||se.error(e[0]),e[4]=+(e[4]?e[5]+(e[6]||1):2*("even"===e[3]||"odd"===e[3])),e[5]=+(e[7]+e[8]||"odd"===e[3])):e[3]&&se.error(e[0]),e},PSEUDO:function(e){var t,n=!e[6]&&e[2];return G.CHILD.test(e[0])?null:(e[3]?e[2]=e[4]||e[5]||"":n&&X.test(n)&&(t=h(n,!0))&&(t=n.indexOf(")",n.length-t)-n.length)&&(e[0]=e[0].slice(0,t),e[2]=n.slice(0,t)),e.slice(0,3))}},filter:{TAG:function(e){var t=e.replace(te,ne).toLowerCase();return"*"===e?function(){return!0}:function(e){return e.nodeName&&e.nodeName.toLowerCase()===t}},CLASS:function(e){var t=m[e+" "];return t||(t=new RegExp("(^|"+M+")"+e+"("+M+"|$)"))&&m(e,function(e){return t.test("string"==typeof e.className&&e.className||"undefined"!=typeof e.getAttribute&&e.getAttribute("class")||"")})},ATTR:function(n,r,i){return function(e){var t=se.attr(e,n);return null==t?"!="===r:!r||(t+="","="===r?t===i:"!="===r?t!==i:"^="===r?i&&0===t.indexOf(i):"*="===r?i&&-1:\x20\t\r\n\f]*)[\x20\t\r\n\f]*\/?>(?:<\/\1>|)$/i;function D(e,n,r){return m(n)?S.grep(e,function(e,t){return!!n.call(e,t,e)!==r}):n.nodeType?S.grep(e,function(e){return e===n!==r}):"string"!=typeof n?S.grep(e,function(e){return-1)[^>]*|#([\w-]+))$/;(S.fn.init=function(e,t,n){var r,i;if(!e)return this;if(n=n||j,"string"==typeof e){if(!(r="<"===e[0]&&">"===e[e.length-1]&&3<=e.length?[null,e,null]:q.exec(e))||!r[1]&&t)return!t||t.jquery?(t||n).find(e):this.constructor(t).find(e);if(r[1]){if(t=t instanceof S?t[0]:t,S.merge(this,S.parseHTML(r[1],t&&t.nodeType?t.ownerDocument||t:E,!0)),N.test(r[1])&&S.isPlainObject(t))for(r in t)m(this[r])?this[r](t[r]):this.attr(r,t[r]);return this}return(i=E.getElementById(r[2]))&&(this[0]=i,this.length=1),this}return e.nodeType?(this[0]=e,this.length=1,this):m(e)?void 0!==n.ready?n.ready(e):e(S):S.makeArray(e,this)}).prototype=S.fn,j=S(E);var L=/^(?:parents|prev(?:Until|All))/,H={children:!0,contents:!0,next:!0,prev:!0};function O(e,t){while((e=e[t])&&1!==e.nodeType);return e}S.fn.extend({has:function(e){var t=S(e,this),n=t.length;return this.filter(function(){for(var e=0;e\x20\t\r\n\f]*)/i,he=/^$|^module$|\/(?:java|ecma)script/i;ce=E.createDocumentFragment().appendChild(E.createElement("div")),(fe=E.createElement("input")).setAttribute("type","radio"),fe.setAttribute("checked","checked"),fe.setAttribute("name","t"),ce.appendChild(fe),y.checkClone=ce.cloneNode(!0).cloneNode(!0).lastChild.checked,ce.innerHTML="",y.noCloneChecked=!!ce.cloneNode(!0).lastChild.defaultValue,ce.innerHTML="",y.option=!!ce.lastChild;var ge={thead:[1,"","
"],col:[2,"","
"],tr:[2,"","
"],td:[3,"","
"],_default:[0,"",""]};function ve(e,t){var n;return n="undefined"!=typeof e.getElementsByTagName?e.getElementsByTagName(t||"*"):"undefined"!=typeof e.querySelectorAll?e.querySelectorAll(t||"*"):[],void 0===t||t&&A(e,t)?S.merge([e],n):n}function ye(e,t){for(var n=0,r=e.length;n",""]);var me=/<|&#?\w+;/;function xe(e,t,n,r,i){for(var o,a,s,u,l,c,f=t.createDocumentFragment(),p=[],d=0,h=e.length;d\s*$/g;function qe(e,t){return A(e,"table")&&A(11!==t.nodeType?t:t.firstChild,"tr")&&S(e).children("tbody")[0]||e}function Le(e){return e.type=(null!==e.getAttribute("type"))+"/"+e.type,e}function He(e){return"true/"===(e.type||"").slice(0,5)?e.type=e.type.slice(5):e.removeAttribute("type"),e}function Oe(e,t){var n,r,i,o,a,s;if(1===t.nodeType){if(Y.hasData(e)&&(s=Y.get(e).events))for(i in Y.remove(t,"handle events"),s)for(n=0,r=s[i].length;n").attr(n.scriptAttrs||{}).prop({charset:n.scriptCharset,src:n.url}).on("load error",i=function(e){r.remove(),i=null,e&&t("error"===e.type?404:200,e.type)}),E.head.appendChild(r[0])},abort:function(){i&&i()}}});var Ut,Xt=[],Vt=/(=)\?(?=&|$)|\?\?/;S.ajaxSetup({jsonp:"callback",jsonpCallback:function(){var e=Xt.pop()||S.expando+"_"+Ct.guid++;return this[e]=!0,e}}),S.ajaxPrefilter("json jsonp",function(e,t,n){var r,i,o,a=!1!==e.jsonp&&(Vt.test(e.url)?"url":"string"==typeof e.data&&0===(e.contentType||"").indexOf("application/x-www-form-urlencoded")&&Vt.test(e.data)&&"data");if(a||"jsonp"===e.dataTypes[0])return r=e.jsonpCallback=m(e.jsonpCallback)?e.jsonpCallback():e.jsonpCallback,a?e[a]=e[a].replace(Vt,"$1"+r):!1!==e.jsonp&&(e.url+=(Et.test(e.url)?"&":"?")+e.jsonp+"="+r),e.converters["script json"]=function(){return o||S.error(r+" was not called"),o[0]},e.dataTypes[0]="json",i=C[r],C[r]=function(){o=arguments},n.always(function(){void 0===i?S(C).removeProp(r):C[r]=i,e[r]&&(e.jsonpCallback=t.jsonpCallback,Xt.push(r)),o&&m(i)&&i(o[0]),o=i=void 0}),"script"}),y.createHTMLDocument=((Ut=E.implementation.createHTMLDocument("").body).innerHTML="
",2===Ut.childNodes.length),S.parseHTML=function(e,t,n){return"string"!=typeof e?[]:("boolean"==typeof t&&(n=t,t=!1),t||(y.createHTMLDocument?((r=(t=E.implementation.createHTMLDocument("")).createElement("base")).href=E.location.href,t.head.appendChild(r)):t=E),o=!n&&[],(i=N.exec(e))?[t.createElement(i[1])]:(i=xe([e],t,o),o&&o.length&&S(o).remove(),S.merge([],i.childNodes)));var r,i,o},S.fn.load=function(e,t,n){var r,i,o,a=this,s=e.indexOf(" ");return-1").append(S.parseHTML(e)).find(r):e)}).always(n&&function(e,t){a.each(function(){n.apply(this,o||[e.responseText,t,e])})}),this},S.expr.pseudos.animated=function(t){return S.grep(S.timers,function(e){return t===e.elem}).length},S.offset={setOffset:function(e,t,n){var r,i,o,a,s,u,l=S.css(e,"position"),c=S(e),f={};"static"===l&&(e.style.position="relative"),s=c.offset(),o=S.css(e,"top"),u=S.css(e,"left"),("absolute"===l||"fixed"===l)&&-1<(o+u).indexOf("auto")?(a=(r=c.position()).top,i=r.left):(a=parseFloat(o)||0,i=parseFloat(u)||0),m(t)&&(t=t.call(e,n,S.extend({},s))),null!=t.top&&(f.top=t.top-s.top+a),null!=t.left&&(f.left=t.left-s.left+i),"using"in t?t.using.call(e,f):("number"==typeof f.top&&(f.top+="px"),"number"==typeof f.left&&(f.left+="px"),c.css(f))}},S.fn.extend({offset:function(t){if(arguments.length)return void 0===t?this:this.each(function(e){S.offset.setOffset(this,t,e)});var e,n,r=this[0];return r?r.getClientRects().length?(e=r.getBoundingClientRect(),n=r.ownerDocument.defaultView,{top:e.top+n.pageYOffset,left:e.left+n.pageXOffset}):{top:0,left:0}:void 0},position:function(){if(this[0]){var e,t,n,r=this[0],i={top:0,left:0};if("fixed"===S.css(r,"position"))t=r.getBoundingClientRect();else{t=this.offset(),n=r.ownerDocument,e=r.offsetParent||n.documentElement;while(e&&(e===n.body||e===n.documentElement)&&"static"===S.css(e,"position"))e=e.parentNode;e&&e!==r&&1===e.nodeType&&((i=S(e).offset()).top+=S.css(e,"borderTopWidth",!0),i.left+=S.css(e,"borderLeftWidth",!0))}return{top:t.top-i.top-S.css(r,"marginTop",!0),left:t.left-i.left-S.css(r,"marginLeft",!0)}}},offsetParent:function(){return this.map(function(){var e=this.offsetParent;while(e&&"static"===S.css(e,"position"))e=e.offsetParent;return e||re})}}),S.each({scrollLeft:"pageXOffset",scrollTop:"pageYOffset"},function(t,i){var o="pageYOffset"===i;S.fn[t]=function(e){return $(this,function(e,t,n){var r;if(x(e)?r=e:9===e.nodeType&&(r=e.defaultView),void 0===n)return r?r[i]:e[t];r?r.scrollTo(o?r.pageXOffset:n,o?n:r.pageYOffset):e[t]=n},t,e,arguments.length)}}),S.each(["top","left"],function(e,n){S.cssHooks[n]=$e(y.pixelPosition,function(e,t){if(t)return t=Be(e,n),Me.test(t)?S(e).position()[n]+"px":t})}),S.each({Height:"height",Width:"width"},function(a,s){S.each({padding:"inner"+a,content:s,"":"outer"+a},function(r,o){S.fn[o]=function(e,t){var n=arguments.length&&(r||"boolean"!=typeof e),i=r||(!0===e||!0===t?"margin":"border");return $(this,function(e,t,n){var r;return x(e)?0===o.indexOf("outer")?e["inner"+a]:e.document.documentElement["client"+a]:9===e.nodeType?(r=e.documentElement,Math.max(e.body["scroll"+a],r["scroll"+a],e.body["offset"+a],r["offset"+a],r["client"+a])):void 0===n?S.css(e,t,i):S.style(e,t,n,i)},s,n?e:void 0,n)}})}),S.each(["ajaxStart","ajaxStop","ajaxComplete","ajaxError","ajaxSuccess","ajaxSend"],function(e,t){S.fn[t]=function(e){return this.on(t,e)}}),S.fn.extend({bind:function(e,t,n){return this.on(e,null,t,n)},unbind:function(e,t){return this.off(e,null,t)},delegate:function(e,t,n,r){return this.on(t,e,n,r)},undelegate:function(e,t,n){return 1===arguments.length?this.off(e,"**"):this.off(t,e||"**",n)},hover:function(e,t){return this.mouseenter(e).mouseleave(t||e)}}),S.each("blur focus focusin focusout resize scroll click dblclick mousedown mouseup mousemove mouseover mouseout mouseenter mouseleave change select submit keydown keypress keyup contextmenu".split(" "),function(e,n){S.fn[n]=function(e,t){return 0= x.length + ) { + email.classList.remove("hidden"); + email.innerHTML = "Email is not in a valid format"; + return false; + } else { + email.classList.add("hidden"); + return true; + } +} + +function valimage() { + var img = document.getElementById("file"); + var modal = document.getElementById("modal"); + if (img.value.length === 0) { + modal.classList.remove("hidden"); + } else { + modal.classList.add("hidden"); + } +} + +function valpassword() { + var pass = document.getElementById("pass").value; + var passcfm = document.getElementById("passcfm").value; + var errormsg = document.getElementById("passerror"); + + if (pass != passcfm) { + errormsg.classList.remove("hidden"); + errormsg.innerHTML = "Passwords do not match"; + return false; + } else { + errormsg.classList.add("hidden"); + return true; + } +} \ No newline at end of file diff --git a/scripts/luxon.adapter.js b/scripts/luxon.adapter.js new file mode 100644 index 0000000..7c893a4 --- /dev/null +++ b/scripts/luxon.adapter.js @@ -0,0 +1,7 @@ +/*! + * chartjs-adapter-luxon v0.2.1 + * https://www.chartjs.org + * (c) 2020 Chart.js Contributors + * Released under the MIT license + */ +!function(e,t){"object"==typeof exports&&"undefined"!=typeof module?t(require("chart.js"),require("luxon")):"function"==typeof define&&define.amd?define(["chart.js","luxon"],t):t((e=e||self).Chart,e.luxon)}(this,(function(e,t){"use strict";e=e&&Object.prototype.hasOwnProperty.call(e,"default")?e.default:e;const r={datetime:t.DateTime.DATETIME_MED_WITH_SECONDS,millisecond:"h:mm:ss.SSS a",second:t.DateTime.TIME_WITH_SECONDS,minute:t.DateTime.TIME_SIMPLE,hour:{hour:"numeric"},day:{day:"numeric",month:"short"},week:"DD",month:{month:"short",year:"numeric"},quarter:"'Q'q - yyyy",year:{year:"numeric"}};e._adapters._date.override({_id:"luxon",_create:function(e){return t.DateTime.fromMillis(e,this.options)},formats:function(){return r},parse:function(r,n){const i=this.options;if(e.helpers.isNullOrUndef(r))return null;const o=typeof r;return"number"===o?r=this._create(r):"string"===o?r="string"==typeof n?t.DateTime.fromFormat(r,n,i):t.DateTime.fromISO(r,i):"object"!==o||r instanceof t.DateTime?r instanceof Date&&(r=t.DateTime.fromJSDate(r,i)):r=t.DateTime.fromObject(r),r.isValid?r.valueOf():null},format:function(e,t){const r=this._create(e);return"string"==typeof t?r.toFormat(t,this.options):r.toLocaleString(t)},add:function(e,t,r){const n={};return n[r]=t,this._create(e).plus(n).valueOf()},diff:function(e,t,r){return this._create(e).diff(this._create(t)).as(r).valueOf()},startOf:function(e,t,r){return"isoWeek"===t?this._create(e).isoWeekday(r).valueOf():t?this._create(e).startOf(t).valueOf():e},endOf:function(e,t){return this._create(e).endOf(t).valueOf()}})})); diff --git a/scripts/luxon.min.js b/scripts/luxon.min.js new file mode 100644 index 0000000..59df19e --- /dev/null +++ b/scripts/luxon.min.js @@ -0,0 +1 @@ +var luxon=function(e){"use strict";function r(e,t){for(var n=0;ne.length)&&(t=e.length);for(var n=0,r=new Array(t);n=e.length?{done:!0}:{done:!1,value:e[t++]}};throw new TypeError("Invalid attempt to iterate non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method.")}var n=function(e){function t(){return e.apply(this,arguments)||this}return a(t,e),t}(t(Error)),l=function(t){function e(e){return t.call(this,"Invalid DateTime: "+e.toMessage())||this}return a(e,t),e}(n),d=function(t){function e(e){return t.call(this,"Invalid Interval: "+e.toMessage())||this}return a(e,t),e}(n),f=function(t){function e(e){return t.call(this,"Invalid Duration: "+e.toMessage())||this}return a(e,t),e}(n),L=function(e){function t(){return e.apply(this,arguments)||this}return a(t,e),t}(n),h=function(t){function e(e){return t.call(this,"Invalid unit "+e)||this}return a(e,t),e}(n),m=function(e){function t(){return e.apply(this,arguments)||this}return a(t,e),t}(n),y=function(e){function t(){return e.call(this,"Zone is an abstract class")||this}return a(t,e),t}(n),v="numeric",g="short",p="long",w={year:v,month:v,day:v},k={year:v,month:g,day:v},b={year:v,month:g,day:v,weekday:g},O={year:v,month:p,day:v},S={year:v,month:p,day:v,weekday:p},T={hour:v,minute:v},M={hour:v,minute:v,second:v},N={hour:v,minute:v,second:v,timeZoneName:g},D={hour:v,minute:v,second:v,timeZoneName:p},E={hour:v,minute:v,hour12:!1},x={hour:v,minute:v,second:v,hour12:!1},C={hour:v,minute:v,second:v,hour12:!1,timeZoneName:g},F={hour:v,minute:v,second:v,hour12:!1,timeZoneName:p},Z={year:v,month:v,day:v,hour:v,minute:v},j={year:v,month:v,day:v,hour:v,minute:v,second:v},A={year:v,month:g,day:v,hour:v,minute:v},z={year:v,month:g,day:v,hour:v,minute:v,second:v},_={year:v,month:g,day:v,weekday:g,hour:v,minute:v},q={year:v,month:p,day:v,hour:v,minute:v,timeZoneName:g},H={year:v,month:p,day:v,hour:v,minute:v,second:v,timeZoneName:g},U={year:v,month:p,day:v,weekday:p,hour:v,minute:v,timeZoneName:p},R={year:v,month:p,day:v,weekday:p,hour:v,minute:v,second:v,timeZoneName:p};function W(e){return void 0===e}function P(e){return"number"==typeof e}function J(e){return"number"==typeof e&&e%1==0}function I(){try{return"undefined"!=typeof Intl&&Intl.DateTimeFormat}catch(e){return!1}}function Y(){return!W(Intl.DateTimeFormat.prototype.formatToParts)}function G(){try{return"undefined"!=typeof Intl&&!!Intl.RelativeTimeFormat}catch(e){return!1}}function $(e,r,i){if(0!==e.length)return e.reduce(function(e,t){var n=[r(t),t];return e&&i(e[0],n[0])===e[0]?e:n},null)[1]}function B(n,e){return e.reduce(function(e,t){return e[t]=n[t],e},{})}function Q(e,t){return Object.prototype.hasOwnProperty.call(e,t)}function K(e,t,n){return J(e)&&t<=e&&e<=n}function X(e,t){return void 0===t&&(t=2),e.toString().lengthtn.indexOf(c)&&an(this.matrix,u,h,a,c)}else P(u[c])&&(o[c]=u[c])}for(var m in o)0!==o[m]&&(a[r]+=m===r?o[m]:o[m]/this.matrix[r][m]);return rn(this,{values:a},!0).normalize()},e.negate=function(){if(!this.isValid)return this;for(var e={},t=0,n=Object.keys(this.values);te},e.isBefore=function(e){return!!this.isValid&&this.e<=e},e.contains=function(e){return!!this.isValid&&(this.s<=e&&this.e>e)},e.set=function(e){var t=void 0===e?{}:e,n=t.start,r=t.end;return this.isValid?f.fromDateTimes(n||this.s,r||this.e):this},e.splitAt=function(){var t=this;if(!this.isValid)return[];for(var e=arguments.length,n=new Array(e),r=0;r+this.e?this.e:s;a.push(f.fromDateTimes(o,c)),o=c,u+=1}return a},e.splitBy=function(e){var t=un(e);if(!this.isValid||!t.isValid||0===t.as("milliseconds"))return[];for(var n,r,i=this.s,a=[];i+this.e?this.e:n,a.push(f.fromDateTimes(i,r)),i=r;return a},e.divideEqually=function(e){return this.isValid?this.splitBy(this.length()/e).slice(0,e):[]},e.overlaps=function(e){return this.e>e.s&&this.s=e.e)},e.equals=function(e){return!(!this.isValid||!e.isValid)&&(this.s.equals(e.s)&&this.e.equals(e.e))},e.intersection=function(e){if(!this.isValid)return this;var t=this.s>e.s?this.s:e.s,n=this.ee.e?this.e:e.e;return f.fromDateTimes(t,n)},f.merge=function(e){var t=e.sort(function(e,t){return e.s-t.s}).reduce(function(e,t){var n=e[0],r=e[1];return r?r.overlaps(t)||r.abutsStart(t)?[n,r.union(t)]:[n.concat([r]),t]:[n,t]},[[],null]),n=t[0],r=t[1];return r&&n.push(r),n},f.xor=function(e){for(var t,n,r=null,i=0,a=[],o=e.map(function(e){return[{time:e.s,type:"s"},{time:e.e,type:"e"}]}),u=V((t=Array.prototype).concat.apply(t,o).sort(function(e,t){return e.time-t.time}));!(n=u()).done;){var s=n.value;r=1===(i+="s"===s.type?1:-1)?s.time:(r&&+r!=+s.time&&a.push(f.fromDateTimes(r,s.time)),null)}return f.merge(a)},e.difference=function(){for(var t=this,e=arguments.length,n=new Array(e),r=0;rue(n)?(t=n+1,u=1):t=n,Object.assign({weekYear:t,weekNumber:u,weekday:o},me(e))}function zn(e){var t,n=e.weekYear,r=e.weekNumber,i=e.weekday,a=Fn(n,1,4),o=ie(n),u=7*r+i-a-3;u<1?u+=ie(t=n-1):othis.valueOf(),u=dn(o?this:e,o?e:this,a,i);return o?u.negate():u},e.diffNow=function(e,t){return void 0===e&&(e="milliseconds"),void 0===t&&(t={}),this.diff(I.local(),e,t)},e.until=function(e){return this.isValid?cn.fromDateTimes(this,e):this},e.hasSame=function(e,t){if(!this.isValid)return!1;if("millisecond"===t)return this.valueOf()===e.valueOf();var n=e.valueOf();return this.startOf(t)<=n&&n<=this.endOf(t)},e.equals=function(e){return this.isValid&&e.isValid&&this.valueOf()===e.valueOf()&&this.zone.equals(e.zone)&&this.loc.equals(e.loc)},e.toRelative=function(e){if(void 0===e&&(e={}),!this.isValid)return null;var t=e.base||I.fromObject({zone:this.zone}),n=e.padding?thisthis.set({month:1}).offset||this.offset>this.set({month:5}).offset)}},{key:"isInLeapYear",get:function(){return re(this.year)}},{key:"daysInMonth",get:function(){return ae(this.year,this.month)}},{key:"daysInYear",get:function(){return this.isValid?ie(this.year):NaN}},{key:"weeksInWeekYear",get:function(){return this.isValid?ue(this.weekYear):NaN}}],[{key:"DATE_SHORT",get:function(){return w}},{key:"DATE_MED",get:function(){return k}},{key:"DATE_MED_WITH_WEEKDAY",get:function(){return b}},{key:"DATE_FULL",get:function(){return O}},{key:"DATE_HUGE",get:function(){return S}},{key:"TIME_SIMPLE",get:function(){return T}},{key:"TIME_WITH_SECONDS",get:function(){return M}},{key:"TIME_WITH_SHORT_OFFSET",get:function(){return N}},{key:"TIME_WITH_LONG_OFFSET",get:function(){return D}},{key:"TIME_24_SIMPLE",get:function(){return E}},{key:"TIME_24_WITH_SECONDS",get:function(){return x}},{key:"TIME_24_WITH_SHORT_OFFSET",get:function(){return C}},{key:"TIME_24_WITH_LONG_OFFSET",get:function(){return F}},{key:"DATETIME_SHORT",get:function(){return Z}},{key:"DATETIME_SHORT_WITH_SECONDS",get:function(){return j}},{key:"DATETIME_MED",get:function(){return A}},{key:"DATETIME_MED_WITH_SECONDS",get:function(){return z}},{key:"DATETIME_MED_WITH_WEEKDAY",get:function(){return _}},{key:"DATETIME_FULL",get:function(){return q}},{key:"DATETIME_FULL_WITH_SECONDS",get:function(){return H}},{key:"DATETIME_HUGE",get:function(){return U}},{key:"DATETIME_HUGE_WITH_SECONDS",get:function(){return R}}]),I}();function lr(e){if(cr.isDateTime(e))return e;if(e&&e.valueOf&&P(e.valueOf()))return cr.fromJSDate(e);if(e&&"object"==typeof e)return cr.fromObject(e);throw new m("Unknown datetime argument: "+e+", of type "+typeof e)}return e.DateTime=cr,e.Duration=on,e.FixedOffsetZone=Re,e.IANAZone=He,e.Info=ln,e.Interval=cn,e.InvalidZone=We,e.LocalZone=je,e.Settings=Ke,e.Zone=Fe,e}({}); \ No newline at end of file diff --git a/scripts/main.js b/scripts/main.js new file mode 100644 index 0000000..5e19452 --- /dev/null +++ b/scripts/main.js @@ -0,0 +1,7 @@ +const DismissModal = (index = 0) => { + document.getElementsByClassName("modal")[index].classList.add("hidden"); +}; + +const ShowModal = (index = 0) => { + document.getElementsByClassName("modal")[index].classList.remove("hidden"); +}; \ No newline at end of file diff --git a/scripts/pagination.js b/scripts/pagination.js new file mode 100644 index 0000000..04c47e9 --- /dev/null +++ b/scripts/pagination.js @@ -0,0 +1,52 @@ +const paginationLinks = document.querySelector(".pagination-links"); +const paginations = Array.from(paginationLinks.children); +const leftNav = paginations[0]; +const rightNav = paginations[paginations.length - 1]; + +const initializePagination = () => { + const urlParams = new URLSearchParams(window.location.search); + if (urlParams.has("page")) { + if (parseInt(urlParams.get("page"))) { + paginations[urlParams.get("page")].classList.add("active"); + } else { + urlParams.set("page", 1); + window.location.search = urlParams.toString(); + } + } else { + paginations[1].classList.add("active"); + } +}; + +initializePagination(); + +leftNav.addEventListener("click", () => { + const urlParams = new URLSearchParams(window.location.search); + if (urlParams.has("page")) { + if (parseInt(urlParams.get("page")) > 1) { + urlParams.set("page", parseInt(urlParams.get("page")) - 1); + window.location.search = urlParams.toString(); + } + } +}); + +rightNav.addEventListener("click", () => { + const urlParams = new URLSearchParams(window.location.search); + if (urlParams.has("page")) { + if (parseInt(urlParams.get("page")) < paginations.length - 2) { + urlParams.set("page", parseInt(urlParams.get("page")) + 1); + window.location.search = urlParams.toString(); + } + } else { + urlParams.set("page", 2); + window.location.search = urlParams.toString(); + } +}); + +paginationLinks.addEventListener("click", (e) => { + if (e.target != leftNav && e.target != rightNav && paginations.includes(e.target)) { + console.log(e.target.dataset.page); + const urlParams = new URLSearchParams(window.location.search); + urlParams.set("page", e.target.dataset.page); + window.location.search = urlParams.toString(); + } +}); \ No newline at end of file diff --git a/scripts/purchase_history_page.js b/scripts/purchase_history_page.js new file mode 100644 index 0000000..78d481b --- /dev/null +++ b/scripts/purchase_history_page.js @@ -0,0 +1,14 @@ +const ShowReportModal = (productid) => { + ShowModal(); + $("#form-report-productid").val(productid); +} + +const SendReportData = () => { + $.post("report.php", $("#form-report").serialize(), () => { + DismissModal(); + ShowModal(1); + }).fail(() => { + DismissModal(); + ShowModal(2); + }); +} \ No newline at end of file diff --git a/scripts/search_filter.js b/scripts/search_filter.js new file mode 100644 index 0000000..de49583 --- /dev/null +++ b/scripts/search_filter.js @@ -0,0 +1,75 @@ + + +// Restricts input for the given textbox to the given inputFilter function. +// https://stackoverflow.com/questions/469357/html-text-input-allow-only-numeric-input +function setInputFilter(textbox, inputFilter) { + [ + "input", + "keydown", + "keyup", + "mousedown", + "mouseup", + "select", + "contextmenu", + "drop" + ].forEach(function (event) { + textbox.addEventListener(event, function () { + if (inputFilter(this.value)) { + this.oldValue = this.value; + this.oldSelectionStart = this.selectionStart; + this.oldSelectionEnd = this.selectionEnd; + } else if (this.hasOwnProperty("oldValue")) { + this.value = this.oldValue; + this.setSelectionRange(this.oldSelectionStart, this.oldSelectionEnd); + } else { + this.value = ""; + } + }); + }); +} + +const minPriceElement = document.getElementById("min-price"); +const maxPriceElement = document.getElementById("max-price"); + +setInputFilter(minPriceElement, function (value) { + return /^\d*\.?\d*$/.test(value); // Allow digits and '.' only, using a RegExp +}); +setInputFilter(maxPriceElement, function (value) { + return /^\d*\.?\d*$/.test(value); // Allow digits and '.' only, using a RegExp +}); + +const FilterPrice = () => { + const urlParams = new URLSearchParams(window.location.search); + urlParams.set("min", minPriceElement.value); + urlParams.set("max", maxPriceElement.value); + window.location.search = urlParams.toString(); +} + +const FilterSearchStar = (star) => { + const urlParams = new URLSearchParams(window.location.search); + urlParams.set("stars", star); + window.location.search = urlParams.toString(); +} + +const SortSearch = () => { + var attribute = document.getElementById("sort-options").value; + const urlParams = new URLSearchParams(window.location.search); + urlParams.set("sortby", attribute); + window.location.search = urlParams.toString(); +} + +const ChangeSortDirection = () => { + var attribute = document.getElementById("sort-options").value; + const urlParams = new URLSearchParams(window.location.search); + urlParams.set("sortby", attribute); + if (!urlParams.has("sort")) { + urlParams.set("sort", "ASC"); + } else { + if (urlParams.get("sort") == "DESC") { + urlParams.set("sort", "ASC"); + } else { + urlParams.set("sort", "DESC"); + } + } + window.location.search = urlParams.toString(); +} \ No newline at end of file diff --git a/scripts/shopping_cart.js b/scripts/shopping_cart.js new file mode 100644 index 0000000..8f46e3e --- /dev/null +++ b/scripts/shopping_cart.js @@ -0,0 +1,27 @@ +const RemoveFromCart = (itemId, inCart = false) => { + // do some jquery shet here + + $.get("shopping_cart.php", { productId: itemId, action: "remove" }, (data, textStatus, jqXHR) => { + if (inCart) { + location.reload(); + } else { + $("#shopping-cart-popup").load("get_cart.php"); + $(".cart-notification").text(parseInt($(".cart-notification").text()) - 1); + } + }) +} + +const AddToCart = (itemId) => { + // do some jquery shet here + const quantity = parseInt(document.querySelector("#product-quantity").textContent); + + $.get("shopping_cart.php", { productId: itemId, quantity: quantity, action: "add" }, (data, textStatus, jqXHR) => { + $("#modal-text").text("Item has been added to cart!"); + ShowModal(); + $("#shopping-cart-popup").load("get_cart.php"); + $(".cart-notification").text(parseInt($(".cart-notification").text()) + 1); + }).fail(() => { + $("#modal-text").text("Item failed to be added to cart!"); + ShowModal(); + }) +}; \ No newline at end of file diff --git a/scripts/store_page.js b/scripts/store_page.js new file mode 100644 index 0000000..db44866 --- /dev/null +++ b/scripts/store_page.js @@ -0,0 +1,36 @@ +const searchBar = document.getElementById("search-bar"); +const searchBarButton = document.getElementById("search-bar-button"); +const shoppingCart = document.querySelector(".top-bar-shopping-cart"); +const shoppingCartPopup = document.querySelector(".shopping-cart-popup"); +const username = document.querySelector(".top-bar-username"); +const usernamePopup = document.querySelector(".top-bar-username-dropdown"); + +searchBar.addEventListener("keyup", (event) => { + if (event.keyCode === 13) { + // Cancel the default action, if needed + event.preventDefault(); + // Trigger the button element with a click + searchBarButton.click(); + } +}); + +const Search = () => { + var q = document.getElementById("search-bar").value.trim(); + window.location.href = encodeURI("search.php?q=" + q); +} + +shoppingCart.addEventListener("mouseenter", (e) => { + shoppingCartPopup.classList.remove("invisible"); +}) + +shoppingCart.addEventListener("mouseleave", (e) => { + shoppingCartPopup.classList.add("invisible"); +}) + +username.addEventListener("mouseenter", (e) => { + usernamePopup.classList.remove("invisible"); +}) + +username.addEventListener("mouseleave", (e) => { + usernamePopup.classList.add("invisible"); +}) \ No newline at end of file diff --git a/scripts/user_profile_page.js b/scripts/user_profile_page.js new file mode 100644 index 0000000..9474ab0 --- /dev/null +++ b/scripts/user_profile_page.js @@ -0,0 +1,137 @@ +const HandleHash = () => { + var anchors = Array.from(document.querySelectorAll("a.profile-menu-item")); + anchors.forEach((element) => { + if (element.getAttribute("href") == location.hash) { + element.classList.add("active"); + } else { + element.classList.remove("active"); + } + }); +}; + +$(function () { + const LoadImage = (e) => { + if (e.files && e.files[0]) { + var reader = new FileReader(); + reader.onload = function (e) { + $('#userImage').attr('src', e.target.result); + }; + reader.readAsDataURL(e.files[0]); + } + } + + // change password validation + const $oldPassword = $("#password"); + const $newPassword = $("#newpassword"); + const $changePasswordBtn = $("#password-submit"); + var $validOldPassword = false; + var $validNewPassword = false; + + $oldPassword.on("change paste keyup", e => { + passwordCheckValid(); + }); + $newPassword.on("change paste keyup", e => { + passwordCheckValid(); + }); + + const passwordCheckValid = () => { + if (!$oldPassword.val()) { + $validOldPassword = false; + $oldPassword.addClass("invalid"); + } else { + $validOldPassword = true; + $oldPassword.removeClass("invalid"); + } + if (!$newPassword.val()) { + $validNewPassword = false; + $newPassword.addClass("invalid"); + } else { + $validNewPassword = true; + $newPassword.removeClass("invalid"); + } + + if ($validNewPassword && $validOldPassword) { + $changePasswordBtn.prop("disabled", false); + } else { + $changePasswordBtn.prop("disabled", true); + } + }; + + // edit profile validation + const $picture = $("#picture"); + const $email = $("#email"); + const $dob = $("#dob"); + const $address = $("#address"); + const $gender = $("#gender"); + const $profileSaveBtn = $("#profile-submit"); + var $validImage = true; + var $validEmail = false; + var $validDob = false; + var $validAddress = false; + var $validGender = false; + + $("#picture").change((e) => { + LoadImage(e.target); + checkValid(); + }); + $email.on("change paste keyup", e => { + checkValid(); + }); + $gender.on("change paste keyup", e => { + checkValid(); + }); + $dob.on("change paste keyup", e => { + checkValid(); + }); + $address.on("change paste keyup", e => { + checkValid(); + }); + const checkValid = () => { + if ($picture[0].files) { + $validImage = true; + } else { + $validImage = false; + } + + if (!$address.val()) { + $address.addClass("invalid"); + $validAddress = false; + } else { + $address.removeClass("invalid"); + $validAddress = true; + } + + const $d1 = new Date($dob.val()); + const $d2 = new Date(); + if ($d1 > $d2) { + $dob.addClass("invalid"); + $validDob = false; + } else { + $dob.removeClass("invalid"); + $validDob = true; + } + + if (!$gender.val()) { + $gender.addClass("invalid"); + $validGender = false; + } else { + $gender.removeClass("invalid"); + $validGender = true; + } + + if (!/^[a-zA-Z0-9.!#$%&’*+/=?^_`{|}~-]+@[a-zA-Z0-9-]+(?:\.[a-zA-Z0-9-]+)*$/i.test($email.val())) { + $email.addClass("invalid"); + $validEmail = false; + } else { + $email.removeClass("invalid"); + $validEmail = true; + } + + if ($validEmail && $validDob && $validAddress && $validGender && $validImage) { + $profileSaveBtn.prop("disabled", false); + } else { + $profileSaveBtn.prop("disabled", true); + } + } +}); + diff --git a/scripts/vendor_conversation_page.js b/scripts/vendor_conversation_page.js new file mode 100644 index 0000000..8815595 --- /dev/null +++ b/scripts/vendor_conversation_page.js @@ -0,0 +1,24 @@ + +var $inputValid = false; +const ValidateInput = () => { + if (!$('#conversationMessage').val() || + ($('#conversationMessage').val().length > 1024) || + ($('#conversationMessage').val().length < 5)) { + $('#conversationMessage').addClass('invalid'); + $inputValid = false; + } else { + $('#conversationMessage').removeClass('invalid'); + $inputValid = true; + } +} + +$('#conversationMessage').on('change paste keyup', e => { + ValidateInput(); +}) + +const SubmitMessage = (e) => { + ValidateInput(); + if ($inputValid) { + $('#messageform').submit(); + } +} \ No newline at end of file diff --git a/scripts/vendor_page.js b/scripts/vendor_page.js new file mode 100644 index 0000000..7deb93f --- /dev/null +++ b/scripts/vendor_page.js @@ -0,0 +1,34 @@ +const vendorItem = document.querySelectorAll(".vendor-sidenav-item"); +const x = window.location.href.split("/"); +const pageHref = x[x.length - 1]; + +vendorItem.forEach(element => { + if (element.getAttribute("href") == pageHref) { + element.classList.add("active"); + } +}); + +const filterProducts = (name) => { + const productCards = document.querySelectorAll(".item-card"); + const headers = document.querySelectorAll(".header-group, .approvalimgs .cultured-dark, br"); + console.log(name); + if (name) { + headers.forEach(e => { + e.classList.add('hidden'); + }); + productCards.forEach(e => { + if (!e.querySelector('.item-name').textContent.toLowerCase().includes(name.trim().toLowerCase())) { + e.classList.add('hidden'); + } else { + e.classList.remove('hidden'); + } + }); + } else { + headers.forEach(e => { + e.classList.remove('hidden'); + }); + productCards.forEach(e => { + e.classList.remove('hidden'); + }); + } +} \ No newline at end of file diff --git a/scripts/yeap.js b/scripts/yeap.js new file mode 100644 index 0000000..83f2f61 --- /dev/null +++ b/scripts/yeap.js @@ -0,0 +1,66 @@ +function displayprofile(event) { + var image = document.getElementById("output"); + image.src = URL.createObjectURL(event.target.files[0]); +} + +function displayproduct(event, id) { + console.log(id); + var image = document.getElementById(id.toString()); + image.src = URL.createObjectURL(event.target.files[0]); +} + +function validateForm(){ + var shopname = document.forms["vendorprof"]["shopname"].value; + var vendadd = document.forms["vendorprof"]["vendadd"].value; + + var x=document.vendorprof.vendem.value; + var atposition=x.indexOf("@"); + var dotposition=x.lastIndexOf("."); + + var firstpassword=document.vendorprof.pass.value; + var secondpassword=document.vendorprof.confpass.value; + + if (atposition<1 || dotposition=x.length){ + alert("Please make sure your Email has @(At) and .(Period)"); + return false; + } + + if(shopname.length<=3){ + alert("Please make sure your Shopname has more than 3 characters"); + return false; + } + + if(vendadd.length<=20){ + alert("Please make sure your Address has more than 20 characters"); + return false; + } + + if(firstpassword==secondpassword){ + return true; + } else{ + alert("Password does not match!"); + return false; + } +} + +function validateProduct(){ + var prodname = document.forms["vendoraddprod"]["prodname"].value; + var proddesc = document.forms["vendoraddprod"]["proddesc"].value; + var prodtag = document.forms["vendoraddprod"]["prodtag"].value; + + if(prodname.length<3){ + alert("Please make sure your Product Name has about 3 characters"); + return false; + } + + if(proddesc.length<=30){ + alert("Please provide sufficient details for Product Description (30 characters)"); + return false; + } + + if(prodtag.length<3){ + alert("Please make sure your Product Tag has about 3 characters"); + return false; + } +} + diff --git a/search.php b/search.php new file mode 100644 index 0000000..2d5d8e1 --- /dev/null +++ b/search.php @@ -0,0 +1,185 @@ += $stars_esc"; + +$minmax_query = ""; +if ($max_esc > 0 && $min_esc > 0) { + $minmax_query = " AND ProductPrice BETWEEN $min_esc AND $max_esc"; +} else if ($max_esc > 0 && $min_esc == 0) { + $minmax_query = " AND ProductPrice <= $max_esc"; +} else if ($max_esc == 0 && $min_esc > 0) { + $minmax_query = " AND ProductPrice >= $min_esc"; +} + +$offset = 0; +$perPage = 25; + +if (isset($_GET["page"])) { + if ($num = intval($_GET["page"])) { + $offset = ($num * $perPage) - $perPage; + } +} + +$limitQuery = " LIMIT $offset, $perPage "; + +$fullQuery = "SELECT * FROM( SELECT t1.*, IFNULL(AVG(review.ReviewRating), 0) AS ProductRating FROM ( SELECT product.*, orderitem.OrderItemId, orderitem.OrderId, orderitem.ProductQuantity, IFNULL( SUM(orderitem.ProductQuantity), 0) AS TotalSold FROM product LEFT JOIN orderitem ON orderitem.ProductId = product.ProductId WHERE ( product.ProductName LIKE '%$q_esc%' OR product.ProductDescription LIKE '%$q_esc%' OR product.ProductTags LIKE '%$q_esc%' ) GROUP BY product.ProductId, orderitem.ProductId ORDER BY `TotalSold` DESC ) t1 LEFT JOIN review ON review.ProductId = t1.ProductId GROUP BY t1.ProductId ORDER BY t1.TotalSold DESC , t1.ProductId ASC ) resultTable WHERE resultTable.ProductStatus = 'Approved' " . $minmax_query . $star_query . $sortby_query . $limitQuery; + +$rows = mysqlidb::fetchAllRows($fullQuery); + +$numrows = mysqlidb::fetchRow("SELECT COUNT(*) as NumRows FROM( SELECT t1.*, IFNULL(AVG(review.ReviewRating), 0) AS ProductRating FROM ( SELECT product.*, orderitem.OrderItemId, orderitem.OrderId, orderitem.ProductQuantity, IFNULL( SUM(orderitem.ProductQuantity), 0) AS TotalSold FROM product LEFT JOIN orderitem ON orderitem.ProductId = product.ProductId WHERE ( product.ProductName LIKE '%$q_esc%' OR product.ProductDescription LIKE '%$q_esc%' OR product.ProductTags LIKE '%$q_esc%' ) GROUP BY product.ProductId, orderitem.ProductId ORDER BY `TotalSold` DESC ) t1 LEFT JOIN review ON review.ProductId = t1.ProductId GROUP BY t1.ProductId ORDER BY t1.TotalSold DESC , t1.ProductId ASC ) resultTable WHERE resultTable.ProductStatus = 'Approved' " . $minmax_query . $star_query . $sortby_query)["NumRows"]; + +?> + + + + + + + + + + Search results for "<?php echo $q_esc ?>" + + + + + + +
+
+ +
+
+
+ info + $q\""; + } else { + echo "Showing all listed products."; + } + ?> +
+
+ Sort By: + + ">sort +
+
+
+
filter_alt Filter Options
+
+
Price Range
+
+ > +
to
+ > +
+
Filter
+
+
+
Rating
+
+
ruby" onclick="FilterSearchStar(5)"> + starstarstarstarstar +
+
ruby" onclick="FilterSearchStar(4)"> + starstarstarstarstar_outline & Up +
+
ruby" onclick="FilterSearchStar(3)"> + starstarstarstar_outlinestar_outline & Up +
+
ruby" onclick="FilterSearchStar(2)"> + starstarstar_outlinestar_outlinestar_outline & Up +
+
ruby" onclick="FilterSearchStar(1)"> + starstar_outlinestar_outlinestar_outlinestar_outline & Up +
+
ruby" onclick="FilterSearchStar(0)"> + star_outlinestar_outlinestar_outlinestar_outlinestar_outline & Up +
+
+
+
+
+ 0) { + foreach ($rows as $row) { + $firstImage = explode(",", $row['ProductImage'])[0]; + echo " + \"\" +
+
+ {$row['ProductName']} +
+
" . number_format($row['TotalSold'], 0) . " sold
+
+
+ RM " . number_format($row['ProductPrice'], 2) . " +
+
+ " . number_format($row['ProductRating'], 1) . "star_rate +
+
+
+
"; + } + } else { + echo '
+ There are no products that match your criteria. +
'; + } + ?> +
+ 1) { + ?> + + +
+ + + + + +
+ + + + + + + \ No newline at end of file diff --git a/sellerfaq.php b/sellerfaq.php new file mode 100644 index 0000000..75372da --- /dev/null +++ b/sellerfaq.php @@ -0,0 +1,64 @@ + + + + + + + + + + + Seller FAQ - Chapalang Clothes + + + + + + + + + + + +
+
+ +
+

Seller FAQ

+
+
+

How do I become a seller?

+

You can be a seller by registering here.

+
+
+

How do I add an item to my shop?

+

You can add items to your shop by clicking on the "Add" navigation link on the sidebar of your seller dashboard. You then have to fill up all the required fields in order to submit your item for approval by an employee. While your item is getting approved, you will see the item in your "Approval" page which can be reached by clicking on the "Approval" navigation link on the sidebar. Once your item has been approved by an employee, your item will be listed on the shop.

+
+
+

How do I notify my customers of their order shipping status?

+

You can notify your customers by navigating to the "Order Info" by clicking on the navigation link on the sidebar. There you can see all the orders placed by customers. To change the shipping status, just select the orders you want to change and click the dropdown below the table to your desired status and click "Edit" to submit your choice.

+
+
+

How do I handle refund requests?

+

When your customers request for refunds, you can choose to fulfil the request by viewing the request ticket by clicking "View Ticket" on the "Refund" page. You can view the item, the reason of refund and any images attached. You can approve or deny the refund and we will handle the rest. However, if you deny the request, the request will be sent to our employees for secondary checking. Once the employee approves the refund, you are obligated to follow up the request, else you don't have to worry about anything.

+
+
+

How do I withdraw my profits from Chapalang Clothing?

+

You can withdraw your revenue from Chapalang Clothing from the "Income" page which can be accessed from the navigation link in the sidebar. After than you can see your revenue in the "Total Balance" area and also a "Withdraw" button. To withdraw just click that button and follow the procedure to withdraw your revenue.

+
+
+
+ + + + + + + + + + + + + + \ No newline at end of file diff --git a/shopping_cart.php b/shopping_cart.php new file mode 100644 index 0000000..4396ed1 --- /dev/null +++ b/shopping_cart.php @@ -0,0 +1,44 @@ + + + + + + + + + + Chapalang Clothes + + + + + + +
+
+ +
+ +
+ + + + + +
+ + + + + + \ No newline at end of file diff --git a/styles/khor.css b/styles/khor.css new file mode 100644 index 0000000..3646802 --- /dev/null +++ b/styles/khor.css @@ -0,0 +1,319 @@ +.visited-ruby:visited { + color: #e0115f; +} + +.visited-white:visited { + color: white; +} + +.pad-sides { + padding: 0 2rem; +} + +.pad-bottom { + padding-bottom: 1rem; +} + +.pad-top { + padding-top: 1rem; +} + +.margin-sides { + margin: 0 1rem; +} + +.margin-y { + margin: 1rem 0; +} + +.wh-auto { + width: auto; + height: auto; +} + +.modal-font { + font-size: large; +} + +.error { + width: 100%; + padding: 0.15rem; + font-size: 75%; + color: white; + background-color: #e01160e5; + border-radius: 0.2rem; +} + +.m-a-0 { + margin: auto 0; +} + +.m-0-a { + margin: 0 auto; +} + +.m-a { + margin: auto; +} + +.card-generic { + margin: 0.5rem; + padding: 1rem 2rem; + background-color: white; + box-shadow: 0 1px 0.1rem rgba(0, 0, 0, 0.15); + border-radius: 5px; +} + +/*------------------------- LOGIN & REGISTER STYLING -------------------------*/ + +.login-logo img { + height: 17rem; + width: auto; +} + +.login-area { + background-color: white; + border-radius: 0.5rem; + height: auto; + width: 45rem; + padding: 2.5rem; +} + +.login-area h1 { + font-size: 2rem; + font-weight: bold; + color: black; + margin: 0; +} + +.login-header { + justify-content: space-between; +} + +.login-pad-top { + padding-top: 15px; +} + +/*------------------------- REFUND APPLICATION STYLING -------------------------*/ + +.refund-product-grid { + display: grid; + grid-template-columns: 350px 3fr; +} + +.refund-product-grid-bottom { + display: grid; + grid-template-columns: 2.8fr 1fr; +} + +.refund-product-grid-bottom textarea { + resize: none; +} + +.refund-product-rows { + display: grid; + grid-template-rows: auto 1fr; +} + +.refund-image { + height: 350px; + width: 100%; +} + +.refund-product-grid-image { + height: 100%; + width: 100%; + object-fit: contain; +} + +.refund-font { + font-size: x-large; + font-weight: bold; +} + +.refund-upload-image { + width: 270px; + height: 270px; + object-fit: contain; +} + +.modal-size { + height: auto; + width: auto; +} + +.modal-center { + display: flex; + flex-direction: column; + justify-content: center; +} + +/*------------------------- EMPLOYEE STYLING -------------------------*/ + +.employee-sidenav { + font-size: large; +} + +.employee-sidenav .material-icons { + position: relative; + top: 0.25rem; +} + +.employee-sidenav-label { + padding: 0.25rem 0; +} + +.employee-sidenav-label a.active { + color: #e01160de; +} + +.employee-wrapper { + display: grid; + grid-template-columns: 1fr 4fr; + padding: 1rem 0; +} + +.employee-table { + border: 1px solid rgba(0, 0, 0, 0.3); + border-radius: 5px; + border-spacing: 0; + text-align: center; + table-layout: fixed; + width: 100%; + height: 0px; +} + +.employee-table th { + background-color: #e01160de; + color: white; + font-size: larger; + height: 2rem; + padding: 0.5rem; +} + +.employee-table th:nth-child(n + 2) { + border-left: 1px solid rgba(0, 0, 0, 0.3); +} + +.employee-table td { + padding: 0.5rem; + font-size: large; + word-wrap: break-word; + font-weight: 550; +} + +.employee-table td:nth-child(n + 2) { + border-left: 1px solid rgba(0, 0, 0, 0.3); +} + +.employee-table tr:not(:last-child) td { + border-bottom: 1px solid rgba(0, 0, 0, 0.3); +} + +.employee-table img { + height: 160px; + width: 160px; + object-fit: cover; +} + +.employee-table-header-tl { + border-radius: 4px 0 0 0; +} + +.employee-table-header-tr { + border-radius: 0 4px 0 0; +} + +.employee-refund-ticket-image img { + height: 22rem; + width: 22rem; + object-fit: contain; +} + +.employee-banner-image { + width: 100%; +} + +.btn-add-banner { + width: 10%; + min-width: 140px; + float: right; + margin: 0.5rem 0; +} + +input[type="file"] { + margin-top: 1rem; +} + +.icons { + position: relative; + top: 0.35rem; +} + +/*------------------------- ORDER INFORMATION STYLING -------------------------*/ + +.order-content-card { + background-color: white; + border: 0.5px rgba(216, 208, 208, 0.774); + border-style: solid; + border-radius: 0.2rem; + box-shadow: 0 0 4px rgb(175, 172, 172); + padding: 1.5rem 2rem; + margin: 1rem 0; +} + +.order-content-card-grid { + display: grid; + grid-template-columns: 1.5fr 7.5fr 1fr; + margin: 1rem 0; +} + +.order-border-bottom { + padding-bottom: 1rem; + border-bottom: 1px solid gray; +} + +.order-border-top { + border-top: 1px solid gray; +} + +.orderinfo-table { + border: none; + border-collapse: collapse; + text-align: center; + table-layout: fixed; + width: 100%; +} + +.orderinfo-table tr { + overflow-x: auto; + word-wrap: break-word; + padding: 1rem; +} + +.orderinfo-table th { + font-size: 22px; + height: 40px; + color: gray; + padding: 1rem 0; +} + +.orderinfo-table img { + width: 80px; + height: 80px; + object-fit: contain; +} + +#shipping, +#total, +#ordertotal { + text-align: right; +} + +.canvas-container { + width: 100%; +} + +#trending-chart { + width: 100%; +} diff --git a/styles/pagetopbar.css b/styles/pagetopbar.css new file mode 100644 index 0000000..8925bd7 --- /dev/null +++ b/styles/pagetopbar.css @@ -0,0 +1,126 @@ +.top-bar { + top: 0; + right: 0; + left: 0; + position: fixed; + z-index: 100; + background-image: linear-gradient(0deg, #e0115f, #cc0a54); + height: 8rem; + box-shadow: 0 0 0.25rem rgba(0, 0, 0, 0.3); +} + +.top-bar-wrapper { + z-index: 400; + height: 2rem; + max-width: 1200px; + width: 75%; +} + +.top-bar-elements { + display: block; + align-items: center; + overflow: auto; +} + +.top-bar-left { + padding: 0.4rem; + float: left; + font-size: small; +} + +.top-bar-right { + padding: 0.4rem; + float: right; + font-size: small; +} + +.top-bar-header { + display: flex; + position: relative; + align-items: center; +} + +.top-bar-logo-wrapper { + position: relative; + margin: 1rem; +} + +.top-bar-search { + height: 4rem; + width: 840px; +} + +.top-bar-title { + border-left: 2px solid white; + position: relative; + top: -4px; + font-size: 2.6rem; + padding: 0 1.25rem; +} + +.search-bar-input { + background-color: white; + display: flex; + padding-left: 0.75rem; + border-radius: 0.2rem; + border: 1px solid #efefef; + box-shadow: 0 0.125rem 0.25rem rgba(0, 0, 0, 0.3); + align-items: center; +} + +.top-bar-search-bar { + flex: 1; + height: 2.4rem; + border: none !important; + box-shadow: none !important; + outline: none; +} + +.top-bar-search-bar:focus { + border: none !important; + box-shadow: none !important; +} + +.top-bar-search-bar::placeholder { + color: rgba(0, 0, 0, 0.54); + font-weight: bold; + font-family: "Nunito", sans-serif; +} + +.top-bar-shopping-cart { + margin: 1rem 4rem; +} + +.top-bar-search-button { + display: flex; + color: white; + border: none; + border-radius: 0.2rem; + box-shadow: 0 0 0.25rem rgba(0, 0, 0, 0.3); + width: 4rem; + padding: 0.3rem; + margin: 0.1rem; + text-align: center; + justify-content: center; + cursor: pointer; +} + +.top-bar-suggestions a { + font-size: small; + position: relative; + top: 0.3rem; + padding: 0 1rem; +} + +.top-bar a { + text-decoration: none; + color: white; +} + +.top-bar a:visited { + color: white; +} + +.top-bar a:active { + color: white; +} diff --git a/styles/storetopbar.css b/styles/storetopbar.css new file mode 100644 index 0000000..7cfa1c5 --- /dev/null +++ b/styles/storetopbar.css @@ -0,0 +1,291 @@ +:root { + --primary: #e0115f; + --primary-dark: #c51056; + --secondary: #c00c51; + --secondary-dark: #a70a46; + --bg-primary: #efefef; + --bg-primary-light: #fafafa; + --bg-primary-dark: #757575; + --col-grey-dark: rgba(0, 0, 0, 0.3); + --col-grey-light: rgba(0, 0, 0, 0.15); +} + +.top-bar { + top: 0; + right: 0; + left: 0; + position: fixed; + z-index: 100; + background-image: linear-gradient(0deg, var(--primary), var(--secondary)); + height: 8rem; + box-shadow: 0 0 0.25rem var(--col-grey-dark); +} + +.top-bar-wrapper { + z-index: 400; + height: 2rem; + max-width: 1200px; + width: 75%; +} + +.top-bar-elements { + height: 2rem; + display: flex; + align-items: center; + justify-content: space-between; + overflow: auto; +} + +.top-bar-text { + font-size: small; +} + +.top-bar-username-image { + margin: 0 0.25rem; + width: 25px; + height: 25px; + object-fit: cover; + border-radius: 50%; +} + +.top-bar-username-dropdown { + padding: 0.75rem 0 0 0; + position: absolute; + z-index: 10; +} + +.top-bar-username-dropdown-body { + box-shadow: 0 0 0.25rem var(--col-grey-dark); +} + +.top-bar-username-dropdown .material-icons { + display: block; + line-height: 1.2rem; + margin-right: 0.5rem; +} + +.top-bar-username-menu-item { + display: flex; + padding: 1rem 2rem 1rem 1rem; + transition: color ease-in-out 0.25s, background-color ease-in-out 0.25s; + font-size: medium; + position: relative; +} + +.top-bar-username-menu-item:hover { + color: var(--primary); + background-color: var(--col-grey-light); +} + +.top-bar-username-menu { + position: fixed; +} + +.top-bar-header { + display: flex; + position: relative; + align-items: center; +} + +.top-bar-logo-wrapper { + position: relative; + margin: 1rem; +} + +.top-bar-search { + height: 4rem; + width: 840px; +} + +.top-bar-title { + border-left: 4px solid white; + position: relative; + top: -4px; + font-size: 2.6rem; + padding: 0 1rem; +} + +.search-bar-input { + background-color: white; + display: flex; + padding-left: 0.75rem; + border-radius: 0.2rem; + border: 1px solid var(--bg-primary); + box-shadow: 0 0.125rem 0.25rem var(--col-grey-dark); + align-items: center; +} + +.top-bar-search-bar { + flex: 1; + height: 2.4rem; + border: none !important; + box-shadow: none !important; + outline: none; +} + +.top-bar-search-bar:focus { + border: none !important; + box-shadow: none !important; + outline: none; +} + +.top-bar-search-bar::placeholder { + color: rgba(0, 0, 0, 0.54); + font-weight: bold; + font-family: "Nunito", sans-serif; +} + +.top-bar-shopping-cart { + position: relative; + margin: 0 2rem; +} + +.top-bar-shopping-cart-icon { + padding: 2rem 2rem; +} + +.top-bar-search-button { + display: flex; + color: white; + border: none; + border-radius: 0.2rem; + box-shadow: 0 0 0.25rem var(--col-grey-dark); + width: 4rem; + padding: 0.3rem; + margin: 0.1rem 0.15rem 0.1rem 0.1rem; + text-align: center; + justify-content: center; + cursor: pointer; +} + +.top-bar-suggestions a { + font-size: small; + position: relative; + top: 0.3rem; + padding: 0 1rem; +} + +.top-bar a { + text-decoration: none; + color: white; +} + +.top-bar a:visited { + color: white; +} + +.top-bar a:active { + color: white; +} + +.shopping-cart-popup { + /* padding: 0.5rem; */ + position: absolute; + width: 25rem; + height: auto; + border-radius: 0.25rem; + box-shadow: 0 0 0.25rem var(--col-grey-dark); + z-index: 10; + top: 6rem; + right: 50%; + transform: translateX(50%); +} + +.shopping-cart-popup-checkout { + margin: 0.25rem; +} + +.shopping-cart-popup-empty { + margin: 2rem 0; + top: 3rem; +} + +.shopping-cart-popup-body { + width: 100%; + height: 100%; + max-height: 28rem; + overflow-y: auto; + scrollbar-color: var(--primary) white; + scrollbar-width: thin; +} + +.shopping-cart-popup-arrow-up { + position: absolute; + width: 0; + height: 0; + left: 50%; + transform: translateX(-50%); + top: -1.5rem; + border-left: 1.5rem solid transparent; + border-right: 1.5rem solid transparent; + border-bottom: 1.5rem solid rgb(255, 255, 255); +} + +.shopping-cart-popup-item { + padding: 0.5rem; + display: grid; + grid-template-columns: 1fr 6fr; + transition: filter ease-in-out 0.25s; +} + +.shopping-cart-popup-item a { + color: black; +} + +.shopping-cart-popup-item a:visited { + color: black; +} + +.shopping-cart-popup-item:hover { + filter: brightness(90%); +} + +.shopping-cart-popup-image { + width: 6rem; + height: 6rem; +} + +.shopping-cart-popup-image img { + object-fit: contain; + width: 100%; + height: 100%; +} + +.shopping-cart-popup-item-remove { + margin: 0.5rem; + color: var(--primary); + cursor: pointer; +} + +.shopping-cart-popup-item-remove:hover { + color: #f3659b; +} + +.shopping-cart-popup-item-details { + justify-content: space-between; + align-items: center; +} + +.shopping-cart-popup-item-price, +.shopping-cart-popup-item-quantity { + font-size: smaller; + padding-right: 0.25rem; +} + +.shopping-cart-popup-item-name { + margin: 0.5rem 0.5rem; + width: 14rem; +} + +.shopping-cart-popup-item-detail-container { + margin: 0.5rem 0.5rem; +} + +.shopping-cart-popup-item-price { + color: var(--primary); +} + +.shopping-cart-popup-item-quantity::before { + content: "✕"; + font-size: smaller; +} \ No newline at end of file diff --git a/styles/style.css b/styles/style.css new file mode 100644 index 0000000..b6f9b28 --- /dev/null +++ b/styles/style.css @@ -0,0 +1,2381 @@ +/* cyrillic-ext */ +@font-face { + font-family: "Nunito"; + font-style: normal; + font-weight: 400; + font-display: swap; + src: local("Nunito Regular"), local("Nunito-Regular"), url(../fonts/XRXV3I6Li01BKofIOOaBXso.woff2) format("woff2"); + unicode-range: U+0460-052F, U+1C80-1C88, U+20B4, U+2DE0-2DFF, U+A640-A69F, U+FE2E-FE2F; +} +/* cyrillic */ +@font-face { + font-family: "Nunito"; + font-style: normal; + font-weight: 400; + font-display: swap; + src: local("Nunito Regular"), local("Nunito-Regular"), url(../fonts/XRXV3I6Li01BKofIMeaBXso.woff2) format("woff2"); + unicode-range: U+0400-045F, U+0490-0491, U+04B0-04B1, U+2116; +} +/* vietnamese */ +@font-face { + font-family: "Nunito"; + font-style: normal; + font-weight: 400; + font-display: swap; + src: local("Nunito Regular"), local("Nunito-Regular"), url(../fonts/XRXV3I6Li01BKofIOuaBXso.woff2) format("woff2"); + unicode-range: U+0102-0103, U+0110-0111, U+0128-0129, U+0168-0169, U+01A0-01A1, U+01AF-01B0, U+1EA0-1EF9, U+20AB; +} +/* latin-ext */ +@font-face { + font-family: "Nunito"; + font-style: normal; + font-weight: 400; + font-display: swap; + src: local("Nunito Regular"), local("Nunito-Regular"), url(../fonts/XRXV3I6Li01BKofIO-aBXso.woff2) format("woff2"); + unicode-range: U+0100-024F, U+0259, U+1E00-1EFF, U+2020, U+20A0-20AB, U+20AD-20CF, U+2113, U+2C60-2C7F, U+A720-A7FF; +} +/* latin */ +@font-face { + font-family: "Nunito"; + font-style: normal; + font-weight: 400; + font-display: swap; + src: local("Nunito Regular"), local("Nunito-Regular"), url(../fonts/XRXV3I6Li01BKofINeaB.woff2) format("woff2"); + unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+2000-206F, U+2074, U+20AC, + U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD; +} + +/* fallback */ +@font-face { + font-family: "Material Icons"; + font-style: normal; + font-weight: 400; + src: url(../fonts/flUhRq6tzZclQEJ-Vdg-IuiaDsNc.woff2) format("woff2"); +} + +.material-icons { + font-family: "Material Icons"; + font-weight: normal; + font-style: normal; + font-size: 24px; + line-height: 1; + letter-spacing: normal; + text-transform: none; + display: inline-block; + white-space: nowrap; + word-wrap: normal; + direction: ltr; + -moz-font-feature-settings: "liga"; + -moz-osx-font-smoothing: grayscale; +} + +html { + scroll-behavior: smooth; +} + +* { + box-sizing: border-box; +} + +body { + margin: 0; + background-color: var(--bg-primary); + font-family: "Nunito", sans-serif !important; +} + +hr { + border-top: 1px solid var(--col-grey-light); + border-bottom: none; + border-left: none; + border-right: none; +} + +label { + margin: 0.25rem 0; +} + +input { + font-family: "Nunito", sans-serif; +} + +form input[type="text"], +form input[type="password"], +form input[type="date"], +form input[type="email"], +form input[type="number"] { + height: 2rem; + border-radius: 0.25rem; + border: 1px solid var(--col-grey-light); + box-shadow: none; + box-sizing: border-box; + margin: 0.25rem 0; + padding: 0 0.5rem; + width: 100%; + transition: box-shadow ease-in-out 0.25s, border ease-in-out 0.25s; +} + +form input[type="text"]:focus, +form input[type="password"]:focus, +form input[type="date"]:focus, +form input[type="email"]:focus, +form input[type="number"]:focus, +form textarea:focus { + box-shadow: 0 0 0.25rem rgb(223, 17, 96); + border: 1px solid rgb(223, 17, 96); + outline: none; +} + +form img { + width: 300px; + height: 300px; + border-radius: 50%; + object-fit: cover; +} + +form input[type="file"] { + width: 0.1px; + height: 0.1px; + opacity: 0; + overflow: hidden; + position: absolute; + z-index: -1; +} + +form input[type="file"] + label { + background-color: var(--primary); + color: white; + text-align: center; + height: 2.5rem; + line-height: 2.5rem; + border-radius: 0.5rem; + border: none; + box-shadow: 0 0 0.1rem var(--col-grey-dark); + cursor: pointer; + transition: box-shadow ease-in-out 0.25s; + margin: 0.25rem 0; +} + +form input[type="file"] + label:hover { + box-shadow: 0 0 0.5rem var(--col-grey-dark); +} + +form input[type="file"] + label:active { + background-color: #db125f; + -webkit-box-shadow: inset 0px 0px 5px var(--col-grey-dark); + -moz-box-shadow: inset 0px 0px 5px var(--col-grey-dark); + box-shadow: inset 0px 0px 5px var(--col-grey-dark); + outline: none; +} + +form input[type="file"] + label[disabled] { + background-color: #dd4580; + color: #e3a3bb; + cursor: default; +} + +form input[type="button"] label[disabled]:hover { + box-shadow: none; +} + +form textarea { + font-family: "Nunito", sans-serif; + border-radius: 0.25rem; + border: 1px solid hsla(0, 0%, 0%, 0.15); + box-shadow: none; + box-sizing: border-box; + margin: 0.25rem 0; + padding: 0.5rem; + width: 100%; + resize: vertical; + min-height: 8rem; + transition: box-shadow ease-in-out 0.25s, border ease-in-out 0.25s; +} + +form input[type="button"], +form input[type="submit"] { + background-color: var(--primary); + color: white; + text-align: center; + height: 2.5rem; + width: 100%; + line-height: 2.5rem; + border-radius: 0.5rem; + border: none; + box-shadow: 0 0 0.1rem var(--col-grey-dark); + cursor: pointer; + transition: box-shadow ease-in-out 0.25s; + margin: 0.25rem 0; +} + +form input[type="button"]:hover, +form input[type="submit"]:hover { + box-shadow: 0 0 0.5rem var(--col-grey-dark); +} + +form input[type="button"]:active, +form input[type="submit"]:active { + background-color: #db125f; + -webkit-box-shadow: inset 0px 0px 5px var(--col-grey-dark); + -moz-box-shadow: inset 0px 0px 5px var(--col-grey-dark); + box-shadow: inset 0px 0px 5px var(--col-grey-dark); + outline: none; +} + +form input[type="button"][disabled], +form input[type="submit"][disabled] { + background-color: #dd4580; + color: #e3a3bb; + cursor: default; +} + +form input[type="button"][disabled]:hover, +form input[type="submit"][disabled]:hover { + box-shadow: none; +} + +form input.invalid, +form textarea.invalid { + box-shadow: 0 0 0.2rem 1px red; +} + +input[type="text"], +input[type="password"], +input[type="date"], +input[type="email"], +input[type="number"], +select { + height: 2rem; + border-radius: 0.25rem; + border: 1px solid var(--col-grey-light); + box-shadow: none; + box-sizing: border-box; + margin: 0.25rem 0; + padding: 0 0.5rem; + transition: box-shadow ease-in-out 0.25s, border ease-in-out 0.25s; +} + +textarea { + font-family: "Nunito", sans-serif; + border-radius: 0.25rem; + border: 1px solid var(--col-grey-light); + box-shadow: none; + box-sizing: border-box; + margin: 0.25rem 0; + padding: 0.5rem; + resize: vertical; + min-height: 8rem; + transition: box-shadow ease-in-out 0.25s, border ease-in-out 0.25s; +} + +input[type="text"]:focus, +input[type="password"]:focus, +input[type="date"]:focus, +input[type="email"]:focus, +input[type="number"]:focus, +select:focus, +textarea:focus { + box-shadow: 0 0 0.25rem hsl(337, 86%, 47%); + border: 1px solid rgba(223, 17, 96); + outline: none; +} + +input[type="text"]::placeholder, +input[type="number"]::placeholder, +select::placeholder, +textarea::placeholder, +input[type="password"]::placeholder { + color: rgba(0, 0, 0, 0.54); + font-weight: bold; + font-family: "Nunito", sans-serif; +} + +select { + position: relative; + -webkit-appearance: none; + -moz-appearance: none; + background-image: url("data:image/svg+xml;utf8,"); + background-repeat: no-repeat; + background-position-x: 100%; + background-position-y: 3px; + width: auto; + padding: 0 1.5rem 0 0.5rem; +} + +input[type="button"], +input[type="submit"] { + background-color: var(--primary); + color: white; + text-align: center; + height: 2.5rem; + width: 100%; + line-height: 2.5rem; + border-radius: 0.5rem; + border: none; + box-shadow: 0 0 0.1rem var(--col-grey-dark); + cursor: pointer; + transition: box-shadow ease-in-out 0.25s; + margin: 0.25rem 0; +} + +input[type="button"]:hover, +input[type="submit"]:hover { + box-shadow: 0 0 0.5rem var(--col-grey-dark); +} + +input[type="button"]:active, +input[type="submit"]:active { + background-color: #db125f; + -webkit-box-shadow: inset 0px 0px 5px var(--col-grey-dark); + -moz-box-shadow: inset 0px 0px 5px var(--col-grey-dark); + box-shadow: inset 0px 0px 5px var(--col-grey-dark); + outline: none; +} + +input[type="button"][disabled], +input[type="submit"][disabled] { + background-color: #dd4580; + color: #e3a3bb; + cursor: default; +} + +input[type="button"][disabled]:hover, +input[type="submit"][disabled]:hover { + box-shadow: none; +} + +a { + text-decoration: none; + color: black; +} + +a:visited { + color: black; +} + +a:active { + color: black; +} + +table.table { + border-collapse: separate; + border-spacing: 0; + margin: 0.5rem; + width: calc(100% - (0.5rem * 2)); +} + +table.table thead td, +table.table thead th { + text-align: left; + color: white; + background-color: var(--primary); + padding: 0.5rem; +} + +table.table tr th, +table.table tr td { + border-right: 1px solid var(--col-grey-light); + border-bottom: 1px solid var(--col-grey-light); +} + +table.table tr th:first-child, +table.table tr td:first-child { + border-left: 1px solid var(--col-grey-light); +} + +table.table tr th { + border-top: 1px solid var(--col-grey-light); +} + +table.table thead th:first-child { + border-radius: 3px 0 0 0; +} + +table.table thead th:last-child { + border-radius: 0 3px 0 0; +} + +table.table tbody tr:last-child td:first-child { + border-radius: 0 0 0 3px; +} +table.table tbody tr:last-child td:last-child { + border-radius: 0 0 3px 0; +} + +table.table tbody td { + text-align: left; + padding: 0.5rem; + max-width: 0rem; +} + +/* table.table tbody tr:nth-child(odd) td { + background-color: hsla(337, 86%, 47%, 0.1); +} */ + +table.conversation { + margin: 1rem 0.5rem; + width: 100%; + border-collapse: separate; + border-spacing: 0; + width: calc(100% - (0.5rem * 2)); +} + +table.conversation tbody td { + vertical-align: top; + text-align: left; + padding: 0.5rem; + max-width: 0rem; +} + +table.conversation tr th, +table.conversation tr td { + border-right: 1px solid var(--col-grey-light); + border-bottom: 1px solid var(--col-grey-light); +} + +table.conversation tr th:first-child, +table.conversation tr td:first-child { + border-left: 1px solid var(--col-grey-light); +} + +table.conversation tr th { + border-top: 1px solid var(--col-grey-light); +} + +table.conversation tbody td:last-child { + background-color: hsl(0, 0%, 96%); + border-radius: 0 0 3px 0; +} + +table.conversation tbody td:first-child { + background-color: hsl(0, 0%, 98%); + border-radius: 0 0 0 3px; +} + +table.conversation tbody th { + vertical-align: top; + text-align: left; + padding: 0.4rem 0.5rem; + max-width: 0rem; + color: white; + background-color: var(--primary); +} + +table.conversation tbody th:first-child { + border-radius: 3px 0 0 0; +} + +table.conversation tbody th:last-child { + border-radius: 0 3px 0 0; +} + +.conversation-title, +.inbox-title { + border: 1px solid var(--primary-dark); + background-color: var(--primary); + color: white; + padding: 0.5rem; + border-radius: 3px; + margin: 0.5rem; + font-size: x-large; +} + +.conversation-user-container { + min-height: 12rem; +} + +.conversation-user img { + width: 10rem; + height: 10rem; + object-fit: cover; + border-radius: 3px; +} + +.conversation-timestamp { + font-size: 12px; + font-weight: bold; +} + +.inbox-timestamp { + font-size: small; + margin-top: 0.3rem; +} + +.pre-line { + white-space: pre-line; +} + +.pre-wrap { + white-space: pre-wrap; +} + +.justify { + text-align: justify; +} + +.form-input-group { + margin: 0.5rem; +} + +.flex { + display: flex; +} + +.block { + display: block; +} + +.hidden { + display: none; +} + +.transparent { + opacity: 0; +} + +.invisible { + visibility: hidden; +} + +.bold { + font-weight: bold; +} + +.underline { + text-decoration: underline; +} + +.pointer { + cursor: pointer; +} + +.b-r-0 { + border-radius: 0 !important; +} +.b-r-1 { + border-radius: 0.25rem !important; +} +.b-r-2 { + border-radius: 0.5rem !important; +} +.b-r-3 { + border-radius: 0.75rem !important; +} +.b-r-4 { + border-radius: 1rem !important; +} +.circle { + border-radius: 50%; +} + +.btn-inset:active { + -webkit-box-shadow: inset 0px 0px 5px var(--col-grey-dark); + -moz-box-shadow: inset 0px 0px 5px var(--col-grey-dark); + box-shadow: inset 0px 0px 5px var(--col-grey-dark); + outline: none; +} + +.btn-inset[disabled]:active { + -webkit-box-shadow: none; + -moz-box-shadow: none; + box-shadow: none; +} + +.unselectable { + -webkit-user-select: none; +} + +.ellipsis-truncate { + white-space: nowrap; + overflow: hidden; + text-overflow: ellipsis; +} + +.strike { + text-decoration: line-through; + color: rgb(161, 161, 161); +} + +:root { + --primary: #e0115f; + --primary-dark: #c51056; + --secondary: #c00c51; + --secondary-dark: #a70a46; + --bg-primary: #efefef; + --bg-primary-light: #fafafa; + --bg-primary-dark: #757575; + --col-grey-dark: rgba(0, 0, 0, 0.3); + --col-grey-light: rgba(0, 0, 0, 0.15); +} + +/* :root { + --primary: #e0115f; + --primary-dark: #c51056; + --secondary: #c00c51; + --secondary-dark: #a70a46; + --bg-primary: #efefef; + --bg-primary-light: #fafafa; + --bg-primary-dark: #757575; + --col-grey-dark: rgba(0, 0, 0, 0.3); + --col-grey-light: rgba(0, 0, 0, 0.15); +} */ + +.cultured { + color: var(--bg-primary); +} +.cultured-light { + color: var(--bg-primary-light); +} +.cultured-dark { + color: var(--bg-primary-dark); +} +.ruby { + color: var(--primary); +} +.vis-ruby:visited { + color: var(--primary) !important; +} +.rubine-red { + color: var(--secondary); +} +.white { + color: white; +} +.vis-white:visited { + color: white !important; +} +.black { + color: black !important; +} +.vis-black:visited { + color: black !important; +} +.grey { + color: var(--col-grey-dark); +} +.bg-cultured { + background-color: var(--bg-primary); +} +.bg-cultured-light { + background-color: var(--bg-primary-light); +} +.bg-cultured-dark { + background-color: var(--bg-primary-dark); +} +.bg-ruby { + background-color: var(--primary); +} +.bg-rubine-red { + background-color: var(--secondary); +} +.bg-white { + background-color: #ffffff; +} +.card-success { + margin: 0.5rem; + padding: 0.5rem; + box-shadow: 0 1px 0.1rem var(--col-grey-light); + border-radius: 3px; + background-color: rgba(52, 185, 63, 0.65); + border: 1px solid rgb(82, 194, 88); + text-shadow: 0 0 0.5rem rgba(0, 0, 0, 0.5); + color: white; +} +.card-error { + margin: 0.5rem; + padding: 0.5rem; + box-shadow: 0 1px 0.1rem var(--col-grey-light); + border-radius: 3px; + background-color: rgba(214, 0, 0, 0.65); + border: 1px solid rgb(211, 0, 0); + text-shadow: 0 0 0.5rem rgba(0, 0, 0, 0.5); + color: white; +} +.card-generic { + margin: 0.5rem; + padding: 0.5rem; + background-color: white; + box-shadow: 0 1px 0.1rem var(--col-grey-light); + border-radius: 3px; +} +.notification { + display: inline-block; + font-weight: bold; + text-align: center; + font-size: x-small; + margin: 0 0.25rem; + border-radius: 50%; + min-width: 1rem; + height: 1rem; + line-height: 1rem; +} +.w-1 { + width: 12.5% !important; +} +.w-2 { + width: 25% !important; +} +.w-3 { + width: 50% !important; +} +.w-4 { + width: 100% !important; +} +.h-0 { + height: 0 !important; +} +.h-1 { + height: 12.5% !important; +} +.h-2 { + height: 25% !important; +} +.h-3 { + height: 50% !important; +} +.h-4 { + height: 100% !important; +} + +.no-overflow { + overflow: hidden; +} + +.text-center { + text-align: center !important; +} + +.flex-justify-center { + justify-content: center; +} + +.flex-justify-space-between { + justify-content: space-between; +} + +.flex-align-center { + align-items: center; +} + +.flex-align-baseline { + align-items: baseline; +} + +.text-left { + text-align: left !important; +} + +.text-right { + text-align: right !important; +} + +.resize-none { + resize: none; +} + +.center-in-page-abs { + position: absolute; + top: 50%; + left: 50%; + transform: translate(-50%, -50%); +} + +.center-in-page-rel { + position: relative; + top: 50%; + left: 50%; + transform: translate(-50%, -50%); +} + +.btn-primary { + background-color: var(--primary); + color: white; + text-align: center; + height: 2.5rem; + line-height: 2.5rem; + border-radius: 0.5rem; + border: none; + box-shadow: 0 0 0.1rem var(--col-grey-dark); + cursor: pointer; + transition: box-shadow ease-in-out 0.25s; + margin: 0.25rem 0; +} + +.btn-primary:hover { + box-shadow: 0 0 0.5rem var(--col-grey-dark); +} + +.btn-primary:active { + background-color: #db125f; + -webkit-box-shadow: inset 0px 0px 5px var(--col-grey-dark); + -moz-box-shadow: inset 0px 0px 5px var(--col-grey-dark); + box-shadow: inset 0px 0px 5px var(--col-grey-dark); + outline: none; +} + +.btn-primary[disabled] { + background-color: #dd4580; + color: #e3a3bb; + cursor: default; +} + +.btn-primary[disabled]:hover { + box-shadow: none; +} + +.body-wrapper { + min-height: 100vh; + display: grid; + grid-template-rows: 8rem minmax(calc(100vh - 8rem), 1fr) auto; +} + +.modal-close-button { + position: absolute; + float: right; + top: -0.75rem; + right: -0.75rem; + background-color: rgb(44, 44, 44); + border-radius: 50%; + width: 2rem; + height: 2rem; + line-height: 2rem; + text-align: center; + cursor: pointer; + box-shadow: 0 0 0.25rem var(--col-grey-dark); + -webkit-user-select: none; +} + +.modal-content { + padding: 1rem; + border-radius: 0.5rem; + background-color: white; + box-sizing: border-box; + z-index: 100; + box-shadow: 0 0 0.25rem var(--col-grey-dark); +} + +.modal-body { + width: 100%; + height: 100%; +} + +.modal-text { + margin: 1rem; +} + +.modal-background { + position: fixed; + top: 0; + left: 0; + width: 100vw; + height: 100vh; + margin: 0; + padding: 0; + z-index: 100; + background-color: rgba(0, 0, 0, 0.5); +} + +.top-bar { + top: 0; + right: 0; + left: 0; + position: fixed; + z-index: 100; + background-image: linear-gradient(0deg, var(--primary), var(--secondary)); + height: 8rem; + box-shadow: 0 0 0.25rem var(--col-grey-dark); +} + +.top-bar-wrapper { + z-index: 400; + height: 2rem; + max-width: 1200px; + width: 75%; +} + +.top-bar-elements { + height: 2rem; + display: flex; + align-items: center; + justify-content: space-between; + overflow: auto; +} + +.top-bar-text { + font-size: small; +} + +.top-bar-username-image { + margin: 0 0.25rem; + width: 25px; + height: 25px; + object-fit: cover; + border-radius: 50%; +} + +.top-bar-username-dropdown { + padding: 0.75rem 0 0 0; + position: absolute; + z-index: 10; +} + +.top-bar-username-dropdown-body { + box-shadow: 0 0 0.25rem var(--col-grey-dark); +} + +.top-bar-username-dropdown .material-icons { + display: block; + line-height: 1.2rem; + margin-right: 0.5rem; + position: relative; + top: 2px; +} + +.top-bar-username-menu-item { + display: flex; + padding: 1rem 2rem 1rem 1rem; + transition: color ease-in-out 0.25s, background-color ease-in-out 0.25s; + font-size: medium; + position: relative; +} + +.top-bar-username-menu-item:hover { + color: var(--primary); + background-color: var(--col-grey-light); +} + +.top-bar-username-menu { + position: fixed; +} + +.top-bar-header { + display: flex; + position: relative; +} + +.top-bar-logo-wrapper { + position: relative; + margin: 1rem; +} + +.top-bar-search { + height: 4rem; + width: 840px; + top: 1.25rem; + position: relative; +} + +.top-bar-title { + border-left: 4px solid white; + position: relative; + top: -4px; + font-size: 2.6rem; + padding: 0 1rem; +} + +.search-bar-input { + background-color: white; + display: flex; + padding-left: 0.75rem; + border-radius: 0.2rem; + border: 1px solid var(--bg-primary); + box-shadow: 0 0.125rem 0.25rem var(--col-grey-dark); + align-items: center; +} + +.top-bar-search-bar { + flex: 1; + height: 2.4rem; + border: none !important; + box-shadow: none !important; + outline: none; +} + +.top-bar-search-bar:focus { + border: none !important; + box-shadow: none !important; + outline: none; +} + +.top-bar-search-bar::placeholder { + color: rgba(0, 0, 0, 0.54); + font-weight: bold; + font-family: "Nunito", sans-serif; +} + +.top-bar-shopping-cart { + position: relative; + margin: 0 2rem; +} + +.top-bar-shopping-cart-icon { + padding: 2rem 2rem; +} + +.cart-notification { + position: relative; + bottom: 2.5rem; + left: 4rem; + z-index: -1; + color: var(--primary); + font-weight: bold; + width: 1.25rem; + height: 1.25rem; + line-height: 1.25rem; + text-align: center; + background-color: white; + border-radius: 50%; +} + +.top-bar-search-button { + display: flex; + color: white; + border: none; + border-radius: 0.2rem; + box-shadow: 0 0 0.25rem var(--col-grey-dark); + width: 4rem; + padding: 0.3rem; + margin: 0.1rem 0.15rem 0.1rem 0.1rem; + text-align: center; + justify-content: center; + cursor: pointer; +} + +.top-bar-suggestions a { + font-size: small; + position: relative; + top: 0.3rem; + padding: 0 1rem; +} + +.top-bar a { + text-decoration: none; + color: white; +} + +.top-bar a:visited { + color: white; +} + +.top-bar a:active { + color: white; +} + +.chapalang-logo { + height: 60px; + width: auto; +} + +.container { + margin-right: auto; + margin-left: auto; +} + +.content-wrapper { + margin-top: 0.4rem; + margin-bottom: 0.4rem; + min-width: 1200px; + width: 60%; +} + +.search-result-header { + display: grid; + grid-template-columns: 1fr 1fr; + margin: 0.25rem 0.5rem; + padding: 0.5rem 0; +} + +.search-result-header .material-icons { + position: relative; + top: 7px; +} + +.search-result-header .material-icons.opposite { + transform: rotateX(180deg); +} + +.search-result-sort { + cursor: pointer; +} + +.search-result-sort.active { + font-weight: bold; + text-decoration: underline; +} + +.search-results-empty { + font-size: larger; + margin: 0.2rem; +} + +#sort-direction { + cursor: pointer; +} + +.search-sidebar { + float: left; + background-color: white; + box-shadow: 0 0 0.15rem var(--col-grey-light); + margin: 0.2rem; + padding: 1rem; + width: 12.6rem; +} + +.search-sidebar-header { + font-size: large; + margin-bottom: 1rem; +} + +.search-sidebar-header .material-icons { + position: relative; + top: 5px; +} + +.search-sidebar-group { + padding: 1rem 0; + border-top: 1px solid var(--col-grey-light); +} + +.search-sidebar-rating .material-icons { + position: relative; + top: 3px; +} + +.search-sidebar-rating-option { + cursor: pointer; +} + +.search-sidebar-rating-option.active { + background-color: hsla(337, 86%, 47%, 0.15); + cursor: pointer; +} + +.search-sidebar-price { + justify-items: center; + display: grid; + grid-template-columns: 2fr 1fr 2fr; + width: 100%; + line-height: 2rem; +} + +.search-sidebar-price input[type="text"] { + width: 100%; +} + +.search-sidebar-price input[type="text"]::placeholder { + width: 100%; +} + +.search-sidebar-text { + line-height: 2rem; + margin: 0.25rem 0; +} + +.shopping-cart-popup { + /* padding: 0.5rem; */ + position: absolute; + width: 25rem; + height: auto; + border-radius: 0.25rem; + box-shadow: 0 0 0.25rem var(--col-grey-dark); + z-index: 10; + top: 6rem; + right: 50%; + transform: translateX(50%); +} + +.shopping-cart-popup-checkout { + margin: 0.25rem; +} + +.shopping-cart-popup-empty { + margin: 2rem 0; + top: 3rem; +} + +.shopping-cart-popup-body { + width: 100%; + height: 100%; + max-height: 28rem; + overflow-y: auto; + scrollbar-color: var(--primary) white; + scrollbar-width: thin; +} + +.shopping-cart-popup-arrow-up { + position: absolute; + width: 0; + height: 0; + left: 50%; + transform: translateX(-50%); + top: -1.5rem; + border-left: 1.5rem solid transparent; + border-right: 1.5rem solid transparent; + border-bottom: 1.5rem solid rgb(255, 255, 255); +} + +.shopping-cart-popup-item { + padding: 0.5rem; + display: grid; + grid-template-columns: 1fr 6fr; + transition: filter ease-in-out 0.25s; +} + +.shopping-cart-popup-item a { + color: black; +} + +.shopping-cart-popup-item a:visited { + color: black; +} + +.shopping-cart-popup-item:hover { + filter: brightness(90%); +} + +.shopping-cart-popup-image { + width: 6rem; + height: 6rem; +} + +.shopping-cart-popup-image img { + object-fit: contain; + width: 100%; + height: 100%; +} + +.shopping-cart-popup-item-remove { + margin: 0.5rem; + color: var(--primary); + cursor: pointer; +} + +.shopping-cart-popup-item-remove:hover { + color: #f3659b; +} + +.shopping-cart-popup-item-details { + justify-content: space-between; + align-items: center; +} + +.shopping-cart-popup-item-price, +.shopping-cart-popup-item-quantity { + font-size: smaller; + padding-right: 0.25rem; +} + +.shopping-cart-popup-item-name { + margin: 0.5rem 0.5rem; + width: 14rem; +} + +.shopping-cart-popup-item-detail-container { + margin: 0.5rem 0.5rem; +} + +.shopping-cart-popup-item-price { + color: var(--primary); +} + +.shopping-cart-popup-item-quantity::before { + content: "✕"; + font-size: smaller; +} + +.pagination-links { + margin: 1rem; +} + +.pagination { + text-align: center; + line-height: 2.5rem; + color: white; + background-color: var(--primary); + width: 2.5rem; + height: 2.5rem; + cursor: pointer; + transition: background-color ease-in-out 0.25s, box-shadow ease-in-out 0.25s; +} + +.pagination:first-child { + border-radius: 5px 0 0 5px; +} + +.pagination:last-child { + border-radius: 0 5px 5px 0; +} + +.pagination:hover { + background-color: var(--secondary-dark); + box-shadow: 0 0 0.25rem var(--col-grey-dark); +} + +.pagination.active { + background-color: var(--primary-dark); +} + +.about-us { + padding: 0 2rem 0 2rem; + min-width: 900px; +} + +.about-us-body { + white-space: pre-line; + text-align: justify; + font-size: large; +} + +.item-listings-wrapper { + display: grid; + grid-template-rows: 590px 1fr; +} + +.special-offers { + margin: 0.2rem 0.5rem; + box-shadow: 0 0 0.1rem var(--col-grey-dark); + display: grid; + grid-template-rows: 300px 1fr; +} + +.special-offers-trending { + margin: 0.5rem 0; + padding: 0.5rem 0.3rem; +} + +.trending-header { + color: var(--bg-primary-dark); + font-weight: bold; + margin: 0 0.5rem 0.5rem 0.5rem; +} + +.trending-icon { + position: relative; + top: 6px; + line-height: 0; +} + +.trending-item { + box-shadow: 0 0 0.1rem var(--col-grey-dark); + text-align: center; + height: 14rem; + min-width: 130px; + max-width: 12rem; + margin: 0.2rem 0.3rem 0 0.3rem; + padding: 0.5rem; + flex: 0 1 200px; + transition: box-shadow ease-in-out 0.25s; +} + +.trending-item:hover { + box-shadow: 0 0 0.5rem var(--col-grey-dark); +} + +.trending-item img { + width: 100%; + height: 75%; + object-fit: cover; +} + +.trending-price { + font-size: small; +} + +.carousel { + position: relative; + display: block; + overflow: hidden; +} + +.carousel-btn-left, +.carousel-btn-right { + height: 4rem; + width: 2.5rem; + text-align: center; + line-height: 4rem; + background-color: var(--col-grey-dark); + cursor: pointer; +} + +.carousel-navigation { + position: absolute; + top: 50%; + transform: translateY(-50%); + justify-content: space-between; + width: 100%; + transition: opacity ease-in-out 0.25s; +} + +.carousel-indicators { + position: absolute; + bottom: 1rem; + width: 100%; + justify-content: center; + z-index: 2; +} + +.indicator { + border: none; + border-radius: 50%; + background-color: hsla(0, 0%, 100%, 0.3); + border: 1px solid rgba(255, 255, 255, 0.75); + height: 0.75rem; + width: 0.75rem; + margin: 0 0.25rem; + cursor: pointer; + transition: background-color ease-in-out 0.5s, border ease-in-out 0.5s; +} + +.indicator.selected { + background-color: var(--secondary-dark); + border: 1px solid var(--primary); +} + +.carousel-item { + position: absolute; + top: 0; + left: 0; + width: 100%; + height: 100%; +} + +.carousel-item-details { + background-color: rgba(0, 0, 0, 0.4); + color: white; + padding: 0.5rem; + position: relative; + height: 5.5rem; + width: 100%; + top: -5.8rem; + z-index: 1; + transition: opacity ease-in-out 0.25s; +} + +.carousel-item-name { + font-weight: bold; +} + +.carousel-item-description { + font-size: small; +} + +.carousel-item img { + height: 100%; + width: 100%; + object-fit: cover; +} + +.carousel-items { + position: relative; + height: 100%; + width: 100%; + transition: transform ease-in-out 0.5s; +} + +.flex-wrapper { + display: flex; + flex-wrap: wrap; + justify-content: center; +} + +.flex-column { + flex-direction: column; +} + +.flex-row { + flex-direction: row; +} + +.product-modal { + width: auto; + height: auto; + padding: 2rem; +} + +.product-container { + margin: 0 0 0.5rem 0; + display: grid; + grid-template-columns: 482px 1fr; + justify-items: center; + box-shadow: 0 1px 0.1rem var(--col-grey-light); + border-radius: 3px; +} + +.product-image { + display: flex; + flex-wrap: wrap; + flex-direction: column; +} + +.product-image > img { + object-fit: contain; + height: 450px; + width: 450px; +} + +.product-image-list { + display: flex; + flex-wrap: wrap; + justify-content: flex-start; +} + +.product-image-small { + margin: 0.5rem; + width: 6rem; + height: 6rem; + cursor: pointer; +} + +.product-image-small.active { + border: 3px solid var(--primary); +} + +.product-image-small img { + object-fit: contain; + width: 100%; + height: 100%; +} + +.product-details { + display: flex; + flex-direction: column; + width: 100%; +} + +.product-name { + display: -webkit-box; + -webkit-box-orient: vertical; + /* to specify the number of lines you want the text to run through... */ + -webkit-line-clamp: 2; + /* hide the overflowing text, i.e, texts that did not fit in to the box */ + overflow: hidden; + + font-size: x-large; + margin: 0.5rem; +} + +.product-rating { + font-size: 18px; + margin: 0.5rem; +} + +.product-total-sold { + margin: 0.5rem; + font-size: medium; +} + +.product-rating .material-icons { + margin: -1px; + position: relative; + top: 0.2rem; +} + +.product-price { + flex: 0 1 auto; + height: 4rem; + font-size: 2rem; + font-weight: bold; + line-height: 4rem; + margin: 0.5rem; + padding: 0 1rem; +} + +.product-quantity-wrapper { + margin: 0.5rem; + width: 70%; + display: grid; + grid-template-columns: 2fr 1fr 1fr 1fr 6fr; +} + +.product-quantity-instock { + margin-left: 1rem; +} + +.product-quantity-text, +.product-quantity-instock, +.product-quantity-minus, +.product-quantity, +.product-quantity-add { + line-height: 2rem; + height: 2rem; +} + +.product-quantity-minus, +.product-quantity, +.product-quantity-add { + width: 100%; + text-align: center; + border: 1px solid var(--col-grey-light); +} + +.product-quantity-minus { + cursor: pointer; + border-radius: 3px 0 0 3px; +} +.product-quantity-add { + cursor: pointer; + border-radius: 0 3px 3px 0; +} + +.product-quantity { + border-right: none; + border-left: none; +} + +.product-addtocart { + margin: 0.5rem; + height: 3.5rem; + background-color: rgba(223, 17, 96, 0.1); + border: 1px solid var(--primary); + border-radius: 5px; + align-items: center; + padding: 0.5rem; + justify-content: center; + cursor: pointer; +} + +.product-atc-text { + margin: 0 0.5rem; +} + +.product-description-wrapper { + margin: 0.5rem; +} + +.product-description-header { + font-size: large; + margin: 0.5rem 0; +} + +.product-description-body { + font-size: medium; + white-space: pre-wrap; +} + +.product-information { + margin: 0 0 0.5rem 0; + display: grid; + grid-template-columns: auto 1fr; + box-shadow: 0 1px 0.1rem var(--col-grey-light); + border-radius: 3px; +} + +.product-information-vendor-image { + object-fit: cover; + width: 65px; + height: 65px; + border-radius: 50%; + border: 2px solid var(--primary); +} + +.product-information-vendor-contact { + margin: 0.5rem 0 0 0; + height: 2.5rem; + width: 7rem; + background-color: rgba(223, 17, 96, 0.1); + border: 1px solid var(--primary); + border-radius: 5px; + align-items: center; + padding: 0.25rem 0.5rem; + justify-content: center; + cursor: pointer; +} + +.product-information-body { + margin: 0.5rem 1rem; + display: grid; + grid-template-rows: auto 1fr; +} + +.product-information-reviews { + margin: 0 0 0.5rem 0; + box-shadow: 0 1px 0.1rem var(--col-grey-light); + border-radius: 3px; +} + +.information-body { + white-space: pre-wrap; +} + +.product-review { + min-height: 10rem; + margin: 0.5rem 0; + border-top: 1px solid var(--col-grey-light); +} + +.product-review-information { + margin: 0.5rem 0; +} + +.product-review-add { + font-size: small; + margin: 0 0.5rem; + cursor: pointer; +} + +#product-review-form { + height: 370px; + transition: height ease-in-out 0.25s; +} + +.review-rating-stars.invalid { + background-color: rgba(255, 0, 0, 0.361); +} + +.review-rating-stars > .material-icons { + cursor: pointer; +} + +.review-rating-stars { + transition: background-color ease-in-out 0.25s; + width: min-content; +} + +.review-rating-stars > .material-icons.active { + color: var(--primary); +} + +.product-review-user-image { + position: relative; + top: 10px; + border-radius: 50%; + width: 35px; + height: 35px; + object-fit: cover; +} + +.product-review-user-name { + margin-left: 0.5rem; +} + +.product-review-rating { + margin: 0.25rem 3rem; + color: var(--primary); + align-items: center; +} + +.product-review-title { + font-size: large; + margin: 0.5rem 3rem; +} + +.product-review-comment { + margin: 0.25rem 3rem; + white-space: pre-wrap; +} + +.product-review-date { + font-size: small; + margin: 1rem 0rem 0.25rem 3rem; +} + +.product-review-delete { + font-size: small; + margin: 1rem 0rem 0.25rem 1rem; +} + +.information-header { + font-size: larger; + margin-bottom: 0.5rem; +} + +.item-card { + padding: 0.4rem; + margin: 0.2rem; + box-shadow: 0 0 0.1rem var(--col-grey-dark); + width: auto; + height: 16.6rem; + flex: 0 1 12rem; + transition: box-shadow ease-in-out 0.25s; +} + +.item-card:hover { + box-shadow: 0 0 0.5rem var(--col-grey-dark); +} + +.item-image { + object-fit: cover; + display: block; + width: 100%; + height: 10rem; +} + +.item-details { + margin: 0.5rem; +} + +.item-name { + display: -webkit-box; + -webkit-box-orient: vertical; + + /* to specify the number of lines you want the text to run through... */ + -webkit-line-clamp: 2; + /* hide the overflowing text, i.e, texts that did not fit in to the box */ + overflow: hidden; + + height: 2.8rem; +} + +.item-price { + position: relative; + top: -0.2rem; + font-size: 0.75rem; +} + +.item-price-highlighted { + position: relative; + font-size: medium; + top: 0.2rem; +} + +.item-ratings { + position: relative; + top: -0.2rem; + font-size: medium; +} + +.item-total-sold { + font-size: small; +} + +.item-ratings .material-icons { + position: relative; + top: 0.15rem; +} + +.cart-item { + font-size: 14px; + background-color: white; + box-shadow: 0 0.05rem 0.1rem var(--col-grey-light); + margin: 0.4rem 0; + border-radius: 0.25rem; +} + +.cart-product img { + object-fit: contain; + width: 6rem; + height: 6rem; + vertical-align: middle; +} + +.cart-product { + width: 50%; +} + +.cart-quantity { + text-align: center; + width: 8%; +} +.cart-actions { + text-align: center; + width: 10%; + cursor: pointer; +} + +.cart-price, +.cart-total { + text-align: center; + width: 16%; +} + +a.cart-actions:visited { + color: var(--primary); +} + +.cart-item-header { + font-size: medium; + padding: 1rem; + display: flex; + border-bottom: 2px solid var(--secondary); +} +.cart-item-body { + padding: 0 0.5rem; +} + +.cart-item-vendor { + padding: 0.5rem; + border-bottom: 1px solid rgba(0, 0, 0, 0.12); +} + +.cart-item-vendor-name { + font-weight: bold; +} + +.cart-item-details { + align-items: center; + padding: 0.5rem 0; + display: flex; +} + +.cart-checkout-area { + position: sticky; + bottom: 0rem; + font-size: large; + background-color: white; + height: 6rem; + margin: 0.4rem 0 0 0; + padding: 0.5rem 0.5rem; + box-shadow: 0 0 0.5rem rgba(0, 0, 0, 0.1); +} + +.cart-checkout-area-wrapper { + margin-top: 0; + margin-bottom: 0; + height: 100%; + display: flex; + align-items: center; + text-align: center; +} + +.cart-checkout-quantity { + width: 75%; + text-align: right; +} + +.cart-checkout-total, +.cart-checkout-button-container { + width: 12.5%; +} + +.cart-checkout-total { + font-weight: bold; +} + +.cart-checkout-button { + width: 90%; + margin: 0 auto; + height: 3rem; + line-height: 3rem; +} + +.payment-modal { + width: 60rem; + height: auto; +} + +.payment-form-container { + width: 100%; + display: flex; + justify-content: space-between; +} + +.payment-form { + width: 65%; + padding: 0.5rem; +} + +.payment-items { + padding: 0.5rem; + width: 35%; +} + +.payment-items-header { + width: 100%; + border-bottom: 1px solid var(--col-grey-light); + margin: 0.25rem 0; +} + +.payment-items-details { + font-size: small; + margin: 0.5rem 0; + align-items: center; + justify-content: space-between; +} + +.payment-items-total { + font-weight: bold; + margin: 0.5rem 0; + padding: 0.5rem 0; + justify-content: space-between; + border-top: 2px solid var(--primary); +} + +.profile-wrapper { + display: grid; + grid-template-columns: minmax(15rem, 25%) 1fr; +} + +.profile-navbar { + position: sticky; + padding: 0.5rem 1rem; + height: 15rem; + top: 8rem; +} + +.profile-owner { + height: 4rem; + align-items: center; + border-bottom: 1px solid rgba(0, 0, 0, 0.1); +} + +.profile-pic { + object-fit: cover; + height: 3rem; + width: 3rem; + border-radius: 50%; +} + +.profile-name { + width: 75%; + margin: 0 auto; +} + +.profile-menu-item { + padding: 0.5rem 0 0 0; +} + +.profile-menu-item-icon { + margin: 0 0.5rem; +} + +.profile-menu-item-name { + margin: 2px 0.5rem; +} + +.profile-menu-item.active { + color: var(--primary); +} + +.profile-body { + padding: 0.4rem 1rem; + margin: 0.5rem; + border-radius: 0.2rem; + box-shadow: 0 0 0.25rem var(--col-grey-dark); + background-color: white; +} + +.purchase-body { + padding: 0.4rem 1rem; + margin: 0.5rem; +} + +.purchase-item-record { + margin: 0 1rem; + display: grid; + grid-template-columns: 5fr 1fr 2fr 2fr; +} + +.purchase-item-name, +.purchase-item-quanity, +.purchase-item-price { + margin: 0.25rem 0.25rem; + font-size: small; +} + +.purchase-item-actions { + justify-content: space-between; + align-items: center; + font-size: small; +} + +.purchase-item-actions a { + color: var(--primary); +} + +.purchase-item-actions a:visited { + color: var(--primary); +} + +.purchase-item-quanity span.material-icons { + position: relative; + top: 0.2rem; +} + +.profile-body-header { + font-size: 2rem; + padding: 0.4rem; + margin: 0px 0.5rem 1rem 0.5rem; + border-bottom: 1px solid rgba(0, 0, 0, 0.1); +} + +.purchase-item { + padding: 0.5rem; + margin: 0.5rem; + border-radius: 0.2rem; + box-shadow: 0 0 0.25rem var(--col-grey-dark); + background-color: white; + min-height: 14rem; + display: grid; + grid-template-rows: auto 1fr auto; +} + +.purchase-item-header { + justify-content: space-between; + border-bottom: 1px solid rgba(0, 0, 0, 0.1); + height: 2rem; + padding: 0.25rem 0.5rem; +} + +.purchase-item-header-details { + font-size: smaller; +} + +.purchase-item-body { + margin: 0.25rem 0; +} + +.purchase-item-footer { + display: grid; + grid-template-columns: 5fr 1fr 2fr 2fr; + border-top: 1px solid rgba(0, 0, 0, 0.1); + padding: 0.5rem 0.25rem 0 0.25rem; + align-items: center; +} + +.purchase-item-total { + font-size: large; + font-weight: bold; +} + +.footer { + z-index: 50; + background-image: linear-gradient(0deg, var(--primary), var(--secondary)); + color: rgba(255, 255, 255); + height: 20rem; + width: 100%; +} + +.footer-small { + z-index: 50; + background-image: linear-gradient(0deg, var(--primary-dark), var(--secondary-dark)); + color: rgba(255, 255, 255, 0.85); + height: 5rem; + width: 100%; +} + +.footer-small .material-icons { + position: relative; + top: 6px; +} + +.footer-wrapper { + display: flex; + justify-content: space-around; + padding: 0 1rem 0 1rem; + min-width: 1200px; + width: 75%; + height: 100%; +} + +.footer-wrapper-small { + align-items: center; + display: grid; + grid-template-columns: repeat(2, 1fr); + padding: 0 1rem 0 1rem; + min-width: 1200px; + width: 75%; + height: 100%; +} + +.footer-group { + margin: 4rem 0; +} + +.footer-group ul { + color: rgba(255, 255, 255); + list-style: none; + padding: 0; +} + +.footer-group a { + color: rgba(255, 255, 255); +} + +.footer-group a:visited { + color: rgba(255, 255, 255); +} + +.footer-logo { + width: 200px; + height: auto; +} + +.footer-group-header { + font-weight: bold; + font-size: large; +} + +.m-a { + margin: auto; +} + +.m-0-a { + margin: 0 auto; +} + +.m-a-0 { + margin: 0 auto; +} + +.m-0 { + margin: 0; +} + +.m-1 { + margin: 0.5rem; +} + +.m-2 { + margin: 1rem; +} + +.p-1 { + padding: 0.5rem; +} + +.p-2 { + padding: 1rem; +} + +.p-l-2 { + padding-left: 1rem; +} + +.p-r-2 { + padding-right: 1rem; +} + +.p-t-2 { + padding-top: 1rem; +} + +.p-b-2 { + padding-bottom: 1rem; +} + +.p-l-1 { + padding-left: 0.5rem; +} + +.p-r-1 { + padding-right: 0.5rem; +} + +.p-t-1 { + padding-top: 0.5rem; +} + +.p-b-1 { + padding-bottom: 0.5rem; +} + +.p-l-0 { + padding-left: 0; +} + +.p-r-0 { + padding-right: 0; +} + +.p-t-0 { + padding-top: 0; +} + +.p-b-0 { + padding-bottom: 0; +} + +.m-l-2 { + margin-left: 1rem; +} + +.m-r-2 { + margin-right: 1rem; +} + +.m-t-2 { + margin-top: 1rem; +} + +.m-b-2 { + margin-bottom: 1rem; +} + +.m-l-1 { + margin-left: 0.5rem; +} + +.m-r-1 { + margin-right: 0.5rem; +} + +.m-t-1 { + margin-top: 0.5rem; +} + +.m-b-1 { + margin-bottom: 0.5rem; +} + +.m-l-0 { + margin-left: 0; +} + +.m-r-0 { + margin-right: 0; +} + +.m-t-0 { + margin-top: 0; +} + +.m-b-0 { + margin-bottom: 0; +} + +/* Rules for sizing the icon. */ +.material-icons.md-12 { + font-size: 12px; +} +.material-icons.md-16 { + font-size: 16px; +} +.material-icons.md-18 { + font-size: 18px; +} +.material-icons.md-24 { + font-size: 24px; +} +.material-icons.md-36 { + font-size: 36px; +} +.material-icons.md-48 { + font-size: 48px; +} +.material-icons.md-72 { + font-size: 72px; +} +.material-icons.md-144 { + font-size: 144px; +} + +@media all and (max-width: 1280px) { + .top-bar-wrapper { + width: 100%; + } + .content-wrapper { + min-width: 0px; + width: 100%; + } + + .special-offers { + grid-template-rows: 2fr 1fr; + grid-template-columns: 1fr; + } + + .item-listings-wrapper { + grid-template-rows: 535px 1fr; + } + + .footer-wrapper { + min-width: 0; + width: 100%; + font-size: smaller; + } + .cart-checkout-area { + font-size: medium; + } + .payment-modal { + width: 40rem; + height: auto; + } +} diff --git a/styles/template.css b/styles/template.css new file mode 100644 index 0000000..6757419 --- /dev/null +++ b/styles/template.css @@ -0,0 +1,958 @@ +/* cyrillic-ext */ + +@font-face { + font-family: 'Nunito'; + font-style: normal; + font-weight: 400; + font-display: swap; + src: local('Nunito Regular'), local('Nunito-Regular'), url(../fonts/XRXV3I6Li01BKofIOOaBXso.woff2) format('woff2'); + unicode-range: U+0460-052F, U+1C80-1C88, U+20B4, U+2DE0-2DFF, U+A640-A69F, U+FE2E-FE2F; +} + +/* cyrillic */ + +@font-face { + font-family: 'Nunito'; + font-style: normal; + font-weight: 400; + font-display: swap; + src: local('Nunito Regular'), local('Nunito-Regular'), url(../fonts/XRXV3I6Li01BKofIMeaBXso.woff2) format('woff2'); + unicode-range: U+0400-045F, U+0490-0491, U+04B0-04B1, U+2116; +} + +/* vietnamese */ + +@font-face { + font-family: 'Nunito'; + font-style: normal; + font-weight: 400; + font-display: swap; + src: local('Nunito Regular'), local('Nunito-Regular'), url(../fonts/XRXV3I6Li01BKofIOuaBXso.woff2) format('woff2'); + unicode-range: U+0102-0103, U+0110-0111, U+0128-0129, U+0168-0169, U+01A0-01A1, U+01AF-01B0, U+1EA0-1EF9, U+20AB; +} + +/* latin-ext */ + +@font-face { + font-family: 'Nunito'; + font-style: normal; + font-weight: 400; + font-display: swap; + src: local('Nunito Regular'), local('Nunito-Regular'), url(../fonts/XRXV3I6Li01BKofIO-aBXso.woff2) format('woff2'); + unicode-range: U+0100-024F, U+0259, U+1E00-1EFF, U+2020, U+20A0-20AB, U+20AD-20CF, U+2113, U+2C60-2C7F, U+A720-A7FF; +} + +/* latin */ + +@font-face { + font-family: 'Nunito'; + font-style: normal; + font-weight: 400; + font-display: swap; + src: local('Nunito Regular'), local('Nunito-Regular'), url(../fonts/XRXV3I6Li01BKofINeaB.woff2) format('woff2'); + unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+2000-206F, U+2074, U+20AC, + U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD; +} + +/* fallback */ + +@font-face { + font-family: 'Material Icons'; + font-style: normal; + font-weight: 400; + src: url(../fonts/flUhRq6tzZclQEJ-Vdg-IuiaDsNc.woff2) format('woff2'); +} + +:root { + --primary: rgb(224, 17, 95); + --primary-dark: rgb(197, 16, 85); + --secondary: rgb(192, 12, 81); + --secondary-dark: rgb(169, 10, 71); + --bg-primary: #efefef; + --bg-primary-light: #fafafa; + --bg-primary-dark: #757575; + --col-grey-dark: rgba(0, 0, 0, 0.3); + --col-grey-light: rgba(0, 0, 0, 0.15); +} + +.material-icons { + font-family: 'Material Icons'; + font-weight: normal; + font-style: normal; + font-size: 24px; + line-height: 1; + letter-spacing: normal; + text-transform: none; + display: inline-block; + white-space: nowrap; + word-wrap: normal; + direction: ltr; + /* -moz-font-feature-settings: "liga"; */ + -moz-osx-font-smoothing: grayscale; +} + +html { + scroll-behavior: smooth; +} + +* { + box-sizing: border-box; +} + +body { + margin: 0; + background-color: #efefef; + font-family: 'Nunito', sans-serif !important; +} + +label { + margin: 0.25rem 0; +} + +input { + font-family: 'Nunito', sans-serif; +} + +form input[type='text'], +form input[type='password'], +form input[type='email'], +form input[type='date'], +form input[type='number'] { + height: 2rem; + border-radius: 0.25rem; + border: 1px solid rgba(0, 0, 0, 0.15); + box-shadow: none; + box-sizing: border-box; + margin: 0.25rem 0; + padding: 0 0.5rem; + width: 100%; + transition: box-shadow ease-in-out 0.25s, border ease-in-out 0.25s; +} + +form input[type='text']:focus, +form input[type='password']:focus, +form input[type='email']:focus, +form input[type='date']:focus, +form input[type='number']:focus, +form textarea:focus { + box-shadow: 0 0 0.25rem rgb(223, 17, 96); + border: 1px solid rgba(223, 17, 96); +} + +form textarea { + font-family: 'Nunito', sans-serif; + border-radius: 0.25rem; + border: 1px solid rgba(0, 0, 0, 0.15); + box-shadow: none; + box-sizing: border-box; + margin: 0.25rem 0; + padding: 0.5rem; + width: 100%; + resize: vertical; + min-height: 8rem; + transition: box-shadow ease-in-out 0.25s, border ease-in-out 0.25s; +} + +form input[type='button'], +form input[type='submit'] { + background-color: #e0115f; + color: white; + text-align: center; + height: 2.5rem; + width: 100%; + line-height: 2.5rem; + border-radius: 0.5rem; + border: none; + box-shadow: 0 0 0.1rem rgba(0, 0, 0, 0.3); + cursor: pointer; + transition: box-shadow ease-in-out 0.25s; + margin: 0.25rem 0; +} + +form input[type='button']:hover, +form input[type='submit']:hover { + box-shadow: 0 0 0.5rem rgba(0, 0, 0, 0.3); +} + +form input[type='button']:active, +form input[type='submit']:active { + background-color: #db125f; + -webkit-box-shadow: inset 0px 0px 5px rgba(0, 0, 0, 0.3); + -moz-box-shadow: inset 0px 0px 5px rgba(0, 0, 0, 0.3); + box-shadow: inset 0px 0px 5px rgba(0, 0, 0, 0.3); + outline: none; +} + +form input.invalid, +form textarea.invalid { + box-shadow: 0 0 0.2rem 1px red; +} + +input[type='text'], +input[type='password'], +input[type='email'], +input[type='date'], +input[type='number'], +select { + height: 2rem; + border-radius: 0.25rem; + border: 1px solid rgba(0, 0, 0, 0.15); + box-shadow: none; + box-sizing: border-box; + margin: 0.25rem 0; + padding: 0 0.5rem; + transition: box-shadow ease-in-out 0.25s, border ease-in-out 0.25s; +} + +input[type='text']:focus, +input[type='password']:focus, +input[type='email']:focus, +input[type='date']:focus, +input[type='number']:focus, +select:focus, +textarea:focus { + box-shadow: 0 0 0.25rem rgb(223, 17, 96); + border: 1px solid rgba(223, 17, 96); +} + +input[type='text']::placeholder, +input[type='number']::placeholder, +input[type='email']::placeholder, +select::placeholder, +textarea::placeholder, +input[type='password']::placeholder { + color: rgba(0, 0, 0, 0.54); + font-weight: bold; + font-family: 'Nunito', sans-serif; +} + +select { + position: relative; + -webkit-appearance: none; + -moz-appearance: none; + background-image: url("data:image/svg+xml;utf8,"); + background-repeat: no-repeat; + background-position-x: 100%; + background-position-y: 3px; + width: auto; + padding: 0 1.5rem 0 0.5rem; +} + +a { + text-decoration: none; + color: black; +} + +a:visited { + color: black; +} + +a:active { + color: black; +} + +hr { + border: 0; + height: 1px; + background: var(--col-grey-light); +} + +table.table { + border-collapse: separate; + border-spacing: 0; + margin: 0.5rem; + width: calc(100% - (0.5rem * 2)); + min-width: 0; + min-height: 0; +} + +table.table thead td, +table.table thead th { + text-align: left; + color: white; + background-color: var(--primary); + padding: 0.5rem; +} + +table.table tr th, +table.table tr td { + border-right: 1px solid var(--col-grey-light); + border-bottom: 1px solid var(--col-grey-light); +} + +table.table tr th:first-child, +table.table tr td:first-child { + border-left: 1px solid var(--col-grey-light); +} + +table.table tr th { + border-top: 1px solid var(--col-grey-light); +} + +table.table th:first-child { + border-radius: 3px 0 0 0; +} + +table.table th:last-child { + border-radius: 0 3px 0 0; +} + +table.table tr:last-child td:first-child { + border-radius: 0 0 0 3px; +} +table.table tr:last-child td:last-child { + border-radius: 0 0 3px 0; +} + +table.table td { + text-align: left; + padding: 0.5rem; + max-width: 0rem; +} + +/* table.table tbody tr:nth-child(odd) td { + background-color: hsla(337, 86%, 47%, 0.1); +} */ + +table.conversation { + margin: 1rem 0.5rem; + width: 100%; + border-collapse: seperate; + border-spacing: 0; + width: calc(100% - (0.5rem * 2)); +} + +table.conversation tbody td { + vertical-align: top; + text-align: left; + padding: 0.5rem; + max-width: 0rem; +} + +table.conversation tr th, +table.conversation tr td { + border-right: 1px solid var(--col-grey-light); + border-bottom: 1px solid var(--col-grey-light); +} + +table.conversation tr th:first-child, +table.conversation tr td:first-child { + border-left: 1px solid var(--col-grey-light); +} + +table.conversation tr th { + border-top: 1px solid var(--col-grey-light); +} + +table.conversation tbody td:last-child { + background-color: hsl(0, 0%, 96%); + border-radius: 0 0 3px 0; +} + +table.conversation tbody td:first-child { + background-color: hsl(0, 0%, 98%); + border-radius: 0 0 0 3px; +} + +table.conversation tbody th { + vertical-align: top; + text-align: left; + padding: 0.4rem 0.5rem; + max-width: 0rem; + color: white; + background-color: var(--primary); +} + +table.conversation tbody th:first-child { + border-radius: 3px 0 0 0; +} + +table.conversation tbody th:last-child { + border-radius: 0 3px 0 0; +} + +.conversation-title, +.inbox-title { + border: 1px solid var(--primary-dark); + background-color: var(--primary); + color: white; + padding: 0.5rem; + border-radius: 3px; + margin: 0.5rem; + font-size: x-large; +} + +.conversation-user-container { + min-height: 12rem; +} + +.conversation-user img { + width: 10rem; + height: 10rem; + object-fit: cover; + border-radius: 3px; +} + +.conversation-timestamp { + font-size: 12px; + font-weight: bold; +} + +.inbox-timestamp { + font-size: small; + margin-top: 0.3rem; +} + +.form-input-group { + margin: 0.5rem; +} + +.flex { + display: flex; +} + +.hidden { + display: none; +} + +.b-r-0 { + border-radius: 0 !important; +} + +.b-r-1 { + border-radius: 0.25rem !important; +} + +.b-r-2 { + border-radius: 0.5rem !important; +} + +.b-r-3 { + border-radius: 0.75rem !important; +} + +.b-r-4 { + border-radius: 1rem !important; +} + +.unselectable { + -webkit-user-select: none; +} + +.ellipsis-truncate { + white-space: nowrap; + overflow: hidden; + text-overflow: ellipsis; +} + +.strike { + text-decoration: line-through; + color: rgb(161, 161, 161); +} + +.underline { + text-decoration: underline; +} + +.cultured { + color: #efefef; +} + +.cultured-light { + color: #fafafa; +} + +.cultured-dark { + color: #757575; +} + +.ruby { + color: #e0115f !important; +} +.vis-ruby:visited { + color: var(--primary) !important; +} + +.rubine-red { + color: #cf1159; +} + +.white { + color: white !important; +} +.vis-white:visited { + color: white !important; +} + +.black { + color: black !important; +} +.vis-black:visited { + color: black !important; +} + +.grey { + color: rgba(0, 0, 0, 0.3); +} + +.bg-cultured { + background-color: #efefef; +} + +.bg-cultured-light { + background-color: #fafafa; +} + +.bg-cultured-dark { + background-color: #757575; +} + +.bg-ruby { + background-color: #e0115f; +} + +.bg-rubine-red { + background-color: #cf1159; +} + +.bg-white { + background-color: #ffffff; +} + +.notification { + display: inline-block; + font-weight: bold; + text-align: center; + font-size: x-small; + padding: 0 0.25rem; + border-radius: 50%; + min-width: 1rem; + height: 1rem; + line-height: 1rem; +} + +.cart-notification { + position: relative; + bottom: 2.5rem; + left: 4rem; + z-index: -1; + color: var(--primary); + font-weight: bold; + width: 1.25rem; + height: 1.25rem; + line-height: 1.25rem; + text-align: center; + background-color: white; + border-radius: 50%; +} + +.card-success { + margin: 0.5rem; + padding: 0.5rem; + box-shadow: 0 1px 0.1rem var(--col-grey-light); + border-radius: 3px; + background-color: rgba(52, 185, 63, 0.65); + border: 1px solid rgb(82, 194, 88); + text-shadow: 0 0 0.5rem rgba(0, 0, 0, 0.5); + color: white; +} +.card-error { + margin: 0.5rem; + padding: 0.5rem; + box-shadow: 0 1px 0.1rem var(--col-grey-light); + border-radius: 3px; + background-color: rgba(214, 0, 0, 0.65); + border: 1px solid rgb(211, 0, 0); + text-shadow: 0 0 0.5rem rgba(0, 0, 0, 0.5); + color: white; +} +.card-generic { + margin: 0.5rem; + padding: 0.5rem; + background-color: white; + box-shadow: 0 1px 0.1rem var(--col-grey-light); + border-radius: 3px; +} + +.w-1 { + width: 12.5% !important; +} + +.w-2 { + width: 25% !important; +} + +.w-3 { + width: 50% !important; +} + +.w-4 { + width: 100% !important; +} + +.h-1 { + height: 12.5% !important; +} + +.h-2 { + height: 25% !important; +} + +.h-3 { + height: 50% !important; +} + +.h-4 { + height: 100% !important; +} + +.text-center { + text-align: center !important; +} + +.text-left { + text-align: left !important; +} + +.text-right { + text-align: right !important; +} + +.pre-line { + white-space: pre-line; +} + +.center-in-page-abs { + position: absolute; + top: 50%; + left: 50%; + transform: translate(-50%, -50%); +} + +.center-in-page-rel { + position: relative; + top: 50%; + left: 50%; + transform: translate(-50%, -50%); +} + +.btn-primary { + background-color: #e0115f; + color: white; + text-align: center; + height: 2.5rem; + line-height: 2.5rem; + border-radius: 0.5rem; + border: none; + box-shadow: 0 0 0.1rem rgba(0, 0, 0, 0.3); + cursor: pointer; + transition: box-shadow ease-in-out 0.25s; + margin: 0.25rem; +} + +.btn-primary:hover { + box-shadow: 0 0 0.5rem rgba(0, 0, 0, 0.3); +} + +.btn-primary:active { + background-color: #db125f; + -webkit-box-shadow: inset 0px 0px 5px rgba(0, 0, 0, 0.3); + -moz-box-shadow: inset 0px 0px 5px rgba(0, 0, 0, 0.3); + box-shadow: inset 0px 0px 5px rgba(0, 0, 0, 0.3); + outline: none; +} + +.body-wrapper { + min-height: 100vh; + display: grid; + grid-template-rows: 8rem minmax(calc(100vh - 8rem), 1fr) auto; +} + +.modal-close-button { + position: absolute; + float: right; + top: -0.75rem; + right: -0.75rem; + background-color: rgb(44, 44, 44); + border-radius: 50%; + width: 2rem; + height: 2rem; + line-height: 2rem; + text-align: center; + cursor: pointer; + box-shadow: 0 0 0.25rem rgba(0, 0, 0, 0.3); +} + +.modal-content { + padding: 1rem; + border-radius: 0.5rem; + background-color: white; + box-sizing: border-box; + z-index: 100; + box-shadow: 0 0 0.25rem rgba(0, 0, 0, 0.3); +} + +.modal-body { + width: 100%; + height: 100%; +} + +.modal-text { + line-height: 48px; +} + +.modal { + position: fixed; + top: 0; + left: 0; + width: 100vw; + height: 100vh; + margin: 0; + padding: 0; + z-index: 100; + background-color: rgba(0, 0, 0, 0.5); +} + +.invisible { + visibility: hidden; +} + +.chapalang-logo { + height: 60px; + width: auto; +} + +.container { + margin-right: auto; + margin-left: auto; +} + +.content-wrapper { + margin-top: 0.4rem; + margin-bottom: 0.4rem; + min-width: 1200px; + width: 60%; +} + +.flex-wrapper { + display: flex; + flex-wrap: wrap; + justify-content: center; +} + +.flex-column { + flex-direction: column; +} + +.flex-row { + flex-direction: row; +} + +.flex-justify-center { + justify-content: center; +} + +.flex-justify-space-between { + justify-content: space-between; +} + +.flex-align-center { + align-items: center; +} + +.flex-align-baseline { + align-items: baseline; +} +.footer { + z-index: 50; + background-image: linear-gradient(0deg, #e0115f, #cc0a54); + color: rgba(255, 255, 255, 0.85); + height: 20rem; + width: 100%; +} + +.footer-small { + z-index: 50; + background-image: linear-gradient(0deg, #c51056, #a70a46); + color: rgba(255, 255, 255, 0.85); + height: 5rem; + width: 100%; +} + +.footer-small .material-icons { + position: relative; + top: 6px; +} + +.footer-wrapper { + display: flex; + justify-content: space-around; + padding: 0 1rem 0 1rem; + min-width: 1200px; + width: 75%; + height: 100%; +} + +.footer-wrapper-small { + align-items: center; + display: grid; + grid-template-columns: repeat(2, 1fr); + padding: 0 1rem 0 1rem; + min-width: 1200px; + width: 75%; + height: 100%; +} + +.footer-group { + margin: 4rem 0; +} + +.footer-group ul { + color: rgba(255, 255, 255); + list-style: none; + padding: 0; +} + +.footer-group a { + color: rgba(255, 255, 255); +} + +.footer-group a:visited { + color: rgba(255, 255, 255); +} + +.footer-logo { + width: 200px; + height: auto; +} + +.footer-group-header { + font-weight: bold; + font-size: large; +} + +.m-0 { + margin: 0 !important; +} + +.m-1 { + margin: 0.5rem !important; +} + +.m-2 { + margin: 1rem !important; +} + +.p-1 { + padding: 0.5rem; +} + +.p-2 { + padding: 1rem; +} + +.p-l-2 { + padding-left: 1rem; +} + +.p-r-2 { + padding-right: 1rem; +} + +.p-t-2 { + padding-top: 1rem; +} + +.p-b-2 { + padding-bottom: 1rem; +} + +.p-l-1 { + padding-left: 0.5rem; +} + +.p-r-1 { + padding-right: 0.5rem; +} + +.p-t-1 { + padding-top: 0.5rem; +} + +.p-b-1 { + padding-bottom: 0.5rem; +} + +.p-l-0 { + padding-left: 0; +} + +.p-r-0 { + padding-right: 0; +} + +.p-t-0 { + padding-top: 0; +} + +.p-b-0 { + padding-bottom: 0; +} + +.m-l-1 { + margin-left: 0.5rem !important; +} + +.m-r-1 { + margin-right: 0.5rem !important; +} + +.m-t-1 { + margin-top: 0.5rem !important; +} + +.m-t-2 { + margin-top: 1rem !important; +} + +.m-b-1 { + margin-bottom: 0.5rem !important; +} + +.m-0-a { + margin: 0 auto !important; +} + +/* Rules for sizing the icon. */ + +.material-icons.md-12 { + font-size: 12px; +} + +.material-icons.md-16 { + font-size: 16px; +} + +.material-icons.md-18 { + font-size: 18px; +} + +.material-icons.md-24 { + font-size: 24px; +} + +.material-icons.md-36 { + font-size: 36px; +} + +.material-icons.md-48 { + font-size: 48px; +} + +.material-icons.md-72 { + font-size: 72px; +} + +.material-icons.md-144 { + font-size: 144px; +} diff --git a/styles/user_profile.css b/styles/user_profile.css new file mode 100644 index 0000000..bbcae2f --- /dev/null +++ b/styles/user_profile.css @@ -0,0 +1,6 @@ +:target { + display: block; + position: relative; + top: -8rem; + visibility: hidden; +} \ No newline at end of file diff --git a/styles/vendor.css b/styles/vendor.css new file mode 100644 index 0000000..1a86958 --- /dev/null +++ b/styles/vendor.css @@ -0,0 +1,183 @@ +.vendor-container { + display: grid; + grid-template-columns: 1fr 5fr; +} + +.vendor-sidenav-header { + margin: 0.5rem 0; + font-size: large; + font-weight: bold; +} + +.vendor-sidenav-item { + display: block; + margin: 0.5rem 0; + padding-left: 1.5rem; +} + +.vendor-sidenav-item.active { + color: #e0115f; +} + +.visited-white:visited { + color: white; +} + +/* --------------------------vendor_profile.php-------------------------- */ +.img-upload { + width: 250px; + height: 250px; + padding-bottom: 3%; + margin: 0.25rem; + object-fit: contain; +} + +.ignorefile { + display: none; +} + +/* --------------------------vendor_add_product.php-------------------------- */ + +.addimages input { + padding: 1%; + margin-left: 3.5px; + width: 250px; +} + +/* --------------------------vendor_product_approval.html-------------------------- */ +.approvalimgs { + display: flex; + flex-wrap: wrap; +} + +.item-card { + padding: 0.2rem; + margin: 0.2rem; + width: 10rem; + height: 16rem; + background-color: white; + border-radius: 3px; + box-shadow: 0 1px 0.1rem var(--col-grey-light); + transition: box-shadow ease-in-out 0.25s; +} + +.item-card:hover { + box-shadow: 0 1px 0.5rem var(--col-grey-light); +} + +.item-image { + display: block; + margin: 0.25rem auto; + height: 10rem; + width: 9rem; + object-fit: cover; +} + +.item-name { + padding: 5px; + display: -webkit-box; + -webkit-box-orient: vertical; + -webkit-line-clamp: 3; + overflow: hidden; +} + +/* --------------------------vendor_product_page & vendor_refund_ticket.html-------------------------- */ +.productdetails { + width: 1000px; + padding: 1rem; + word-wrap: break-word; + background-color: white; + border-radius: 3px; + box-shadow: 0 1px 0.1rem var(--col-grey-light); +} + +/* --------------------------tables-------------------------- */ +table { + border-collapse: separate; + border-spacing: 0; + width: 100%; + background-color: white; +} + +tr { + word-wrap: break-word; +} + +tr th, +tr td { + border-right: 1px solid rgba(0, 0, 0, 0.3); + border-bottom: 1px solid rgba(0, 0, 0, 0.3); + padding: 2px; + text-align: center; +} + +tr th:first-child, +tr td:first-child { + border-left: 1px solid rgba(0, 0, 0, 0.3); +} + +tr th { + background-color: #e0115f; + color: white; + border-top: 1px solid rgba(0, 0, 0, 0.3); +} + +tr:first-child th:first-child { + border-top-left-radius: 5px; +} + +tr:first-child th:last-child { + border-top-right-radius: 5px; +} + +tr:last-child td:first-child { + border-bottom-left-radius: 5px; +} + +tr:last-child td:last-child { + border-bottom-right-radius: 5px; +} + +tbody { + overflow-y: scroll; +} + +/* --------------------------combo box-------------------------- */ +select { + background-color: #e0115f; + color: white; + height: 30px; +} + +/* --------------------------vendor_refund.php-------------------------- */ +.vendor-product-image img { + width: 250px; + height: 250px; + padding-bottom: 3%; + margin: 0.25rem; + object-fit: contain; +} + +/* --------------------------vendor_income.php-------------------------- */ +.total { + text-align: center; +} + +.total h4 { + width: 150px; + text-align: center; + padding: 20px; + border: 1px solid #e0115f; + border-radius: 5px; + word-wrap: break-word; + background-color: white; +} + +/* --------------------------readonly input-------------------------- */ +.readonly input[type="text"] { + background-color: rgb(220, 220, 220); +} + +#income-chart { + width: 80%; +} diff --git a/terms.php b/terms.php new file mode 100644 index 0000000..b90f078 --- /dev/null +++ b/terms.php @@ -0,0 +1,155 @@ + + + + + + + + + + About Us - Chapalang Clothes + + + + + + + + + + + +
+
+ +
+

TERMS OF SERVICE

+
+

OVERVIEW

+

This website is operated by Chapalang Clothes. Throughout the site, the terms “we”, “us” and “our” refer to Chapalang Clothes. Chapalang Clothes offers this website, including all information, tools and services available from this site to you, the user, conditioned upon your acceptance of all terms, conditions, policies and notices stated here. + + By visiting our site and/ or purchasing something from us, you engage in our “Service” and agree to be bound by the following terms and conditions (“Terms of Service”, “Terms”), including those additional terms and conditions and policies referenced herein and/or available by hyperlink. These Terms of Service apply to all users of the site, including without limitation users who are browsers, vendors, customers, merchants, and/ or contributors of content. + + Please read these Terms of Service carefully before accessing or using our website. By accessing or using any part of the site, you agree to be bound by these Terms of Service. If you do not agree to all the terms and conditions of this agreement, then you may not access the website or use any services. If these Terms of Service are considered an offer, acceptance is expressly limited to these Terms of Service. + + Any new features or tools which are added to the current store shall also be subject to the Terms of Service. You can review the most current version of the Terms of Service at any time on this page. We reserve the right to update, change or replace any part of these Terms of Service by posting updates and/or changes to our website. It is your responsibility to check this page periodically for changes. Your continued use of or access to the website following the posting of any changes constitutes acceptance of those changes. + + Our store is hosted on Shopify Inc. They provide us with the online e-commerce platform that allows us to sell our products and services to you.

+

SECTION 1 - ONLINE STORE TERMS

+

By agreeing to these Terms of Service, you represent that you are at least the age of majority in your state or province of residence, or that you are the age of majority in your state or province of residence and you have given us your consent to allow any of your minor dependents to use this site. + + You may not use our products for any illegal or unauthorized purpose nor may you, in the use of the Service, violate any laws in your jurisdiction (including but not limited to copyright laws). + + You must not transmit any worms or viruses or any code of a destructive nature. + + A breach or violation of any of the Terms will result in an immediate termination of your Services.

+

SECTION 2 - GENERAL CONDITIONS

+

We reserve the right to refuse service to anyone for any reason at any time. + + You understand that your content (not including credit card information), may be transferred unencrypted and involve (a) transmissions over various networks; and (b) changes to conform and adapt to technical requirements of connecting networks or devices. Credit card information is always encrypted during transfer over networks. + + You agree not to reproduce, duplicate, copy, sell, resell or exploit any portion of the Service, use of the Service, or access to the Service or any contact on the website through which the service is provided, without express written permission by us. + + The headings used in this agreement are included for convenience only and will not limit or otherwise affect these Terms.

+

SECTION 3 - ACCURACY, COMPLETENESS AND TIMELINESS OF INFORMATION

+

We are not responsible if information made available on this site is not accurate, complete or current. The material on this site is provided for general information only and should not be relied upon or used as the sole basis for making decisions without consulting primary, more accurate, more complete or more timely sources of information. Any reliance on the material on this site is at your own risk. + + This site may contain certain historical information. Historical information, necessarily, is not current and is provided for your reference only. We reserve the right to modify the contents of this site at any time, but we have no obligation to update any information on our site. You agree that it is your responsibility to monitor changes to our site.

+

SECTION 4 - MODIFICATIONS TO THE SERVICE AND PRICES

+

Prices for our products are subject to change without notice. + + We reserve the right at any time to modify or discontinue the Service (or any part or content thereof) without notice at any time. + + We shall not be liable to you or to any third-party for any modification, price change, suspension or discontinuance of the Service.

+

SECTION 5 - PRODUCTS OR SERVICES (if applicable)

+

Certain products or services may be available exclusively online through the website. These products or services may have limited quantities and are subject to return or exchange only according to our Return Policy. + + We have made every effort to display as accurately as possible the colors and images of our products that appear at the store. We cannot guarantee that your computer monitor's display of any color will be accurate. + + We reserve the right, but are not obligated, to limit the sales of our products or Services to any person, geographic region or jurisdiction. We may exercise this right on a case-by-case basis. We reserve the right to limit the quantities of any products or services that we offer. All descriptions of products or product pricing are subject to change at anytime without notice, at the sole discretion of us. We reserve the right to discontinue any product at any time. Any offer for any product or service made on this site is void where prohibited. + + We do not warrant that the quality of any products, services, information, or other material purchased or obtained by you will meet your expectations, or that any errors in the Service will be corrected.

+

SECTION 6 - ACCURACY OF BILLING AND ACCOUNT INFORMATION

+

We reserve the right to refuse any order you place with us. We may, in our sole discretion, limit or cancel quantities purchased per person, per household or per order. These restrictions may include orders placed by or under the same customer account, the same credit card, and/or orders that use the same billing and/or shipping address. In the event that we make a change to or cancel an order, we may attempt to notify you by contacting the e-mail and/or billing address/phone number provided at the time the order was made. We reserve the right to limit or prohibit orders that, in our sole judgment, appear to be placed by dealers, resellers or distributors. + + You agree to provide current, complete and accurate purchase and account information for all purchases made at our store. You agree to promptly update your account and other information, including your email address and credit card numbers and expiration dates, so that we can complete your transactions and contact you as needed. + + For more detail, please review our Returns Policy.

+

SECTION 7 - OPTIONAL TOOLS

+

We may provide you with access to third-party tools over which we neither monitor nor have any control nor input. + + You acknowledge and agree that we provide access to such tools ”as is” and “as available” without any warranties, representations or conditions of any kind and without any endorsement. We shall have no liability whatsoever arising from or relating to your use of optional third-party tools. + + Any use by you of optional tools offered through the site is entirely at your own risk and discretion and you should ensure that you are familiar with and approve of the terms on which tools are provided by the relevant third-party provider(s). + + We may also, in the future, offer new services and/or features through the website (including, the release of new tools and resources). Such new features and/or services shall also be subject to these Terms of Service.

+

SECTION 8 - THIRD-PARTY LINKS

+

Certain content, products and services available via our Service may include materials from third-parties. + + Third-party links on this site may direct you to third-party websites that are not affiliated with us. We are not responsible for examining or evaluating the content or accuracy and we do not warrant and will not have any liability or responsibility for any third-party materials or websites, or for any other materials, products, or services of third-parties. + + We are not liable for any harm or damages related to the purchase or use of goods, services, resources, content, or any other transactions made in connection with any third-party websites. Please review carefully the third-party's policies and practices and make sure you understand them before you engage in any transaction. Complaints, claims, concerns, or questions regarding third-party products should be directed to the third-party.

+

SECTION 9 - USER COMMENTS, FEEDBACK AND OTHER SUBMISSIONS

+

If, at our request, you send certain specific submissions (for example contest entries) or without a request from us you send creative ideas, suggestions, proposals, plans, or other materials, whether online, by email, by postal mail, or otherwise (collectively, 'comments'), you agree that we may, at any time, without restriction, edit, copy, publish, distribute, translate and otherwise use in any medium any comments that you forward to us. We are and shall be under no obligation (1) to maintain any comments in confidence; (2) to pay compensation for any comments; or (3) to respond to any comments. + + We may, but have no obligation to, monitor, edit or remove content that we determine in our sole discretion are unlawful, offensive, threatening, libelous, defamatory, pornographic, obscene or otherwise objectionable or violates any party’s intellectual property or these Terms of Service. + + You agree that your comments will not violate any right of any third-party, including copyright, trademark, privacy, personality or other personal or proprietary right. You further agree that your comments will not contain libelous or otherwise unlawful, abusive or obscene material, or contain any computer virus or other malware that could in any way affect the operation of the Service or any related website. You may not use a false e-mail address, pretend to be someone other than yourself, or otherwise mislead us or third-parties as to the origin of any comments. You are solely responsible for any comments you make and their accuracy. We take no responsibility and assume no liability for any comments posted by you or any third-party.

+

SECTION 10 - PERSONAL INFORMATION

+

Your submission of personal information through the store is governed by our Privacy Policy. To view our Privacy Policy.

+

SECTION 11 - ERRORS, INACCURACIES AND OMISSIONS

+

Occasionally there may be information on our site or in the Service that contains typographical errors, inaccuracies or omissions that may relate to product descriptions, pricing, promotions, offers, product shipping charges, transit times and availability. We reserve the right to correct any errors, inaccuracies or omissions, and to change or update information or cancel orders if any information in the Service or on any related website is inaccurate at any time without prior notice (including after you have submitted your order). + + We undertake no obligation to update, amend or clarify information in the Service or on any related website, including without limitation, pricing information, except as required by law. No specified update or refresh date applied in the Service or on any related website, should be taken to indicate that all information in the Service or on any related website has been modified or updated.

+

SECTION 12 - PROHIBITED USES

+

In addition to other prohibitions as set forth in the Terms of Service, you are prohibited from using the site or its content: (a) for any unlawful purpose; (b) to solicit others to perform or participate in any unlawful acts; (c) to violate any international, federal, provincial or state regulations, rules, laws, or local ordinances; (d) to infringe upon or violate our intellectual property rights or the intellectual property rights of others; (e) to harass, abuse, insult, harm, defame, slander, disparage, intimidate, or discriminate based on gender, sexual orientation, religion, ethnicity, race, age, national origin, or disability; (f) to submit false or misleading information; (g) to upload or transmit viruses or any other type of malicious code that will or may be used in any way that will affect the functionality or operation of the Service or of any related website, other websites, or the Internet; (h) to collect or track the personal information of others; (i) to spam, phish, pharm, pretext, spider, crawl, or scrape; (j) for any obscene or immoral purpose; or (k) to interfere with or circumvent the security features of the Service or any related website, other websites, or the Internet. We reserve the right to terminate your use of the Service or any related website for violating any of the prohibited uses.

+

SECTION 13 - DISCLAIMER OF WARRANTIES; LIMITATION OF LIABILITY

+ +

We do not guarantee, represent or warrant that your use of our service will be uninterrupted, timely, secure or error-free. + + We do not warrant that the results that may be obtained from the use of the service will be accurate or reliable. + + You agree that from time to time we may remove the service for indefinite periods of time or cancel the service at any time, without notice to you. + + You expressly agree that your use of, or inability to use, the service is at your sole risk. The service and all products and services delivered to you through the service are (except as expressly stated by us) provided 'as is' and 'as available' for your use, without any representation, warranties or conditions of any kind, either express or implied, including all implied warranties or conditions of merchantability, merchantable quality, fitness for a particular purpose, durability, title, and non-infringement. + + In no case shall Chapalang Clothes, our directors, officers, employees, affiliates, agents, contractors, interns, suppliers, service providers or licensors be liable for any injury, loss, claim, or any direct, indirect, incidental, punitive, special, or consequential damages of any kind, including, without limitation lost profits, lost revenue, lost savings, loss of data, replacement costs, or any similar damages, whether based in contract, tort (including negligence), strict liability or otherwise, arising from your use of any of the service or any products procured using the service, or for any other claim related in any way to your use of the service or any product, including, but not limited to, any errors or omissions in any content, or any loss or damage of any kind incurred as a result of the use of the service or any content (or product) posted, transmitted, or otherwise made available via the service, even if advised of their possibility. Because some states or jurisdictions do not allow the exclusion or the limitation of liability for consequential or incidental damages, in such states or jurisdictions, our liability shall be limited to the maximum extent permitted by law.

+

SECTION 14 - INDEMNIFICATION

+

You agree to indemnify, defend and hold harmless Chapalang Clothes and our parent, subsidiaries, affiliates, partners, officers, directors, agents, contractors, licensors, service providers, subcontractors, suppliers, interns and employees, harmless from any claim or demand, including reasonable attorneys’ fees, made by any third-party due to or arising out of your breach of these Terms of Service or the documents they incorporate by reference, or your violation of any law or the rights of a third-party.

+

SECTION 15 - SEVERABILITY

+

In the event that any provision of these Terms of Service is determined to be unlawful, void or unenforceable, such provision shall nonetheless be enforceable to the fullest extent permitted by applicable law, and the unenforceable portion shall be deemed to be severed from these Terms of Service, such determination shall not affect the validity and enforceability of any other remaining provisions.

+

SECTION 16 - TERMINATION

+

The obligations and liabilities of the parties incurred prior to the termination date shall survive the termination of this agreement for all purposes. + + These Terms of Service are effective unless and until terminated by either you or us. You may terminate these Terms of Service at any time by notifying us that you no longer wish to use our Services, or when you cease using our site. + + If in our sole judgment you fail, or we suspect that you have failed, to comply with any term or provision of these Terms of Service, we also may terminate this agreement at any time without notice and you will remain liable for all amounts due up to and including the date of termination; and/or accordingly may deny you access to our Services (or any part thereof).

+

SECTION 17 - ENTIRE AGREEMENT

+

The failure of us to exercise or enforce any right or provision of these Terms of Service shall not constitute a waiver of such right or provision. + + These Terms of Service and any policies or operating rules posted by us on this site or in respect to The Service constitutes the entire agreement and understanding between you and us and govern your use of the Service, superseding any prior or contemporaneous agreements, communications and proposals, whether oral or written, between you and us (including, but not limited to, any prior versions of the Terms of Service). + + Any ambiguities in the interpretation of these Terms of Service shall not be construed against the drafting party.

+

SECTION 18 - GOVERNING LAW

+

These Terms of Service and any separate agreements whereby we provide you Services shall be governed by and construed in accordance with the laws of 2086 Simons Hollow Road, Scranton, RI, 18503, United States.

+

SECTION 19 - CHANGES TO TERMS OF SERVICE

+

You can review the most current version of the Terms of Service at any time at this page. + + We reserve the right, at our sole discretion, to update, change or replace any part of these Terms of Service by posting updates and changes to our website. It is your responsibility to check our website periodically for changes. Your continued use of or access to our website or the Service following the posting of any changes to these Terms of Service constitutes acceptance of those changes.

+

SECTION 20 - CONTACT INFORMATION

+

Questions about the Terms of Service should be sent to us at admin@chapalang.com.

+
+
+ + + + + + + + + + + + + \ No newline at end of file diff --git a/transaction.php b/transaction.php new file mode 100644 index 0000000..7424adb --- /dev/null +++ b/transaction.php @@ -0,0 +1,91 @@ + + + + + + + + + + + Chapalang Clothes + + + + +
+ Your order is successful! To see your purchase history, go to your profile.

Return to home

"; + } else { + die(header("Location: error.php?error=error")); + } + } + } catch (\Throwable $th) { + die(header("Location: error.php?error=cart")); + } + } + ?> +
+ + + + \ No newline at end of file diff --git a/update_review.php b/update_review.php new file mode 100644 index 0000000..3c1093c --- /dev/null +++ b/update_review.php @@ -0,0 +1,32 @@ + time()) || empty($address) +) { + die(header("Location:user_profile.php?message=error")); +} + +// https://www.php.net/manual/en/features.file-upload.php - upload image + +// Undefined | Multiple Files | $_FILES Corruption Attack +// If this request falls under any of them, treat it invalid. +if ( + !isset($_FILES['picture']['error']) || + is_array($_FILES['picture']['error']) +) { + die(header("Location:user_profile.php?message=error")); +} + +if ($_FILES['picture']['error'] !== UPLOAD_ERR_NO_FILE) { + try { + // DO NOT TRUST $_FILES['picture']['mime'] VALUE !! + // Check MIME Type by yourself. + $finfo = new finfo(FILEINFO_MIME_TYPE); + if (false === $ext = array_search( + $finfo->file($_FILES['picture']['tmp_name']), + array( + 'jpg' => 'image/jpeg', + 'png' => 'image/png', + 'gif' => 'image/gif', + ), + true + )) { + die(header("Location:user_profile.php?message=error")); + } + + // You should name it uniquely. + // DO NOT USE $_FILES['picture']['name'] WITHOUT ANY VALIDATION !! + // On this example, obtain safe unique name from its binary data. + if (!move_uploaded_file( + $_FILES['picture']['tmp_name'], + $imageUrl = sprintf( + 'images/users/%s.%s', + sha1_file($_FILES['picture']['tmp_name']), + $ext + ) + )) { + die(header("Location:user_profile.php?message=error")); + } + $imageQuery = "UserImage='$imageUrl',"; + } catch (RuntimeException $e) { + die(header("Location:user_profile.php?message=error")); + } +} + +include_once "includes/database.php"; + +$email_esc = mysqlidb::escape($email); +$gender_esc = mysqlidb::escape($gender); +$address_esc = mysqlidb::escape($address); + +mysqlidb::query("UPDATE user SET $imageQuery Email='$email_esc',Gender='$gender_esc',DOB='$dob',Address='$address_esc' WHERE UserId = $userId"); + +die(header("Location: user_profile.php?message=edited")); diff --git a/user_profile.php b/user_profile.php new file mode 100644 index 0000000..5670cc6 --- /dev/null +++ b/user_profile.php @@ -0,0 +1,155 @@ + + + + + + + + + + + + User Profile + + + + + + +
+
+ +
+ +
+ +
+ Error Occured
'; + } + if ($_GET['message'] == "edited") { + echo '
Successfully Edited
'; + } + } + ?> +
+
+
My Profile
+
+
+ +
+ " id="userImage" alt=""> + + +
+
+
+ + " disabled /> +
+
+ + " /> +
+
+ + +
+
+ + " /> +
+
+ + +
+
+ +
+
+
+
+
+
Change Password
+ Your new password cannot be the same as your old password!
'; + break; + case 'wrong_password': + echo '
Your old password provided is wrong!
'; + break; + case 'ok_password': + echo '
Successfully changed password!
'; + break; + } + } + ?> +
+
+ + + +
+
+ +
+
+
+
+
+ + + + + + + + + + + + + + \ No newline at end of file diff --git a/vendor_add_message.php b/vendor_add_message.php new file mode 100644 index 0000000..87a029b --- /dev/null +++ b/vendor_add_message.php @@ -0,0 +1,24 @@ + 1024 || + strlen($_POST["conversationMessage"]) < 5 +) { + die(header("Location: vendor_inbox.php?error=true")); +} + +$messageBody = mysqlidb::escape(trim($_POST["conversationMessage"])); +$vendorId = $_SESSION["vendorid"]; +$userId = mysqlidb::escape($_POST["userId"]); + +try { + mysqlidb::query("INSERT INTO `message` (`UserId`, `VendorId`, `Sender`, `MessageBody`) VALUES ($userId, $vendorId, 1, '$messageBody')"); +} catch (\Throwable $th) { + die(header("Location: vendor_inbox.php?error=true")); +} + +die(header("Location: vendor_conversation.php?userid=$userId")); diff --git a/vendor_add_product.php b/vendor_add_product.php new file mode 100644 index 0000000..7f10de6 --- /dev/null +++ b/vendor_add_product.php @@ -0,0 +1,114 @@ + + + + + + + + + + + + + Chapalang Clothes + + + + + + +
+
+ +
+
+ +
+
+

Add Product

+

+ + +

+ + +

+ +
+
+ +
+ +
+ +
+ +
+ +
+ +
+ +
+ +
+ +
+ +
+ +
+ +
+ +
+ +
+ +
+ +
+ +
+
+

+ + +

+ + +

+ + +

+ + +

+
+
+
+
+ + + + + +
+ + + + \ No newline at end of file diff --git a/vendor_adding_products.php b/vendor_adding_products.php new file mode 100644 index 0000000..dd56932 --- /dev/null +++ b/vendor_adding_products.php @@ -0,0 +1,34 @@ + + alert('Product added, please wait for approval.'); + window.location.href = 'vendor_add_product.php' + "; +} diff --git a/vendor_conversation.php b/vendor_conversation.php new file mode 100644 index 0000000..da9e458 --- /dev/null +++ b/vendor_conversation.php @@ -0,0 +1,123 @@ + + + + + + + + + + + + + + Chapalang Clothes + + + + + + +
+
+ +
+
+ +
+
+ Back to Inbox +
+ Conversation with +
+ 0) { + foreach ($convoMessages as $message) { + echo " + + + + + + + + + + +
+
Posted on + " . date('g:ia d/n/Y', strtotime($message['MessageTimestamp'])) . " +
+
+ +
+
+
+
" . ($message['Sender'] == 0 ? $userData['Username'] : $vendorData['ShopName']) . "
+ \"\" +
+
+
+
$message[MessageBody]
+
"; + } + } else { + echo "

You have no conversations with this user.

+

Start one by sending a message.

"; + } + ?> + Back to Inbox + +
+ +
+ + +
+
+ +
+
+
+
+
+
+ + + + + +
+ + + + + \ No newline at end of file diff --git a/vendor_delete_product.php b/vendor_delete_product.php new file mode 100644 index 0000000..a3e3973 --- /dev/null +++ b/vendor_delete_product.php @@ -0,0 +1,25 @@ + + alert('Product failed to delete!'); + window.location.href = 'vendor_profile.php' + "; + } + + echo ""; +} diff --git a/vendor_edit_product.php b/vendor_edit_product.php new file mode 100644 index 0000000..9126d77 --- /dev/null +++ b/vendor_edit_product.php @@ -0,0 +1,93 @@ + + alert('Error! This item does not belong to you!'); + window.location.href = 'vendor_profile.php' + "; +} +?> + + + + + + + + + + + + Chapalang Clothes + + + + + + +
+
+ +
+
+ +
+
+

Edit Product

+

+ +
+ + +

+ + +

+ + +

+ + +

+ + +

+ + +

+
+
+
+
+ + + + + +
+ + + + \ No newline at end of file diff --git a/vendor_editing_products.php b/vendor_editing_products.php new file mode 100644 index 0000000..f9d168b --- /dev/null +++ b/vendor_editing_products.php @@ -0,0 +1,54 @@ + + alert('$notificationMessage'); + window.location.href = 'vendor_profile.php' + "; +} diff --git a/vendor_inbox.php b/vendor_inbox.php new file mode 100644 index 0000000..28f4f49 --- /dev/null +++ b/vendor_inbox.php @@ -0,0 +1,86 @@ + + + + + + + + + + + + + Chapalang Clothes + + + + + + +
+
+ +
+
+ +
+
+
Inbox
+ Unknown error occured
'; + } + if (count($conversations) > 0) { + + echo ' + + + + + + + '; + + foreach ($conversations as $conversation) { + echo " + + + "; + } + + echo ' +
Last MessageUser Name
+ + {$conversation['MessageBody']} + +
Posted on " . date("g:ia d/n/Y", strtotime($conversation['MessageTimestamp'])) . "
+
+
{$conversation['Username']}
+
'; + } else { + echo "

Your inbox is empty!

"; + } + ?> +
+
+
+
+ + + + + + + + + + \ No newline at end of file diff --git a/vendor_income.php b/vendor_income.php new file mode 100644 index 0000000..5e57058 --- /dev/null +++ b/vendor_income.php @@ -0,0 +1,181 @@ + + + + + + + + + + + + + + + + Chapalang Clothes + + + + + + +
+
+ +
+
+ +
+

Income

+
+ + + + + + +
+

Total Received

+

RM " . number_format($receivedrow['Total'], 2) . "

+
+ +
+

Total Balance

+

RM " . number_format($balancerow['Balance'], 2) . "

+ Withdraw +
+
"; + ?> + + + // + // Order ID + // Customer + // Purchase Date + // Amount Paid + // + // 0) { + // foreach ($rows as $row) { + // echo " + // + // {$row['OrderId']} + // {$row['Username']} + // {$row['PurchaseDate']} + // RM " . number_format($row['OrderTotal'], 2) . " + // "; + // } + // } else { + // echo "

There are no orders.

"; + // } + // + ?> + + +
+
+
+

+ + + + + + + + + + + \ No newline at end of file diff --git a/vendor_order_info.php b/vendor_order_info.php new file mode 100644 index 0000000..de61e13 --- /dev/null +++ b/vendor_order_info.php @@ -0,0 +1,108 @@ + + + + + + + + + + + + + Chapalang Clothes + + + + + + +
+
+ +
+
+ +
+

Order Info

+
+
+ + + + + + + + + + + + + 0) { + foreach ($rows as $row) { + echo " + + + + + + + + + + + "; + } + } else { + echo ""; + } + + ?> +
Order IDCustomerPurchase DateAddressProductQuantityAmount PaidOrder Status
{$row['OrderId']}{$row['Username']}{$row['PurchaseDate']}{$row['Address']}{$row['ProductName']}{$row['ProductQuantity']}RM {$row['TotalPrice']}{$row['OrderStatus']}

There are no orders.

+ +
+ + + +

+ + +

+
+
+
+
+ + + + + +
+ + + + \ No newline at end of file diff --git a/vendor_order_statuschange.php b/vendor_order_statuschange.php new file mode 100644 index 0000000..7dc1b49 --- /dev/null +++ b/vendor_order_statuschange.php @@ -0,0 +1,14 @@ + \ No newline at end of file diff --git a/vendor_product_approval.php b/vendor_product_approval.php new file mode 100644 index 0000000..c51fae0 --- /dev/null +++ b/vendor_product_approval.php @@ -0,0 +1,188 @@ + 0"); +$outofstockprodrows = mysqlidb::fetchAllRows("SELECT * FROM product +WHERE ProductStatus = 'Approved' +AND VendorId = $vendorid +AND ProductStock <= 0"); +$pendingprodrows = mysqlidb::fetchAllRows("SELECT * FROM product +WHERE ProductStatus = 'Pending' +AND VendorId = $vendorid"); +$rejectedprodrows = mysqlidb::fetchAllRows("SELECT * FROM product +WHERE ProductStatus = 'Denied' +AND VendorId = $vendorid"); +$blacklistprodrows = mysqlidb::fetchAllRows("SELECT * FROM product +WHERE ProductStatus = 'Blacklisted' +AND VendorId = $vendorid"); +?> + + + + + + + + + + + + Chapalang Clothes + + + + + + +
+
+ +
+
+ +
+ +
+

Approved Products

+
+
+
+ 0) { + foreach ($approvprodrows as $row) { + $productimages = explode(",", $row['ProductImage'])[0]; + echo " + +
+ $row[ProductName] +
+
+ "; + } + } else { + echo "

You have no approved products.

"; + } + ?> +
+ +
+ +
+

Pending Approval

+
+
+
+ 0) { + foreach ($pendingprodrows as $row) { + $productimages = explode(",", $row['ProductImage'])[0]; + echo " + +
+ $row[ProductName] +
+
+ "; + } + } else { + echo "

You have no pending products.

"; + } + ?> +
+ +
+ +
+

Out of Stock

+
+
+
+ 0) { + foreach ($outofstockprodrows as $row) { + $productimages = explode(",", $row['ProductImage'])[0]; + echo " + +
+ $row[ProductName] +
+
+ "; + } + } else { + echo "

All your products are in stock.

"; + } + ?> +
+ +
+ +
+

Rejected Products

+
+
+
+ 0) { + foreach ($rejectedprodrows as $row) { + $productimages = explode(",", $row['ProductImage'])[0]; + echo " + +
+ $row[ProductName] +
+
+ "; + } + } else { + echo "

You have no rejected products.

"; + } + + ?> +
+ +
+ +
+

Blacklisted Products

+
+
+
+ 0) { + foreach ($blacklistprodrows as $row) { + $productimages = explode(",", $row['ProductImage'])[0]; + echo " + +
+ $row[ProductName] +
+
+ "; + } + } else { + echo "

You have no blacklisted products.

"; + } + + ?> +
+


+
+
+
+ + + + + +
+ + + + \ No newline at end of file diff --git a/vendor_product_page.php b/vendor_product_page.php new file mode 100644 index 0000000..7480537 --- /dev/null +++ b/vendor_product_page.php @@ -0,0 +1,100 @@ + + alert('Error! This item does not belong to you!'); + window.location.href = 'vendor_profile.php' + "; +} +?> + + + + + + + + + + + + Chapalang Clothes + + + + + + + + + +
+
+ +
+
+ +
+ +

Product Details for {$row['ProductName']}

+
+

Product ID : $row[ProductId]

+

Product Name : $row[ProductName]

+

Description : $row[ProductDescription]

+

Product Tags : $row[ProductTags]

+

Product Price : RM $row[ProductPrice]

+

Product Stock : $row[ProductStock]

+

Product Image:

+
+
+
Delete
+ +
Edit
+
+
+
+ "; + ?> + +
+
+
+ + + + + + + + + + + \ No newline at end of file diff --git a/vendor_profile.php b/vendor_profile.php new file mode 100644 index 0000000..fdbadba --- /dev/null +++ b/vendor_profile.php @@ -0,0 +1,108 @@ + + + + + + + + + + + + + + Chapalang Clothes + + + + + + +
+
+ +
+
+ +
+
+ "> +

Manage Account

+

+ + " + ?> +
+
+ + +
+

+ + +

+ +
+
+
+ + +

+ + +

+ + +

+ +

Change Password

+

+ +

+ + +

+ + + +

+
+
+
+
+ + + + + +
+ + + + + \ No newline at end of file diff --git a/vendor_profile_edit.php b/vendor_profile_edit.php new file mode 100644 index 0000000..7b6686d --- /dev/null +++ b/vendor_profile_edit.php @@ -0,0 +1,34 @@ + + alert('Profile Edited'); + window.location.href = 'vendor_profile.php' + "; diff --git a/vendor_refund.php b/vendor_refund.php new file mode 100644 index 0000000..ba345ad --- /dev/null +++ b/vendor_refund.php @@ -0,0 +1,120 @@ + + + + + + + + + + + + + Chapalang Clothes + + + + + + +
+
+ +
+
+ +
+

Pending Refund

+
+ + + + + + + + + + 0) { + foreach ($pendingrows as $row) { + echo " + + + + + + + "; + } + } else { + echo ""; + } + + ?> +
Order IDProduct NameDate RequestedDetailAction
{$row['OrderId']}{$row['ProductName']}{$row['DateRequested']}{$row['Reason']}View Ticket

There are no pending refunds.

+ +
+ +

Approved Refund

+
+ + + + + + + + + 0) { + foreach ($approvedrows as $row) { + echo " + + + + + + "; + } + } else { + echo ""; + } + + ?> +
Order IDProduct NameDate RequestedDetail
{$row['OrderId']}{$row['ProductName']}{$row['DateRequested']}{$row['Reason']}

There are no approved refunds.

+

+
+
+
+ + + + + +
+ + + + \ No newline at end of file diff --git a/vendor_refund_approval.php b/vendor_refund_approval.php new file mode 100644 index 0000000..85f4e4e --- /dev/null +++ b/vendor_refund_approval.php @@ -0,0 +1,13 @@ + + + + + + + + + + + + + Chapalang Clothes + + + + + + +
+
+ +
+
+ +
+ +

Refund Details for {$rows[0]['ProductName']}

+
+

Refund ID : $row[RefundId]

+

Order ID : $row[OrderId]

+

Product ID : $row[ProductId]

+

Customer Name : $row[Username]

+

Customer Address : $row[Address]

+

Order Total : RM $row[OrderTotal]

+

Refund Reason : $row[Reason]

+

Customer Image :

+
+
+
+
+ "; + } + echo " + Approve Refund + Deny Refund +
+ "; + ?> +
+
+
+ + + + + + + + + + + + \ No newline at end of file diff --git a/vendor_withdraw.php b/vendor_withdraw.php new file mode 100644 index 0000000..cc8597e --- /dev/null +++ b/vendor_withdraw.php @@ -0,0 +1,8 @@ +