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"
],
"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",
"name": "DarioDarko",
+6 -4
View File
@@ -108,6 +108,7 @@ This extension only communicates to the Chhoto URL server instance you configure
<table>
<tbody>
<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>
</tr>
</tbody>
@@ -121,10 +122,11 @@ This extension only communicates to the Chhoto URL server instance you configure
## Acknowledgements
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.
2. Changing the appearance of the `options.html` page.
3. Removing options which were either not possible to implement (because of the Chhoto URL server's limitations), or were not practical.
4. Optimizing code where possible.
5. Et cetera
2. Adding a popup page for manual URL generation.
3. Changing the appearance of the `options.html` page.
4. Removing options which were either not possible to implement (because of the Chhoto URL server's limitations), or were not practical.
5. Optimizing code where possible.
6. Et cetera.
## Information
| 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
// 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
* 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",
"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.",
"icons": {
"16": "icons/chhoto-url-16.png",
@@ -16,13 +16,16 @@
"notifications",
"clipboardWrite",
"storage",
"https://*/*",
"contextMenus"
"contextMenus",
"offscreen"
],
"host_permissions": [
"https://*/*"
],
"options_ui": {
"page": "options/options.html"
},
"browser_action": {
"action": {
"default_icon": {
"16": "icons/chhoto-url-16.png",
"48": "icons/chhoto-url-48.png",
@@ -34,9 +37,13 @@
"default_title": "Generate a Chhoto URL!"
},
"background": {
"scripts": [
"lib/browser-polyfill.min.js",
"background/background.js"
]
"service_worker": "service_worker.js",
"type": "module",
"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;
font-size: 20px;
font-family: sans-serif;
margin-bottom: 20px;
}
.header {
@@ -34,7 +35,9 @@
cursor: pointer;
}
.generate-button {
.generate-button,
#url-conflict-yes,
#url-conflict-no {
border: black 1px solid;
border-radius: 7px;
background-color: white;
@@ -45,7 +48,9 @@
}
#close:hover,
.generate-button:hover {
.generate-button:hover,
#url-conflict-yes:hover,
#url-conflict-no:hover {
background-color: #bbb;
}
@@ -72,7 +77,9 @@
#message,
#message2,
#message3 {
#message3,
#url-conflict,
#delete-error {
display: none;
}
@@ -119,6 +126,19 @@
</div>
</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="/popup/popup.js"></script>
+225 -42
View File
@@ -36,14 +36,221 @@ const messageEle = document.querySelector("#message");
const message2Ele = document.querySelector("#message2");
const message3Ele = document.querySelector("#message3");
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
let shorturl;
let longurl;
// Get the passed long URL, if present
const requestParams = new URLSearchParams(window.location.search);
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
*/
@@ -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', () => {
close();
});
/**
* When the short URL is inputted
* */
// When the short URL is inputted
shortURLEle.oninput = (event) => {
@@ -111,6 +308,13 @@ shortURLEle.oninput = (event) => {
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 (shortURLEle.value.length > 0) {
// Get the browser storage
@@ -122,10 +326,12 @@ shortURLEle.oninput = (event) => {
const chhotoHost = data.chhotoHost;
// 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 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
// And if the configured host is not undefined
@@ -146,10 +352,13 @@ shortURLEle.oninput = (event) => {
} else {
// If the short URL is an empty string, hide the message
messageEle.classList.remove("shown");
}
};
};
/**
* When the long URL is inputted
* */
// When the long URL is inputted
longURLEle.oninput = (event) => {
if (event.type === "click") {
@@ -195,8 +404,6 @@ longURLEle.oninput = (event) => {
message2Ele.classList.add("warning");
longURLEle.style.color = "red";
};
});
} else {
// If the long URL is an empty string, hide the warning styling
@@ -204,27 +411,3 @@ longURLEle.oninput = (event) => {
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
"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
*/
@@ -51,7 +70,10 @@
* @type {object}
* @property {number} status
* @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>}
*/
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 }) => {
// Initialize a list of protocols that are allowed if unset.
// This needs to be synced with the initialization code in options.js.
@@ -143,16 +171,20 @@ function generateChhotoRequest([url, title, type]) {
data.title = "";
}
// Set the type of the request
data.type = type;
// If "generateWithTitle" is true
if (data.generateWithTitle) {
// Get the configured word limit
let wordLimit = data.titleWordLimit;
// 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 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 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,
}),
},
// Add the HTTP status code, 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}` })))
// 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, host: chhotoRequest.chhotoHost, requestedLink: chhotoRequest.title, type: chhotoRequest.type })))
// If there was a HTTP error
// This does not activate if the Chhoto server returns a JSON response with an error
.catch(err => {
@@ -225,11 +257,26 @@ function validateChhotoResponse(httpResp) {
if (httpResp.json.success) {
return httpResp.json;
} else {
// If there was a URL conflict
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 = {
success: true,
error: false,
shorturl: httpResp.requestedLink,
shorturl: `${httpResp.host}/${httpResp.requestedLink}`,
}
return json;
}
@@ -243,24 +290,29 @@ function validateChhotoResponse(httpResp) {
/**
* 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
* 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 :(
if (typeof chrome !== "undefined") {
const prevSelected = document.activeElement;
const tempEle = document.createElement("input");
document.body.appendChild(tempEle);
tempEle.value = chhotoResp.shorturl;
tempEle.select();
document.execCommand('copy');
document.body.removeChild(tempEle);
// Depending on what was previously selected, we might not be able to select the text.
if (prevSelected?.select) {
prevSelected.select();
}
// Currently the "getBrowserInfo" function is only available on Firefox browsers
// So, we can partially detect the type of the browser
// This check may change in the future
if (typeof browser.runtime.getBrowserInfo !== "function") {
// Create an offscreen document
await browser.offscreen.createDocument({
url: "/offscreen/offscreen.html",
reasons: [chrome.offscreen.Reason.CLIPBOARD],
justification: "Copy the shortened URL to the clipboard"
});
// Send a runtime message to the offscreen page
browser.runtime.sendMessage({type: "clipboard", link: chhotoResp.shorturl});
return Promise.resolve(chhotoResp);
} else {
return navigator.clipboard
@@ -335,16 +387,27 @@ function popupGenerateChhoto(url, title) {
.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
browser.browserAction.onClicked.addListener(generateChhoto);
browser.action.onClicked.addListener(generateChhoto);
// Create a context menu
browser.contextMenus.create({
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"});
});
// When a message is sent
browser.runtime.onMessage.addListener(handleMessages);