Commit 61299bbf authored by henriquez's avatar henriquez

make it actually do stuff

parent 18adab75
# menu-demo
# Memespeech Web Extension
A demo of the [menus API](https://developer.mozilla.org/en-US/Add-ons/WebExtensions/API/menus/).
**This add-on injects JavaScript into web pages. The `addons.mozilla.org` domain disallows this operation, so this add-on will not work properly when it's run on pages in the `addons.mozilla.org` domain.**
**This add-on uses the `menus` namespace to access the functions it needs to create menu items. Note that Chrome, Edge, and Opera all use the `contextMenus` namespace for this, so this extension will not work in these browsers. For compatibility with these browsers, Firefox also offers the `contextMenus` namespace, so to make this extension work with other browsers, use `contextMenus`.**
## What it does
This add-on adds several items to the browser's context menu:
* one shown when there is a selection in the page, that logs the selected text
to the browser console when clicked.
* one shown in all contexts, that is removed when clicked.
* two "radio" items that are shown in all contexts.
These items are grouped using a separator item on each side.
One radio item adds a blue border to the page, the other adds a green border.
Note that these buttons only work on normal web pages, not special pages
like about:debugging.
* one "checkbox" item, shown in all contexts, whose title is updated when the
item is clicked.
* one item that uses the "commands" property to open the add-on's sidebar.
It also adds one item to the browser's "Tools" menu.
## What it shows
* How to create various types of menu item:
* normal
* radio
* separator
* checkbox
* How to use contexts to control when an item appears.
* How to update an item's properties.
* How to remove an item.
Browser add-on for Chrome and Firefox.
\ No newline at end of file
......@@ -17,6 +17,41 @@
"menuItemSelectionDecryptMemespeech": {
"message": "Decrypt Memespeech",
"description": "Title of context menu item that allows decryption of Memespeech."
},
"splashPrivacyAndTermsHeading": {
"message": "Privacy and Terms",
"description": "Title of the main heading on the splash screen."
},
"splashDataStorageDisclosure": {
"message": "All data is stored locally on your computer. This extension sends no data to the Internet whatsoever. That's up to you!",
"description": "Data storage disclosure on the splash screen."
},
"splashLicenseTermSentenceBeginning": {
"message": "Please use Memespeech in the spirit of loving kindness. This software is",
"description": "Beginning of splash screen license term disclosure sentence, prior to the link to the GPLv3 license"
},
"splashLicenseTermSentenceLicenseDescription": {
"message": "GPLv3 licensed",
"description": "Localized GPLv3 license name in the splash screen license term disclosure sentence."
},
"splashLicenseTermSentenceSourceCodeLinkText": {
"message": "; source code is available at",
"description": "End portion of splash screen license term disclosure describing the hyperlink to the extension source code. BE CAREFUL WITH THIS ONE - there is no whitespace added between this string and splashLicenseTermSentenceLicenseDescription, so add a leading space if it doesn't make sense in the locale."
},
"splashEncryptionWarning": {
"message": "Don't use encryption if it is illegal in your jurisdiction ;)",
"description": "Splash screen warning about not using encryption features where it is illegal to do so."
},
"splashButtonText": {
"message": "ok lol",
"description": "Text for the OK button on the splash screen."
}
}
/*
Called when the item has been created, or when creation failed due to an error.
We'll just log success/failure here.
*/
function onCreated() {
if (chrome.runtime.lastError) {
console.log(`Error: ${chrome.runtime.lastError}`);
} else {
console.log("Context menu item created successfully");
}
}
/*
Called when there was an error.
We'll just log the error here.
*/
function onError(error) {
console.log(`Error: ${error}`);
}
/*
Create all the context menu items.
*/
chrome.menus.create({
id: "convert-to-memespeech",
title: chrome.i18n.getMessage("menuItemSelectionConvertToMemespeech"),
contexts: ["selection"]
}, onCreated);
id: "convert-to-memespeech",
title: chrome.i18n.getMessage("menuItemSelectionConvertToMemespeech"),
contexts: ["selection"]
});
chrome.menus.create({
id: "decrypt-memespeech",
title: chrome.i18n.getMessage("menuItemSelectionDecryptMemespeech"),
contexts: ["selection"]
}, onCreated);
id: "decrypt-memespeech",
title: chrome.i18n.getMessage("menuItemSelectionDecryptMemespeech"),
contexts: ["selection"]
});
/*
The click event listener, where we perform the appropriate action given the
ID of the menu item that was clicked.
*/
chrome.menus.onClicked.addListener((info, tab) => {
console.log('menu clicked: ', info, tab);
// switch (info.menuItemId) {
// case "log-selection":
// console.log(info.selectionText);
// break;
// case "remove-me":
// var removing = chrome.menus.remove(info.menuItemId);
// removing.then(onRemoved, onError);
// break;
// case "bluify":
// borderify(tab.id, blue);
// break;
// case "greenify":
// borderify(tab.id, green);
// break;
// case "check-uncheck":
// updateCheckUncheck();
// break;
// case "open-sidebar":
// console.log("Opening my sidebar");
// break;
// case "tools-menu":
// console.log("Clicked the tools menu item");
// break;
// }
console.log('menu clicked: ', info, tab);
});
var splashTab;
const showSplashScreen = () => {
chrome.tabs.create({
active: true,
url: '/html/splash.html'
}, (tab) => {
splashTab = tab;
chrome.storage.local.set({splashScreenShown: true});
});
}
// display the splash screen if it's a new installation
chrome.storage.local.get(['splashScreenShown'], (result) => {
if (!result.splashScreenShown) showSplashScreen();
})
// listen for messages and send responses
function openPort(port) {
port.onMessage.addListener((message) => {
console.log('background.js received message: ', message);
switch (message.type) {
case 'close-splash-screen':
if (typeof splashTab !== "undefined") {
chrome.tabs.remove(splashTab.id);
}
break;
case 'get-i18n':
if (typeof message.keys !== "undefined") {
let vals = {};
for (key of message.keys) {
vals[key] = chrome.i18n.getMessage(key);
}
port.postMessage({id: message.id, vals: vals});
}
break;
}
});
}
browser.runtime.onConnect.addListener(openPort);
/* http://meyerweb.com/eric/tools/css/reset/
v2.0 | 20110126
License: none (public domain)
*/
html, body, div, span, applet, object, iframe,
h1, h2, h3, h4, h5, h6, p, blockquote, pre,
a, abbr, acronym, address, big, cite, code,
del, dfn, em, img, ins, kbd, q, s, samp,
small, strike, strong, sub, sup, tt, var,
b, u, i, center,
dl, dt, dd, ol, ul, li,
fieldset, form, label, legend,
table, caption, tbody, tfoot, thead, tr, th, td,
article, aside, canvas, details, embed,
figure, figcaption, footer, header, hgroup,
menu, nav, output, ruby, section, summary,
time, mark, audio, video {
margin: 0;
padding: 0;
border: 0;
font-size: 100%;
font: inherit;
vertical-align: baseline;
}
/* HTML5 display-role reset for older browsers */
article, aside, details, figcaption, figure,
footer, header, hgroup, menu, nav, section {
display: block;
}
body {
line-height: 1;
}
ol, ul {
list-style: none;
}
blockquote, q {
quotes: none;
}
blockquote:before, blockquote:after,
q:before, q:after {
content: '';
content: none;
}
table {
border-collapse: collapse;
border-spacing: 0;
}
\ No newline at end of file
@font-face {
font-family: 'DejaVu Serif';
src: url('/fonts/dejavuserif_regular.woff2') format('woff2');
font-weight: normal;
font-style: normal;
}
@font-face {
font-family: 'DejaVu Serif';
src: url('/fonts/dejavuserif_bold.woff2') format('woff2');
font-weight: bold;
font-style: normal;
}
body {
background: #c3461b;
transition: background .2s linear;
font-family: 'DejaVu Serif';
color: #222;
}
.flex-layout {
display: flex;
flex-direction: column;
height: 100%;
max-height: 100vh;
overflow-x: hidden;
overflow-y: scroll;
}
.flex-layout .splash {
flex-grow: 0;
flex-shrink: 1;
overflow: hidden;
height: 100vh;
}
.flex-layout .flex-content {
box-sizing: border-box;
transition: height .5s linear, opacity .7s ease-in;
height: 0vh;
opacity: 0;
flex-grow: 1;
flex-shrink: 0;
text-align: center;
}
.splash {
display: flex;
flex-direction: column;
height: 100%;
align-items: center;
}
.splash .top {
width: 30px;
height: 5%;
flex-shrink: 0;
flex-grow: 0;
}
.splash .logo {
width: 100%;
height: 100%;
display: flex;
flex-direction: column;
justify-content: center;
position: relative;
overflow: hidden;
flex-shrink: 0;
flex-grow: 1;
}
.splash .logo svg.symbol {
width: 100%;
height: 100%;
max-width: 100%;
max-height: 100%;
fill: black;
transition: fill .2s linear;
}
.splash .logo svg.smiley {
position: absolute;
left: 0px;
top: 50%;
transform: translateY(-50%) scale(0) rotate(0deg);
width: 100%;
height: 100%;
max-width: 100%;
max-height: 100%;
stroke: none;
}
.splash .logo svg.smiley path {
transition: fill .2s linear;
fill: black;
}
.splash .logo svg.smiley .face {
fill: #c3461b;
transition: fill .2s linear;
}
body.happy {
background: #ffe6dd;
}
body.happy .splash .logo svg.symbol {
fill: #c3461b;
}
body.happy .splash .logo svg.smiley path {
fill: #c3461b;
}
body.happy .splash .logo svg.smiley .face {
fill: #ffe6dd;
}
body.content-visible .flex-layout .flex-content {
height: 70vh;
opacity: 1;
}
a {
color: #71280f;
text-decoration: none;
}
a:hover {
color: #300;
text-decoration: underline;
}
h1, h2, p {
box-sizing: border-box;
margin: 0px auto 30px auto;
line-height: 1.3em;
max-width: 775px;
width: 100%;
padding: 0px 15px 0px 15px;
}
h1 {
padding-top: 25px;
font-size: 3em;
font-weight: bold;
}
h1 b {
display: inline-block;
box-sizing: border-box;
padding: 5px;
width: 65px;
text-align: center;
margin: 3px 3px;
position: relative;
color: transparent;
}
h1 b:after, h1 b:before {
position: absolute;
left: -1px;
top: -1px;
display: block;
box-sizing: border-box;
padding: 5px;
border: 1px solid rgba(195, 70, 27, .2);
background: rgba(255, 255, 255, .5);
width: 65px;
text-align: center;
transition: all .3s ease-in;
color: black;
}
h1 b:after {
transform: scale(1.5,1.5);
opacity: 0;
}
h1 b.tweak:before {
opacity: 0;
transform: scale(.5, .5);
}
h1 b.tweak:after {
opacity: 1;
transform: scale(1,1);
}
h1 b:nth-of-type(1):before { content: "M"; }
h1 b:nth-of-type(2):before { content: "E"; }
h1 b:nth-of-type(3):before { content: "M"; }
h1 b:nth-of-type(4):before { content: "E"; }
h1 b:nth-of-type(5):before { content: "S"; }
h1 b:nth-of-type(6):before { content: "P"; }
h1 b:nth-of-type(7):before { content: "E"; }
h1 b:nth-of-type(8):before { content: "E"; }
h1 b:nth-of-type(9):before { content: "C"; }
h1 b:nth-of-type(10):before { content: "H"; }
h1 b:nth-of-type(1):after { content: "m"; }
h1 b:nth-of-type(2):after { content: "e"; }
h1 b:nth-of-type(3):after { content: "m"; }
h1 b:nth-of-type(4):after { content: "e"; }
h1 b:nth-of-type(5):after { content: "s"; }
h1 b:nth-of-type(6):after { content: "p"; }
h1 b:nth-of-type(7):after { content: "e"; }
h1 b:nth-of-type(8):after { content: "e"; }
h1 b:nth-of-type(9):after { content: "c"; }
h1 b:nth-of-type(10):after { content: "h"; }
h2 {
font-size: 1.5em;
font-weight: bold;
}
p {
font-size: 1.5em;
}
button {
color: white;
background: #c3461b;
border: 0px none transparent;
font-size: 2em;
padding: 5px 25px;
border-radius: 5px;
cursor: pointer;
margin-bottom: 50px;
}
button:hover {
background: #b3360b;
}
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>Welcome to Memespeech!</title>
<link rel="stylesheet" href="../css/reset.css"/>
<link rel="stylesheet" href="../css/splash.css"/>
</head>
<body>
<div class="flex-layout">
<div class="splash">
<div class="top"></div>
<div class="logo">
<svg class="symbol" width="100%" height="100%" viewBox="0 0 425.19684 425.19684" xmlns="http://www.w3.org/2000/svg">
<g transform="matrix(2.4 0 0 2.4 -470.26 -1331.4)">
<path d="m284.53 595.5c-11.508 0-21.93 4.2502-30.188 11.031 1.831 3.7688 4.3005 7.1245 7.3438 9.9375 6.1843-5.262 14.095-8.5625 22.844-8.5625s16.66 3.3005 22.844 8.5625c3.0434-2.8094 5.5413-6.1437 7.375-9.9062-8.2575-6.8025-18.69-11.062-30.219-11.062zm-35.531 16.094c-4.329 4.8415-7.6364 10.539-9.7188 16.844 4.2143-0.04479 8.3696 0.60992 12.375 1.8438 1.2804-3.213 3.0738-6.1416 5.1875-8.8125-3.1043-2.8695-5.7607-6.1685-7.8438-9.875zm71.062 0.03125c-2.0803 3.6874-4.7513 6.9862-7.8438 9.8438 2.1197 2.6786 3.906 5.6202 5.1875 8.8438 3.9963-1.2278 8.1393-1.9194 12.344-1.875-2.0771-6.2974-5.3703-11.975-9.6875-16.812zm-78.25 23.812c-1.4389-0.08088-2.8958 0.04895-4.3438 0.15625-0.41758 2.5402-0.78125 5.0923-0.78125 7.75 0 20.458 12.922 37.822 31 44.656 2.376-3.4849 4.0426-7.3402 4.9688-11.406-13.691-4.9053-23.562-17.88-23.562-33.25 0-2.188 0.27593-4.2962 0.65625-6.375-2.5846-0.79287-5.2288-1.379-7.9375-1.5312zm85.438 0c-2.7087 0.15008-5.3529 0.74071-7.9375 1.5312 0.38032 2.0788 0.65625 4.187 0.65625 6.375-1e-5 15.38-9.888 28.353-23.594 33.25 0.9185 4.0678 2.6007 7.9193 4.9688 11.406 18.084-6.8271 31-24.188 31-44.656 0-2.6577-0.36424-5.2098-0.78125-7.75-1.4373-0.10613-2.8842-0.23539-4.3125-0.15625zm-47.781 42.844c-0.9516 4.1796-2.5307 8.1769-4.75 11.875 3.1738 0.66413 6.4427 1.0312 9.8125 1.0312 3.3594 0 6.6174-0.37113 9.7812-1.0312-2.2108-3.6987-3.7742-7.6961-4.7188-11.875-1.6702 0.24186-3.3257 0.5-5.0625 0.5s-3.3923-0.25814-5.0625-0.5z"/>
<path d="m277.47 555.31c-25.995 3.4627-46.094 25.656-46.094 52.594 0 1.6596 0.12906 3.2866 0.28125 4.9062-1.4814 0.67864-2.9664 1.3874-4.4062 2.2188-23.322 13.465-32.486 41.948-22.5 66.188l0.15625-0.09375c-5.7027-16.507 0.84872-35.258 16.531-44.312 17.264-9.9675 39.147-4.4084 49.812 12.188l3.4062-2c-0.4276-1.1516-0.75-2.3559-0.75-3.6562 1e-5 -5.2335 3.8623-9.3708 8.8438-10.25v-4.0938c-19.692-0.95055-35.406-17.104-35.406-37.031 1e-5 -18.119 12.969-33.166 30.125-36.469v-0.1875zm14.156 0v0.1875c17.147 3.3147 30.125 18.36 30.125 36.469 0 19.938-15.76 36.096-35.469 37.031v4.0625c4.9995 0.86427 8.875 5.0349 8.875 10.281 0 1.3004-0.3224 2.5046-0.75 3.6562l3.4062 2c10.665-16.594 32.548-22.124 49.812-12.156 15.692 9.0596 22.218 27.804 16.5 44.312l0.1875 0.09375c9.9988-24.244 0.8285-52.75-22.5-66.219-1.4429-0.83307-2.9216-1.5396-4.4062-2.2188 0.15219-1.6197 0.28125-3.2467 0.28125-4.9062 1e-5 -26.93-20.078-49.122-46.062-52.594zm-15.156 94.719-3.4688 2c9.0511 17.536 2.895 39.248-14.375 49.219-15.692 9.0596-35.187 5.3309-46.625-7.875l-0.1875 0.09375c15.996 20.781 45.265 27.094 68.594 13.625 1.4442-0.83382 2.7922-1.7718 4.125-2.7188 1.3358 0.94744 2.6788 1.8838 4.125 2.7188 23.322 13.465 52.563 7.1739 68.562-13.594l-0.15625-0.09375c-11.444 13.192-30.974 16.929-46.656 7.875-17.262-9.966-23.377-31.686-14.344-49.219l-3.5-2.0312c-1.949 2.3402-4.7479 3.9375-8.0312 3.9375-3.2897-1e-5 -6.1135-1.5895-8.0625-3.9375z"/>
</g>
</svg>
<svg class="smiley" width="100%" height="100%" viewBox="0 0 500 500" xmlns="http://www.w3.org/2000/svg">
<g>
<circle class="face" cx="250" cy="250" r="105"/>
<path class="eye left" d="M 190 215 A 20 20 0 0 1 230 215 A 20 20 0 0 1 190 215" />
<path class="eye right" d="M 270 215 A 20 20 0 0 1 310 215 A 20 20 0 0 1 270 215" />
<path class="mouth" d="M 200 270 A 50 0 0 0 0 300 270 A 50 40 0 0 1 200 270" />
</g>
</svg>
</div>
</div>
<div class="flex-content">
<h1 class="logo-text"><b>M</b><b>E</b><b>M</b><b>E</b><b>S</b><b>P</b><b>E</b><b>E</b><b>C</b><b>H</b></h1>
<h2 id="i18n-splashPrivacyAndTermsHeading"></h2>
<p id="i18n-splashDataStorageDisclosure"></p>
<p>
<span id="i18n-splashLicenseTermSentenceBeginning"></span><a id="i18n-splashLicenseTermSentenceLicenseDescription" href="https://www.gnu.org/licenses/gpl-3.0.en.html" target="_blank"></a><span id="i18n-splashLicenseTermSentenceSourceCodeLinkText"></span>
<a href="https://gitlab.com/obsessivefacts/memespeech-webextension" target="_blank">gitlab.com/obsessivefacts/memespeech-webextension</a>.
</p>
<p id="i18n-splashEncryptionWarning"></p>
<button id="i18n-splashButtonText"></button>
</div>
</div>
</body>
<script type="text/javascript" src="../js/common.js"></script>
<script type="text/javascript" src="../js/splash.js"></script>
</html>
let id = document.getElementById.bind(document),
callbacks = {},
port = browser.runtime.connect({name:"port-from-cs"});
const sendMessage = function(type, data, callbackFn) {
let messageId = Math.round(Math.random() * 4200000000); // do you feel lucky
console.log('common.js sending message: ', type, messageId, callbackFn);
if (callbackFn) {
callbacks[messageId] = {
fn: callbackFn,
once: true
}
}
port.postMessage({
...data,
type: type,
id: messageId
});
}
port.onMessage.addListener((message) => {
console.log('common.js received message: ', message);
if (message.id && typeof callbacks[message.id] !== "undefined") {
let callback = callbacks[message.id];
console.log('message matches callback: ', callback);
callback.fn(message);
if (callback.once) delete callback;
}
});
\ No newline at end of file
const q = animate = () => {
const ANIMATION_TIME = 520, // milliseconds
CENTER = 250, // pixels
FACE_ROTATION = 20, // degrees
EYE_END_RADIUS = 20,
EYE_X_OFFSET = 60,
EYE_Y_OFFSET = 30,
MOUTH_END_RADIUS = 40,
MOUTH_X_OFFSET = 45,
MOUTH_Y_OFFSET = 30,
START_TIME = new Date().getTime();
let eyeLeftX = CENTER - EYE_X_OFFSET,
eyeRightX = CENTER + (EYE_X_OFFSET - 2*EYE_END_RADIUS),
eyeY = CENTER - EYE_Y_OFFSET,
mouthStartX = CENTER - MOUTH_X_OFFSET,
mouthEndX = CENTER + MOUTH_X_OFFSET
mouthY = CENTER + MOUTH_Y_OFFSET;
let back = document.body,
symbol = document.querySelector('svg.symbol'),
face = document.querySelector('svg.smiley'),
faceBg = document.querySelector('svg.smiley .face'),
eyeLeft = document.querySelector('path.eye.left'),
eyeRight = document.querySelector('path.eye.right'),
mouth = document.querySelector('path.mouth');
const animateFace = () => {
requestAnimationFrame(() => {
// calculate shit
var curTime = new Date().getTime(),
pct = (curTime - START_TIME) / ANIMATION_TIME,
pct = pct > 1 ? 1 : pct,
eyeWidth = (2 - (pct / pct)) * EYE_END_RADIUS, // ease in
eyeRadius = pct * EYE_END_RADIUS,
mouthRadius = pct * MOUTH_END_RADIUS,
mouthCurve = (10 - (pct*10)),
mouthRadius = mouthRadius < mouthCurve ? mouthCurve : mouthRadius,
rotate = (1 - pct) * FACE_ROTATION;
eyeLeft.setAttribute('d', 'M ' + eyeLeftX + ' ' + eyeY + ' '
+ 'A ' + eyeWidth + ' ' + eyeRadius + ' 0 0 1 '
+ (eyeLeftX + 2*EYE_END_RADIUS) + ' ' + eyeY + ' A '
+ eyeWidth + ' ' + eyeRadius + ' 0 0 1 '
+ eyeLeftX + ' ' + eyeY);