-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
4 changed files
with
166 additions
and
9,384 deletions.
There are no files selected for viewing
Empty file.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,39 @@ | ||
SPDX-License-Identifier: Universal Permissive License v1.0 | ||
|
||
Smart 404 Redirect | ||
|
||
Copyright (c) 2024 Sam Bacha | ||
|
||
The Universal Permissive License (UPL), Version 1.0 | ||
|
||
Subject to the condition set forth below, permission is hereby granted to any | ||
person obtaining a copy of this software, associated documentation and/or data | ||
(collectively the "Software"), free of charge and under any and all copyright | ||
rights in the Software, and any and all patent rights owned or freely | ||
licensable by each licensor hereunder covering either (i) the unmodified | ||
Software as contributed to or provided by such licensor, or (ii) the Larger | ||
Works (as defined below), to deal in both | ||
|
||
(a) the Software, and | ||
(b) any piece of software and/or hardware listed in the lrgrwrks.txt file if | ||
one is included with the Software (each a "Larger Work" to which the Software | ||
is contributed by such licensors), | ||
|
||
without restriction, including without limitation the rights to copy, create | ||
derivative works of, display, perform, and distribute the Software and make, | ||
use, sell, offer for sale, import, export, have made, and have sold the | ||
Software and the Larger Work(s), and to sublicense the foregoing rights on | ||
either these or other terms. | ||
|
||
This license is subject to the following condition: | ||
The above copyright notice and either this complete permission notice or at | ||
a minimum a reference to the UPL must be included in all copies or | ||
substantial portions of the Software. | ||
|
||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | ||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | ||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE | ||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | ||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, | ||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE | ||
SOFTWARE. |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,127 @@ | ||
# Smart 404 Redirect | ||
|
||
![](https://img.shields.io/badge/license-upl--1.0-black) | ||
|
||
## Abstract | ||
|
||
This is a javascript script for a 404 webpage where the browser should find the valid path that has the shortest edit distance (re: Levenshtein distance) from the path that was requested from the submitted URL based off of the latest sitemap.xml file. | ||
|
||
It then presents the suggestion (the shortest edit distance result based off of the sitemap.xml links) as suggested link (i.e. the path determined by the Levenshtein algo) on the 404 page. | ||
|
||
## Usage | ||
|
||
To use this script: | ||
|
||
Create a 404.html page and include the above JavaScript code within a `<script>` tag. | ||
Make sure you have a valid sitemap.xml file in the root directory of your website. The sitemap should contain the URLs of your website's pages. | ||
In the HTML body of your 404 page, include an element with the ID "suggestion" where the suggested link will be displayed. For example: | ||
|
||
```html | ||
<div id="suggestion"></div> | ||
``` | ||
When a user lands on the 404 page, the script will automatically fetch the sitemap.xml file, find the path with the shortest edit distance from the requested path using the Levenshtein distance algorithm, and display the suggested link on the page. | ||
|
||
### Generating a Sitemap | ||
|
||
I use the script `static-sitemap-cli` from npm. | ||
|
||
```bash | ||
$ static-sitemap-cli --base http://localhost:3000 --no-clean | ||
``` | ||
Then move the `sitemap.xml` file into the root of your projects html source directory that is to be published. | ||
|
||
|
||
### Code | ||
|
||
```javascript | ||
|
||
// Function to calculate the Levenshtein distance between two strings | ||
function levenshteinDistance(a, b) { | ||
const matrix = []; | ||
|
||
for (let i = 0; i <= b.length; i++) { | ||
matrix[i] = [i]; | ||
} | ||
|
||
for (let j = 0; j <= a.length; j++) { | ||
matrix[0][j] = j; | ||
} | ||
|
||
for (let i = 1; i <= b.length; i++) { | ||
for (let j = 1; j <= a.length; j++) { | ||
if (b.charAt(i - 1) === a.charAt(j - 1)) { | ||
matrix[i][j] = matrix[i - 1][j - 1]; | ||
} else { | ||
matrix[i][j] = Math.min( | ||
matrix[i - 1][j - 1] + 1, | ||
matrix[i][j - 1] + 1, | ||
matrix[i - 1][j] + 1 | ||
); | ||
} | ||
} | ||
} | ||
|
||
return matrix[b.length][a.length]; | ||
} | ||
|
||
// Function to find the path with the shortest edit distance from the sitemap | ||
function findClosestPath(requestedPath, sitemapUrls) { | ||
let closestPath = ''; | ||
let minDistance = Infinity; | ||
|
||
for (const url of sitemapUrls) { | ||
const distance = levenshteinDistance(requestedPath, url); | ||
if (distance < minDistance) { | ||
minDistance = distance; | ||
closestPath = url; | ||
} | ||
} | ||
|
||
return closestPath; | ||
} | ||
|
||
// Function to fetch the sitemap and find the closest path | ||
async function suggestClosestPath() { | ||
try { | ||
const response = await fetch('/sitemap.xml'); | ||
const sitemapXml = await response.text(); | ||
|
||
const parser = new DOMParser(); | ||
const xmlDoc = parser.parseFromString(sitemapXml, 'text/xml'); | ||
const urlElements = xmlDoc.getElementsByTagName('url'); | ||
|
||
const sitemapUrls = Array.from(urlElements).map( | ||
(urlElement) => urlElement.getElementsByTagName('loc')[0].textContent | ||
); | ||
|
||
const requestedPath = window.location.pathname; | ||
const closestPath = findClosestPath(requestedPath, sitemapUrls); | ||
|
||
const suggestionElement = document.getElementById('suggestion'); | ||
suggestionElement.innerHTML = `Did you mean: <a href="${closestPath}">${closestPath}</a>`; | ||
} catch (error) { | ||
console.error('Error fetching sitemap:', error); | ||
} | ||
} | ||
|
||
// Call the function when the page loads | ||
window.onload = suggestClosestPath; | ||
``` | ||
|
||
## License | ||
|
||
Licensed under either of | ||
|
||
* Universal Permissive License 1.0 | ||
([LICENSE-UPL](LICENSE-UPL) or https://opensource.org/licenses/UPL) | ||
|
||
* Apache License, Version 2.0 | ||
([LICENSE-APACHE](LICENSE-APACHE) or http://www.apache.org/licenses/LICENSE-2.0) | ||
|
||
at your option. | ||
|
||
### Contribution | ||
|
||
Unless you explicitly state otherwise, any contribution intentionally submitted | ||
for inclusion in the work by you, as defined in the Universal Permissive License v 1.0 | ||
license, shall be dual licensed as above, without any additional terms or conditions. |
Oops, something went wrong.