VoltIC commited on
Commit
5e338b8
·
verified ·
1 Parent(s): fdb058b

Upload 3 files

Browse files
Files changed (3) hide show
  1. templates/index.html +90 -0
  2. templates/script.js +189 -0
  3. templates/style.css +302 -0
templates/index.html ADDED
@@ -0,0 +1,90 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <!DOCTYPE html>
2
+ <html lang="en">
3
+ <head>
4
+ <meta charset="UTF-8">
5
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
6
+ <title>Hate Speech Detection System</title>
7
+ <link rel="stylesheet" href="/style.css">
8
+ <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css">
9
+ </head>
10
+ <body>
11
+ <div class="container">
12
+ <div class="header">
13
+ <h1><i class="fas fa-shield-alt"></i> Hate Speech Detector</h1>
14
+ <p>Analyze text content to detect hate speech, offensive language, or neutral content</p>
15
+ </div>
16
+
17
+ <div class="main-content">
18
+ <div class="input-section">
19
+ <h2>Enter Text to Analyze</h2>
20
+ <div class="text-input-container">
21
+ <textarea id="textInput" placeholder="Type or paste your text here... Minimum 10 characters."></textarea>
22
+ <div class="char-count">
23
+ <span id="charCount">0</span> characters
24
+ </div>
25
+ </div>
26
+
27
+ <div class="controls">
28
+ <button class="btn analyze-btn" id="analyzeBtn" disabled>
29
+ <i class="fas fa-search"></i> Analyze Text
30
+ </button>
31
+ <button class="btn clear-btn" id="clearBtn">
32
+ <i class="fas fa-eraser"></i> Clear
33
+ </button>
34
+ </div>
35
+ </div>
36
+
37
+ <div class="loading" id="loading">
38
+ <div class="spinner"></div>
39
+ <p>Analyzing text...</p>
40
+ </div>
41
+
42
+ <div class="results-section" id="resultsSection" style="display: none;">
43
+ <h2>Analysis Results</h2>
44
+
45
+ <div class="result-card">
46
+ <div class="classification">
47
+ <h3>Classification</h3>
48
+ <div class="class-badge" id="classBadge">Class 0</div>
49
+ </div>
50
+
51
+ <div class="confidence">
52
+ <h4>Confidence</h4>
53
+ <div class="confidence-bar">
54
+ <div class="confidence-fill" id="confidenceFill" style="width: 0%"></div>
55
+ </div>
56
+ <div class="confidence-value" id="confidenceValue">0%</div>
57
+ </div>
58
+
59
+ <div class="details">
60
+ <h4>Class Information</h4>
61
+ <div class="class-info" id="classInfo">
62
+ <div class="info-item">
63
+ <h5>Class 0: Hate Speech</h5>
64
+ <p>Attacks based on race, religion, gender, etc.</p>
65
+ </div>
66
+ <div class="info-item">
67
+ <h5>Class 1: Offensive Language</h5>
68
+ <p>Insulting or inappropriate content</p>
69
+ </div>
70
+ <div class="info-item">
71
+ <h5>Class 2: Neither</h5>
72
+ <p>Normal, non-offensive content</p>
73
+ </div>
74
+ </div>
75
+ </div>
76
+ </div>
77
+ </div>
78
+ </div>
79
+
80
+ <div class="footer">
81
+ <p>Powered by Machine Learning |
82
+ <a href="#" id="modelInfoBtn">Model Info</a> |
83
+ <a href="#" id="testBtn">Test Connection</a>
84
+ </p>
85
+ </div>
86
+ </div>
87
+
88
+ <script src="/script.js"></script>
89
+ </body>
90
+ </html>
templates/script.js ADDED
@@ -0,0 +1,189 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ console.log("script.js loaded!");
2
+
3
+ const textInput = document.getElementById('textInput');
4
+ const charCount = document.getElementById('charCount');
5
+ const analyzeBtn = document.getElementById('analyzeBtn');
6
+ const clearBtn = document.getElementById('clearBtn');
7
+ const loading = document.getElementById('loading');
8
+ const resultsSection = document.getElementById('resultsSection');
9
+
10
+ const API_URL = '/api/analyze';
11
+ const HEALTH_URL = '/api/health';
12
+
13
+ console.log("API URL:", API_URL);
14
+ console.log("Health URL:", HEALTH_URL);
15
+
16
+ window.addEventListener('DOMContentLoaded', async () => {
17
+ console.log("Testing server connection...");
18
+
19
+ try {
20
+ const response = await fetch(HEALTH_URL);
21
+ const data = await response.json();
22
+ console.log("Server response:", data);
23
+
24
+ if (data.status === 'healthy') {
25
+ console.log("Server is connected!");
26
+ document.body.insertAdjacentHTML('beforeend',
27
+ '<div style="position:fixed; top:10px; right:10px; background:green; color:white; padding:5px; border-radius:3px;">Connected</div>'
28
+ );
29
+ }
30
+ } catch (error) {
31
+ console.error("Server connection failed:", error);
32
+ document.body.insertAdjacentHTML('beforeend',
33
+ '<div style="position:fixed; top:10px; right:10px; background:red; color:white; padding:5px; border-radius:3px;">Server Error</div>'
34
+ );
35
+
36
+ alert(`Cannot connect to server!\n\nMake sure Flask is running:\n1. Open terminal\n2. Run: python app.py\n3. Check for errors\n\nError: ${error.message}`);
37
+ }
38
+ });
39
+
40
+ if (textInput && charCount) {
41
+ textInput.addEventListener('input', function() {
42
+ const count = this.value.length;
43
+ charCount.textContent = count;
44
+ if (analyzeBtn) {
45
+ analyzeBtn.disabled = count < 10;
46
+ }
47
+ });
48
+ }
49
+
50
+ if (clearBtn) {
51
+ clearBtn.addEventListener('click', function() {
52
+ if (textInput) textInput.value = '';
53
+ if (charCount) charCount.textContent = '0';
54
+ if (resultsSection) resultsSection.style.display = 'none';
55
+ if (analyzeBtn) analyzeBtn.disabled = true;
56
+ });
57
+ }
58
+
59
+ if (analyzeBtn) {
60
+ analyzeBtn.addEventListener('click', async function() {
61
+ console.log("Analyze button clicked");
62
+
63
+ if (!textInput) {
64
+ alert("Text input not found!");
65
+ return;
66
+ }
67
+
68
+ const text = textInput.value.trim();
69
+ console.log("Text to analyze:", text.substring(0, 50) + "...");
70
+
71
+ if (text.length < 10) {
72
+ alert('Please enter at least 10 characters.');
73
+ return;
74
+ }
75
+
76
+ if (loading) loading.style.display = 'block';
77
+ if (resultsSection) resultsSection.style.display = 'none';
78
+
79
+ try {
80
+ console.log("📡 Sending request to:", API_URL);
81
+
82
+ const response = await fetch(API_URL, {
83
+ method: 'POST',
84
+ headers: {
85
+ 'Content-Type': 'application/json',
86
+ 'Accept': 'application/json'
87
+ },
88
+ body: JSON.stringify({
89
+ text: text,
90
+ test: "test from frontend"
91
+ })
92
+ });
93
+
94
+ console.log("Response status:", response.status);
95
+ console.log("Response headers:", [...response.headers.entries()]);
96
+
97
+ const result = await response.json();
98
+ console.log("Response data:", result);
99
+
100
+ if (!response.ok) {
101
+ throw new Error(result.error || `HTTP ${response.status}`);
102
+ }
103
+
104
+ displayResults(result);
105
+
106
+ } catch (error) {
107
+ console.error("Analysis failed:", error);
108
+ alert(`Analysis failed:\n${error.message}\n\nCheck console for details.`);
109
+
110
+ const mockResult = {
111
+ class: Math.floor(Math.random() * 3),
112
+ confidence: 75 + Math.random() * 20,
113
+ probabilities: {
114
+ class_0: 0.3,
115
+ class_1: 0.3,
116
+ class_2: 0.4
117
+ },
118
+ test: "Using mock data - server offline"
119
+ };
120
+ displayResults(mockResult);
121
+
122
+ } finally {
123
+ if (loading) loading.style.display = 'none';
124
+ }
125
+ });
126
+ }
127
+
128
+ function displayResults(result) {
129
+ console.log("Displaying results:", result);
130
+
131
+ let classBadge = document.getElementById('classBadge');
132
+ let confidenceFill = document.getElementById('confidenceFill');
133
+ let confidenceValue = document.getElementById('confidenceValue');
134
+
135
+ if (!classBadge) {
136
+ console.warn("Results elements not found, creating them...");
137
+ resultsSection.innerHTML = `
138
+ <h2>Analysis Results</h2>
139
+ <div class="result-card">
140
+ <div class="classification">
141
+ <h3>Classification</h3>
142
+ <div class="class-badge" id="classBadge">Class ${result.class}</div>
143
+ </div>
144
+ <div class="confidence">
145
+ <h4>Confidence</h4>
146
+ <div class="confidence-bar">
147
+ <div class="confidence-fill" id="confidenceFill" style="width: ${result.confidence}%"></div>
148
+ </div>
149
+ <div class="confidence-value" id="confidenceValue">${result.confidence.toFixed(1)}%</div>
150
+ </div>
151
+ <div class="details">
152
+ <h4>Raw Result Data:</h4>
153
+ <pre style="background: white; padding: 10px; border-radius: 5px; overflow: auto;">
154
+ ${JSON.stringify(result, null, 2)}
155
+ </pre>
156
+ </div>
157
+ </div>
158
+ `;
159
+ } else {
160
+ const classNames = ['Hate Speech', 'Offensive Language', 'Neither'];
161
+ const classColors = ['class-0', 'class-1', 'class-2'];
162
+
163
+ classBadge.textContent = classNames[result.class];
164
+ classBadge.className = `class-badge ${classColors[result.class]}`;
165
+
166
+ if (confidenceFill) {
167
+ confidenceFill.style.width = `${result.confidence}%`;
168
+ }
169
+
170
+ if (confidenceValue) {
171
+ confidenceValue.textContent = `${result.confidence.toFixed(1)}%`;
172
+ }
173
+ }
174
+
175
+ if (resultsSection) {
176
+ resultsSection.style.display = 'block';
177
+ resultsSection.scrollIntoView({ behavior: 'smooth' });
178
+ }
179
+ }
180
+
181
+ if (textInput) {
182
+ textInput.addEventListener('keydown', function(e) {
183
+ if (e.ctrlKey && e.key === 'Enter' && analyzeBtn && !analyzeBtn.disabled) {
184
+ analyzeBtn.click();
185
+ }
186
+ });
187
+ }
188
+
189
+ console.log("script.js setup complete!");
templates/style.css ADDED
@@ -0,0 +1,302 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ * {
2
+ margin: 0;
3
+ padding: 0;
4
+ box-sizing: border-box;
5
+ }
6
+
7
+ body {
8
+ font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
9
+ background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
10
+ min-height: 100vh;
11
+ display: flex;
12
+ justify-content: center;
13
+ align-items: center;
14
+ padding: 20px;
15
+ }
16
+
17
+ .container {
18
+ width: 100%;
19
+ max-width: 800px;
20
+ background: white;
21
+ border-radius: 15px;
22
+ box-shadow: 0 10px 40px rgba(0, 0, 0, 0.2);
23
+ overflow: hidden;
24
+ }
25
+
26
+ .header {
27
+ background: linear-gradient(to right, #4a00e0, #8e2de2);
28
+ color: white;
29
+ padding: 25px;
30
+ text-align: center;
31
+ }
32
+
33
+ .header h1 {
34
+ font-size: 2.2rem;
35
+ margin-bottom: 10px;
36
+ }
37
+
38
+ .header p {
39
+ opacity: 0.9;
40
+ line-height: 1.5;
41
+ }
42
+
43
+ .main-content {
44
+ padding: 30px;
45
+ }
46
+
47
+ .input-section {
48
+ margin-bottom: 30px;
49
+ }
50
+
51
+ .input-section h2 {
52
+ color: #333;
53
+ margin-bottom: 15px;
54
+ font-size: 1.4rem;
55
+ }
56
+
57
+ .text-input-container {
58
+ position: relative;
59
+ }
60
+
61
+ textarea {
62
+ width: 100%;
63
+ height: 180px;
64
+ padding: 15px;
65
+ border: 2px solid #e0e0e0;
66
+ border-radius: 8px;
67
+ font-size: 16px;
68
+ font-family: inherit;
69
+ resize: vertical;
70
+ transition: border-color 0.3s;
71
+ }
72
+
73
+ textarea:focus {
74
+ outline: none;
75
+ border-color: #4a00e0;
76
+ }
77
+
78
+ .char-count {
79
+ text-align: right;
80
+ margin-top: 5px;
81
+ color: #666;
82
+ font-size: 14px;
83
+ }
84
+
85
+ .controls {
86
+ display: flex;
87
+ gap: 15px;
88
+ margin-top: 20px;
89
+ }
90
+
91
+ .btn {
92
+ flex: 1;
93
+ padding: 15px;
94
+ border: none;
95
+ border-radius: 8px;
96
+ font-size: 16px;
97
+ font-weight: 600;
98
+ cursor: pointer;
99
+ transition: all 0.3s;
100
+ display: flex;
101
+ align-items: center;
102
+ justify-content: center;
103
+ gap: 8px;
104
+ }
105
+
106
+ .analyze-btn {
107
+ background: linear-gradient(to right, #4a00e0, #8e2de2);
108
+ color: white;
109
+ }
110
+
111
+ .analyze-btn:hover {
112
+ transform: translateY(-2px);
113
+ box-shadow: 0 5px 15px rgba(74, 0, 224, 0.3);
114
+ }
115
+
116
+ .analyze-btn:disabled {
117
+ opacity: 0.5;
118
+ cursor: not-allowed;
119
+ transform: none;
120
+ box-shadow: none;
121
+ }
122
+
123
+ .clear-btn {
124
+ background: #f0f0f0;
125
+ color: #666;
126
+ }
127
+
128
+ .clear-btn:hover {
129
+ background: #e0e0e0;
130
+ }
131
+
132
+ .results-section {
133
+ margin-top: 30px;
134
+ }
135
+
136
+ .results-section h2 {
137
+ color: #333;
138
+ margin-bottom: 15px;
139
+ font-size: 1.4rem;
140
+ }
141
+
142
+ .result-card {
143
+ background: #f9f9f9;
144
+ border-radius: 8px;
145
+ padding: 25px;
146
+ border-left: 5px solid #4a00e0;
147
+ }
148
+
149
+ .classification {
150
+ margin-bottom: 20px;
151
+ }
152
+
153
+ .classification h3 {
154
+ margin-bottom: 10px;
155
+ color: #333;
156
+ }
157
+
158
+ .class-badge {
159
+ display: inline-block;
160
+ padding: 8px 20px;
161
+ border-radius: 20px;
162
+ font-weight: 600;
163
+ font-size: 16px;
164
+ }
165
+
166
+ .class-0 {
167
+ background: #ff4d4d;
168
+ color: white;
169
+ }
170
+
171
+ .class-1 {
172
+ background: #ff9800;
173
+ color: white;
174
+ }
175
+
176
+ .class-2 {
177
+ background: #4caf50;
178
+ color: white;
179
+ }
180
+
181
+ .confidence {
182
+ margin: 20px 0;
183
+ }
184
+
185
+ .confidence h4 {
186
+ margin-bottom: 10px;
187
+ color: #666;
188
+ }
189
+
190
+ .confidence-bar {
191
+ height: 10px;
192
+ background: #e0e0e0;
193
+ border-radius: 5px;
194
+ overflow: hidden;
195
+ margin-bottom: 5px;
196
+ }
197
+
198
+ .confidence-fill {
199
+ height: 100%;
200
+ background: linear-gradient(to right, #4a00e0, #8e2de2);
201
+ border-radius: 5px;
202
+ transition: width 0.5s;
203
+ }
204
+
205
+ .confidence-value {
206
+ text-align: right;
207
+ font-weight: 600;
208
+ color: #4a00e0;
209
+ }
210
+
211
+ .details {
212
+ margin-top: 20px;
213
+ padding-top: 20px;
214
+ border-top: 1px solid #e0e0e0;
215
+ }
216
+
217
+ .details h4 {
218
+ margin-bottom: 15px;
219
+ color: #666;
220
+ }
221
+
222
+ .class-info {
223
+ display: grid;
224
+ grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
225
+ gap: 15px;
226
+ }
227
+
228
+ .info-item {
229
+ background: white;
230
+ padding: 15px;
231
+ border-radius: 6px;
232
+ border: 1px solid #e0e0e0;
233
+ }
234
+
235
+ .info-item h5 {
236
+ color: #4a00e0;
237
+ margin-bottom: 5px;
238
+ }
239
+
240
+ .info-item p {
241
+ color: #666;
242
+ font-size: 14px;
243
+ }
244
+
245
+ .loading {
246
+ display: none;
247
+ text-align: center;
248
+ padding: 30px;
249
+ }
250
+
251
+ .spinner {
252
+ width: 40px;
253
+ height: 40px;
254
+ border: 4px solid #f3f3f3;
255
+ border-top: 4px solid #4a00e0;
256
+ border-radius: 50%;
257
+ animation: spin 1s linear infinite;
258
+ margin: 0 auto 15px;
259
+ }
260
+
261
+ @keyframes spin {
262
+ 0% { transform: rotate(0deg); }
263
+ 100% { transform: rotate(360deg); }
264
+ }
265
+
266
+ .footer {
267
+ text-align: center;
268
+ padding: 20px;
269
+ color: #666;
270
+ font-size: 14px;
271
+ border-top: 1px solid #e0e0e0;
272
+ background: #f9f9f9;
273
+ }
274
+
275
+ .footer a {
276
+ color: #4a00e0;
277
+ text-decoration: none;
278
+ font-weight: 600;
279
+ }
280
+
281
+ .footer a:hover {
282
+ text-decoration: underline;
283
+ }
284
+
285
+ @media (max-width: 600px) {
286
+ .container {
287
+ margin: 10px;
288
+ padding: 15px;
289
+ }
290
+
291
+ .main-content {
292
+ padding: 20px;
293
+ }
294
+
295
+ .controls {
296
+ flex-direction: column;
297
+ }
298
+
299
+ .class-info {
300
+ grid-template-columns: 1fr;
301
+ }
302
+ }