Enhance dashboard UX, fix logout, and localize all external libraries

- Make all dashboard cards clickable (13 cards total) for better UX
- Fix logout confirmation for kaur level with robust jQuery handler
- Download all external libraries locally for offline capability:
  * Ionicons CSS + fonts
  * Cropper.js v1.5.13 (CSS + JS)
  * Chart.js v3.9.1 (fixed chart display)
  * jscanify library
  * Google Fonts - Source Sans Pro (CSS + TTF)
  * OpenCV.js v4.7.0 (8.75MB)
- Update all CDN references to local paths in index.php and login.php
- Add vendor directory exception to .gitignore
- Improve session destruction in logout.php
- Add console debugging for chart initialization
This commit is contained in:
2026-01-22 17:04:07 +08:00
parent 3b10745f49
commit 858435acfe
23 changed files with 2685 additions and 155 deletions

1
.gitignore vendored
View File

@@ -13,6 +13,7 @@ Thumbs.db
# Dependencies (if any in future)
node_modules/
vendor/
!plugins/vendor/
# Uploads
foto/

View File

@@ -304,52 +304,105 @@
</div>
</div>
<script>
document.addEventListener("DOMContentLoaded", function() {
// Data from PHP
var donutChartCanvas = $('#donutChart').get(0).getContext('2d')
var donutData = {
labels: [
'Balita',
'Anak-anak',
'Remaja',
'Dewasa',
'Lansia'
],
datasets: [
{
data: [
<?php echo $balita; ?>,
<?php echo $anak; ?>,
<?php echo $remaja; ?>,
<?php echo $dewasa; ?>,
<?php echo $lansia; ?>
],
backgroundColor : ['#17a2b8', '#28a745', '#6f42c1', '#007bff', '#6c757d'],
}
]
}
var donutOptions = {
maintainAspectRatio : false,
responsive : true,
plugins: {
title: {
display: true,
text: 'Komposisi Penduduk Berdasarkan Usia',
font: {
size: 16
}
},
legend: {
display: true,
position: 'right'
}
}
}
new Chart(donutChartCanvas, {
type: 'doughnut',
data: donutData,
options: donutOptions
})
});
<script>
window.addEventListener('load', function() {
console.log('Page fully loaded - Checking libraries...');
console.log('jQuery available?', typeof $);
console.log('Chart.js available?', typeof Chart);
// Check if jQuery is loaded
if (typeof $ === 'undefined') {
console.error('jQuery is not loaded. Chart cannot be initialized.');
return;
}
// Check if Chart.js is loaded
if (typeof Chart === 'undefined') {
console.error('Chart.js is not loaded. Check if the script is loaded correctly.');
// Try to load Chart.js dynamically as fallback
var script = document.createElement('script');
script.src = 'plugins/vendor/chartjs/chart-3.9.1.min.js';
script.onload = function() {
console.log('Chart.js loaded dynamically, initializing chart...');
initializeChart();
};
document.head.appendChild(script);
} else {
initializeChart();
}
function initializeChart() {
console.log('Initializing chart...');
// Check if canvas exists
var canvas = document.getElementById('donutChart');
if (!canvas) {
console.error('Canvas element with id "donutChart" not found');
return;
}
// Check if jQuery can get the canvas
var $canvas = $('#donutChart');
if ($canvas.length === 0) {
console.error('jQuery cannot find canvas with id "donutChart"');
return;
}
// Get context
var donutChartCanvas = $canvas.get(0).getContext('2d');
if (!donutChartCanvas) {
console.error('Could not get 2d context from canvas');
return;
}
// Data from PHP
var donutData = {
labels: [
'Balita',
'Anak-anak',
'Remaja',
'Dewasa',
'Lansia'
],
datasets: [{
data: [
<?php echo $balita; ?>,
<?php echo $anak; ?>,
<?php echo $remaja; ?>,
<?php echo $dewasa; ?>,
<?php echo $lansia; ?>
],
backgroundColor: ['#17a2b8', '#28a745', '#6f42c1', '#007bff', '#6c757d']
}]
};
var donutOptions = {
maintainAspectRatio: false,
responsive: true,
plugins: {
title: {
display: true,
text: 'Komposisi Penduduk Berdasarkan Usia',
font: { size: 16 }
},
legend: {
display: true,
position: 'right'
}
}
};
try {
new Chart(donutChartCanvas, {
type: 'doughnut',
data: donutData,
options: donutOptions
});
console.log('Chart initialized successfully');
} catch (error) {
console.error('Error initializing chart:', error);
console.error('Error details:', error.message, error.stack);
}
}
});
</script>

View File

@@ -304,52 +304,77 @@
</div>
</div>
<script>
document.addEventListener("DOMContentLoaded", function() {
// Data from PHP
var donutChartCanvas = $('#donutChart').get(0).getContext('2d')
var donutData = {
labels: [
'Balita',
'Anak-anak',
'Remaja',
'Dewasa',
'Lansia'
],
datasets: [
{
data: [
<?php echo $balita; ?>,
<?php echo $anak; ?>,
<?php echo $remaja; ?>,
<?php echo $dewasa; ?>,
<?php echo $lansia; ?>
],
backgroundColor : ['#17a2b8', '#28a745', '#6f42c1', '#007bff', '#6c757d'],
}
]
}
var donutOptions = {
maintainAspectRatio : false,
responsive : true,
plugins: {
title: {
display: true,
text: 'Komposisi Penduduk Berdasarkan Usia',
font: {
size: 16
}
},
legend: {
display: true,
position: 'right'
}
}
}
new Chart(donutChartCanvas, {
type: 'doughnut',
data: donutData,
options: donutOptions
})
});
<script>
document.addEventListener("DOMContentLoaded", function() {
console.log('DOMContentLoaded - Chart.js available?', typeof Chart);
// Check if Chart.js is loaded
if (typeof Chart === 'undefined') {
console.error('Chart.js is not loaded. Check if the script is loaded correctly.');
// Try to load Chart.js dynamically as fallback
var script = document.createElement('script');
script.src = 'plugins/vendor/chartjs/chart.min.js';
script.onload = function() {
console.log('Chart.js loaded dynamically, initializing chart...');
initializeChart();
};
document.head.appendChild(script);
} else {
initializeChart();
}
function initializeChart() {
console.log('Initializing chart...');
// Data from PHP
var donutChartCanvas = $('#donutChart').get(0).getContext('2d')
var donutData = {
labels: [
'Balita',
'Anak-anak',
'Remaja',
'Dewasa',
'Lansia'
],
datasets: [
{
data: [
<?php echo $balita; ?>,
<?php echo $anak; ?>,
<?php echo $remaja; ?>,
<?php echo $dewasa; ?>,
<?php echo $lansia; ?>
],
backgroundColor : ['#17a2b8', '#28a745', '#6f42c1', '#007bff', '#6c757d'],
}
]
}
var donutOptions = {
maintainAspectRatio : false,
responsive : true,
plugins: {
title: {
display: true,
text: 'Komposisi Penduduk Berdasarkan Usia',
font: {
size: 16
}
},
legend: {
display: true,
position: 'right'
}
}
}
try {
new Chart(donutChartCanvas, {
type: 'doughnut',
data: donutData,
options: donutOptions
});
console.log('Chart initialized successfully');
} catch (error) {
console.error('Error initializing chart:', error);
}
}
});
</script>

112
index.php
View File

@@ -35,7 +35,7 @@
<!-- Font Awesome -->
<link rel="stylesheet" href="plugins/fontawesome-free/css/all.min.css">
<!-- Ionicons -->
<link rel="stylesheet" href="https://code.ionicframework.com/ionicons/2.0.1/css/ionicons.min.css">
<link rel="stylesheet" href="plugins/vendor/ionicons/css/ionicons.min.css">
<!-- DataTables -->
<link rel="stylesheet" href="plugins/datatables-bs4/css/dataTables.bootstrap4.css">
<!-- overlayScrollbars -->
@@ -44,9 +44,9 @@
<link rel="stylesheet" href="plugins/select2/css/select2.min.css">
<link rel="stylesheet" href="plugins/select2-bootstrap4-theme/select2-bootstrap4.min.css">
<!-- Cropper.js -->
<link href="https://cdnjs.cloudflare.com/ajax/libs/cropperjs/1.5.13/cropper.min.css" rel="stylesheet">
<link href="plugins/vendor/cropperjs/css/cropper.min.css" rel="stylesheet">
<!-- Google Font: Source Sans Pro -->
<link href="https://fonts.googleapis.com/css?family=Source+Sans+Pro:300,400,400i,700" rel="stylesheet">
<link href="plugins/vendor/google-fonts/source-sans-pro/css/fonts-local.css" rel="stylesheet">
<!-- Modern CSS -->
<link rel="stylesheet" href="dist/css/modern.css">
<!-- Alert -->
@@ -483,7 +483,7 @@
?>
<li class="nav-item">
<a onclick="confirmLogout(event)" href="logout.php" class="nav-link">
<a href="logout.php" class="nav-link logout-link">
<i class="nav-icon fas fa-arrow-circle-right"></i>
<p>
Logout
@@ -724,16 +724,18 @@
<!-- jQuery -->
<script src="plugins/jquery/jquery.min.js"></script>
<!-- ChartJS -->
<script src="plugins/vendor/chartjs/chart-3.9.1.min.js"></script>
<!-- Bootstrap 4 -->
<script src="plugins/bootstrap/js/bootstrap.bundle.min.js"></script>
<!-- Select2 -->
<script src="plugins/select2/js/select2.full.min.js"></script>
<!-- Cropper.js -->
<script src="https://cdnjs.cloudflare.com/ajax/libs/cropperjs/1.5.13/cropper.min.js"></script>
<script src="plugins/vendor/cropperjs/js/cropper.min.js"></script>
<!-- OpenCV.js (Required for jscanify) -->
<script src="https://docs.opencv.org/4.7.0/opencv.js" async></script>
<script src="plugins/vendor/opencv/opencv.js" async></script>
<!-- jscanify -->
<script src="https://cdn.jsdelivr.net/gh/ColonelParrot/jscanify@master/src/jscanify.min.js"></script>
<script src="plugins/vendor/jscanify/jscanify.min.js"></script>
<!-- DataTables -->
<script src="plugins/datatables/jquery.dataTables.js"></script>
<script src="plugins/datatables-bs4/js/dataTables.bootstrap4.js"></script>
@@ -799,32 +801,47 @@
$('#imageModal').modal('show');
}
function confirmLogout(event) {
event.preventDefault();
var logoutUrl = event.currentTarget.href;
// Check if SweetAlert2 is available
if (typeof Swal !== 'undefined' && Swal.fire) {
Swal.fire({
title: 'Konfirmasi Logout',
text: 'Apakah Anda yakin akan keluar dari sistem?',
icon: 'question',
showCancelButton: true,
confirmButtonColor: '#3085d6',
cancelButtonColor: '#d33',
confirmButtonText: 'Ya, Keluar',
cancelButtonText: 'Batal'
}).then((result) => {
if (result.isConfirmed) {
window.location.href = logoutUrl;
}
});
} else {
// Fallback to standard confirm dialog
if (confirm('Apakah Anda yakin akan keluar dari sistem?')) {
window.location.href = logoutUrl;
function confirmLogout(element, event) {
try {
console.log('Logout function called for element:', element);
// Always prevent default behavior
if (event) {
event.preventDefault();
event.stopPropagation();
}
// Prevent multiple clicks
if (element.getAttribute('data-logging-out') === 'true') {
console.log('Already logging out, ignoring');
return false;
}
// Mark as logging out
element.setAttribute('data-logging-out', 'true');
var logoutUrl = element.href;
console.log('Logout URL:', logoutUrl);
// Debug SweetAlert2 availability
console.log('Swal exists:', typeof Swal, 'Swal.fire:', Swal && Swal.fire);
// Use simple confirm for now to ensure it works
if (confirm('Apakah Anda yakin akan keluar dari sistem?')) {
console.log('User confirmed logout, redirecting...');
window.location.href = logoutUrl;
} else {
console.log('User cancelled logout');
element.removeAttribute('data-logging-out');
}
} catch (error) {
console.error('Logout error:', error);
// As last resort, go directly to logout
window.location.href = element.href;
}
return false;
}
function zoomIn() {
@@ -882,10 +899,39 @@
(delta > 0) ? zoomIn() : zoomOut();
}
}
// Handle logout link clicks
$(document).ready(function() {
console.log('Document ready, attaching logout handler');
$(document).on('click', '.logout-link', function(event) {
console.log('Logout link clicked');
event.preventDefault();
event.stopPropagation();
var element = this;
var logoutUrl = element.href;
// Prevent multiple clicks
if ($(element).attr('data-logging-out') === 'true') {
console.log('Already logging out');
return false;
}
$(element).attr('data-logging-out', 'true');
// Show confirmation
if (confirm('Apakah Anda yakin akan keluar dari sistem?')) {
console.log('User confirmed logout');
window.location.href = logoutUrl;
} else {
console.log('User cancelled logout');
$(element).removeAttr('data-logging-out');
}
return false;
});
});
</script>
<!-- ChartJS -->
<script src="https://cdn.jsdelivr.net/npm/chart.js"></script>
</body>
</html>

View File

@@ -19,7 +19,7 @@
<!-- Font Awesome -->
<link rel="stylesheet" href="plugins/fontawesome-free/css/all.min.css">
<!-- Ionicons -->
<link rel="stylesheet" href="https://code.ionicframework.com/ionicons/2.0.1/css/ionicons.min.css">
<link rel="stylesheet" href="plugins/vendor/ionicons/css/ionicons.min.css">
<!-- icheck bootstrap -->
<link rel="stylesheet" href="plugins/icheck-bootstrap/icheck-bootstrap.min.css">
<!-- Theme style -->
@@ -27,7 +27,7 @@
<!-- Modern CSS -->
<link rel="stylesheet" href="dist/css/modern.css">
<!-- Google Font: Source Sans Pro -->
<link href="https://fonts.googleapis.com/css?family=Source+Sans+Pro:300,400,400i,700" rel="stylesheet">
<link href="plugins/vendor/google-fonts/source-sans-pro/css/fonts-local.css" rel="stylesheet">
</head>
<body class="hold-transition login-page">

View File

@@ -1,25 +1,38 @@
<?php
// Start session if not already started
if (session_status() === PHP_SESSION_NONE) {
session_start();
}
// Clear all session variables
$_SESSION = array();
// If it's desired to kill the session, also delete the session cookie
if (ini_get("session.use_cookies")) {
$params = session_get_cookie_params();
setcookie(session_name(), '', time() - 42000,
$params["path"], $params["domain"],
$params["secure"], $params["httponly"]
);
}
// Finally, destroy the session
session_destroy();
// Redirect to login page using PHP header first
header("Location: login.php");
exit();
?>
// Turn off error reporting to prevent warnings
error_reporting(0);
// Prevent any output
ob_start();
// Start session if not already started
if (session_status() === PHP_SESSION_NONE) {
@session_start();
}
// Clear session data
$_SESSION = [];
// Delete session cookie
if (ini_get("session.use_cookies")) {
$params = session_get_cookie_params();
setcookie(session_name(), '', time() - 3600,
$params["path"], $params["domain"],
$params["secure"], $params["httponly"]
);
}
// Destroy session if active
if (session_status() === PHP_SESSION_ACTIVE) {
@session_destroy();
}
// Clear output buffer
ob_end_clean();
// Set headers
header("Cache-Control: no-cache, no-store, must-revalidate");
header("Pragma: no-cache");
header("Expires: 0");
header("Location: login.php");
exit();

File diff suppressed because one or more lines are too long

14
plugins/vendor/chartjs/chart.min.js vendored Normal file

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1,9 @@
/*!
* Cropper.js v1.5.13
* https://fengyuanchen.github.io/cropperjs
*
* Copyright 2015-present Chen Fengyuan
* Released under the MIT license
*
* Date: 2022-11-20T05:30:43.444Z
*/.cropper-container{direction:ltr;font-size:0;line-height:0;position:relative;-ms-touch-action:none;touch-action:none;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none}.cropper-container img{-webkit-backface-visibility:hidden;backface-visibility:hidden;display:block;height:100%;image-orientation:0deg;max-height:none!important;max-width:none!important;min-height:0!important;min-width:0!important;width:100%}.cropper-canvas,.cropper-crop-box,.cropper-drag-box,.cropper-modal,.cropper-wrap-box{bottom:0;left:0;position:absolute;right:0;top:0}.cropper-canvas,.cropper-wrap-box{overflow:hidden}.cropper-drag-box{background-color:#fff;opacity:0}.cropper-modal{background-color:#000;opacity:.5}.cropper-view-box{display:block;height:100%;outline:1px solid #39f;outline-color:rgba(51,153,255,.75);overflow:hidden;width:100%}.cropper-dashed{border:0 dashed #eee;display:block;opacity:.5;position:absolute}.cropper-dashed.dashed-h{border-bottom-width:1px;border-top-width:1px;height:33.33333%;left:0;top:33.33333%;width:100%}.cropper-dashed.dashed-v{border-left-width:1px;border-right-width:1px;height:100%;left:33.33333%;top:0;width:33.33333%}.cropper-center{display:block;height:0;left:50%;opacity:.75;position:absolute;top:50%;width:0}.cropper-center:after,.cropper-center:before{background-color:#eee;content:" ";display:block;position:absolute}.cropper-center:before{height:1px;left:-3px;top:0;width:7px}.cropper-center:after{height:7px;left:0;top:-3px;width:1px}.cropper-face,.cropper-line,.cropper-point{display:block;height:100%;opacity:.1;position:absolute;width:100%}.cropper-face{background-color:#fff;left:0;top:0}.cropper-line{background-color:#39f}.cropper-line.line-e{cursor:ew-resize;right:-3px;top:0;width:5px}.cropper-line.line-n{cursor:ns-resize;height:5px;left:0;top:-3px}.cropper-line.line-w{cursor:ew-resize;left:-3px;top:0;width:5px}.cropper-line.line-s{bottom:-3px;cursor:ns-resize;height:5px;left:0}.cropper-point{background-color:#39f;height:5px;opacity:.75;width:5px}.cropper-point.point-e{cursor:ew-resize;margin-top:-3px;right:-3px;top:50%}.cropper-point.point-n{cursor:ns-resize;left:50%;margin-left:-3px;top:-3px}.cropper-point.point-w{cursor:ew-resize;left:-3px;margin-top:-3px;top:50%}.cropper-point.point-s{bottom:-3px;cursor:s-resize;left:50%;margin-left:-3px}.cropper-point.point-ne{cursor:nesw-resize;right:-3px;top:-3px}.cropper-point.point-nw{cursor:nwse-resize;left:-3px;top:-3px}.cropper-point.point-sw{bottom:-3px;cursor:nesw-resize;left:-3px}.cropper-point.point-se{bottom:-3px;cursor:nwse-resize;height:20px;opacity:1;right:-3px;width:20px}@media (min-width:768px){.cropper-point.point-se{height:15px;width:15px}}@media (min-width:992px){.cropper-point.point-se{height:10px;width:10px}}@media (min-width:1200px){.cropper-point.point-se{height:5px;opacity:.75;width:5px}}.cropper-point.point-se:before{background-color:#39f;bottom:-50%;content:" ";display:block;height:200%;opacity:0;position:absolute;right:-50%;width:200%}.cropper-invisible{opacity:0}.cropper-bg{background-image:url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQAQMAAAAlPW0iAAAAA3NCSVQICAjb4U/gAAAABlBMVEXMzMz////TjRV2AAAACXBIWXMAAArrAAAK6wGCiw1aAAAAHHRFWHRTb2Z0d2FyZQBBZG9iZSBGaXJld29ya3MgQ1M26LyyjAAAABFJREFUCJlj+M/AgBVhF/0PAH6/D/HkDxOGAAAAAElFTkSuQmCC")}.cropper-hide{display:block;height:0;position:absolute;width:0}.cropper-hidden{display:none!important}.cropper-move{cursor:move}.cropper-crop{cursor:crosshair}.cropper-disabled .cropper-drag-box,.cropper-disabled .cropper-face,.cropper-disabled .cropper-line,.cropper-disabled .cropper-point{cursor:not-allowed}

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1,24 @@
@font-face {
font-family: 'Source Sans Pro';
font-style: italic;
font-weight: 400;
src: url(../fonts/source-sans-pro-italic-400.ttf) format('truetype');
}
@font-face {
font-family: 'Source Sans Pro';
font-style: normal;
font-weight: 300;
src: url(../fonts/source-sans-pro-light-300.ttf) format('truetype');
}
@font-face {
font-family: 'Source Sans Pro';
font-style: normal;
font-weight: 400;
src: url(../fonts/source-sans-pro-regular-400.ttf) format('truetype');
}
@font-face {
font-family: 'Source Sans Pro';
font-style: normal;
font-weight: 700;
src: url(../fonts/source-sans-pro-bold-700.ttf) format('truetype');
}

View File

@@ -0,0 +1,24 @@
@font-face {
font-family: 'Source Sans Pro';
font-style: italic;
font-weight: 400;
src: url(https://fonts.gstatic.com/s/sourcesanspro/v23/6xK1dSBYKcSV-LCoeQqfX1RYOo3qPZ7nsDc.ttf) format('truetype');
}
@font-face {
font-family: 'Source Sans Pro';
font-style: normal;
font-weight: 300;
src: url(https://fonts.gstatic.com/s/sourcesanspro/v23/6xKydSBYKcSV-LCoeQqfX1RYOo3ik4zwlxdr.ttf) format('truetype');
}
@font-face {
font-family: 'Source Sans Pro';
font-style: normal;
font-weight: 400;
src: url(https://fonts.gstatic.com/s/sourcesanspro/v23/6xK3dSBYKcSV-LCoeQqfX1RYOo3qOK7g.ttf) format('truetype');
}
@font-face {
font-family: 'Source Sans Pro';
font-style: normal;
font-weight: 700;
src: url(https://fonts.gstatic.com/s/sourcesanspro/v23/6xKydSBYKcSV-LCoeQqfX1RYOo3ig4vwlxdr.ttf) format('truetype');
}

File diff suppressed because one or more lines are too long

Binary file not shown.

2230
plugins/vendor/ionicons/fonts/ionicons.svg vendored Normal file

File diff suppressed because it is too large Load Diff

After

Width:  |  Height:  |  Size: 326 KiB

Binary file not shown.

Binary file not shown.

View File

@@ -0,0 +1,9 @@
/**
* Minified by jsDelivr using Terser v5.39.0.
* Original file: /gh/ColonelParrot/jscanify@master/src/jscanify.js
*
* Do NOT use SRI with dynamically generated files! More information: https://www.jsdelivr.com/using-sri-with-dynamic-files
*/
/*! jscanify v1.4.0 | (c) ColonelParrot and other contributors | MIT License */
!function(e,t){"object"==typeof exports&&"undefined"!=typeof module?module.exports=t():"function"==typeof define&&define.amd?define(t):e.jscanify=t()}(this,(function(){"use strict";return class{constructor(){}findPaperContour(e){const t=new cv.Mat;cv.Canny(e,t,50,200);const o=new cv.Mat;cv.GaussianBlur(t,o,new cv.Size(3,3),0,0,cv.BORDER_DEFAULT);const n=new cv.Mat;cv.threshold(o,n,0,255,cv.THRESH_OTSU);let r=new cv.MatVector,c=new cv.Mat;cv.findContours(n,r,c,cv.RETR_CCOMP,cv.CHAIN_APPROX_SIMPLE);let i=0,a=-1;for(let e=0;e<r.size();++e){let t=cv.contourArea(r.get(e));t>i&&(i=t,a=e)}const s=a>=0?r.get(a):null;return t.delete(),o.delete(),n.delete(),r.delete(),c.delete(),s}highlightPaper(e,t){(t=t||{}).color=t.color||"orange",t.thickness=t.thickness||10;const o=document.createElement("canvas"),n=o.getContext("2d"),r=cv.imread(e),c=this.findPaperContour(r);if(cv.imshow(o,r),c){const{topLeftCorner:e,topRightCorner:o,bottomLeftCorner:i,bottomRightCorner:a}=this.getCornerPoints(c,r);e&&o&&i&&a&&(n.strokeStyle=t.color,n.lineWidth=t.thickness,n.beginPath(),n.moveTo(...Object.values(e)),n.lineTo(...Object.values(o)),n.lineTo(...Object.values(a)),n.lineTo(...Object.values(i)),n.lineTo(...Object.values(e)),n.stroke())}return r.delete(),o}extractPaper(e,t,o,n){const r=document.createElement("canvas"),c=cv.imread(e),i=n?null:this.findPaperContour(c);if(null==i&&void 0===n)return null;const{topLeftCorner:a,topRightCorner:s,bottomLeftCorner:l,bottomRightCorner:v}=n||this.getCornerPoints(i,c);let u=new cv.Mat,d=new cv.Size(t,o),C=cv.matFromArray(4,1,cv.CV_32FC2,[a.x,a.y,s.x,s.y,l.x,l.y,v.x,v.y]),f=cv.matFromArray(4,1,cv.CV_32FC2,[0,0,t,0,0,o,t,o]),h=cv.getPerspectiveTransform(C,f);return cv.warpPerspective(c,u,h,d,cv.INTER_LINEAR,cv.BORDER_CONSTANT,new cv.Scalar),cv.imshow(r,u),c.delete(),u.delete(),r}getCornerPoints(e){const t=cv.minAreaRect(e).center;let o,n,r,c,i=0,a=0,s=0,l=0;for(let d=0;d<e.data32S.length;d+=2){const C={x:e.data32S[d],y:e.data32S[d+1]},f=(v=C,u=t,Math.hypot(v.x-u.x,v.y-u.y));C.x<t.x&&C.y<t.y?f>i&&(o=C,i=f):C.x>t.x&&C.y<t.y?f>a&&(n=C,a=f):C.x<t.x&&C.y>t.y?f>s&&(r=C,s=f):C.x>t.x&&C.y>t.y&&f>l&&(c=C,l=f)}var v,u;return{topLeftCorner:o,topRightCorner:n,bottomLeftCorner:r,bottomRightCorner:c}}}}));
//# sourceMappingURL=/sm/f4b0fbf42a9b611deea75f2299eea9ad053e3664950195e6d617ef82058d69b3.map

48
plugins/vendor/opencv/opencv.js vendored Normal file

File diff suppressed because one or more lines are too long