",
+ "storage"
+ ],
+ "content_scripts": [
+ {
+ "matches": [ "*://*/*" ],
+ "js": [ "content.js" ],
+ "run_at": "document_end"
+ }
+ ],
+ "options_ui": {
+ "chrome_style": true,
+ "page": "options.html"
+ },
+ "background": {
+ "persistent": false,
+ "scripts": [
+ "background.js"
+ ]
+ }
+}
diff --git a/source/options-storage.js b/source/options-storage.js
new file mode 100644
index 0000000..ee70baf
--- /dev/null
+++ b/source/options-storage.js
@@ -0,0 +1,13 @@
+import OptionsSync from 'webext-options-sync';
+
+export default new OptionsSync({
+ defaults: {
+ createBlankAnchorLink: false,
+ markdownSites: 'hackerone.com, reddit.com, teuxdeux.com, trello.com, zendesk.com',
+ blockedSites: 'github.com, facebook.com, slack.com, twitter.com, whatsapp.com, google.com',
+ },
+ migrations: [
+ OptionsSync.migrations.removeUnused,
+ ],
+ logging: true,
+});
diff --git a/source/options.css b/source/options.css
new file mode 100644
index 0000000..1bb5b9e
--- /dev/null
+++ b/source/options.css
@@ -0,0 +1,45 @@
+html {
+ min-width: 550px;
+ overflow-x: hidden; /* Required to hide horizontal scroll on Firefox */
+}
+
+/* For use with screen readers */
+.sr-only {
+ display: none;
+}
+
+/* Hide spinbox for number inputs */
+input::-webkit-outer-spin-button,
+input::-webkit-inner-spin-button {
+ -webkit-appearance: none;
+ margin: 0;
+}
+
+input[type='number'] {
+ -moz-appearance: textfield;
+}
+
+input[type='number'],
+input[type='text'] {
+ padding: 0.25rem 0.375rem;
+}
+
+
+/* Firefox only */
+.only-firefox {
+ display: none;
+}
+
+@-moz-document url-prefix('') {
+ .only-firefox {
+ display: block;
+ }
+}
+
+textarea {
+ display: block;
+}
+
+label {
+ line-height: 30px;
+}
diff --git a/source/options.html b/source/options.html
new file mode 100644
index 0000000..a62d61c
--- /dev/null
+++ b/source/options.html
@@ -0,0 +1,23 @@
+
+
+
+
+Options
+
+
+
+
+
diff --git a/source/options.js b/source/options.js
new file mode 100644
index 0000000..a386c72
--- /dev/null
+++ b/source/options.js
@@ -0,0 +1,6 @@
+// Don't forget to import this wherever you use it
+import browser from 'webextension-polyfill';
+
+import optionsStorage from './options-storage.js';
+
+optionsStorage.syncForm('#options-form');
diff --git a/src/js/content.js b/src/js/content.js
deleted file mode 100644
index 8ec6b63..0000000
--- a/src/js/content.js
+++ /dev/null
@@ -1,194 +0,0 @@
-let shiftPressed = false;
-
-const isFirefox = navigator.userAgent.toLowerCase().includes('firefox');
-
-function pasteHandler(event) {
- if (shiftPressed) {
- return;
- }
-
- if (typeof (event.clipboardData) === 'undefined') {
- return;
- }
-
- const pasted = event.clipboardData.getData('text/plain');
- if (pasted === '') {
- return;
- }
-
- if (!pasted.match(/^https?:\/\//i)) {
- return;
- }
-
- const editor = event.target;
-
- if (!editor) {
- return;
- }
-
- if (editor.isContentEditable) {
- event.preventDefault();
- document.execCommand('createLink', false, pasted);
- return;
- }
-
- let inputTypeAllowed = false;
-
- if (editor.nodeName === 'TEXTAREA') {
- inputTypeAllowed = true;
- }
-
- // Normally we don't want to do this on tags, but there are exceptions.
- const inputElementSites = [
- 'teuxdeux.com'
- ];
-
- if (editor.nodeName === 'INPUT' && editor.type === 'text' && inputElementSites.some(site => document.domain.includes(site))) {
- inputTypeAllowed = true;
- }
-
- if (!inputTypeAllowed) {
- return;
- }
-
- let start;
- let end;
-
- if (editor.selectionStart > editor.selectionEnd) {
- start = editor.selectionEnd;
- end = editor.selectionStart;
- } else {
- start = editor.selectionStart;
- end = editor.selectionEnd;
- }
-
- // If the current selection is also a URL, assume we want to replace it (not wrap it in an anchor)
- if (editor.value.slice(start, end).match(/^https?:\/\/\S*$/i)) {
- return;
- }
-
- const markdownSites = [
- 'hackerone.com',
- 'github.com',
- 'reddit.com',
- 'teuxdeux.com',
- 'trello.com'
- ];
-
- let bbCodeElement = document.querySelector('#disable_bbcode');
- if (!bbCodeElement) {
- bbCodeElement = document.querySelectorAll('.show_bbcode').item(0);
- }
-
- const tracForm = document.querySelector('#propertyform');
- if (tracForm &&
- (tracForm.getAttribute('action').includes('/newticket') ||
- tracForm.getAttribute('action').includes('/ticket/'))
- ) {
- pasteTrac(event, editor, pasted, start, end);
- } else if (markdownSites.some(site => document.domain.includes(site))) {
- pasteMarkdown(event, editor, pasted, start, end);
- } else if (bbCodeElement) {
- pasteBBcode(event, editor, pasted, start, end);
- } else if (editor.classList.contains('remarkup-assist-textarea')) {
- pasteRemarkup(event, editor, pasted, start, end);
- } else {
- pasteHTML(event, editor, pasted, start, end);
- }
-}
-
-function shiftChecker(event) {
- shiftPressed = event.shiftKey;
-}
-
-function pasteTrac(event, editor, pasted, start, end) {
- event.preventDefault();
- insertText('[' + pasted + ' ' + editor.value.slice(start, end) + ']', editor, start, end);
-
- const newPos = start === end ? start + pasted.length + 2 : end + pasted.length + 3;
-
- editor.setSelectionRange(newPos, newPos);
-}
-
-function pasteMarkdown(event, editor, pasted, start, end) {
- event.preventDefault();
- insertText('[' + editor.value.slice(start, end) + '](' + pasted + ')', editor, start, end);
-
- const newPos = start === end ? start + 1 : end + pasted.length + 4;
-
- editor.setSelectionRange(newPos, newPos);
-}
-
-function pasteBBcode(event, editor, pasted, start, end) {
- event.preventDefault();
- insertText('[url=' + pasted + ']' + editor.value.slice(start, end) + '[/url]', editor, start, end);
-
- const newPos = start === end ? start + pasted.length + 6 : end + pasted.length + 12;
-
- editor.setSelectionRange(newPos, newPos);
-}
-
-function pasteRemarkup(event, editor, pasted, start, end) {
- event.preventDefault();
- insertText('[[ ' + pasted + ' | ' + editor.value.slice(start, end) + ' ]]', editor, start, end);
-
- const newPos = start === end ? start + pasted.length + 6 : end + pasted.length + 9;
-
- editor.setSelectionRange(newPos, newPos);
-}
-
-function pasteHTML(event, editor, pasted, start, end) {
- // Make sure we are not in a tag first
- const leftString = editor.value.slice(0, start).toLowerCase();
-
- // If we are inside a (start) tag for any HTML element, its not ok to paste as a an a-href
- if ((leftString.lastIndexOf('<') > -1) && (leftString.lastIndexOf('<') > leftString.lastIndexOf('>'))) {
- return;
- }
-
- // If we are inside an anchor's content, its not ok to paste as a an a-href
- if ((leftString.lastIndexOf(' -1) && (leftString.lastIndexOf(' leftString.lastIndexOf(''))) {
- return;
- }
-
- // Looks safe, let's do this.
- event.preventDefault();
- insertText('' + editor.value.slice(start, end) + '', editor, start, end);
-
- const newPos = start === end ? start + pasted.length + 11 : end + pasted.length + 15;
-
- editor.setSelectionRange(newPos, newPos);
-}
-
-function insertText(text, editor, start, end) {
- if (isFirefox) {
- editor.value = editor.value.slice(0, start) + text + editor.value.slice(end);
- } else {
- document.execCommand('insertText', false, text);
- }
-}
-
-// Don't bother attaching the paste event if we're on a site we don't want to run on.
-let attach = true;
-
-const blockedSites = [
- 'gist.github.com',
- 'facebook.com',
- 'slack.com',
- 'twitter.com',
- 'whatsapp.com'
-];
-
-if (blockedSites.some(site => document.domain.includes(site))) {
- attach = false;
-}
-
-// Don't load on o2 sites, as they already have this feature.
-if (document.body.classList.contains('o2')) {
- attach = false;
-}
-
-if (attach) {
- document.addEventListener('paste', pasteHandler);
- document.addEventListener('keydown', shiftChecker);
-}
diff --git a/src/manifest.json b/src/manifest.json
deleted file mode 100644
index ae907e9..0000000
--- a/src/manifest.json
+++ /dev/null
@@ -1,29 +0,0 @@
-{
- "name": "Linkify",
- "version": "0.0.0",
- "description": "Automatically transform pasted URLs into links.",
- "homepage_url": "https://github.com/pento/linkify",
- "manifest_version": 2,
- "minimum_chrome_version": "74",
- "applications": {
- "gecko": {
- "id": "{a7f5bd94-1e41-4cbb-a49b-e2e10a30587e}",
- "strict_min_version": "67.0"
- }
- },
- "icons": {
- "16": "images/linkify-ico-16.png",
- "48": "images/linkify-ico-48.png",
- "128": "images/linkify-ico-128.png"
- },
-
- "content_scripts": [ {
- "matches": [ "*://*/*" ],
- "js": [ "js/content.js" ],
- "run_at": "document_end"
- }
- ],
-
- "permissions": [ "" ]
-}
-