Skip to content

Commit 769cfa9

Browse files
authored
Merge pull request #87 from psy-duck1/url-shortener
Add URL-Shrotener in Javascript
2 parents 7fbb8a6 + 8c19706 commit 769cfa9

File tree

4 files changed

+668
-0
lines changed

4 files changed

+668
-0
lines changed

Javascript/URL-Shortener/README.md

Lines changed: 117 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,117 @@
1+
# 🔗 URL Shortener
2+
3+
A simple, client-side URL shortener built with vanilla JavaScript, HTML, and CSS. No server required!
4+
5+
## ✨ Features
6+
7+
- **URL Validation** - Validates URLs before shortening
8+
- **Short Code Generation** - Creates unique 6-character codes
9+
- **Local Storage** - Persists URLs in browser storage
10+
- **Click Tracking** - Counts URL access statistics
11+
- **Copy to Clipboard** - One-click copying functionality
12+
- **Recent URLs Management** - View and manage your shortened URLs
13+
- **Automatic Redirection** - Seamlessly redirects to original URLs
14+
- **Responsive Design** - Works on all devices
15+
- **Modern UI** - Beautiful gradient design with smooth animations
16+
17+
## 🚀 Quick Start
18+
19+
1. **Clone or download** this repository
20+
2. **Open** `index.html` in your web browser
21+
3. **Start shortening URLs!**
22+
23+
### Local Development Server
24+
25+
For the best experience, serve the files using a local server:
26+
27+
```bash
28+
# Using Python
29+
python3 -m http.server 8000
30+
31+
# Using Node.js (if you have http-server installed)
32+
npx http-server
33+
34+
# Using PHP
35+
php -S localhost:8000
36+
```
37+
38+
Then open `http://localhost:8000` in your browser.
39+
40+
## 📖 How It Works
41+
42+
1. **Enter a long URL** in the input field
43+
2. **Click "Shorten URL"** or press Enter
44+
3. **Copy the shortened URL** and share it
45+
4. **Access the short URL** to automatically redirect to the original
46+
47+
### URL Format
48+
- Short URLs follow the pattern: `your-domain.com#AbC123`
49+
- The hash fragment (`#AbC123`) contains the unique short code
50+
- Original URLs are stored in browser's localStorage
51+
52+
## 🛠️ Technical Details
53+
54+
- **Pure JavaScript** - No frameworks or dependencies
55+
- **localStorage** - Client-side data persistence
56+
- **Hash-based routing** - Uses URL fragments for redirection
57+
- **Responsive CSS** - Mobile-first design approach
58+
- **Modern ES6+** - Uses classes, arrow functions, and modern JavaScript features
59+
60+
## 📱 Browser Support
61+
62+
- Chrome 60+
63+
- Firefox 55+
64+
- Safari 12+
65+
- Edge 79+
66+
67+
## 🔧 Customization
68+
69+
### Changing the Base URL
70+
Update the `baseUrl` property in `script.js`:
71+
72+
```javascript
73+
this.baseUrl = 'https://your-domain.com';
74+
```
75+
76+
### Modifying the Short Code Length
77+
Change the loop in the `generateShortCode()` method:
78+
79+
```javascript
80+
for (let i = 0; i < 8; i++) { // Change 6 to desired length
81+
```
82+
83+
### Styling
84+
All styles are in `style.css`. The design uses CSS custom properties and modern layout techniques.
85+
86+
## 📊 Data Storage
87+
88+
URLs are stored in browser's localStorage with the following structure:
89+
90+
```javascript
91+
{
92+
"AbC123": {
93+
"originalUrl": "https://example.com/very/long/url",
94+
"createdAt": "2024-01-01T00:00:00.000Z",
95+
"clickCount": 5
96+
}
97+
}
98+
```
99+
100+
## 🤝 Contributing
101+
102+
1. Fork the repository
103+
2. Create a feature branch: `git checkout -b feature-name`
104+
3. Make your changes
105+
4. Commit your changes: `git commit -m 'Add some feature'`
106+
5. Push to the branch: `git push origin feature-name`
107+
6. Submit a pull request
108+
109+
## 📝 License
110+
111+
This project is open source and available under the [MIT License](LICENSE).
112+
113+
## 🙏 Acknowledgments
114+
115+
- Built with vanilla JavaScript for maximum compatibility
116+
- Inspired by popular URL shorteners like bit.ly and tinyurl.com
117+
- Uses modern web standards for optimal performance
Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
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>URL Shortener</title>
7+
<link rel="stylesheet" href="style.css">
8+
</head>
9+
<body>
10+
<div class="container">
11+
<header>
12+
<h1>🔗 URL Shortener</h1>
13+
<p>Make your long URLs short and shareable</p>
14+
</header>
15+
16+
<main>
17+
<div class="input-section">
18+
<input type="url" id="urlInput" placeholder="Enter your long URL here..." required>
19+
<button id="shortenBtn">Shorten URL</button>
20+
</div>
21+
22+
<div id="result" class="result-section" style="display: none;">
23+
<h3>Your shortened URL:</h3>
24+
<div class="short-url-container">
25+
<input type="text" id="shortUrl" readonly>
26+
<button id="copyBtn">Copy</button>
27+
</div>
28+
<div class="original-url">
29+
<strong>Original:</strong> <span id="originalUrl"></span>
30+
</div>
31+
</div>
32+
33+
<div id="error" class="error-message" style="display: none;">
34+
<p>Please enter a valid URL</p>
35+
</div>
36+
</main>
37+
38+
<section class="recent-urls">
39+
<h3>Recent Shortened URLs</h3>
40+
<div id="urlList" class="url-list">
41+
<!-- Recent URLs will be displayed here -->
42+
</div>
43+
</section>
44+
</div>
45+
46+
<script src="script.js"></script>
47+
</body>
48+
</html>

Javascript/URL-Shortener/script.js

Lines changed: 206 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,206 @@
1+
class URLShortener {
2+
constructor() {
3+
this.urlStorage = this.loadFromStorage();
4+
this.baseUrl = window.location.origin + window.location.pathname;
5+
this.initializeElements();
6+
this.bindEvents();
7+
this.displayRecentUrls();
8+
}
9+
10+
initializeElements() {
11+
this.urlInput = document.getElementById('urlInput');
12+
this.shortenBtn = document.getElementById('shortenBtn');
13+
this.result = document.getElementById('result');
14+
this.shortUrl = document.getElementById('shortUrl');
15+
this.originalUrl = document.getElementById('originalUrl');
16+
this.copyBtn = document.getElementById('copyBtn');
17+
this.error = document.getElementById('error');
18+
this.urlList = document.getElementById('urlList');
19+
}
20+
21+
bindEvents() {
22+
this.shortenBtn.addEventListener('click', () => this.shortenUrl());
23+
this.urlInput.addEventListener('keypress', (e) => {
24+
if (e.key === 'Enter') this.shortenUrl();
25+
});
26+
this.copyBtn.addEventListener('click', () => this.copyToClipboard());
27+
}
28+
29+
isValidUrl(string) {
30+
try {
31+
new URL(string);
32+
return true;
33+
} catch (_) {
34+
return false;
35+
}
36+
}
37+
38+
generateShortCode() {
39+
// Generate a random 6-character code
40+
const chars = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789';
41+
let result = '';
42+
for (let i = 0; i < 6; i++) {
43+
result += chars.charAt(Math.floor(Math.random() * chars.length));
44+
}
45+
return result;
46+
}
47+
48+
shortenUrl() {
49+
const longUrl = this.urlInput.value.trim();
50+
51+
// Hide previous results
52+
this.result.style.display = 'none';
53+
this.error.style.display = 'none';
54+
55+
if (!longUrl) {
56+
this.showError('Please enter a URL');
57+
return;
58+
}
59+
60+
if (!this.isValidUrl(longUrl)) {
61+
this.showError('Please enter a valid URL');
62+
return;
63+
}
64+
65+
// Check if URL already exists
66+
const existingEntry = this.findExistingUrl(longUrl);
67+
if (existingEntry) {
68+
this.displayResult(existingEntry.shortCode, longUrl);
69+
return;
70+
}
71+
72+
// Generate new short code
73+
let shortCode;
74+
do {
75+
shortCode = this.generateShortCode();
76+
} while (this.urlStorage[shortCode]); // Ensure uniqueness
77+
78+
// Store the URL
79+
this.urlStorage[shortCode] = {
80+
originalUrl: longUrl,
81+
createdAt: new Date().toISOString(),
82+
clickCount: 0
83+
};
84+
85+
this.saveToStorage();
86+
this.displayResult(shortCode, longUrl);
87+
this.displayRecentUrls();
88+
}
89+
90+
findExistingUrl(url) {
91+
for (const [code, data] of Object.entries(this.urlStorage)) {
92+
if (data.originalUrl === url) {
93+
return { shortCode: code, ...data };
94+
}
95+
}
96+
return null;
97+
}
98+
99+
displayResult(shortCode, originalUrl) {
100+
const shortUrl = `${this.baseUrl}#${shortCode}`;
101+
this.shortUrl.value = shortUrl;
102+
this.originalUrl.textContent = originalUrl;
103+
this.result.style.display = 'block';
104+
this.urlInput.value = '';
105+
}
106+
107+
showError(message) {
108+
this.error.querySelector('p').textContent = message;
109+
this.error.style.display = 'block';
110+
}
111+
112+
copyToClipboard() {
113+
this.shortUrl.select();
114+
this.shortUrl.setSelectionRange(0, 99999); // For mobile devices
115+
document.execCommand('copy');
116+
117+
// Visual feedback
118+
const originalText = this.copyBtn.textContent;
119+
this.copyBtn.textContent = 'Copied!';
120+
this.copyBtn.style.backgroundColor = '#28a745';
121+
122+
setTimeout(() => {
123+
this.copyBtn.textContent = originalText;
124+
this.copyBtn.style.backgroundColor = '';
125+
}, 2000);
126+
}
127+
128+
displayRecentUrls() {
129+
const entries = Object.entries(this.urlStorage)
130+
.sort((a, b) => new Date(b[1].createdAt) - new Date(a[1].createdAt))
131+
.slice(0, 10); // Show only last 10
132+
133+
if (entries.length === 0) {
134+
this.urlList.innerHTML = '<p class="no-urls">No URLs shortened yet</p>';
135+
return;
136+
}
137+
138+
this.urlList.innerHTML = entries.map(([code, data]) => `
139+
<div class="url-item">
140+
<div class="url-info">
141+
<div class="short-url">
142+
<a href="${this.baseUrl}#${code}" target="_blank">${this.baseUrl}#${code}</a>
143+
</div>
144+
<div class="original-url">${data.originalUrl}</div>
145+
<div class="url-meta">
146+
Created: ${new Date(data.createdAt).toLocaleDateString()} |
147+
Clicks: ${data.clickCount}
148+
</div>
149+
</div>
150+
<button class="delete-btn" onclick="urlShortener.deleteUrl('${code}')">Delete</button>
151+
</div>
152+
`).join('');
153+
}
154+
155+
deleteUrl(shortCode) {
156+
if (confirm('Are you sure you want to delete this URL?')) {
157+
delete this.urlStorage[shortCode];
158+
this.saveToStorage();
159+
this.displayRecentUrls();
160+
}
161+
}
162+
163+
loadFromStorage() {
164+
try {
165+
const stored = localStorage.getItem('urlShortener');
166+
return stored ? JSON.parse(stored) : {};
167+
} catch (e) {
168+
console.error('Error loading from storage:', e);
169+
return {};
170+
}
171+
}
172+
173+
saveToStorage() {
174+
try {
175+
localStorage.setItem('urlShortener', JSON.stringify(this.urlStorage));
176+
} catch (e) {
177+
console.error('Error saving to storage:', e);
178+
}
179+
}
180+
181+
// Handle URL redirection
182+
handleRedirect() {
183+
const hash = window.location.hash.substring(1);
184+
if (hash && this.urlStorage[hash]) {
185+
// Increment click count
186+
this.urlStorage[hash].clickCount++;
187+
this.saveToStorage();
188+
189+
// Redirect to original URL
190+
window.location.href = this.urlStorage[hash].originalUrl;
191+
}
192+
}
193+
}
194+
195+
// Initialize the URL shortener
196+
const urlShortener = new URLShortener();
197+
198+
// Handle page load for redirects
199+
window.addEventListener('load', () => {
200+
urlShortener.handleRedirect();
201+
});
202+
203+
// Handle hash changes
204+
window.addEventListener('hashchange', () => {
205+
urlShortener.handleRedirect();
206+
});

0 commit comments

Comments
 (0)