Compare commits
24 Commits
| Author | SHA1 | Date |
|---|---|---|
|
|
d7d53e8cbc | |
|
|
106361363d | |
|
|
9410140cad | |
|
|
713c2b0f04 | |
|
|
61c0a11524 | |
|
|
924a8f1350 | |
|
|
7930e94f85 | |
|
|
a5f12daa3c | |
|
|
88eead2285 | |
|
|
1a86c434eb | |
|
|
50dcc2191c | |
|
|
8d3a4ca588 | |
|
|
dea8c293aa | |
|
|
f33e5642c5 | |
|
|
26d9434b28 | |
|
|
e977862e44 | |
|
|
f5005a877e | |
|
|
2a4911d827 | |
|
|
5aff71826b | |
|
|
322c2303a1 | |
|
|
9f34c1ce74 | |
|
|
387397878b | |
|
|
46452221b1 | |
|
|
818ab1c99a |
|
|
@ -0,0 +1,9 @@
|
||||||
|
*.xpi
|
||||||
|
|
||||||
|
*.swp
|
||||||
|
|
||||||
|
# Locally built binaries
|
||||||
|
/native_application/gorecv
|
||||||
|
/example_client/gosend
|
||||||
|
|
||||||
|
/web-ext-artifacts
|
||||||
120
README.md
120
README.md
|
|
@ -1,2 +1,118 @@
|
||||||
#Clipboard Inserter
|
# Native Inserter
|
||||||
A simple addon whose purpose is to automatically insert contents of clipboard into the page
|
|
||||||
|
Fork of the Clipboard Inserter whose purpose it is to automatically insert the
|
||||||
|
data received by a native application into a broswer page.
|
||||||
|
|
||||||
|
It is useful for use with text extraction tools like Textractor for looking up
|
||||||
|
words in the browser.
|
||||||
|
|
||||||
|
This repository contains the browser plugin, native application, native
|
||||||
|
messaging manifest and an example client. Textractor plugin is not included.
|
||||||
|
|
||||||
|
Currently this addon is not available on any of the browsers addon stores, nor
|
||||||
|
are releases past the fork signed by our benevolent overlords at
|
||||||
|
Mozilla/Google.
|
||||||
|
|
||||||
|
Only tested on GNU/Linux with Firefox 91 ESR.
|
||||||
|
|
||||||
|
## Install
|
||||||
|
|
||||||
|
You will need:
|
||||||
|
|
||||||
|
- This code
|
||||||
|
- A go compiler
|
||||||
|
- A browser supporting native extensions (eg modern Firefox, Chrome)
|
||||||
|
|
||||||
|
1) Compile and install the native application
|
||||||
|
2) Edit `native_inserter.json` to find the native application
|
||||||
|
3) Put `native_inserter.json` into your browsers native messaging manifest directory
|
||||||
|
4) Build and install the addon in your browser
|
||||||
|
|
||||||
|
For this to be useful you will also need a sender, for example a [Textractor
|
||||||
|
plugin](https://github.com/45Tatami/Textractor-TCPSender) and a HTML page ([for
|
||||||
|
example](https://pastebin.com/raw/DRDE075L)) that the can be inserted into.
|
||||||
|
|
||||||
|
### Firefox
|
||||||
|
|
||||||
|
This addon is not signed. From the [official Firefox
|
||||||
|
documentation](https://extensionworkshop.com/documentation/publish/signing-and-distribution-overview/):
|
||||||
|
|
||||||
|
> Unsigned extensions can be installed in Developer Edition, Nightly, and ESR
|
||||||
|
> versions of Firefox, after toggling the xpinstall.signatures.required
|
||||||
|
> preference in about:config.
|
||||||
|
|
||||||
|
You can download the unsigned build from the releases or follow the official
|
||||||
|
instruction for building addons, for example via `web-ext`:
|
||||||
|
|
||||||
|
```
|
||||||
|
$ npm install -g web-ext
|
||||||
|
$ web-ext build
|
||||||
|
```
|
||||||
|
|
||||||
|
You can then install the resulting zip via the `Install Add-On From File`
|
||||||
|
dialog under `about:addons`.
|
||||||
|
|
||||||
|
The native messaging manifest directory under linux is
|
||||||
|
`~/.mozilla/native-messaging-hosts`.
|
||||||
|
|
||||||
|
## How does it work
|
||||||
|
|
||||||
|
The browser plugin starts a native application which creates a raw TCP listen
|
||||||
|
socket currently on all interfaces on port 30501 for incoming connections.
|
||||||
|
|
||||||
|
One or more applications (eg Textractor plugin) will connect to this socket and
|
||||||
|
send messages. Messages consist of a 4 Byte little-endian length header and
|
||||||
|
UTF-8 payload.
|
||||||
|
|
||||||
|
The native application will forward the UTF-8 data to the browser plugin which
|
||||||
|
in turn will insert the text into a webpage similar to the original clipboard
|
||||||
|
version.
|
||||||
|
|
||||||
|
## Troubleshooting
|
||||||
|
|
||||||
|
The native application is writing to stderr. Browsers usually forward this to
|
||||||
|
their default log.
|
||||||
|
|
||||||
|
The browser addon itself will log to the console. Problems with the native
|
||||||
|
messaging manifest might not necessarily be logged.
|
||||||
|
|
||||||
|
If the native application does not run in the background after loading the
|
||||||
|
add-on, double-check if the native messaging manifest is in the correct
|
||||||
|
directory and has the binary path set correctly.
|
||||||
|
|
||||||
|
## (Not so) FAQ
|
||||||
|
|
||||||
|
#### Why not the clipboard
|
||||||
|
|
||||||
|
Abusing the clipboard functionality to transfer data to the browser is ugly. It
|
||||||
|
does also not work under Wayland which does not allow unfocused applications to
|
||||||
|
access the clipboard.
|
||||||
|
|
||||||
|
(No opinion here whether the native application/messaging setup is more or less
|
||||||
|
ugly than the clipboard approach)
|
||||||
|
|
||||||
|
It also has the bonus of working with VMs without setting up clipboard
|
||||||
|
forwarding between host and guest.
|
||||||
|
|
||||||
|
#### Why a native application
|
||||||
|
|
||||||
|
The APIs including TCP listen sockets are no longer supported by modern
|
||||||
|
Browsers. The only alternative would be connecting to a WebSocket server, but
|
||||||
|
this has two drawbacks:
|
||||||
|
|
||||||
|
1) The API only supports the client side protocol and does not implement a
|
||||||
|
server. This would mean the sending side would need to be the server and the
|
||||||
|
native application the client, which would not work with multiple senders.
|
||||||
|
|
||||||
|
2) The sending side (eg C++ Textractor plugin) would need to implement a
|
||||||
|
Websocket server which is a lot more work than raw tcp.
|
||||||
|
|
||||||
|
## Bugs
|
||||||
|
|
||||||
|
- Native messaging expects the length prefix in native byte order. Go does
|
||||||
|
neither have a non-unsafe way to convert an integer to a byte stream in
|
||||||
|
native order for writing nor a way to detect endianness. The native
|
||||||
|
application will write in little-endian, which will break the protocol on
|
||||||
|
big-endian machines
|
||||||
|
- Messages over the native-messaging 1MB limit will be discarded instead of
|
||||||
|
split up
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,10 @@
|
||||||
|
<html>
|
||||||
|
<head>
|
||||||
|
<meta charset="utf-8"/>
|
||||||
|
<script src="/default-options.js"></script>
|
||||||
|
<script src="monitor.js"></script>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<div id="paste-target" contenteditable="true" />
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
|
|
@ -0,0 +1,59 @@
|
||||||
|
console.log("I'm alive")
|
||||||
|
|
||||||
|
let listeningTabs = []
|
||||||
|
let options = defaultOptions
|
||||||
|
|
||||||
|
chrome.storage.local.get(defaultOptions,
|
||||||
|
o => options = o)
|
||||||
|
|
||||||
|
|
||||||
|
chrome.storage.onChanged.addListener((changes, area) => {
|
||||||
|
if(area === "local") {
|
||||||
|
const optionKeys = Object.keys(options)
|
||||||
|
for(key of Object.keys(changes)) {
|
||||||
|
if(optionKeys.indexOf(key) >= 0) {
|
||||||
|
options[key] = changes[key].newValue
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
chrome.browserAction.onClicked.addListener((tab) => {
|
||||||
|
toggleTab(tab.id)
|
||||||
|
})
|
||||||
|
|
||||||
|
napp = browser.runtime.connectNative("native_inserter")
|
||||||
|
napp.onDisconnect.addListener((p) => {
|
||||||
|
if (p.error) {
|
||||||
|
console.error(`Disconnected native app due to an error: ${p.error.message}`)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
napp.onMessage.addListener((msg) => {
|
||||||
|
console.log("Received: " + msg.body)
|
||||||
|
const pasteTarget = document.querySelector("#paste-target")
|
||||||
|
pasteTarget.innerText = msg.body
|
||||||
|
const content = pasteTarget.innerText
|
||||||
|
listeningTabs.forEach(id => notifyForeground(id, content))
|
||||||
|
})
|
||||||
|
|
||||||
|
function toggleTab(id) {
|
||||||
|
const index = listeningTabs.indexOf(id)
|
||||||
|
if(index >= 0) {
|
||||||
|
uninject(id)
|
||||||
|
listeningTabs.splice(index, 1)
|
||||||
|
chrome.browserAction.setBadgeText({ text: "", tabId: id })
|
||||||
|
} else {
|
||||||
|
chrome.tabs.executeScript({file: "/fg/insert.js"})
|
||||||
|
listeningTabs.push(id)
|
||||||
|
chrome.browserAction.setBadgeBackgroundColor({ color: "green", tabId: id })
|
||||||
|
chrome.browserAction.setBadgeText({ text: "ON", tabId: id })
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function notifyForeground(id, text) {
|
||||||
|
chrome.tabs.sendMessage(id, { action: "insert", text, options })
|
||||||
|
}
|
||||||
|
|
||||||
|
function uninject(id) {
|
||||||
|
chrome.tabs.sendMessage(id, { action: "uninject" })
|
||||||
|
}
|
||||||
|
|
@ -1,6 +0,0 @@
|
||||||
self.port.on('insert', (data, prefs) => {
|
|
||||||
var elem = document.createElement(prefs['element-name'])
|
|
||||||
elem.textContent = data
|
|
||||||
var container = document.querySelector(prefs['container-selector'])
|
|
||||||
document.body.appendChild(elem)
|
|
||||||
})
|
|
||||||
|
|
@ -0,0 +1,5 @@
|
||||||
|
const defaultOptions = {
|
||||||
|
elemName: 'p',
|
||||||
|
containerSelector: 'body',
|
||||||
|
monitorInterval: 300
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,3 @@
|
||||||
|
module gosend
|
||||||
|
|
||||||
|
go 1.17
|
||||||
|
|
@ -0,0 +1,46 @@
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bufio"
|
||||||
|
"encoding/binary"
|
||||||
|
"log"
|
||||||
|
"math"
|
||||||
|
"net"
|
||||||
|
"os"
|
||||||
|
)
|
||||||
|
|
||||||
|
const remote_addr = "localhost:30501"
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
c, err := net.Dial("tcp", remote_addr)
|
||||||
|
if err != nil {
|
||||||
|
log.Panic("Error connecting:", err)
|
||||||
|
}
|
||||||
|
defer c.Close()
|
||||||
|
|
||||||
|
log.Printf("Connected to '%s'. Please input lines\n", remote_addr)
|
||||||
|
|
||||||
|
s := bufio.NewScanner(os.Stdin)
|
||||||
|
for s.Scan() {
|
||||||
|
msg := []byte(s.Text())
|
||||||
|
|
||||||
|
msg_len := len(msg)
|
||||||
|
if msg_len > math.MaxUint32 {
|
||||||
|
log.Println("Message too long")
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
len_buf := make([]byte, 4)
|
||||||
|
binary.LittleEndian.PutUint32(len_buf, uint32(msg_len))
|
||||||
|
|
||||||
|
if _, err := c.Write(len_buf); err != nil {
|
||||||
|
log.Panic("Error sending len:", err)
|
||||||
|
}
|
||||||
|
if _, err := c.Write(msg); err != nil {
|
||||||
|
log.Panic("Error sending payload:", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if err := s.Err(); err != nil {
|
||||||
|
log.Println("Error reading:", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,16 @@
|
||||||
|
(() => {
|
||||||
|
const processMessage = msg => {
|
||||||
|
switch(msg.action) {
|
||||||
|
case "insert":
|
||||||
|
const elem = document.createElement(msg.options.elemName)
|
||||||
|
elem.textContent = msg.text
|
||||||
|
document.querySelector(msg.options.containerSelector).appendChild(elem)
|
||||||
|
break
|
||||||
|
case "uninject":
|
||||||
|
chrome.runtime.onMessage.removeListener(processMessage)
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
chrome.runtime.onMessage.addListener(processMessage)
|
||||||
|
})()
|
||||||
File diff suppressed because one or more lines are too long
|
Before Width: | Height: | Size: 309 B After Width: | Height: | Size: 309 B |
|
Before Width: | Height: | Size: 423 B After Width: | Height: | Size: 423 B |
|
Before Width: | Height: | Size: 610 B After Width: | Height: | Size: 610 B |
63
index.js
63
index.js
|
|
@ -1,63 +0,0 @@
|
||||||
var self = require('sdk/self');
|
|
||||||
var clipboard = require('sdk/clipboard')
|
|
||||||
var buttons = require('sdk/ui/button/action');
|
|
||||||
var tabs = require('sdk/tabs');
|
|
||||||
var { ToggleButton } = require('sdk/ui/button/toggle')
|
|
||||||
var timers = require('sdk/timers')
|
|
||||||
var { prefs } = require('sdk/simple-prefs')
|
|
||||||
|
|
||||||
var button = ToggleButton({
|
|
||||||
id: "clipboard-inserter-btn",
|
|
||||||
label: "Toggle Clipboard Cnserter",
|
|
||||||
icon: {
|
|
||||||
16: "./icon16.png",
|
|
||||||
32: "./icon32.png",
|
|
||||||
64: "./icon64.png"
|
|
||||||
},
|
|
||||||
onChange: function(state) {
|
|
||||||
this.state('window', null)
|
|
||||||
let { checked } = this.state('tab')
|
|
||||||
checked = !checked
|
|
||||||
this.state('tab', { checked })
|
|
||||||
if(checked) {
|
|
||||||
enableMonitor()
|
|
||||||
} else {
|
|
||||||
disableMonitor()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
})
|
|
||||||
//<div>Icons made by <a href="http://www.flaticon.com/authors/google" title="Google">Google</a> from <a href="http://www.flaticon.com" title="Flaticon">www.flaticon.com</a> is licensed by <a href="http://creativecommons.org/licenses/by/3.0/" title="Creative Commons BY 3.0" target="_blank">CC 3.0 BY</a></div>
|
|
||||||
|
|
||||||
function enableMonitor() {
|
|
||||||
console.log("Enabling clipboard monitor")
|
|
||||||
tabs.activeTab.clinClipboardMonitor = new ClipboardMonitor()
|
|
||||||
}
|
|
||||||
|
|
||||||
function disableMonitor() {
|
|
||||||
console.log("Disabling clipboard monitor")
|
|
||||||
var monitor = tabs.activeTab.clinClipboardMonitor
|
|
||||||
if(monitor) {
|
|
||||||
timers.clearInterval(monitor.intervalID)
|
|
||||||
delete tabs.activeTab.clinClipboardMonitor
|
|
||||||
} else {
|
|
||||||
console.error("Trying to disable clipboard monitor on tab without active ClipboardMonitor!")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function ClipboardMonitor() {
|
|
||||||
|
|
||||||
var lastContent = ''
|
|
||||||
|
|
||||||
this.worker = tabs.activeTab.attach({ contentScriptFile: self.data.url('inserter.js') })
|
|
||||||
|
|
||||||
this.intervalID = timers.setInterval(() => {
|
|
||||||
if(clipboard.currentFlavors.indexOf('text') != -1) {
|
|
||||||
var currentContent = clipboard.get('text/unicode')
|
|
||||||
if(lastContent !== currentContent) {
|
|
||||||
this.worker.port.emit('insert', currentContent, prefs)
|
|
||||||
lastContent = currentContent
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}, 100)
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
@ -0,0 +1,41 @@
|
||||||
|
{
|
||||||
|
"manifest_version": 2,
|
||||||
|
"name": "Native Inserter",
|
||||||
|
"version": "1.0.0",
|
||||||
|
|
||||||
|
"description": "An addon that inserts contents received from a native application into a page. Forked from clipboard-inserter. Uses icon made by Google from www.flaticon.com licensed by CC 3.0 BY",
|
||||||
|
|
||||||
|
"icons": {},
|
||||||
|
|
||||||
|
"browser_specific_settings": {
|
||||||
|
"gecko": {
|
||||||
|
"id": "@native-inserter",
|
||||||
|
"strict_min_version": "50.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
"permissions": [
|
||||||
|
"activeTab",
|
||||||
|
"storage",
|
||||||
|
"nativeMessaging",
|
||||||
|
"file://*/*"
|
||||||
|
],
|
||||||
|
|
||||||
|
"browser_action": {
|
||||||
|
"browser_style": true,
|
||||||
|
"default_icon": {
|
||||||
|
"16": "icon/icon16.png",
|
||||||
|
"32": "icon/icon32.png",
|
||||||
|
"64": "icon/icon64.png"
|
||||||
|
},
|
||||||
|
"default_title": "Toggle Native Inserter"
|
||||||
|
},
|
||||||
|
|
||||||
|
"background": {
|
||||||
|
"page": "bg/index.html"
|
||||||
|
},
|
||||||
|
|
||||||
|
"options_ui": {
|
||||||
|
"page": "options.html"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,3 @@
|
||||||
|
module gorecv
|
||||||
|
|
||||||
|
go 1.17
|
||||||
|
|
@ -0,0 +1,93 @@
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/binary"
|
||||||
|
"encoding/json"
|
||||||
|
"fmt"
|
||||||
|
"io"
|
||||||
|
"log"
|
||||||
|
"net"
|
||||||
|
)
|
||||||
|
|
||||||
|
type Message struct {
|
||||||
|
Body string `json:"body"`
|
||||||
|
}
|
||||||
|
|
||||||
|
const MSG_SIZE_LIMIT = 1000 * 1000 // Unclear if 1MiB or 1MB, using lower bound
|
||||||
|
const LISTEN_INTERFACE = ":30501"
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
log.Println("Listening on", LISTEN_INTERFACE)
|
||||||
|
|
||||||
|
l, err := net.Listen("tcp", LISTEN_INTERFACE)
|
||||||
|
if err != nil {
|
||||||
|
log.Fatal(err)
|
||||||
|
}
|
||||||
|
defer l.Close()
|
||||||
|
|
||||||
|
msg_pipe := make(chan string)
|
||||||
|
|
||||||
|
// Output routine
|
||||||
|
go func() {
|
||||||
|
for {
|
||||||
|
msg, err := json.Marshal(Message{<-msg_pipe})
|
||||||
|
if err != nil {
|
||||||
|
log.Println("Error encoding message:", err)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
msg_len := len(msg)
|
||||||
|
if msg_len >= MSG_SIZE_LIMIT {
|
||||||
|
log.Println("Message over webextension limit. Discarding")
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
len_buf := make([]byte, 4)
|
||||||
|
binary.LittleEndian.PutUint32(len_buf, uint32(msg_len))
|
||||||
|
|
||||||
|
log.Println(len_buf, string(msg))
|
||||||
|
fmt.Print(string(len_buf))
|
||||||
|
fmt.Print(string(msg))
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
|
||||||
|
// Accept connections
|
||||||
|
for {
|
||||||
|
conn, err := l.Accept()
|
||||||
|
if err != nil {
|
||||||
|
log.Println("Error accepting conn:", err)
|
||||||
|
}
|
||||||
|
go conn_hndlr(conn, msg_pipe)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func conn_hndlr(c net.Conn, ch chan<- string) {
|
||||||
|
defer c.Close()
|
||||||
|
log.Println("Connection opened:", c)
|
||||||
|
for {
|
||||||
|
msg, err := read_msg(c)
|
||||||
|
if err != nil {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
ch <- msg
|
||||||
|
}
|
||||||
|
log.Println("Closing connection:", c)
|
||||||
|
}
|
||||||
|
|
||||||
|
func read_msg(c net.Conn) (string, error) {
|
||||||
|
len_buf := make([]byte, 4)
|
||||||
|
|
||||||
|
if _, err := io.ReadFull(c, len_buf); err != nil {
|
||||||
|
log.Println("Len read error:", err)
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
|
||||||
|
msg_len := binary.LittleEndian.Uint32(len_buf)
|
||||||
|
msg_buf := make([]byte, msg_len)
|
||||||
|
if _, err := io.ReadFull(c, msg_buf); err != nil {
|
||||||
|
log.Println("Msg read error:", err)
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
|
||||||
|
return string(msg_buf), nil
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,7 @@
|
||||||
|
{
|
||||||
|
"name": "native_inserter",
|
||||||
|
"description": "Example host for native messaging",
|
||||||
|
"path": "/usr/local/bin/gorecv",
|
||||||
|
"type": "stdio",
|
||||||
|
"allowed_extensions": [ "@native-inserter" ]
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,29 @@
|
||||||
|
<html>
|
||||||
|
<head>
|
||||||
|
<meta charset="utf-8"/>
|
||||||
|
<script src="default-options.js"></script>
|
||||||
|
<script src="options.js"></script>
|
||||||
|
<link href="forms-min.css" rel="stylesheet"/>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<form class="pure-form pure-form-aligned">
|
||||||
|
<fieldset>
|
||||||
|
<div class="pure-control-group">
|
||||||
|
<label for="elem-name">Element name </label>
|
||||||
|
<input id="elem-name" type="text"
|
||||||
|
title="Element name used when inserting the clipboard contents"/>
|
||||||
|
</div>
|
||||||
|
<div class="pure-control-group">
|
||||||
|
<label for="container-selector">Container selector </label>
|
||||||
|
<input id="container-selector" type="text"
|
||||||
|
title="Css selector used to find element into which new elements will be inserted. Default value - 'body' - means that new elements will be appended at the end of the document."/>
|
||||||
|
</div>
|
||||||
|
<div class="pure-control-group">
|
||||||
|
<label for="monitor-interval">Timer interval </label>
|
||||||
|
<input id="monitor-interval" type="number" min="100" step="10"
|
||||||
|
title="Time (in milliseconds) between clipboard checks. Low values may cause performance problems."/>
|
||||||
|
</div>
|
||||||
|
</fieldset>
|
||||||
|
</form>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
|
|
@ -0,0 +1,21 @@
|
||||||
|
document.addEventListener("DOMContentLoaded", () => {
|
||||||
|
const elemName = document.querySelector("#elem-name"),
|
||||||
|
containerSelector = document.querySelector("#container-selector"),
|
||||||
|
monitorInterval = document.querySelector("#monitor-interval")
|
||||||
|
|
||||||
|
const storage = chrome.storage.local
|
||||||
|
|
||||||
|
storage.get(defaultOptions, o => {
|
||||||
|
elemName.value = o.elemName
|
||||||
|
containerSelector.value = o.containerSelector
|
||||||
|
monitorInterval.value = o.monitorInterval
|
||||||
|
})
|
||||||
|
elemName.onchange = () => storage.set({ elemName: elemName.value })
|
||||||
|
containerSelector.onchange = () => storage.set({ containerSelector: containerSelector.value })
|
||||||
|
monitorInterval.onchange = () => {
|
||||||
|
const newVal = monitorInterval.value
|
||||||
|
if(newVal >= 100) {
|
||||||
|
storage.set({ monitorInterval: monitorInterval.value })
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
28
package.json
28
package.json
|
|
@ -1,28 +0,0 @@
|
||||||
{
|
|
||||||
"title": "Clipboard Inserter",
|
|
||||||
"name": "clipboard-inserter",
|
|
||||||
"version": "0.0.1",
|
|
||||||
"description": "A simple addon whose purpose is to automatically insert contents of clipboard into the page. Uses icon made by Google from www.flaticon.com licensed by CC 3.0 BY",
|
|
||||||
"main": "index.js",
|
|
||||||
"author": "Yoitsumi",
|
|
||||||
"engines": {
|
|
||||||
"firefox": ">=38.0a1",
|
|
||||||
"fennec": ">=38.0a1"
|
|
||||||
},
|
|
||||||
"license": "MIT",
|
|
||||||
"keywords": [
|
|
||||||
"jetpack"
|
|
||||||
],
|
|
||||||
"preferences": [{
|
|
||||||
"name": "element-name",
|
|
||||||
"title": "Added element name",
|
|
||||||
"type": "string",
|
|
||||||
"value": "p"
|
|
||||||
}, {
|
|
||||||
"name": "container-selector",
|
|
||||||
"title": "Containing element selector",
|
|
||||||
"description": "css selector of the element which should contain added elements. If you don't know what this means leaving this as 'body' will just append at the end of the document",
|
|
||||||
"type": "string",
|
|
||||||
"value": "body"
|
|
||||||
}]
|
|
||||||
}
|
|
||||||
|
|
@ -1 +0,0 @@
|
||||||
var main = require("../");
|
|
||||||
Loading…
Reference in New Issue