mirror of
https://github.com/CorentinTh/it-tools.git
synced 2025-05-05 13:57:10 -04:00
Merge b779beb695
into a07806cd15
This commit is contained in:
commit
cf693f09c0
5 changed files with 219 additions and 0 deletions
|
@ -2,6 +2,7 @@ import { tool as base64FileConverter } from './base64-file-converter';
|
|||
import { tool as base64StringConverter } from './base64-string-converter';
|
||||
import { tool as basicAuthGenerator } from './basic-auth-generator';
|
||||
import { tool as textToUnicode } from './text-to-unicode';
|
||||
import { tool as urlTextFragmentMaker } from './url-text-fragment-maker';
|
||||
import { tool as pdfSignatureChecker } from './pdf-signature-checker';
|
||||
import { tool as numeronymGenerator } from './numeronym-generator';
|
||||
import { tool as macAddressGenerator } from './mac-address-generator';
|
||||
|
@ -111,6 +112,7 @@ export const toolsByCategory: ToolCategory[] = [
|
|||
urlEncoder,
|
||||
htmlEntities,
|
||||
urlParser,
|
||||
urlTextFragmentMaker,
|
||||
deviceInformation,
|
||||
basicAuthGenerator,
|
||||
metaTagGenerator,
|
||||
|
|
12
src/tools/url-text-fragment-maker/index.ts
Normal file
12
src/tools/url-text-fragment-maker/index.ts
Normal file
|
@ -0,0 +1,12 @@
|
|||
import { FileSearch } from '@vicons/tabler';
|
||||
import { defineTool } from '../tool';
|
||||
|
||||
export const tool = defineTool({
|
||||
name: 'Url Text Search Fragment Maker',
|
||||
path: '/url-text-fragment-maker',
|
||||
description: 'Create url that allows linking directly to a specific portion of text in a web document',
|
||||
keywords: ['url', 'text', 'fragment'],
|
||||
component: () => import('./url-text-fragment-maker.vue'),
|
||||
icon: FileSearch,
|
||||
createdAt: new Date('2024-01-17'),
|
||||
});
|
|
@ -0,0 +1,76 @@
|
|||
import { describe, expect, it } from 'vitest';
|
||||
import { getUrlWithTextFragment } from './url-text-fragment-maker.service';
|
||||
|
||||
describe('url-text-fragment-maker.service', () => {
|
||||
describe('getUrlWithTextFragment', () => {
|
||||
describe('compute url with text fragment', () => {
|
||||
it('throws on invalid url', () => {
|
||||
expect(() => getUrlWithTextFragment({
|
||||
url: 'example',
|
||||
textStartSearch: 'for',
|
||||
})).toThrow('Invalid url');
|
||||
expect(() => getUrlWithTextFragment({
|
||||
url: 'htt://example',
|
||||
textStartSearch: 'for',
|
||||
})).toThrow('Url must have http:// or https:// prefix');
|
||||
expect(() => getUrlWithTextFragment({
|
||||
url: 'http:/example',
|
||||
textStartSearch: 'for',
|
||||
})).toThrow('Url must have http:// or https:// prefix');
|
||||
});
|
||||
|
||||
it('should handle basic cases', () => {
|
||||
expect(getUrlWithTextFragment({
|
||||
url: 'https://example.com',
|
||||
textStartSearch: 'for',
|
||||
}))
|
||||
.toBe('https://example.com#:~:text=for');
|
||||
expect(getUrlWithTextFragment({
|
||||
url: 'https://developer.mozilla.org/en-US/docs/Web/HTML/Element/a',
|
||||
textStartSearch: 'human',
|
||||
}))
|
||||
.toBe('https://developer.mozilla.org/en-US/docs/Web/HTML/Element/a#:~:text=human');
|
||||
});
|
||||
|
||||
it('should be url encoded', () => {
|
||||
expect(getUrlWithTextFragment({
|
||||
url: 'https://developer.mozilla.org/en-US/docs/Web/HTML/Element/a',
|
||||
textStartSearch: 'linked URL',
|
||||
suffixSearch: '\'s format',
|
||||
}))
|
||||
.toBe('https://developer.mozilla.org/en-US/docs/Web/HTML/Element/a#:~:text=linked%20URL,-\'s%20format');
|
||||
expect(getUrlWithTextFragment({
|
||||
url: 'https://developer.mozilla.org/en-US/docs/Web/HTML/Element/a',
|
||||
textStartSearch: 'The Referer',
|
||||
textStopSearch: 'be sent',
|
||||
prefixSearch: 'downgrade:',
|
||||
suffixSearch: 'to origins',
|
||||
}))
|
||||
.toBe('https://developer.mozilla.org/en-US/docs/Web/HTML/Element/a#:~:text=downgrade%3A-,The%20Referer,be%20sent,-to%20origins');
|
||||
});
|
||||
|
||||
it('should handle multiple comma separated and encoded', () => {
|
||||
expect(
|
||||
getUrlWithTextFragment({
|
||||
url: 'https://developer.mozilla.org/en-US/docs/Web/HTML/Element/a',
|
||||
textStartSearch: 'Causes,linked',
|
||||
}))
|
||||
.toBe('https://developer.mozilla.org/en-US/docs/Web/HTML/Element/a#:~:text=Causes&text=linked');
|
||||
|
||||
expect(
|
||||
getUrlWithTextFragment({
|
||||
url: 'https://developer.mozilla.org/en-US/docs/Web/HTML/Element/a',
|
||||
textStartSearch: 'Causes 1,linked 1',
|
||||
}))
|
||||
.toBe('https://developer.mozilla.org/en-US/docs/Web/HTML/Element/a#:~:text=Causes%201&text=linked%201');
|
||||
|
||||
expect(
|
||||
getUrlWithTextFragment({
|
||||
url: 'https://developer.mozilla.org/en-US/docs/Web/HTML/Element/a',
|
||||
textStartSearch: 'Causes , linked',
|
||||
}))
|
||||
.toBe('https://developer.mozilla.org/en-US/docs/Web/HTML/Element/a#:~:text=Causes&text=linked');
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
|
@ -0,0 +1,35 @@
|
|||
export function getUrlWithTextFragment(
|
||||
{ url, textStartSearch, textStopSearch, prefixSearch, suffixSearch }:
|
||||
{ url: string
|
||||
textStartSearch: string
|
||||
textStopSearch?: string
|
||||
prefixSearch?: string
|
||||
suffixSearch?: string
|
||||
},
|
||||
) {
|
||||
const isValidUrl = (urlString: string) => {
|
||||
try {
|
||||
return Boolean(new URL(urlString));
|
||||
}
|
||||
catch (e) {
|
||||
return false;
|
||||
}
|
||||
};
|
||||
if (!isValidUrl(url)) {
|
||||
throw new Error('Invalid url');
|
||||
}
|
||||
|
||||
if (!url.match(/^https?:\/\//)) {
|
||||
throw new Error('Url must have http:// or https:// prefix');
|
||||
}
|
||||
|
||||
const [textStartSearchFirstText, ...textStartSearchOtherTexts] = textStartSearch.split(',');
|
||||
const text = `${encodeURIComponent(prefixSearch ?? '')}-,${encodeURIComponent(textStartSearchFirstText.trim())},${encodeURIComponent(textStopSearch ?? '')},-${encodeURIComponent(suffixSearch ?? '')}`
|
||||
.replace(/^-,|,(?=,)|,-$/g, '')
|
||||
.replace(/,+/g, ',');
|
||||
let textStartSearchOtherTextEncoded = textStartSearchOtherTexts.map(t => `text=${encodeURIComponent(t.trim())}`).join('&');
|
||||
if (textStartSearchOtherTextEncoded.length) {
|
||||
textStartSearchOtherTextEncoded = `&${textStartSearchOtherTextEncoded}`;
|
||||
}
|
||||
return `${url.trim()}#:~:text=${text}${textStartSearchOtherTextEncoded}`;
|
||||
}
|
|
@ -0,0 +1,94 @@
|
|||
<script setup lang="ts">
|
||||
import { getUrlWithTextFragment } from './url-text-fragment-maker.service';
|
||||
import TextareaCopyable from '@/components/TextareaCopyable.vue';
|
||||
|
||||
const url = ref('');
|
||||
const prefixSearch = ref('');
|
||||
const textStartSearch = ref('');
|
||||
const textStopSearch = ref('');
|
||||
const suffixSearch = ref('');
|
||||
|
||||
const searchableUrl = computed(() => {
|
||||
try {
|
||||
return getUrlWithTextFragment({
|
||||
url: url.value,
|
||||
textStartSearch: textStartSearch.value,
|
||||
textStopSearch: textStopSearch.value,
|
||||
prefixSearch: prefixSearch.value,
|
||||
suffixSearch: suffixSearch.value,
|
||||
});
|
||||
}
|
||||
catch (e: any) {
|
||||
return e.toString();
|
||||
}
|
||||
});
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div>
|
||||
<n-p>
|
||||
Url with Text Fragments allows to make link to content that has no anchor or @id.
|
||||
<n-a href="https://developer.mozilla.org/en-US/docs/Web/Text_fragments" target="blank" rel="noopener">
|
||||
See MDN for more info
|
||||
</n-a>
|
||||
</n-p>
|
||||
<div>
|
||||
<c-input-text
|
||||
v-model:value="url"
|
||||
label="Base url:"
|
||||
placeholder="Base url..."
|
||||
type="url"
|
||||
clearable raw-text mb-5
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div flex justify-center gap-2>
|
||||
<c-input-text
|
||||
v-model:value="textStartSearch"
|
||||
label="Start Search(es) (comma separated)"
|
||||
placeholder="Start Search(es) (comma separated)..."
|
||||
clearable
|
||||
raw-text
|
||||
mb-2
|
||||
/>
|
||||
<c-input-text
|
||||
v-model:value="textStopSearch"
|
||||
label="Stop Search"
|
||||
placeholder="Stop Search text..."
|
||||
clearable
|
||||
raw-text
|
||||
mb-2
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div flex justify-center gap-2>
|
||||
<c-input-text
|
||||
v-model:value="prefixSearch"
|
||||
label="Prefix"
|
||||
placeholder="Prefix search"
|
||||
clearable
|
||||
raw-text
|
||||
mb-2
|
||||
/>
|
||||
<c-input-text
|
||||
v-model:value="suffixSearch"
|
||||
label="Suffix"
|
||||
placeholder="Suffix search"
|
||||
clearable
|
||||
raw-text
|
||||
mb-2
|
||||
/>
|
||||
</div>
|
||||
|
||||
<n-divider />
|
||||
|
||||
<n-form-item label="Searchable Url:">
|
||||
<TextareaCopyable :value="searchableUrl" />
|
||||
</n-form-item>
|
||||
<div flex justify-center>
|
||||
<n-a :href="searchableUrl" target="blank" rel="noopener">
|
||||
Test Searchable Url
|
||||
</n-a>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
Loading…
Add table
Add a link
Reference in a new issue