14 Commits
Author SHA1 Message Date
Solninja A 35120f752a Convert extension to manifest v3. Still under testing 2025-02-01 16:04:22 +10:00
SolninjaA 9aef574167 Merge pull request #6 from SolninjaA/v1.3.1
v1.3.1 - Fix multiple context menus when the extension is refreshed on Chromium browsers
2025-01-30 19:11:39 +10:00
Solninja A 870f4aef37 Fix multiple context menus when the extension is refreshed on Chromium browsers 2025-01-30 19:10:28 +10:00
SolninjaA 6bb72d73be Merge pull request #5 from SolninjaA/v1.3.1
v1.3.1 - Fix version number
2025-01-30 19:03:02 +10:00
Solninja A 1cd26a7eb4 Fix version number 2025-01-30 19:00:20 +10:00
SolninjaA 603f15b0c1 Merge pull request #3 from SolninjaA/v1.3.1
v1.3.1
2025-01-30 18:58:47 +10:00
SolninjaA 5e2c4ca32e Merge pull request #4 from SolninjaA/all-contributors/add-SolninjaA
docs: add SolninjaA as a contributor for code, projectManagement, and doc
2025-01-30 18:49:49 +10:00
SolninjaA 3e4af6e537 Update README.md 2025-01-30 18:49:00 +10:00
SolninjaA 9aabd10fbb Update .all-contributorsrc 2025-01-30 18:47:44 +10:00
allcontributors[bot] 04a3a6ebe8 docs: update .all-contributorsrc 2025-01-30 08:45:54 +00:00
allcontributors[bot] 4e2ebac696 docs: update README.md 2025-01-30 08:45:53 +00:00
Solninja A d222666b40 Add new point to the acknowledgement section 2025-01-30 18:31:07 +10:00
Solninja A a6293facd2 Clean up code 2025-01-30 18:28:28 +10:00
Solninja A c5f8d3a7da Fix popup bugs, such as conflicting URLs and trailing whitespaces 2025-01-30 17:40:10 +10:00
10 changed files with 467 additions and 91 deletions

No files matched your search

+11
View File
@@ -7,6 +7,17 @@
"README.md" "README.md"
], ],
"contributors": [ "contributors": [
{
"login": "SolninjaA",
"name": "SolninjaA",
"avatar_url": "https://avatars.githubusercontent.com/u/51935570?v=4",
"profile": "https://github.com/SolninjaA",
"contributions": [
"code",
"projectManagement",
"doc"
]
},
{ {
"login": "DarioDarko", "login": "DarioDarko",
"name": "DarioDarko", "name": "DarioDarko",
+6 -4
View File
@@ -108,6 +108,7 @@ This extension only communicates to the Chhoto URL server instance you configure
<table> <table>
<tbody> <tbody>
<tr> <tr>
<td align="center" valign="top" width="14.28%"><a href="https://github.com/SolninjaA"><img src="https://avatars.githubusercontent.com/u/51935570?v=4?s=100" width="100px;" alt="SolninjaA"/><br /><sub><b>SolninjaA</b></sub></a><br /><a href="https://github.com/SolninjaA/Chhoto-URL-Extension/commits?author=SolninjaA" title="Code">💻</a> <a href="#projectManagement-SolninjaA" title="Project Management">📆</a> <a href="https://github.com/SolninjaA/Chhoto-URL-Extension/commits?author=SolninjaA" title="Documentation">📖</a></td>
<td align="center" valign="top" width="14.28%"><a href="https://github.com/DarioDarko"><img src="https://avatars.githubusercontent.com/u/154679092?v=4?s=100" width="100px;" alt="DarioDarko"/><br /><sub><b>DarioDarko</b></sub></a><br /><a href="https://github.com/SolninjaA/Chhoto-URL-Extension/commits?author=DarioDarko" title="Code">💻</a></td> <td align="center" valign="top" width="14.28%"><a href="https://github.com/DarioDarko"><img src="https://avatars.githubusercontent.com/u/154679092?v=4?s=100" width="100px;" alt="DarioDarko"/><br /><sub><b>DarioDarko</b></sub></a><br /><a href="https://github.com/SolninjaA/Chhoto-URL-Extension/commits?author=DarioDarko" title="Code">💻</a></td>
</tr> </tr>
</tbody> </tbody>
@@ -121,10 +122,11 @@ This extension only communicates to the Chhoto URL server instance you configure
## Acknowledgements ## Acknowledgements
This project was inspired by and modified from Edward Shen's [Shlink extension][shlink-extension]. Modifications include: This project was inspired by and modified from Edward Shen's [Shlink extension][shlink-extension]. Modifications include:
1. Rewriting the backend code to contact a Chhoto URL server, rather than a Shlink server. 1. Rewriting the backend code to contact a Chhoto URL server, rather than a Shlink server.
2. Changing the appearance of the `options.html` page. 2. Adding a popup page for manual URL generation.
3. Removing options which were either not possible to implement (because of the Chhoto URL server's limitations), or were not practical. 3. Changing the appearance of the `options.html` page.
4. Optimizing code where possible. 4. Removing options which were either not possible to implement (because of the Chhoto URL server's limitations), or were not practical.
5. Et cetera 5. Optimizing code where possible.
6. Et cetera.
## Information ## Information
| Syncing from (main repository) | Syncing to | Syncing every | | Syncing from (main repository) | Syncing to | Syncing every |
+2 -2
View File
@@ -1,7 +1,7 @@
(function(a,b){if("function"==typeof define&&define.amd)define("webextension-polyfill",["module"],b);else if("undefined"!=typeof exports)b(module);else{var c={exports:{}};b(c),a.browser=c.exports}})("undefined"==typeof globalThis?"undefined"==typeof self?this:self:globalThis,function(a){"use strict";if("undefined"==typeof browser||Object.getPrototypeOf(browser)!==Object.prototype){if("object"!=typeof chrome||!chrome||!chrome.runtime||!chrome.runtime.id)throw new Error("This script should only be loaded in a browser extension.");a.exports=(a=>{const b={alarms:{clear:{minArgs:0,maxArgs:1},clearAll:{minArgs:0,maxArgs:0},get:{minArgs:0,maxArgs:1},getAll:{minArgs:0,maxArgs:0}},bookmarks:{create:{minArgs:1,maxArgs:1},get:{minArgs:1,maxArgs:1},getChildren:{minArgs:1,maxArgs:1},getRecent:{minArgs:1,maxArgs:1},getSubTree:{minArgs:1,maxArgs:1},getTree:{minArgs:0,maxArgs:0},move:{minArgs:2,maxArgs:2},remove:{minArgs:1,maxArgs:1},removeTree:{minArgs:1,maxArgs:1},search:{minArgs:1,maxArgs:1},update:{minArgs:2,maxArgs:2}},browserAction:{disable:{minArgs:0,maxArgs:1,fallbackToNoCallback:!0},enable:{minArgs:0,maxArgs:1,fallbackToNoCallback:!0},getBadgeBackgroundColor:{minArgs:1,maxArgs:1},getBadgeText:{minArgs:1,maxArgs:1},getPopup:{minArgs:1,maxArgs:1},getTitle:{minArgs:1,maxArgs:1},openPopup:{minArgs:0,maxArgs:0},setBadgeBackgroundColor:{minArgs:1,maxArgs:1,fallbackToNoCallback:!0},setBadgeText:{minArgs:1,maxArgs:1,fallbackToNoCallback:!0},setIcon:{minArgs:1,maxArgs:1},setPopup:{minArgs:1,maxArgs:1,fallbackToNoCallback:!0},setTitle:{minArgs:1,maxArgs:1,fallbackToNoCallback:!0}},browsingData:{remove:{minArgs:2,maxArgs:2},removeCache:{minArgs:1,maxArgs:1},removeCookies:{minArgs:1,maxArgs:1},removeDownloads:{minArgs:1,maxArgs:1},removeFormData:{minArgs:1,maxArgs:1},removeHistory:{minArgs:1,maxArgs:1},removeLocalStorage:{minArgs:1,maxArgs:1},removePasswords:{minArgs:1,maxArgs:1},removePluginData:{minArgs:1,maxArgs:1},settings:{minArgs:0,maxArgs:0}},commands:{getAll:{minArgs:0,maxArgs:0}},contextMenus:{remove:{minArgs:1,maxArgs:1},removeAll:{minArgs:0,maxArgs:0},update:{minArgs:2,maxArgs:2}},cookies:{get:{minArgs:1,maxArgs:1},getAll:{minArgs:1,maxArgs:1},getAllCookieStores:{minArgs:0,maxArgs:0},remove:{minArgs:1,maxArgs:1},set:{minArgs:1,maxArgs:1}},devtools:{inspectedWindow:{eval:{minArgs:1,maxArgs:2,singleCallbackArg:!1}},panels:{create:{minArgs:3,maxArgs:3,singleCallbackArg:!0},elements:{createSidebarPane:{minArgs:1,maxArgs:1}}}},downloads:{cancel:{minArgs:1,maxArgs:1},download:{minArgs:1,maxArgs:1},erase:{minArgs:1,maxArgs:1},getFileIcon:{minArgs:1,maxArgs:2},open:{minArgs:1,maxArgs:1,fallbackToNoCallback:!0},pause:{minArgs:1,maxArgs:1},removeFile:{minArgs:1,maxArgs:1},resume:{minArgs:1,maxArgs:1},search:{minArgs:1,maxArgs:1},show:{minArgs:1,maxArgs:1,fallbackToNoCallback:!0}},extension:{isAllowedFileSchemeAccess:{minArgs:0,maxArgs:0},isAllowedIncognitoAccess:{minArgs:0,maxArgs:0}},history:{addUrl:{minArgs:1,maxArgs:1},deleteAll:{minArgs:0,maxArgs:0},deleteRange:{minArgs:1,maxArgs:1},deleteUrl:{minArgs:1,maxArgs:1},getVisits:{minArgs:1,maxArgs:1},search:{minArgs:1,maxArgs:1}},i18n:{detectLanguage:{minArgs:1,maxArgs:1},getAcceptLanguages:{minArgs:0,maxArgs:0}},identity:{launchWebAuthFlow:{minArgs:1,maxArgs:1}},idle:{queryState:{minArgs:1,maxArgs:1}},management:{get:{minArgs:1,maxArgs:1},getAll:{minArgs:0,maxArgs:0},getSelf:{minArgs:0,maxArgs:0},setEnabled:{minArgs:2,maxArgs:2},uninstallSelf:{minArgs:0,maxArgs:1}},notifications:{clear:{minArgs:1,maxArgs:1},create:{minArgs:1,maxArgs:2},getAll:{minArgs:0,maxArgs:0},getPermissionLevel:{minArgs:0,maxArgs:0},update:{minArgs:2,maxArgs:2}},pageAction:{getPopup:{minArgs:1,maxArgs:1},getTitle:{minArgs:1,maxArgs:1},hide:{minArgs:1,maxArgs:1,fallbackToNoCallback:!0},setIcon:{minArgs:1,maxArgs:1},setPopup:{minArgs:1,maxArgs:1,fallbackToNoCallback:!0},setTitle:{minArgs:1,maxArgs:1,fallbackToNoCallback:!0},show:{minArgs:1,maxArgs:1,fallbackToNoCallback:!0}},permissions:{contains:{minArgs:1,maxArgs:1},getAll:{minArgs:0,maxArgs:0},remove:{minArgs:1,maxArgs:1},request:{minArgs:1,maxArgs:1}},runtime:{getBackgroundPage:{minArgs:0,maxArgs:0},getPlatformInfo:{minArgs:0,maxArgs:0},openOptionsPage:{minArgs:0,maxArgs:0},requestUpdateCheck:{minArgs:0,maxArgs:0},sendMessage:{minArgs:1,maxArgs:3},sendNativeMessage:{minArgs:2,maxArgs:2},setUninstallURL:{minArgs:1,maxArgs:1}},sessions:{getDevices:{minArgs:0,maxArgs:1},getRecentlyClosed:{minArgs:0,maxArgs:1},restore:{minArgs:0,maxArgs:1}},storage:{local:{clear:{minArgs:0,maxArgs:0},get:{minArgs:0,maxArgs:1},getBytesInUse:{minArgs:0,maxArgs:1},remove:{minArgs:1,maxArgs:1},set:{minArgs:1,maxArgs:1}},managed:{get:{minArgs:0,maxArgs:1},getBytesInUse:{minArgs:0,maxArgs:1}},sync:{clear:{minArgs:0,maxArgs:0},get:{minArgs:0,maxArgs:1},getBytesInUse:{minArgs:0,maxArgs:1},remove:{minArgs:1,maxArgs:1},set:{minArgs:1,maxArgs:1}}},tabs:{captureVisibleTab:{minArgs:0,maxArgs:2},create:{minArgs:1,maxArgs:1},detectLanguage:{minArgs:0,maxArgs:1},discard:{minArgs:0,maxArLine truncated (function(a,b){if("function"==typeof define&&define.amd)define("webextension-polyfill",["module"],b);else if("undefined"!=typeof exports)b(module);else{var c={exports:{}};b(c),a.browser=c.exports}})("undefined"==typeof globalThis?"undefined"==typeof self?this:self:globalThis,function(a){"use strict";if(!(globalThis.chrome&&globalThis.chrome.runtime&&globalThis.chrome.runtime.id))throw new Error("This script should only be loaded in a browser extension.");if(!(globalThis.browser&&globalThis.browser.runtime&&globalThis.browser.runtime.id)){a.exports=(a=>{const b={alarms:{clear:{minArgs:0,maxArgs:1},clearAll:{minArgs:0,maxArgs:0},get:{minArgs:0,maxArgs:1},getAll:{minArgs:0,maxArgs:0}},bookmarks:{create:{minArgs:1,maxArgs:1},get:{minArgs:1,maxArgs:1},getChildren:{minArgs:1,maxArgs:1},getRecent:{minArgs:1,maxArgs:1},getSubTree:{minArgs:1,maxArgs:1},getTree:{minArgs:0,maxArgs:0},move:{minArgs:2,maxArgs:2},remove:{minArgs:1,maxArgs:1},removeTree:{minArgs:1,maxArgs:1},search:{minArgs:1,maxArgs:1},update:{minArgs:2,maxArgs:2}},browserAction:{disable:{minArgs:0,maxArgs:1,fallbackToNoCallback:!0},enable:{minArgs:0,maxArgs:1,fallbackToNoCallback:!0},getBadgeBackgroundColor:{minArgs:1,maxArgs:1},getBadgeText:{minArgs:1,maxArgs:1},getPopup:{minArgs:1,maxArgs:1},getTitle:{minArgs:1,maxArgs:1},openPopup:{minArgs:0,maxArgs:0},setBadgeBackgroundColor:{minArgs:1,maxArgs:1,fallbackToNoCallback:!0},setBadgeText:{minArgs:1,maxArgs:1,fallbackToNoCallback:!0},setIcon:{minArgs:1,maxArgs:1},setPopup:{minArgs:1,maxArgs:1,fallbackToNoCallback:!0},setTitle:{minArgs:1,maxArgs:1,fallbackToNoCallback:!0}},browsingData:{remove:{minArgs:2,maxArgs:2},removeCache:{minArgs:1,maxArgs:1},removeCookies:{minArgs:1,maxArgs:1},removeDownloads:{minArgs:1,maxArgs:1},removeFormData:{minArgs:1,maxArgs:1},removeHistory:{minArgs:1,maxArgs:1},removeLocalStorage:{minArgs:1,maxArgs:1},removePasswords:{minArgs:1,maxArgs:1},removePluginData:{minArgs:1,maxArgs:1},settings:{minArgs:0,maxArgs:0}},commands:{getAll:{minArgs:0,maxArgs:0}},contextMenus:{remove:{minArgs:1,maxArgs:1},removeAll:{minArgs:0,maxArgs:0},update:{minArgs:2,maxArgs:2}},cookies:{get:{minArgs:1,maxArgs:1},getAll:{minArgs:1,maxArgs:1},getAllCookieStores:{minArgs:0,maxArgs:0},remove:{minArgs:1,maxArgs:1},set:{minArgs:1,maxArgs:1}},devtools:{inspectedWindow:{eval:{minArgs:1,maxArgs:2,singleCallbackArg:!1}},panels:{create:{minArgs:3,maxArgs:3,singleCallbackArg:!0},elements:{createSidebarPane:{minArgs:1,maxArgs:1}}}},downloads:{cancel:{minArgs:1,maxArgs:1},download:{minArgs:1,maxArgs:1},erase:{minArgs:1,maxArgs:1},getFileIcon:{minArgs:1,maxArgs:2},open:{minArgs:1,maxArgs:1,fallbackToNoCallback:!0},pause:{minArgs:1,maxArgs:1},removeFile:{minArgs:1,maxArgs:1},resume:{minArgs:1,maxArgs:1},search:{minArgs:1,maxArgs:1},show:{minArgs:1,maxArgs:1,fallbackToNoCallback:!0}},extension:{isAllowedFileSchemeAccess:{minArgs:0,maxArgs:0},isAllowedIncognitoAccess:{minArgs:0,maxArgs:0}},history:{addUrl:{minArgs:1,maxArgs:1},deleteAll:{minArgs:0,maxArgs:0},deleteRange:{minArgs:1,maxArgs:1},deleteUrl:{minArgs:1,maxArgs:1},getVisits:{minArgs:1,maxArgs:1},search:{minArgs:1,maxArgs:1}},i18n:{detectLanguage:{minArgs:1,maxArgs:1},getAcceptLanguages:{minArgs:0,maxArgs:0}},identity:{launchWebAuthFlow:{minArgs:1,maxArgs:1}},idle:{queryState:{minArgs:1,maxArgs:1}},management:{get:{minArgs:1,maxArgs:1},getAll:{minArgs:0,maxArgs:0},getSelf:{minArgs:0,maxArgs:0},setEnabled:{minArgs:2,maxArgs:2},uninstallSelf:{minArgs:0,maxArgs:1}},notifications:{clear:{minArgs:1,maxArgs:1},create:{minArgs:1,maxArgs:2},getAll:{minArgs:0,maxArgs:0},getPermissionLevel:{minArgs:0,maxArgs:0},update:{minArgs:2,maxArgs:2}},pageAction:{getPopup:{minArgs:1,maxArgs:1},getTitle:{minArgs:1,maxArgs:1},hide:{minArgs:1,maxArgs:1,fallbackToNoCallback:!0},setIcon:{minArgs:1,maxArgs:1},setPopup:{minArgs:1,maxArgs:1,fallbackToNoCallback:!0},setTitle:{minArgs:1,maxArgs:1,fallbackToNoCallback:!0},show:{minArgs:1,maxArgs:1,fallbackToNoCallback:!0}},permissions:{contains:{minArgs:1,maxArgs:1},getAll:{minArgs:0,maxArgs:0},remove:{minArgs:1,maxArgs:1},request:{minArgs:1,maxArgs:1}},runtime:{getBackgroundPage:{minArgs:0,maxArgs:0},getPlatformInfo:{minArgs:0,maxArgs:0},openOptionsPage:{minArgs:0,maxArgs:0},requestUpdateCheck:{minArgs:0,maxArgs:0},sendMessage:{minArgs:1,maxArgs:3},sendNativeMessage:{minArgs:2,maxArgs:2},setUninstallURL:{minArgs:1,maxArgs:1}},sessions:{getDevices:{minArgs:0,maxArgs:1},getRecentlyClosed:{minArgs:0,maxArgs:1},restore:{minArgs:0,maxArgs:1}},storage:{local:{clear:{minArgs:0,maxArgs:0},get:{minArgs:0,maxArgs:1},getBytesInUse:{minArgs:0,maxArgs:1},remove:{minArgs:1,maxArgs:1},set:{minArgs:1,maxArgs:1}},managed:{get:{minArgs:0,maxArgs:1},getBytesInUse:{minArgs:0,maxArgs:1}},sync:{clear:{minArgs:0,maxArgs:0},get:{minArgs:0,maxArgs:1},getBytesInUse:{minArgs:0,maxArgs:1},remove:{minArgs:1,maxArgs:1},set:{minArgs:1,maxArgs:1}}},tabs:{captureVisibleTab:{minArgs:0,maxArgs:2},create:{minArgs:1,maxArgs:1},detectLanguage:{minArgs:0,maxArgs:1},discard:{minArLine truncated
//# sourceMappingURL=browser-polyfill.min.js.map //# sourceMappingURL=browser-polyfill.min.js.map
// webextension-polyfill v.0.8.0 (https://github.com/mozilla/webextension-polyfill) // webextension-polyfill v.0.12.0 (https://github.com/mozilla/webextension-polyfill)
/* This Source Code Form is subject to the terms of the Mozilla Public /* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this * License, v. 2.0. If a copy of the MPL was not distributed with this
+1 -1
View File
@@ -1 +1 @@
{"version":3,"sources":["browser-polyfill.js"],"names":["Object","extensionAPIs","apiMetadata","constructor","items","get","isThenable","value","makeCallback","promise","metadata","callbackArgs","pluralizeArguments","numArgs","wrapAsyncFunction","args","minArgs","name","length","maxArgs","target","resolve","reject","console","wrapMethod","apply","wrapper","hasOwnProperty","Function","wrapObject","wrappers","cache","has","prop","configurable","enumerable","set","defineProperty","Reflect","deleteProperty","proxyTarget","wrapEvent","wrapperMap","addListener","hasListener","removeListener","onRequestFinishedWrappers","listener","wrappedReq","getContent","loggedSendResponseDeprecationWarning","onMessageWrappers","didCallSendResponse","sendResponsePromise","wrappedSendResponse","result","Promise","isResultThenable","sendPromisedResult","msg","sendResponse","error","message","__mozWebExtensionPolyfillReject__","err","wrappedSendMessageCallback","reply","wrappedSendMessage","wrappedCb","apiNamespaceObj","staticWrappers","devtools","network","onRequestFinished","runtime","onMessage","onMessageExternal","sendMessage","tabs","settingMetadata","clear","services","websites","chrome","module","wrapAPIs"],"mappings":"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 truncated {"version":3,"file":"browser-polyfill.min.js","mappings":"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 truncated
+16 -9
View File
@@ -1,7 +1,7 @@
{ {
"manifest_version": 2, "manifest_version": 3,
"name": "Chhoto URL", "name": "Chhoto URL",
"version": "1.3.0", "version": "1.3.1",
"description": "An unofficial extension for shortening URLs using the Chhoto URL API. Requires a Chhoto URL instance.", "description": "An unofficial extension for shortening URLs using the Chhoto URL API. Requires a Chhoto URL instance.",
"icons": { "icons": {
"16": "icons/chhoto-url-16.png", "16": "icons/chhoto-url-16.png",
@@ -16,13 +16,16 @@
"notifications", "notifications",
"clipboardWrite", "clipboardWrite",
"storage", "storage",
"https://*/*", "contextMenus",
"contextMenus" "offscreen"
],
"host_permissions": [
"https://*/*"
], ],
"options_ui": { "options_ui": {
"page": "options/options.html" "page": "options/options.html"
}, },
"browser_action": { "action": {
"default_icon": { "default_icon": {
"16": "icons/chhoto-url-16.png", "16": "icons/chhoto-url-16.png",
"48": "icons/chhoto-url-48.png", "48": "icons/chhoto-url-48.png",
@@ -34,9 +37,13 @@
"default_title": "Generate a Chhoto URL!" "default_title": "Generate a Chhoto URL!"
}, },
"background": { "background": {
"scripts": [ "service_worker": "service_worker.js",
"lib/browser-polyfill.min.js", "type": "module",
"background/background.js" "scripts": ["lib/browser-polyfill.min.js", "service_worker.js"]
] },
"browser_specific_settings": {
"gecko": {
"id": "{49edc8ea-f91d-4510-8710-e0e807ad5842}"
}
} }
} }
+3
View File
@@ -0,0 +1,3 @@
<!doctype html>
<textarea id="text"></textarea>
<script src="offscreen.js"></script>
+87
View File
@@ -0,0 +1,87 @@
/**
* COPYRIGHT NOTICE
* THIS FILE WAS INSPIRED FROM: https://github.com/GoogleChrome/chrome-extensions-samples/tree/main/functional-samples/cookbook.offscreen-clipboard-write
* THE BASE CODE IS LICENSED UNDER THE APACHE 2 LICENSE
* (https://www.apache.org/licenses/LICENSE-2.0)
*/
// Copyright 2023 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
/**
* COPYRIGHT NOTICE
* ALL PERSONAL **MODIFICATIONS** ARE LICENSED UNDER THE GNU GENEARL PUBLIC LICENSE VERSION 3
* YOU MAY FIND THIS LICENSE IN THE "LICENSE.md" FILE.
* IF NOT, SEE https://www.gnu.org/licenses
* */
/*
* An unofficial extension for easy link shortening using the Chhoto URL API.
* Copyright (C) 2025 Solomon Tech
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
// Get element
const textEle = document.querySelector("#text");
// Function which actually copies the link
async function copy(link) {
try {
// If the link is not a string
if (typeof link !== 'string') {
// Return an error
throw new TypeError(`The shortened URL's type must be a 'string', not "${typeof link}"`);
}
// Set the value of the text element
textEle.value = link;
// Select the element
textEle.select();
// Copy to the clipboard
document.execCommand('copy');
} finally {
// Close the offscreen document
window.close();
}
};
// Ensures the message was meant for this message handler
async function handleMessages(message) {
// Only activate if the received message was intended for the offscreen handler
if (message.type === 'clipboard') {
// Call the copy function and pass the link
copy(message.link);
} else {
// Return, if the message wasn't intended for the offscreen handler
return;
};
};
// Listen for runtime messages
chrome.runtime.onMessage.addListener(handleMessages);
+23 -3
View File
@@ -11,6 +11,7 @@
height: 300px; height: 300px;
font-size: 20px; font-size: 20px;
font-family: sans-serif; font-family: sans-serif;
margin-bottom: 20px;
} }
.header { .header {
@@ -34,7 +35,9 @@
cursor: pointer; cursor: pointer;
} }
.generate-button { .generate-button,
#url-conflict-yes,
#url-conflict-no {
border: black 1px solid; border: black 1px solid;
border-radius: 7px; border-radius: 7px;
background-color: white; background-color: white;
@@ -45,7 +48,9 @@
} }
#close:hover, #close:hover,
.generate-button:hover { .generate-button:hover,
#url-conflict-yes:hover,
#url-conflict-no:hover {
background-color: #bbb; background-color: #bbb;
} }
@@ -72,7 +77,9 @@
#message, #message,
#message2, #message2,
#message3 { #message3,
#url-conflict,
#delete-error {
display: none; display: none;
} }
@@ -119,6 +126,19 @@
</div> </div>
</form> </form>
<div id="url-conflict">
<h2 id="url-conflict-header"></h2>
<h3>Would you like to replace it?</h3>
<input id="url-conflict-yes" type="submit" value="Yes">
<input id="url-conflict-no" type="submit" value="No (append a random string)">
<p>If you replace a link, your browser may still cache the old destination. Others will be able to see the correct destination, if their browser didn't do the same.</p>
</div>
<div id="delete-error">
<h2>Error: Failed to replace link</h2>
<p id="delete-error-message"></p>
</div>
<script type="application/javascript" src="/lib/browser-polyfill.min.js"></script> <script type="application/javascript" src="/lib/browser-polyfill.min.js"></script>
<script type="application/javascript" src="/popup/popup.js"></script> <script type="application/javascript" src="/popup/popup.js"></script>
+225 -42
View File
@@ -36,14 +36,221 @@ const messageEle = document.querySelector("#message");
const message2Ele = document.querySelector("#message2"); const message2Ele = document.querySelector("#message2");
const message3Ele = document.querySelector("#message3"); const message3Ele = document.querySelector("#message3");
const generateEle = document.querySelector("#generate"); const generateEle = document.querySelector("#generate");
const urlConflictEle = document.querySelector("#url-conflict");
const urlConflictHeaderEle = document.querySelector("#url-conflict-header");
const urlConflictYesEle = document.querySelector("#url-conflict-yes");
const urlConflictNoEle = document.querySelector("#url-conflict-no");
const deleteErrorEle = document.querySelector("#delete-error");
const deleteErrorMessageEle = document.querySelector("#delete-error-message");
// Define values // Define values
let shorturl; let shorturl;
let longurl; let longurl;
// Get the passed long URL, if present
const requestParams = new URLSearchParams(window.location.search); const requestParams = new URLSearchParams(window.location.search);
const requestValue = requestParams.get('url'); const requestValue = requestParams.get('url');
// Get the background page, and call the sendRequest function (which is in this script)
if (typeof chrome === undefined) {
const backgroundFunc = browser.runtime.getBackgroundPage();
};
/**
* Functions
*/
/**
* Closing the popup
*/
// Close function
async function close() {
try {
const windowId = (await browser.windows.getCurrent()).id;
await browser.windows.remove(windowId);
} catch (error) {
console.log("Closing failed:", error);
};
};
/**
* Send requests to the background page
*/
// Define the sendRequest function (which calls a background.js function to generate a shortened link)
function sendRequest(page) {
page.popupGenerateChhoto(longurl, shorturl);
};
// Handle errors for sendRequest() function
function onError(error) {
console.log(`Error: ${error}`);
}
/**
* Random string generation (to append to the shorturl, when requested)
*/
// Define the "generate()" function
// This will generate a random string, if requested
function generate() {
// Define the alphabet
// The letters "cfhistu" are commonly used in rude words, so they are omitted
const ALPHABET = '0123456789abdegjklmnopqrvwxyz';
const ID_LENGTH = 8;
// Generate
let rtn = "";
for (let i = 0; i < ID_LENGTH; i++) {
rtn += ALPHABET.charAt(Math.floor(Math.random() * ALPHABET.length));
};
// Return the string
return rtn;
}
/**
* Link deletion / link replacement
*/
// Delete the link, and then make another request to the server to generate it again
// In other words, replace the link
function deleteLink(host, link) {
browser.storage.local.get("chhotoKey").then((chhotoKey) => {
const headers = new Headers();
headers.append("accept", "application/json");
headers.append("Content-Type", "application/json");
// This has been pushed to the main branch of Chhoto URL!
headers.append("X-API-Key", chhotoKey.chhotoKey);
// Fetch
return fetch(new Request(
`${host}/api/del/${link}`,
{
method: "DELETE",
headers,
},
)).then(r => r.json().then(data => ({ status: r.status, json: data })))
.then(result => {
if (result.json.success) {
// Remove error class, if applied
deleteErrorEle.classList.remove("warning");
// Send request to function which handles requests to the background page
if (typeof chrome !== undefined) {
browser.runtime.sendMessage({type: "generate-via-popup", longurl: longurl, shorturl: shorturl});
} else {
backgroundFunc.then(sendRequest, onError);
};
} else if (result.json.error) {
// Set error message
deleteErrorMessageEle.innerText = `Error (${result.status}): ${result.json.reason}`;
// Add warning style
deleteErrorEle.classList.add("warning");
};
})
.catch(err => {
// Set error message
deleteErrorMessageEle.innerText = `Error (${result.status}): ${result.json.reason}`;
// Add warning style
deleteErrorEle.classList.add("warning");
// Error
return Promise.reject(new Error(
`Error: ${err.message}`
));
});
});
};
/**
* Listening to runtime messages
*/
// Handle the messages (i.e. errors)
function handleMessage(request) {
// If the error was a URL conflict
if (request.type && request.type === "url-conflict") {
// Update the error message
urlConflictHeaderEle.innerText = request.errorMessage;
// Add warning class
urlConflictEle.classList.add("warning");
// When the user clicks "yes"
urlConflictYesEle.onclick = () => {
// Call the deleteLink function
deleteLink(request.host, request.shorturl);
};
// When the user clicks "no"
urlConflictNoEle.onclick = () => {
// Reassign the short URL
shorturl = shorturl + "-" + generate();
// Update the value of the input field
shortURLEle.value = shorturl;
// Remove the warning class
urlConflictEle.classList.remove("warning");
};
} else if (request.message === "finished") {
close();
};
};
/**
* Event listeners
* */
// If the close button is clicked, close the window
closeEle.addEventListener('click', () => {
close();
});
// Listen for messages (i.e. errors)
browser.runtime.onMessage.addListener(handleMessage);
// If the generate button was clicked
generateEle.addEventListener("submit", (event) => {
event.preventDefault();
// Ensure both fields have been filled out, and the long URL is valid
if ( shorturl !== undefined && longurl !== undefined && !message2Ele.classList.contains("warning") ) {
// Remove the warning class
message3Ele.classList.remove("warning");
// Send request to function which handles requests to the background page
if (typeof chrome !== undefined) {
browser.runtime.sendMessage({type: "generate-via-popup", longurl: longurl, shorturl: shorturl});
} else {
backgroundFunc.then(sendRequest, onError);
};
} else {
// Add the warning class
message3Ele.classList.add("warning");
};
});
/**
* If statements, i.e. the main functionality
* */
/** /**
* Automatically insert the long URL, if enabled * Automatically insert the long URL, if enabled
*/ */
@@ -90,20 +297,10 @@ if (requestValue) {
}); });
}; };
// Close function
async function close() {
try {
const windowId = (await browser.windows.getCurrent()).id;
await browser.windows.remove(windowId);
} catch (error) {
console.log("Closing failed:", error);
}
}
// If the close button is clicked, close the window /**
closeEle.addEventListener('click', () => { * When the short URL is inputted
close(); * */
});
// When the short URL is inputted // When the short URL is inputted
shortURLEle.oninput = (event) => { shortURLEle.oninput = (event) => {
@@ -111,6 +308,13 @@ shortURLEle.oninput = (event) => {
event.preventDefault(); event.preventDefault();
}; };
// Hide the URL conflict error, if it is present
urlConflictEle.classList.remove("warning");
// Hide the deletion error, if it is present
deleteErrorEle.classList.remove("warning");
// If the short URL value is greater than 0 (i.e. not an empty string) // If the short URL value is greater than 0 (i.e. not an empty string)
if (shortURLEle.value.length > 0) { if (shortURLEle.value.length > 0) {
// Get the browser storage // Get the browser storage
@@ -122,10 +326,12 @@ shortURLEle.oninput = (event) => {
const chhotoHost = data.chhotoHost; const chhotoHost = data.chhotoHost;
// Format the short URL // Format the short URL
// Trim all the whitespaces from the beginning and end of the string
// Replace all occurences of ' - ' to '-' // Replace all occurences of ' - ' to '-'
// Replace all occurences of ' ' to '-' // Replace all occurences of ' ' to '-'
// Replace all characters except for 'a-z', '0-9', '-' and '_' with '' // Replace all characters except for 'a-z', '0-9', '-' and '_' with ''
let shortURLText = shortURLEle.value.toLowerCase().replace(/ - /g, '-').replace(/\s+/g, '-').replace(/[^a-z0-9-_]/g, ''); let shortURLText = shortURLEle.value.trim().toLowerCase().replace(/ - /g, '-').replace(/\s+/g, '-').replace(/[^a-z0-9-_]/g, '');
// If the wordLimit is not 0, and thus limited // If the wordLimit is not 0, and thus limited
// And if the configured host is not undefined // And if the configured host is not undefined
@@ -146,9 +352,12 @@ shortURLEle.oninput = (event) => {
} else { } else {
// If the short URL is an empty string, hide the message // If the short URL is an empty string, hide the message
messageEle.classList.remove("shown"); messageEle.classList.remove("shown");
}
}; };
};
/**
* When the long URL is inputted
* */
// When the long URL is inputted // When the long URL is inputted
longURLEle.oninput = (event) => { longURLEle.oninput = (event) => {
@@ -195,8 +404,6 @@ longURLEle.oninput = (event) => {
message2Ele.classList.add("warning"); message2Ele.classList.add("warning");
longURLEle.style.color = "red"; longURLEle.style.color = "red";
}; };
}); });
} else { } else {
// If the long URL is an empty string, hide the warning styling // If the long URL is an empty string, hide the warning styling
@@ -204,27 +411,3 @@ longURLEle.oninput = (event) => {
longURLEle.style.color = "black"; longURLEle.style.color = "black";
} }
}; };
// Define the sendRequest function (which calls a background.js function to generate a shortened link)
function sendRequest(page) {
page.popupGenerateChhoto(longurl, shorturl);
close();
}
// If the generate button was clicked
generateEle.addEventListener("submit", (event) => {
event.preventDefault();
// Ensure both fields have been filled out, and the long URL is valid
if ( shorturl !== undefined && longurl !== undefined && !message2Ele.classList.contains("warning") ) {
// Remove the warning class
message3Ele.classList.remove("warning");
// Get the background page, and call the sendRequest function (which is in this script)
const backgroundFunc = browser.runtime.getBackgroundPage();
backgroundFunc.then(sendRequest);
} else {
// Add the warning class
message3Ele.classList.add("warning");
};
});
+93 -30
View File
@@ -28,6 +28,25 @@
// https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Strict_mode // https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Strict_mode
"use strict"; "use strict";
// Import polyfill
import "/lib/browser-polyfill.min.js";
// Create a context menu
browser.runtime.onInstalled.addListener(() => {
browser.contextMenus.create({
title: "Manually generate a Chhoto URL",
contexts: ["all"],
id: "generate-chhoto-url"
});
});
// Run code when the context menu is clicked
browser.contextMenus.onClicked.addListener( (info) => {
browser.windows.create({url: `/popup/popup.html?url=${info.pageUrl}`, type: "popup"});
});
/* /*
* Type Definitions * Type Definitions
*/ */
@@ -51,7 +70,10 @@
* @type {object} * @type {object}
* @property {number} status * @property {number} status
* @property {{success: boolean, error: boolean, shorturl: string}} json * @property {{success: boolean, error: boolean, shorturl: string}} json
* @property {string} requestedLink The link that tried to be created. This only activates, and is only accurate, if the generateWithTitle option is enabled. * @property {string} host The host of the server.
* @property {string} requestedLink The link that tried to be created. This only activates, and is only accurate, if the generateWithTitle option is enabled or the type of the request is "popup"
* @property {string} type The type of the request. This can be "background", which means the request came from the background page.
* This can also be "popup", which means the request came from the popup page.
*/ */
/** /**
@@ -78,6 +100,12 @@
* @returns {!Promise<[URL, string, string], Error>} * @returns {!Promise<[URL, string, string], Error>}
*/ */
function validateURL(url, title, type) { function validateURL(url, title, type) {
// Define a new URL object, in case the url was collected from a runtime message
// In which case, the URL object wouldn't be preserved
if (type === "popup") {
url = new URL(url);
}
return browser.storage.local.get("allowedProtocols").then(({ allowedProtocols }) => { return browser.storage.local.get("allowedProtocols").then(({ allowedProtocols }) => {
// Initialize a list of protocols that are allowed if unset. // Initialize a list of protocols that are allowed if unset.
// This needs to be synced with the initialization code in options.js. // This needs to be synced with the initialization code in options.js.
@@ -143,16 +171,20 @@ function generateChhotoRequest([url, title, type]) {
data.title = ""; data.title = "";
} }
// Set the type of the request
data.type = type;
// If "generateWithTitle" is true // If "generateWithTitle" is true
if (data.generateWithTitle) { if (data.generateWithTitle) {
// Get the configured word limit // Get the configured word limit
let wordLimit = data.titleWordLimit; let wordLimit = data.titleWordLimit;
// Format title name // Format title name
// Trim all the whitespaces from the beginning and end of the string
// Replace all occurences of ' - ' to '-' // Replace all occurences of ' - ' to '-'
// Replace all occurences of ' ' to '-' // Replace all occurences of ' ' to '-'
// Replace all characters except for 'a-z', '0-9', '-' and '_' with '' // Replace all characters except for 'a-z', '0-9', '-' and '_' with ''
let titleName = title.toLowerCase().replace(/ - /g, '-').replace(/\s+/g, '-').replace(/[^a-z0-9-_]/g, ''); let titleName = title.trim().toLowerCase().replace(/ - /g, '-').replace(/\s+/g, '-').replace(/[^a-z0-9-_]/g, '');
// If the wordLimit is not 0, and thus limited // If the wordLimit is not 0, and thus limited
// If the type of the request does not equal "popup" (the word limit is always unlimited if it's generated from the popup page) // If the type of the request does not equal "popup" (the word limit is always unlimited if it's generated from the popup page)
@@ -195,8 +227,8 @@ function requestChhoto(chhotoRequest) {
longlink: chhotoRequest.longUrl, longlink: chhotoRequest.longUrl,
}), }),
}, },
// Add the HTTP status code, and requestedLink (see ChhotoResponse in type definitions for details) to the response // Add the HTTP status code, type of the request, and requestedLink (see ChhotoResponse in type definitions for details) to the response
)).then(r => r.json().then(data => ({ status: r.status, json: data, requestedLink: `${chhotoRequest.chhotoHost}/${chhotoRequest.title}` }))) )).then(r => r.json().then(data => ({ status: r.status, json: data, host: chhotoRequest.chhotoHost, requestedLink: chhotoRequest.title, type: chhotoRequest.type })))
// If there was a HTTP error // If there was a HTTP error
// This does not activate if the Chhoto server returns a JSON response with an error // This does not activate if the Chhoto server returns a JSON response with an error
.catch(err => { .catch(err => {
@@ -225,11 +257,26 @@ function validateChhotoResponse(httpResp) {
if (httpResp.json.success) { if (httpResp.json.success) {
return httpResp.json; return httpResp.json;
} else { } else {
// If there was a URL conflict
if (httpResp.status === 409) { if (httpResp.status === 409) {
// If the type is popup
if (httpResp.type === "popup") {
// Define the error
const error = `Error: The URL "${httpResp.host}/${httpResp.requestedLink}" already exists.`;
// Send a message to the popup
browser.runtime.sendMessage({type: "url-conflict", errorMessage: error, host: httpResp.host, shorturl: httpResp.requestedLink});
// Return an error
return Promise.reject(new Error(
error
));
};
const json = { const json = {
success: true, success: true,
error: false, error: false,
shorturl: httpResp.requestedLink, shorturl: `${httpResp.host}/${httpResp.requestedLink}`,
} }
return json; return json;
} }
@@ -243,24 +290,29 @@ function validateChhotoResponse(httpResp) {
/** /**
* Copies the returned shortened link to the clipboard. * Copies the returned shortened link to the clipboard.
* *
* @param {!ChhotoJSON} chhotoResp The JSON response from a Chhoto URL instance. * @param {!ChhotoJSON} chhotoResp The JSON response from a Chhoto URL instance. **May also include "type"** if the request came from the popup.js script.
* @returns {!Promise<ChhotoJSON, Error>} `ChhotoJSON`, unmodified, on * @returns {!Promise<ChhotoJSON, Error>} `ChhotoJSON`, unmodified, on
* success, or an error indicating that we failed to copy to the clipboard. * success, or an error indicating that we failed to copy to the clipboard.
*/ */
function copyLinkToClipboard(chhotoResp) { async function copyLinkToClipboard(chhotoResp) {
// Send finished message
browser.runtime.sendMessage({message: "finished"});
// Chrome requires this hacky workaround :( // Chrome requires this hacky workaround :(
if (typeof chrome !== "undefined") { // Currently the "getBrowserInfo" function is only available on Firefox browsers
const prevSelected = document.activeElement; // So, we can partially detect the type of the browser
const tempEle = document.createElement("input"); // This check may change in the future
document.body.appendChild(tempEle); if (typeof browser.runtime.getBrowserInfo !== "function") {
tempEle.value = chhotoResp.shorturl; // Create an offscreen document
tempEle.select(); await browser.offscreen.createDocument({
document.execCommand('copy'); url: "/offscreen/offscreen.html",
document.body.removeChild(tempEle); reasons: [chrome.offscreen.Reason.CLIPBOARD],
// Depending on what was previously selected, we might not be able to select the text. justification: "Copy the shortened URL to the clipboard"
if (prevSelected?.select) { });
prevSelected.select();
} // Send a runtime message to the offscreen page
browser.runtime.sendMessage({type: "clipboard", link: chhotoResp.shorturl});
return Promise.resolve(chhotoResp); return Promise.resolve(chhotoResp);
} else { } else {
return navigator.clipboard return navigator.clipboard
@@ -335,16 +387,27 @@ function popupGenerateChhoto(url, title) {
.catch(notifyError); .catch(notifyError);
}; };
/**
* Function to handle messages
* */
async function handleMessages(message) {
// Only activate if the messge was intended for the background page
if (message.type === "generate-via-popup") {
if (typeof message.longurl !== 'string' && typeof message.shorturl !== 'string') {
throw new TypeError(`The long URL and short URL's type must be a 'string', got "${typeof message.longurl}" and "${typeof message.shorturl} respectively."`)
};
// Generate
console.log(message.longurl);
popupGenerateChhoto(message.longurl, message.shorturl);
} else {
return;
}
}
// When the extension icon is clicked, call the function above // When the extension icon is clicked, call the function above
browser.browserAction.onClicked.addListener(generateChhoto); browser.action.onClicked.addListener(generateChhoto);
// Create a context menu // When a message is sent
browser.contextMenus.create({ browser.runtime.onMessage.addListener(handleMessages);
title: "Manually generate a Chhoto URL",
contexts: ["all"]
});
// Run code when the context menu is clicked
browser.contextMenus.onClicked.addListener( (info) => {
browser.windows.create({url: `/popup/popup.html?url=${info.pageUrl}`, type: "popup"});
});