Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
117 changes: 117 additions & 0 deletions Javascript/URL-Shortener/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,117 @@
# πŸ”— URL Shortener

A simple, client-side URL shortener built with vanilla JavaScript, HTML, and CSS. No server required!

## ✨ Features

- **URL Validation** - Validates URLs before shortening
- **Short Code Generation** - Creates unique 6-character codes
- **Local Storage** - Persists URLs in browser storage
- **Click Tracking** - Counts URL access statistics
- **Copy to Clipboard** - One-click copying functionality
- **Recent URLs Management** - View and manage your shortened URLs
- **Automatic Redirection** - Seamlessly redirects to original URLs
- **Responsive Design** - Works on all devices
- **Modern UI** - Beautiful gradient design with smooth animations

## πŸš€ Quick Start

1. **Clone or download** this repository
2. **Open** `index.html` in your web browser
3. **Start shortening URLs!**

### Local Development Server

For the best experience, serve the files using a local server:

```bash
# Using Python
python3 -m http.server 8000

# Using Node.js (if you have http-server installed)
npx http-server

# Using PHP
php -S localhost:8000
```

Then open `http://localhost:8000` in your browser.

## πŸ“– How It Works

1. **Enter a long URL** in the input field
2. **Click "Shorten URL"** or press Enter
3. **Copy the shortened URL** and share it
4. **Access the short URL** to automatically redirect to the original

### URL Format
- Short URLs follow the pattern: `your-domain.com#AbC123`
- The hash fragment (`#AbC123`) contains the unique short code
- Original URLs are stored in browser's localStorage

## πŸ› οΈ Technical Details

- **Pure JavaScript** - No frameworks or dependencies
- **localStorage** - Client-side data persistence
- **Hash-based routing** - Uses URL fragments for redirection
- **Responsive CSS** - Mobile-first design approach
- **Modern ES6+** - Uses classes, arrow functions, and modern JavaScript features

## πŸ“± Browser Support

- Chrome 60+
- Firefox 55+
- Safari 12+
- Edge 79+

## πŸ”§ Customization

### Changing the Base URL
Update the `baseUrl` property in `script.js`:

```javascript
this.baseUrl = 'https://your-domain.com';
```

### Modifying the Short Code Length
Change the loop in the `generateShortCode()` method:

```javascript
for (let i = 0; i < 8; i++) { // Change 6 to desired length
```

### Styling
All styles are in `style.css`. The design uses CSS custom properties and modern layout techniques.

## πŸ“Š Data Storage

URLs are stored in browser's localStorage with the following structure:

```javascript
{
"AbC123": {
"originalUrl": "https://example.com/very/long/url",
"createdAt": "2024-01-01T00:00:00.000Z",
"clickCount": 5
}
}
```

## 🀝 Contributing

1. Fork the repository
2. Create a feature branch: `git checkout -b feature-name`
3. Make your changes
4. Commit your changes: `git commit -m 'Add some feature'`
5. Push to the branch: `git push origin feature-name`
6. Submit a pull request

## πŸ“ License

This project is open source and available under the [MIT License](LICENSE).

## πŸ™ Acknowledgments

- Built with vanilla JavaScript for maximum compatibility
- Inspired by popular URL shorteners like bit.ly and tinyurl.com
- Uses modern web standards for optimal performance
48 changes: 48 additions & 0 deletions Javascript/URL-Shortener/index.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>URL Shortener</title>
<link rel="stylesheet" href="style.css">
</head>
<body>
<div class="container">
<header>
<h1>πŸ”— URL Shortener</h1>
<p>Make your long URLs short and shareable</p>
</header>

<main>
<div class="input-section">
<input type="url" id="urlInput" placeholder="Enter your long URL here..." required>
<button id="shortenBtn">Shorten URL</button>
</div>

<div id="result" class="result-section" style="display: none;">
<h3>Your shortened URL:</h3>
<div class="short-url-container">
<input type="text" id="shortUrl" readonly>
<button id="copyBtn">Copy</button>
</div>
<div class="original-url">
<strong>Original:</strong> <span id="originalUrl"></span>
</div>
</div>

<div id="error" class="error-message" style="display: none;">
<p>Please enter a valid URL</p>
</div>
</main>

<section class="recent-urls">
<h3>Recent Shortened URLs</h3>
<div id="urlList" class="url-list">
<!-- Recent URLs will be displayed here -->
</div>
</section>
</div>

<script src="script.js"></script>
</body>
</html>
206 changes: 206 additions & 0 deletions Javascript/URL-Shortener/script.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,206 @@
class URLShortener {
constructor() {
this.urlStorage = this.loadFromStorage();
this.baseUrl = window.location.origin + window.location.pathname;
this.initializeElements();
this.bindEvents();
this.displayRecentUrls();
}

initializeElements() {
this.urlInput = document.getElementById('urlInput');
this.shortenBtn = document.getElementById('shortenBtn');
this.result = document.getElementById('result');
this.shortUrl = document.getElementById('shortUrl');
this.originalUrl = document.getElementById('originalUrl');
this.copyBtn = document.getElementById('copyBtn');
this.error = document.getElementById('error');
this.urlList = document.getElementById('urlList');
}

bindEvents() {
this.shortenBtn.addEventListener('click', () => this.shortenUrl());
this.urlInput.addEventListener('keypress', (e) => {
if (e.key === 'Enter') this.shortenUrl();
});
this.copyBtn.addEventListener('click', () => this.copyToClipboard());
}

isValidUrl(string) {
try {
new URL(string);
return true;
} catch (_) {
return false;
}
}

generateShortCode() {
// Generate a random 6-character code
const chars = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789';
let result = '';
for (let i = 0; i < 6; i++) {
result += chars.charAt(Math.floor(Math.random() * chars.length));
}
return result;
}

shortenUrl() {
const longUrl = this.urlInput.value.trim();

// Hide previous results
this.result.style.display = 'none';
this.error.style.display = 'none';

if (!longUrl) {
this.showError('Please enter a URL');
return;
}

if (!this.isValidUrl(longUrl)) {
this.showError('Please enter a valid URL');
return;
}

// Check if URL already exists
const existingEntry = this.findExistingUrl(longUrl);
if (existingEntry) {
this.displayResult(existingEntry.shortCode, longUrl);
return;
}

// Generate new short code
let shortCode;
do {
shortCode = this.generateShortCode();
} while (this.urlStorage[shortCode]); // Ensure uniqueness

// Store the URL
this.urlStorage[shortCode] = {
originalUrl: longUrl,
createdAt: new Date().toISOString(),
clickCount: 0
};

this.saveToStorage();
this.displayResult(shortCode, longUrl);
this.displayRecentUrls();
}

findExistingUrl(url) {
for (const [code, data] of Object.entries(this.urlStorage)) {
if (data.originalUrl === url) {
return { shortCode: code, ...data };
}
}
return null;
}

displayResult(shortCode, originalUrl) {
const shortUrl = `${this.baseUrl}#${shortCode}`;
this.shortUrl.value = shortUrl;
this.originalUrl.textContent = originalUrl;
this.result.style.display = 'block';
this.urlInput.value = '';
}

showError(message) {
this.error.querySelector('p').textContent = message;
this.error.style.display = 'block';
}

copyToClipboard() {
this.shortUrl.select();
this.shortUrl.setSelectionRange(0, 99999); // For mobile devices
document.execCommand('copy');

// Visual feedback
const originalText = this.copyBtn.textContent;
this.copyBtn.textContent = 'Copied!';
this.copyBtn.style.backgroundColor = '#28a745';

setTimeout(() => {
this.copyBtn.textContent = originalText;
this.copyBtn.style.backgroundColor = '';
}, 2000);
}

displayRecentUrls() {
const entries = Object.entries(this.urlStorage)
.sort((a, b) => new Date(b[1].createdAt) - new Date(a[1].createdAt))
.slice(0, 10); // Show only last 10

if (entries.length === 0) {
this.urlList.innerHTML = '<p class="no-urls">No URLs shortened yet</p>';
return;
}

this.urlList.innerHTML = entries.map(([code, data]) => `
<div class="url-item">
<div class="url-info">
<div class="short-url">
<a href="${this.baseUrl}#${code}" target="_blank">${this.baseUrl}#${code}</a>
</div>
<div class="original-url">${data.originalUrl}</div>
<div class="url-meta">
Created: ${new Date(data.createdAt).toLocaleDateString()} |
Clicks: ${data.clickCount}
</div>
</div>
<button class="delete-btn" onclick="urlShortener.deleteUrl('${code}')">Delete</button>
</div>
`).join('');
}

deleteUrl(shortCode) {
if (confirm('Are you sure you want to delete this URL?')) {
delete this.urlStorage[shortCode];
this.saveToStorage();
this.displayRecentUrls();
}
}

loadFromStorage() {
try {
const stored = localStorage.getItem('urlShortener');
return stored ? JSON.parse(stored) : {};
} catch (e) {
console.error('Error loading from storage:', e);
return {};
}
}

saveToStorage() {
try {
localStorage.setItem('urlShortener', JSON.stringify(this.urlStorage));
} catch (e) {
console.error('Error saving to storage:', e);
}
}

// Handle URL redirection
handleRedirect() {
const hash = window.location.hash.substring(1);
if (hash && this.urlStorage[hash]) {
// Increment click count
this.urlStorage[hash].clickCount++;
this.saveToStorage();

// Redirect to original URL
window.location.href = this.urlStorage[hash].originalUrl;
}
}
}

// Initialize the URL shortener
const urlShortener = new URLShortener();

// Handle page load for redirects
window.addEventListener('load', () => {
urlShortener.handleRedirect();
});

// Handle hash changes
window.addEventListener('hashchange', () => {
urlShortener.handleRedirect();
});
Loading
Loading