Skip to content

Commit f1adc66

Browse files
committed
feat(Url Encoder): enhance encoding options
Handles RFC3986 - URIComponent ; RFC5987 (Link - Content-Disposition) and others Fix CorentinTh#1445
1 parent bf2f63c commit f1adc66

File tree

2 files changed

+79
-6
lines changed

2 files changed

+79
-6
lines changed

src/tools/url-encoder/index.ts

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,10 @@
11
import { Link } from '@vicons/tabler';
22
import { defineTool } from '../tool';
3-
import { translate } from '@/plugins/i18n.plugin';
43

54
export const tool = defineTool({
6-
name: translate('tools.url-encoder.title'),
5+
name: 'Encode/decode url formatted strings',
76
path: '/url-encoder',
8-
description: translate('tools.url-encoder.description'),
7+
description: 'Encode to url-encoded format (also known as "percent-encoded") or decode from it.',
98
keywords: ['url', 'encode', 'decode', 'percent', '%20', 'format'],
109
component: () => import('./url-encoder.vue'),
1110
icon: Link,

src/tools/url-encoder/url-encoder.vue

Lines changed: 77 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,82 @@
11
<script setup lang="ts">
22
import { useCopy } from '@/composable/copy';
3+
import { useQueryParamOrStorage } from '@/composable/queryParams';
34
import { useValidation } from '@/composable/validation';
45
import { isNotThrowing } from '@/utils/boolean';
56
import { withDefaultOnError } from '@/utils/defaults';
67
8+
const encodings = [
9+
{ value: 'URIComponent', label: 'URIComponent' },
10+
{ value: 'RFC3986URIComponent', label: 'RFC3986 - URIComponent' },
11+
{ value: 'RFC5987', label: 'RFC5987 (Link - Content-Disposition)' },
12+
{ value: 'URI', label: 'URI' },
13+
{ value: 'RFC3986URI', label: 'RFC3986 - URI' },
14+
];
15+
const encoding = useQueryParamOrStorage({ name: 'enc', storageName: 'url-encode:enc', defaultValue: 'URIComponent' });
16+
17+
function encodeRFC3986URIComponent(str: string) {
18+
return encodeURIComponent(str).replace(
19+
/[!'()*]/g,
20+
(c: string) => `%${c.charCodeAt(0).toString(16).toUpperCase()}`,
21+
);
22+
}
23+
24+
function encodeRFC5987ValueChars(str: string) {
25+
return (
26+
encodeURIComponent(str)
27+
// The following creates the sequences %27 %28 %29 %2A (Note that
28+
// the valid encoding of "*" is %2A, which necessitates calling
29+
// toUpperCase() to properly encode). Although RFC3986 reserves "!",
30+
// RFC5987 does not, so we do not need to escape it.
31+
.replace(
32+
/['()*]/g,
33+
c => `%${c.charCodeAt(0).toString(16).toUpperCase()}`,
34+
)
35+
// The following are not required for percent-encoding per RFC5987,
36+
// so we can allow for a little better readability over the wire: |`^
37+
.replace(/%(7C|60|5E)/g, (str, hex) =>
38+
String.fromCharCode(Number.parseInt(hex, 16)),
39+
)
40+
);
41+
}
42+
43+
function encodeRFC3986URI(str: string) {
44+
return encodeURI(str)
45+
.replace(/%5B/g, '[')
46+
.replace(/%5D/g, ']')
47+
.replace(
48+
/[!'()*]/g,
49+
c => `%${c.charCodeAt(0).toString(16).toUpperCase()}`,
50+
);
51+
}
52+
53+
function encode(str: string) {
54+
if (encoding.value === 'URIComponent') {
55+
return encodeURIComponent(str);
56+
}
57+
if (encoding.value === 'RFC3986URIComponent') {
58+
return encodeRFC3986URIComponent(str);
59+
}
60+
if (encoding.value === 'RFC5987') {
61+
return encodeRFC5987ValueChars(str);
62+
}
63+
if (encoding.value === 'URI') {
64+
return encodeURI(str);
65+
}
66+
if (encoding.value === 'RFC3986URI') {
67+
return encodeRFC3986URI(str);
68+
}
69+
return str;
70+
}
71+
772
const encodeInput = ref('Hello world :)');
8-
const encodeOutput = computed(() => withDefaultOnError(() => encodeURIComponent(encodeInput.value), ''));
73+
const encodeOutput = computed(() => withDefaultOnError(() => encode(encodeInput.value), ''));
974
1075
const encodedValidation = useValidation({
1176
source: encodeInput,
1277
rules: [
1378
{
14-
validator: value => isNotThrowing(() => encodeURIComponent(value)),
79+
validator: value => isNotThrowing(() => encode(value)),
1580
message: 'Impossible to parse this string',
1681
},
1782
],
@@ -23,7 +88,7 @@ const decodeInput = ref('Hello%20world%20%3A)');
2388
const decodeOutput = computed(() => withDefaultOnError(() => decodeURIComponent(decodeInput.value), ''));
2489
2590
const decodeValidation = useValidation({
26-
source: decodeInput,
91+
source: encodeInput,
2792
rules: [
2893
{
2994
validator: value => isNotThrowing(() => decodeURIComponent(value)),
@@ -37,6 +102,15 @@ const { copy: copyDecoded } = useCopy({ source: decodeOutput, text: 'Decoded str
37102

38103
<template>
39104
<c-card title="Encode">
105+
<c-select
106+
v-model:value="encoding"
107+
label="Encoding Kind:"
108+
label-position="left"
109+
:options="encodings"
110+
/>
111+
112+
<n-divider />
113+
40114
<c-input-text
41115
v-model:value="encodeInput"
42116
label="Your string :"

0 commit comments

Comments
 (0)