diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..6311779 --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +web-ext-artifacts/ diff --git a/.web-extension-id b/.web-extension-id new file mode 100644 index 0000000..4e1d7a5 --- /dev/null +++ b/.web-extension-id @@ -0,0 +1,3 @@ +# This file was created by https://github.com/mozilla/web-ext +# Your auto-generated extension ID for addons.mozilla.org is: +anything_to_anything@snake.dog \ No newline at end of file diff --git a/STYLE.css b/STYLE.css new file mode 100644 index 0000000..3dbd8a8 --- /dev/null +++ b/STYLE.css @@ -0,0 +1,33 @@ +body { + background: #ff2a50; + color: white; + font-size: 5vw; + display: flex; + justify-content: center; + align-items: center; +} + +input { + border: none; + border-radius: 2px; + padding: .5em; + font-size: .4em; +} + +input[type="text"] { + width: 9em; +} + +input:focus { + outline: 5px solid #33ffcc; + border: 2px solid black; + box-shadow: 0 0 2px 5px rgba(40, 60, 120, 0.5); +} + +th { + text-align: left; +} + +th, td { + padding: .2em .4em; +} diff --git a/background.js b/background.js new file mode 100644 index 0000000..f496dc7 --- /dev/null +++ b/background.js @@ -0,0 +1,97 @@ +var escape = char => `\\${char}` + +var special = new RegExp('[' + + '()}{[]|\\^$*+?.'.split`` + .map(escape) + .join`|` + + ']', 'g' +) + +var escapeRegex = string => string.replace(special, '\\$&') + +var wordStartRegex = /((?:^|\s|[-_])\w)/g + +String.prototype.toTitleCase = function () { + return this.toLowerCase().replace(wordStartRegex, $1 => $1.toUpperCase()) +} + +function createInsensitiveReplacements ({from, to}) { + var replacements = [] + replacements.push({from, to}) + + replacements.push({ + from: from.toUpperCase(), + to: to.toUpperCase() + }) + + replacements.push({ + from: from.toLowerCase(), + to: to.toLowerCase() + }) + + replacements.push({ + from: from.toTitleCase(), + to: to.toTitleCase() + }) + + return replacements +} + +var build = items => items.reduce((map, item) => { + var { + from = '', + to = '', + sensitive = false, + isRegex = false + } = item + + if (!from) return map + + var replacements = sensitive + ? [{from, to}] + : createInsensitiveReplacements({from, to}) + + return replacements.reduce((map, {from, to}) => ( + map[isRegex + ? from + : escapeRegex(from) + ] = to, map + ), map) + +}, {}) + +var currentData = {} + +browser.storage.local.get('map').then(results => { + currentData = results +}) + +browser.tabs.onUpdated.addListener = (tabId, changeInfo, tab) => { + browsers.tabs.sendMessage(tabId, currentData) +} + +function handleBrowserActionMessage ({type, value}) { + if (type === 'items:change') { + currentData.map = build(value) + browser.storage.local.set(currentData) + } +} + +browser.runtime.onMessage.addListener((message, sender) => { + if (sender.tab && sender.url.endsWith('the-replacements.htm')) { + // we are getting messages from our special tab + handleBrowserActionMessage(message) + } +}) + +browser.browserAction.onClicked.addListener(() => { + browser.tabs.create({ + active: true, + url: './the-replacements.htm' + }).then(() => { + setTimeout(() => { + browser.runtime.sendMessage('hello m8') + browser.runtime.sendMessage(['like this', '?']) + }, 4000) + }) +}) diff --git a/browser-action.js b/browser-action.js new file mode 100644 index 0000000..b23f567 --- /dev/null +++ b/browser-action.js @@ -0,0 +1,70 @@ +var dom = 'tr td input'.split` `.reduce((dom, nodename) => { + dom[nodename] = (attrs = {}, children = '') => { + var node = document.createElement(nodename) + + Object.entries(attrs).forEach(([key, value]) => { + value != null && value !== false && node.setAttribute(key, value) + }) + + if (typeof children === 'string') { + node.textContent = children + } else if (Array.isArray(children)) { + node.append(...children) + } else { + node.append(children) + } + + return node + } + return dom +}, {}) + +function createRow ({from = '', to = '', sensitive = false, isRegex = false} = {}) { + return dom.tr({class: 'js-row'}, [ + dom.td({}, dom.input({class: 'js-from', type: 'text', value: from})), + dom.td({}, dom.input({class: 'js-to', type: 'text', value: to})), + dom.td({}, dom.input({class: 'js-sensitive', type: 'checkbox', checked: sensitive})), + dom.td({}, dom.input({class: 'js-isRegex', type: 'checkbox', checked: isRegex})) + ]) +} + +var table = document.getElementById('table') +var body = document.getElementById('body') + +function serialise () { + return [].reduce.call(table.querySelectorAll('.js-row'), (items, row) => items.concat({ + from: row.querySelector('.js-from').value || '', + to: row.querySelector('.js-to').value || '', + sensitive: !!row.querySelector('.js-sensitive').checked, + isRegex: !!row.querySelector('.js-isRegex').checked + }), []) +} + +Array.prototype.last = function () { + return this[Math.max(this.length - 1, 0)] +} + +function appendRow ({from, to, sensitive, isRegex} = {}) { + return body.append(createRow({from, to, sensitive, isRegex})) +} + +function handleKeyDown () { + var items = serialise() + + browser.storage.local.set({items}).then(() => browser.runtime.sendMessage({ + type: 'items:change', + value: items + })) + + var last = items.last() + + if (last.from && last.to) appendRow() +} + +document.addEventListener('keydown', handleKeyDown) + +browser.storage.local.get('items').then(({items}) => { + if (!Array.isArray(items) || !items.length) return + body.textContent = '' + items.forEach(appendRow) +}) diff --git a/content.js b/content.js new file mode 100644 index 0000000..4c105bc --- /dev/null +++ b/content.js @@ -0,0 +1,44 @@ +var escape = char => `\\${char}` + +var special = new RegExp('[' + + '()}{[]|\\^$*+?.'.split`` + .map(escape) + .join`|` + + ']', 'g' +) + +var escapeRegex = string => string.replace(special, '\\$&') + +var createReplace = replacements => match => { + var straightMatch = replacements[match] + if (straightMatch) return straightMatch + + // else find gay match + var demiMatch = replacements[escapeRegex(match)] + if (demiMatch) return demiMatch + + var [, okcupidMatch] = (Object.entries(replacements).find(([from]) => + RegExp(escapeRegex(from)).test(match) + ) || []) + if (okcupidMatch) return okcupidMatch + + var [, tinderFuck] = (Object.entries(replacements).find(([from]) => + RegExp(from).test(match) + ) || []) + if (tinderFuck) return tinderFuck +} + +var walker = document.createTreeWalker(document.body, NodeFilter.SHOW_TEXT) + +browser.storage.local.get('map').then(results => { + var {map} = results + var keys = Object.keys(map) + if (!keys.length) return + var regex = RegExp(`\\b(${keys.join('|')})\\b`, 'g') + var replace = createReplace(map) + var text + while (text = walker.nextNode()) { + text.textContent = text.textContent.replace(regex, replace) + } +}) + diff --git a/icon.png b/icon.png new file mode 100644 index 0000000..f491851 Binary files /dev/null and b/icon.png differ diff --git a/manifest.json b/manifest.json new file mode 100644 index 0000000..b37510c --- /dev/null +++ b/manifest.json @@ -0,0 +1,35 @@ +{ + "manifest_version": 2, + "name": "anything-to-anything", + "version": "0.0.17", + "description": "replace any term with anything, everywhere. cloud->butt millenial->snake peopel, blockchain->slow database", + "background": { + "scripts": ["background.js"] + }, + "content_scripts": [ + { + "matches": [ + "*://*/*" + ], + "js": [ + "content.js" + ] + } + ], + "permissions": ["storage"], + "icons": { + "64": "icon.png" + }, + "browser_action": { + "browser_style": true, + "default_title": "Anything to Anything", + "default_icon": { + "64": "icon.png" + } + }, + "applications": { + "gecko": { + "id": "anything_to_anything@snake.dog" + } + } +} diff --git a/the-replacements.htm b/the-replacements.htm new file mode 100644 index 0000000..7de8bd3 --- /dev/null +++ b/the-replacements.htm @@ -0,0 +1,20 @@ + + +the replacements + + + + + + + +
from + to + sensitive + regex +
+ + + +
+