Neu: URL Expander
This commit is contained in:
@ -221,6 +221,10 @@
|
|||||||
<h2 class="tool-title">Gronkh Games Suche</h2>
|
<h2 class="tool-title">Gronkh Games Suche</h2>
|
||||||
<p class="tool-description">Durchsuche Spiele, die Gronkh auf YouTube gespielt hat</p>
|
<p class="tool-description">Durchsuche Spiele, die Gronkh auf YouTube gespielt hat</p>
|
||||||
</a>
|
</a>
|
||||||
|
<a href="https://tools.ponywave.de/url_expander" class="tool-bubble">
|
||||||
|
<h2 class="tool-title">URL Expander</h2>
|
||||||
|
<p class="tool-description">Erweitere gekürzte URLs und prüfe, wohin sie führen, bevor du sie besuchst</p>
|
||||||
|
</a>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
@ -22,4 +22,5 @@ https://tools.ponywave.de/depp_gpt
|
|||||||
https://tools.ponywave.de/emoji
|
https://tools.ponywave.de/emoji
|
||||||
https://tools.ponywave.de/banana_run
|
https://tools.ponywave.de/banana_run
|
||||||
https://tools.ponywave.de/pokemon_quiz
|
https://tools.ponywave.de/pokemon_quiz
|
||||||
https://tools.ponywave.de/gronkh_games
|
https://tools.ponywave.de/gronkh_games
|
||||||
|
https://tools.ponywave.de/url_expander/
|
175
url_expander/expand.php
Normal file
175
url_expander/expand.php
Normal file
@ -0,0 +1,175 @@
|
|||||||
|
<?php
|
||||||
|
header('Content-Type: application/json');
|
||||||
|
|
||||||
|
// Fehlerberichterstattung für Debug-Zwecke (in der Produktion auskommentieren)
|
||||||
|
// error_reporting(E_ALL);
|
||||||
|
// ini_set('display_errors', 1);
|
||||||
|
|
||||||
|
// CORS-Header setzen (bei Bedarf anpassen)
|
||||||
|
header("Access-Control-Allow-Origin: *");
|
||||||
|
header("Access-Control-Allow-Methods: POST, OPTIONS");
|
||||||
|
header("Access-Control-Allow-Headers: Content-Type");
|
||||||
|
|
||||||
|
// OPTIONS-Anfragen für CORS-Preflight behandeln
|
||||||
|
if ($_SERVER['REQUEST_METHOD'] === 'OPTIONS') {
|
||||||
|
http_response_code(200);
|
||||||
|
exit();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Nur POST-Anfragen zulassen
|
||||||
|
if ($_SERVER['REQUEST_METHOD'] !== 'POST') {
|
||||||
|
echo json_encode(['error' => 'Nur POST-Anfragen erlaubt']);
|
||||||
|
exit();
|
||||||
|
}
|
||||||
|
|
||||||
|
// URL aus der Anfrage abrufen
|
||||||
|
$url = isset($_POST['url']) ? trim($_POST['url']) : '';
|
||||||
|
|
||||||
|
// Überprüfen, ob eine URL angegeben wurde
|
||||||
|
if (empty($url)) {
|
||||||
|
echo json_encode(['error' => 'Keine URL angegeben']);
|
||||||
|
exit();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Überprüfen, ob die URL gültig ist
|
||||||
|
if (!filter_var($url, FILTER_VALIDATE_URL)) {
|
||||||
|
echo json_encode(['error' => 'Ungültige URL']);
|
||||||
|
exit();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Funktion, um eine URL zu erweitern und weiterleitungen zu verfolgen
|
||||||
|
function expandUrl($url, $maxRedirects = 10) {
|
||||||
|
$redirects = 0;
|
||||||
|
$redirectChain = [$url]; // Speichern der ursprünglichen URL als erster Eintrag in der Kette
|
||||||
|
|
||||||
|
// Zuerst versuchen mit HEAD-Anfrage (schneller)
|
||||||
|
$options = [
|
||||||
|
CURLOPT_URL => $url,
|
||||||
|
CURLOPT_HEADER => true,
|
||||||
|
CURLOPT_NOBODY => true, // Nur HEAD-Anfrage
|
||||||
|
CURLOPT_RETURNTRANSFER => true,
|
||||||
|
CURLOPT_FOLLOWLOCATION => false, // Wir verfolgen Weiterleitungen manuell
|
||||||
|
CURLOPT_MAXREDIRS => 0, // Keine automatischen Weiterleitungen
|
||||||
|
CURLOPT_TIMEOUT => 8, // Zeitlimit (Sekunden)
|
||||||
|
CURLOPT_USERAGENT => 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/134.0.0.0 Safari/537.36',
|
||||||
|
CURLOPT_SSL_VERIFYPEER => false, // SSL-Prüfung deaktivieren
|
||||||
|
CURLOPT_SSL_VERIFYHOST => 0, // SSL-Hostprüfung deaktivieren
|
||||||
|
CURLOPT_HTTPHEADER => [ // Standard-Header für eine normale Browser-Anfrage
|
||||||
|
'Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8',
|
||||||
|
'Accept-Language: de,en-US;q=0.7,en;q=0.3',
|
||||||
|
'Connection: keep-alive',
|
||||||
|
'Upgrade-Insecure-Requests: 1',
|
||||||
|
'Cache-Control: max-age=0'
|
||||||
|
]
|
||||||
|
];
|
||||||
|
|
||||||
|
$currentUrl = $url;
|
||||||
|
$method = 'HEAD'; // Wir fangen mit HEAD an, könnten aber zu GET wechseln
|
||||||
|
|
||||||
|
while ($redirects < $maxRedirects) {
|
||||||
|
// cURL-Session initialisieren
|
||||||
|
$ch = curl_init();
|
||||||
|
$options[CURLOPT_URL] = $currentUrl;
|
||||||
|
curl_setopt_array($ch, $options);
|
||||||
|
|
||||||
|
// Anfrage ausführen
|
||||||
|
$response = curl_exec($ch);
|
||||||
|
$httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
|
||||||
|
|
||||||
|
// Bei einem 405 (Method Not Allowed) oder sonstigen Fehler, zu GET wechseln
|
||||||
|
if (($response === false || $httpCode >= 400) && $method === 'HEAD') {
|
||||||
|
curl_close($ch);
|
||||||
|
|
||||||
|
// Auf GET-Anfrage umstellen
|
||||||
|
$method = 'GET';
|
||||||
|
$options[CURLOPT_NOBODY] = false; // GET statt HEAD
|
||||||
|
$options[CURLOPT_HEADER] = true; // Headers im Output behalten
|
||||||
|
|
||||||
|
continue; // Wiederholen mit neuer Methode
|
||||||
|
}
|
||||||
|
|
||||||
|
// Fehlerprüfung
|
||||||
|
if ($response === false) {
|
||||||
|
$error = curl_error($ch);
|
||||||
|
curl_close($ch);
|
||||||
|
return ['error' => 'cURL-Fehler: ' . $error];
|
||||||
|
}
|
||||||
|
|
||||||
|
// Bei Fehlern, die nicht durch Methodenwechsel behoben werden können, abbrechen
|
||||||
|
if ($httpCode >= 400 && $method === 'GET') {
|
||||||
|
curl_close($ch);
|
||||||
|
return ['error' => 'HTTP-Fehler: ' . $httpCode];
|
||||||
|
}
|
||||||
|
|
||||||
|
// Auf Weiterleitungen prüfen
|
||||||
|
if ($httpCode >= 300 && $httpCode < 400) {
|
||||||
|
// Header extrahieren
|
||||||
|
$headerSize = curl_getinfo($ch, CURLINFO_HEADER_SIZE);
|
||||||
|
$header = substr($response, 0, $headerSize);
|
||||||
|
|
||||||
|
// Location-Header suchen
|
||||||
|
if (preg_match('/Location:(.*?)\r/i', $header, $matches)) {
|
||||||
|
$locationUrl = trim($matches[1]);
|
||||||
|
|
||||||
|
// Relative URL in absolute umwandeln
|
||||||
|
if (!preg_match('/^https?:\/\//i', $locationUrl)) {
|
||||||
|
$parts = parse_url($currentUrl);
|
||||||
|
$base = $parts['scheme'] . '://' . $parts['host'];
|
||||||
|
if (isset($parts['port'])) $base .= ':' . $parts['port'];
|
||||||
|
|
||||||
|
if (substr($locationUrl, 0, 1) === '/') {
|
||||||
|
$locationUrl = $base . $locationUrl;
|
||||||
|
} else {
|
||||||
|
$path = isset($parts['path']) ? $parts['path'] : '/';
|
||||||
|
$path = substr($path, 0, strrpos($path, '/') + 1);
|
||||||
|
$locationUrl = $base . $path . $locationUrl;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Zur Weiterleitungskette hinzufügen
|
||||||
|
$redirectChain[] = $locationUrl;
|
||||||
|
$currentUrl = $locationUrl;
|
||||||
|
$redirects++;
|
||||||
|
|
||||||
|
curl_close($ch);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Keine Weiterleitung mehr - wir haben die Ziel-URL erreicht
|
||||||
|
curl_close($ch);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Letzter URL ist die Ziel-URL
|
||||||
|
$finalUrl = end($redirectChain);
|
||||||
|
|
||||||
|
// Überprüfe ob wir das Maximum an Weiterleitungen erreicht haben
|
||||||
|
if ($redirects >= $maxRedirects) {
|
||||||
|
return [
|
||||||
|
'expanded_url' => $finalUrl,
|
||||||
|
'redirect_chain' => $redirectChain,
|
||||||
|
'info' => 'Maximale Anzahl von Weiterleitungen erreicht'
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
// URL auf Sonderzeichen und Encodierung prüfen
|
||||||
|
$finalUrl = htmlspecialchars_decode($finalUrl);
|
||||||
|
|
||||||
|
// Alle URLs in der Kette dekodieren
|
||||||
|
$decodedChain = array_map('htmlspecialchars_decode', $redirectChain);
|
||||||
|
|
||||||
|
// Erfolgreiche Expansion
|
||||||
|
return [
|
||||||
|
'expanded_url' => $finalUrl,
|
||||||
|
'redirect_chain' => $decodedChain,
|
||||||
|
'redirect_count' => count($decodedChain) - 1
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
// URL erweitern
|
||||||
|
$result = expandUrl($url);
|
||||||
|
|
||||||
|
// Ergebnis zurückgeben
|
||||||
|
echo json_encode($result);
|
||||||
|
?>
|
BIN
url_expander/icon.jpg
Normal file
BIN
url_expander/icon.jpg
Normal file
Binary file not shown.
After Width: | Height: | Size: 8.2 KiB |
522
url_expander/index.html
Normal file
522
url_expander/index.html
Normal file
@ -0,0 +1,522 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="de">
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8">
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||||
|
<title>URL Expander | PonyWave Tools</title>
|
||||||
|
<meta property="og:title" content="URL Expander | PonyWave Tools">
|
||||||
|
<meta property="og:description" content="Gekürzte Links sicher erweitern und überprüfen">
|
||||||
|
<meta property="og:type" content="website">
|
||||||
|
<meta property="og:url" content="https://tools.ponywave.de/url_expander">
|
||||||
|
<meta property="og:image" content="https://tools.ponywave.de/url_expander/icon.jpg">
|
||||||
|
<link rel="icon" href="https://tools.ponywave.de/url_expander/icon.jpg">
|
||||||
|
<script defer src="https://stats.ponywave.de/script" data-website-id="9ef713d2-adb9-4906-9df5-708d8a8b9131" data-tag="url_expander"></script>
|
||||||
|
<style>
|
||||||
|
:root {
|
||||||
|
--bg-color: #f5f5f5;
|
||||||
|
--text-color: #333333;
|
||||||
|
--card-bg: #ffffff;
|
||||||
|
--accent-color: #7F006E;
|
||||||
|
--secondary-color: #FF7FED;
|
||||||
|
--border-color: #ddd;
|
||||||
|
--shadow-color: rgba(0, 0, 0, 0.1);
|
||||||
|
--footer-bg: #f0f0f0;
|
||||||
|
--footer-text: #555;
|
||||||
|
--button-hover: #690058;
|
||||||
|
--toggle-light: #ffd700;
|
||||||
|
--toggle-dark: #333;
|
||||||
|
--toast-success: #4CAF50;
|
||||||
|
--toast-error: #f44336;
|
||||||
|
--redirect-item-bg: #f9f9f9;
|
||||||
|
--redirect-arrow: #888;
|
||||||
|
}
|
||||||
|
|
||||||
|
.dark-theme {
|
||||||
|
--bg-color: #222;
|
||||||
|
--text-color: #eee;
|
||||||
|
--card-bg: #333;
|
||||||
|
--accent-color: #FF7FED;
|
||||||
|
--secondary-color: #af5fa0;
|
||||||
|
--border-color: #444;
|
||||||
|
--shadow-color: rgba(0, 0, 0, 0.3);
|
||||||
|
--footer-bg: #333;
|
||||||
|
--footer-text: #ccc;
|
||||||
|
--button-hover: #d76ac0;
|
||||||
|
--redirect-item-bg: #444;
|
||||||
|
--redirect-arrow: #aaa;
|
||||||
|
}
|
||||||
|
|
||||||
|
body {
|
||||||
|
font-family: 'Segoe UI', Arial, sans-serif;
|
||||||
|
background-color: var(--bg-color);
|
||||||
|
color: var(--text-color);
|
||||||
|
margin: 0;
|
||||||
|
padding: 20px 20px 70px 20px;
|
||||||
|
box-sizing: border-box;
|
||||||
|
min-height: 100vh;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
transition: background-color 0.3s, color 0.3s;
|
||||||
|
}
|
||||||
|
|
||||||
|
.container {
|
||||||
|
max-width: 800px;
|
||||||
|
margin: 0 auto;
|
||||||
|
padding: 20px;
|
||||||
|
flex: 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
h1 {
|
||||||
|
color: var(--accent-color);
|
||||||
|
text-align: center;
|
||||||
|
margin-bottom: 30px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.card {
|
||||||
|
background-color: var(--card-bg);
|
||||||
|
border-radius: 8px;
|
||||||
|
box-shadow: 0 4px 10px var(--shadow-color);
|
||||||
|
padding: 25px;
|
||||||
|
margin-bottom: 30px;
|
||||||
|
transition: background-color 0.3s;
|
||||||
|
}
|
||||||
|
|
||||||
|
.input-group {
|
||||||
|
display: flex;
|
||||||
|
gap: 10px;
|
||||||
|
margin-bottom: 20px;
|
||||||
|
}
|
||||||
|
|
||||||
|
input[type="text"], input[type="url"] {
|
||||||
|
flex: 1;
|
||||||
|
padding: 12px 15px;
|
||||||
|
border: 1px solid var(--border-color);
|
||||||
|
border-radius: 4px;
|
||||||
|
font-size: 16px;
|
||||||
|
background-color: var(--card-bg);
|
||||||
|
color: var(--text-color);
|
||||||
|
transition: border-color 0.3s, background-color 0.3s, color 0.3s;
|
||||||
|
}
|
||||||
|
|
||||||
|
input[type="text"]:focus, input[type="url"]:focus {
|
||||||
|
outline: none;
|
||||||
|
border-color: var(--accent-color);
|
||||||
|
}
|
||||||
|
|
||||||
|
button {
|
||||||
|
background-color: var(--accent-color);
|
||||||
|
color: white;
|
||||||
|
border: none;
|
||||||
|
border-radius: 4px;
|
||||||
|
padding: 12px 20px;
|
||||||
|
font-size: 16px;
|
||||||
|
cursor: pointer;
|
||||||
|
transition: background-color 0.3s;
|
||||||
|
}
|
||||||
|
|
||||||
|
button:hover {
|
||||||
|
background-color: var(--button-hover);
|
||||||
|
}
|
||||||
|
|
||||||
|
.result {
|
||||||
|
margin-top: 20px;
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.result-card {
|
||||||
|
background-color: var(--card-bg);
|
||||||
|
border-radius: 8px;
|
||||||
|
box-shadow: 0 2px 6px var(--shadow-color);
|
||||||
|
padding: 15px;
|
||||||
|
margin-top: 15px;
|
||||||
|
border-left: 4px solid var(--accent-color);
|
||||||
|
word-break: break-all;
|
||||||
|
transition: background-color 0.3s;
|
||||||
|
}
|
||||||
|
|
||||||
|
.result-title {
|
||||||
|
font-weight: bold;
|
||||||
|
margin-bottom: 5px;
|
||||||
|
color: var(--accent-color);
|
||||||
|
}
|
||||||
|
|
||||||
|
.redirect-chain {
|
||||||
|
margin-top: 15px;
|
||||||
|
border-left: 4px solid var(--secondary-color);
|
||||||
|
margin-bottom: 15px;
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.redirect-chain-header {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
cursor: pointer;
|
||||||
|
padding: 8px 12px;
|
||||||
|
background-color: var(--redirect-item-bg);
|
||||||
|
border-radius: 4px;
|
||||||
|
margin-bottom: 5px;
|
||||||
|
user-select: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.redirect-chain-header .arrow {
|
||||||
|
margin-right: 10px;
|
||||||
|
transition: transform 0.3s;
|
||||||
|
}
|
||||||
|
|
||||||
|
.redirect-chain-header.open .arrow {
|
||||||
|
transform: rotate(90deg);
|
||||||
|
}
|
||||||
|
|
||||||
|
.redirect-items {
|
||||||
|
padding-left: 10px;
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.redirect-item {
|
||||||
|
padding: 8px 15px;
|
||||||
|
margin: 5px 0;
|
||||||
|
background-color: var(--redirect-item-bg);
|
||||||
|
border-radius: 4px;
|
||||||
|
position: relative;
|
||||||
|
word-break: break-all;
|
||||||
|
}
|
||||||
|
|
||||||
|
.redirect-item:not(:last-child):after {
|
||||||
|
content: "↓";
|
||||||
|
position: absolute;
|
||||||
|
bottom: -15px;
|
||||||
|
left: 15px;
|
||||||
|
color: var(--redirect-arrow);
|
||||||
|
font-size: 16px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.checkbox-group {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
margin: 15px 0;
|
||||||
|
gap: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.theme-toggle {
|
||||||
|
position: fixed;
|
||||||
|
top: 20px;
|
||||||
|
right: 20px;
|
||||||
|
background-color: var(--card-bg);
|
||||||
|
border-radius: 50%;
|
||||||
|
width: 50px;
|
||||||
|
height: 50px;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
cursor: pointer;
|
||||||
|
box-shadow: 0 2px 5px var(--shadow-color);
|
||||||
|
z-index: 1000;
|
||||||
|
transition: background-color 0.3s;
|
||||||
|
}
|
||||||
|
|
||||||
|
.theme-icon {
|
||||||
|
width: 24px;
|
||||||
|
height: 24px;
|
||||||
|
fill: var(--toggle-light);
|
||||||
|
}
|
||||||
|
|
||||||
|
.dark-theme .theme-icon {
|
||||||
|
fill: var(--toggle-light);
|
||||||
|
}
|
||||||
|
|
||||||
|
footer {
|
||||||
|
position: fixed;
|
||||||
|
bottom: 0;
|
||||||
|
left: 0;
|
||||||
|
right: 0;
|
||||||
|
padding: 15px 0;
|
||||||
|
background-color: var(--footer-bg);
|
||||||
|
border-top: 1px solid var(--border-color);
|
||||||
|
text-align: center;
|
||||||
|
font-size: 14px;
|
||||||
|
color: var(--footer-text);
|
||||||
|
z-index: 100;
|
||||||
|
transition: background-color 0.3s, color 0.3s;
|
||||||
|
}
|
||||||
|
|
||||||
|
footer a {
|
||||||
|
color: var(--accent-color);
|
||||||
|
text-decoration: none;
|
||||||
|
font-weight: bold;
|
||||||
|
}
|
||||||
|
|
||||||
|
footer a:hover {
|
||||||
|
text-decoration: underline;
|
||||||
|
}
|
||||||
|
|
||||||
|
.heart {
|
||||||
|
color: #ff0000;
|
||||||
|
animation: heartbeat 1.5s infinite;
|
||||||
|
}
|
||||||
|
|
||||||
|
@keyframes heartbeat {
|
||||||
|
0% { transform: scale(1); }
|
||||||
|
50% { transform: scale(1.2); }
|
||||||
|
100% { transform: scale(1); }
|
||||||
|
}
|
||||||
|
|
||||||
|
.toast {
|
||||||
|
position: fixed;
|
||||||
|
bottom: 80px;
|
||||||
|
left: 50%;
|
||||||
|
transform: translateX(-50%);
|
||||||
|
background: rgba(0, 0, 0, 0.9);
|
||||||
|
color: white;
|
||||||
|
padding: 15px 25px;
|
||||||
|
border-radius: 25px;
|
||||||
|
display: none;
|
||||||
|
align-items: center;
|
||||||
|
gap: 10px;
|
||||||
|
font-size: 14px;
|
||||||
|
opacity: 0;
|
||||||
|
transition: opacity 0.3s ease;
|
||||||
|
z-index: 1000;
|
||||||
|
}
|
||||||
|
|
||||||
|
.toast.visible {
|
||||||
|
display: flex;
|
||||||
|
opacity: 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
.toast-success {
|
||||||
|
background: var(--toast-success);
|
||||||
|
}
|
||||||
|
|
||||||
|
.toast-error {
|
||||||
|
background: var(--toast-error);
|
||||||
|
}
|
||||||
|
|
||||||
|
.toast-icon {
|
||||||
|
width: 20px;
|
||||||
|
height: 20px;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<div class="theme-toggle" id="themeToggle" title="Thema wechseln">
|
||||||
|
<svg class="theme-icon" viewBox="0 0 24 24">
|
||||||
|
<path d="M12,7c-2.76,0-5,2.24-5,5s2.24,5,5,5s5-2.24,5-5S14.76,7,12,7L12,7z M2,13l2,0c0.55,0,1-0.45,1-1s-0.45-1-1-1l-2,0 c-0.55,0-1,0.45-1,1S1.45,13,2,13z M20,13l2,0c0.55,0,1-0.45,1-1s-0.45-1-1-1l-2,0c-0.55,0-1,0.45-1,1S19.45,13,20,13z M11,2v2 c0,0.55,0.45,1,1,1s1-0.45,1-1V2c0-0.55-0.45-1-1-1S11,1.45,11,2z M11,20v2c0,0.55,0.45,1,1,1s1-0.45,1-1v-2c0-0.55-0.45-1-1-1 S11,19.45,11,20z M5.99,4.58c-0.39-0.39-1.03-0.39-1.41,0c-0.39,0.39-0.39,1.03,0,1.41l1.06,1.06c0.39,0.39,1.03,0.39,1.41,0 s0.39-1.03,0-1.41L5.99,4.58z M18.36,16.95c-0.39-0.39-1.03-0.39-1.41,0c-0.39,0.39-0.39,1.03,0,1.41l1.06,1.06 c0.39,0.39,1.03,0.39,1.41,0c0.39-0.39,0.39-1.03,0-1.41L18.36,16.95z M19.42,5.99c0.39-0.39,0.39-1.03,0-1.41 c-0.39-0.39-1.03-0.39-1.41,0l-1.06,1.06c-0.39,0.39-0.39,1.03,0,1.41s1.03,0.39,1.41,0L19.42,5.99z M7.05,18.36 c0.39-0.39,0.39-1.03,0-1.41c-0.39-0.39-1.03-0.39-1.41,0l-1.06,1.06c-0.39,0.39-0.39,1.03,0,1.41s1.03,0.39,1.41,0L7.05,18.36z"/>
|
||||||
|
</svg>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="container">
|
||||||
|
<h1>🔍 URL Expander</h1>
|
||||||
|
|
||||||
|
<div class="card">
|
||||||
|
<p>Mit diesem Tool kannst du gekürzte URLs (z.B. bit.ly, t.co, goo.gl, etc.) erweitern und sehen, wohin sie wirklich führen, ohne sie direkt zu besuchen.</p>
|
||||||
|
|
||||||
|
<div class="input-group">
|
||||||
|
<input type="url" id="url-input" placeholder="Gib den gekürzten Link ein (z.B. https://bit.ly/abc123)" required>
|
||||||
|
<button id="expand-button">Erweitern</button>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="checkbox-group">
|
||||||
|
<input type="checkbox" id="auto-redirect" name="auto-redirect">
|
||||||
|
<label for="auto-redirect">Automatisch zur Ziel-URL weiterleiten</label>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div id="result" class="result">
|
||||||
|
<h3>Ergebnis:</h3>
|
||||||
|
|
||||||
|
<div id="redirect-chain" class="redirect-chain">
|
||||||
|
<div class="redirect-chain-header" id="redirect-chain-toggle">
|
||||||
|
<span class="arrow">▶</span>
|
||||||
|
<span id="redirect-count">Weiterleitungskette anzeigen</span>
|
||||||
|
</div>
|
||||||
|
<div class="redirect-items" id="redirect-items">
|
||||||
|
<!-- Redirect chain items will be inserted here -->
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="result-card">
|
||||||
|
<div class="result-title">Ziel-URL:</div>
|
||||||
|
<div id="expanded-url"></div>
|
||||||
|
</div>
|
||||||
|
<div class="button-group" style="margin-top: 15px;">
|
||||||
|
<button id="copy-button">📋 URL kopieren</button>
|
||||||
|
<button id="visit-button">🔗 URL besuchen</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div id="toast" class="toast"></div>
|
||||||
|
|
||||||
|
<footer>
|
||||||
|
<p>
|
||||||
|
<a href="https://tools.ponywave.de/">Zurück zur Startseite</a> |
|
||||||
|
© <span id="current-year"></span> Akamaru | Made with <span class="heart">❤️</span> by Claude
|
||||||
|
</p>
|
||||||
|
</footer>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
// Theme-Wechsel Funktionalität
|
||||||
|
document.addEventListener('DOMContentLoaded', function() {
|
||||||
|
const themeToggle = document.getElementById('themeToggle');
|
||||||
|
const prefersDarkScheme = window.matchMedia('(prefers-color-scheme: dark)');
|
||||||
|
const autoRedirectCheckbox = document.getElementById('auto-redirect');
|
||||||
|
|
||||||
|
// Beim Laden der Seite prüfen, ob das Betriebssystem ein dunkles Theme verwendet
|
||||||
|
if (prefersDarkScheme.matches) {
|
||||||
|
document.body.classList.add('dark-theme');
|
||||||
|
}
|
||||||
|
|
||||||
|
// Theme-Toggle-Button-Funktion
|
||||||
|
themeToggle.addEventListener('click', function() {
|
||||||
|
document.body.classList.toggle('dark-theme');
|
||||||
|
});
|
||||||
|
|
||||||
|
// Lade Auto-Redirect-Einstellung aus localStorage
|
||||||
|
if (localStorage.getItem('autoRedirect') === 'true') {
|
||||||
|
autoRedirectCheckbox.checked = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Speichere Auto-Redirect-Einstellung in localStorage bei Änderung
|
||||||
|
autoRedirectCheckbox.addEventListener('change', function() {
|
||||||
|
localStorage.setItem('autoRedirect', this.checked);
|
||||||
|
});
|
||||||
|
|
||||||
|
// Weiterleitungskette Toggle
|
||||||
|
document.getElementById('redirect-chain-toggle').addEventListener('click', function() {
|
||||||
|
this.classList.toggle('open');
|
||||||
|
const redirectItems = document.getElementById('redirect-items');
|
||||||
|
redirectItems.style.display = redirectItems.style.display === 'block' ? 'none' : 'block';
|
||||||
|
});
|
||||||
|
|
||||||
|
// URL-Parameter beim Laden verarbeiten
|
||||||
|
const urlParams = new URLSearchParams(window.location.search);
|
||||||
|
const urlParam = urlParams.get('url');
|
||||||
|
if (urlParam) {
|
||||||
|
document.getElementById('url-input').value = decodeURIComponent(urlParam);
|
||||||
|
expandUrl();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Expand-Button Event Listener
|
||||||
|
document.getElementById('expand-button').addEventListener('click', expandUrl);
|
||||||
|
|
||||||
|
// Enter-Taste zur Ausführung
|
||||||
|
document.getElementById('url-input').addEventListener('keypress', function(e) {
|
||||||
|
if (e.key === 'Enter') {
|
||||||
|
expandUrl();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// Copy-Button Event Listener
|
||||||
|
document.getElementById('copy-button').addEventListener('click', function() {
|
||||||
|
const expandedUrl = document.getElementById('expanded-url').textContent;
|
||||||
|
navigator.clipboard.writeText(expandedUrl)
|
||||||
|
.then(() => showToast('URL kopiert!'))
|
||||||
|
.catch(() => showToast('Fehler beim Kopieren!', 'error'));
|
||||||
|
});
|
||||||
|
|
||||||
|
// Visit-Button Event Listener
|
||||||
|
document.getElementById('visit-button').addEventListener('click', function() {
|
||||||
|
const expandedUrl = document.getElementById('expanded-url').textContent;
|
||||||
|
if (expandedUrl) {
|
||||||
|
window.open(expandedUrl, '_blank');
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// Jahr für das Copyright aktualisieren
|
||||||
|
document.getElementById('current-year').textContent = new Date().getFullYear();
|
||||||
|
});
|
||||||
|
|
||||||
|
function expandUrl() {
|
||||||
|
const input = document.getElementById('url-input').value.trim();
|
||||||
|
const resultDiv = document.getElementById('result');
|
||||||
|
const expandedUrlDiv = document.getElementById('expanded-url');
|
||||||
|
const redirectChainDiv = document.getElementById('redirect-chain');
|
||||||
|
const redirectItemsDiv = document.getElementById('redirect-items');
|
||||||
|
const redirectCountSpan = document.getElementById('redirect-count');
|
||||||
|
|
||||||
|
if (!input) {
|
||||||
|
showToast('Bitte gib eine URL ein!', 'error');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// URL validieren
|
||||||
|
if (!input.startsWith('http://') && !input.startsWith('https://')) {
|
||||||
|
showToast('Ungültige URL! Starte mit http:// oder https://', 'error');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Laden anzeigen
|
||||||
|
expandedUrlDiv.textContent = 'Lade...';
|
||||||
|
resultDiv.style.display = 'block';
|
||||||
|
redirectChainDiv.style.display = 'none';
|
||||||
|
|
||||||
|
// Server-Anfrage zum Erweitern der URL
|
||||||
|
fetch('expand.php', {
|
||||||
|
method: 'POST',
|
||||||
|
headers: {
|
||||||
|
'Content-Type': 'application/x-www-form-urlencoded',
|
||||||
|
},
|
||||||
|
body: 'url=' + encodeURIComponent(input)
|
||||||
|
})
|
||||||
|
.then(response => response.json())
|
||||||
|
.then(data => {
|
||||||
|
if (data.error) {
|
||||||
|
expandedUrlDiv.textContent = 'Fehler: ' + data.error;
|
||||||
|
showToast('Fehler beim Erweitern der URL!', 'error');
|
||||||
|
} else {
|
||||||
|
expandedUrlDiv.textContent = data.expanded_url;
|
||||||
|
showToast('URL erfolgreich erweitert!');
|
||||||
|
|
||||||
|
// Weiterleitungskette anzeigen, wenn vorhanden
|
||||||
|
if (data.redirect_chain && data.redirect_chain.length > 1) {
|
||||||
|
// Zähler anzeigen
|
||||||
|
const redirectCount = data.redirect_count;
|
||||||
|
const schrittText = redirectCount === 1 ? 'Schritt' : 'Schritte';
|
||||||
|
redirectCountSpan.textContent = `Weiterleitungskette (${redirectCount} ${schrittText})`;
|
||||||
|
|
||||||
|
// Elemente in der Kette anzeigen
|
||||||
|
redirectItemsDiv.innerHTML = '';
|
||||||
|
data.redirect_chain.forEach((url, index) => {
|
||||||
|
const redirectItem = document.createElement('div');
|
||||||
|
redirectItem.className = 'redirect-item';
|
||||||
|
redirectItem.textContent = url;
|
||||||
|
redirectItemsDiv.appendChild(redirectItem);
|
||||||
|
});
|
||||||
|
|
||||||
|
redirectChainDiv.style.display = 'block';
|
||||||
|
} else {
|
||||||
|
redirectChainDiv.style.display = 'none';
|
||||||
|
}
|
||||||
|
|
||||||
|
// Automatische Weiterleitung, wenn aktiviert
|
||||||
|
if (document.getElementById('auto-redirect').checked) {
|
||||||
|
window.location.href = data.expanded_url;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.catch(error => {
|
||||||
|
expandedUrlDiv.textContent = 'Fehler beim Verarbeiten der Anfrage.';
|
||||||
|
showToast('Fehler beim Verarbeiten der Anfrage!', 'error');
|
||||||
|
console.error('Error:', error);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function showToast(message, type = 'success') {
|
||||||
|
const toast = document.getElementById('toast');
|
||||||
|
toast.className = `toast toast-${type} visible`;
|
||||||
|
toast.innerHTML = `
|
||||||
|
<svg class="toast-icon" viewBox="0 0 24 24">
|
||||||
|
${type === 'success' ?
|
||||||
|
'<path fill="currentColor" d="M9 16.2L4.8 12l-1.4 1.4L9 19 21 7l-1.4-1.4L9 16.2z"/>' :
|
||||||
|
'<path fill="currentColor" d="M19 6.41L17.59 5 12 10.59 6.41 5 5 6.41 10.59 12 5 17.59 6.41 19 12 13.41 17.59 19 19 17.59 13.41 12 19 6.41z"/>'}
|
||||||
|
</svg>
|
||||||
|
${message}
|
||||||
|
`;
|
||||||
|
|
||||||
|
setTimeout(() => {
|
||||||
|
toast.classList.remove('visible');
|
||||||
|
}, 3000);
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
</body>
|
||||||
|
</html>
|
Reference in New Issue
Block a user