View example.css
div input { | |
background-color: lightgreen; | |
border-radius: 5px; | |
padding: 0 5px; | |
} |
View isStreakInstalled.js
var INSTALL_CHECK = 'chrome-extension://pnnfemgpilpdaojpnkjdgfgbnnjojfik/blank.png'; | |
function isExtensionInstalled() { | |
//we have different logic for Chrome vs Safari | |
if(BrowserDetect.browser === 'Chrome'){ | |
// the Chrome extension makes an image "web accessible", so we have the page try to load that image. | |
// if it loads successfully then the Streak extension is installed. If the image does not load successfully, then the image is not installed | |
var img = new Image(); | |
img.onload = function() { | |
clearInterval(installTimerId); |
View contenteditable.html
<div contenteditable="true"> | |
<div> | |
hi <span contenteditable="false"><contact_givenName></span>, | |
</div> | |
<div> | |
How are you? | |
</div> | |
</div> |
View cursorEvents.js
Kefir.merge([ | |
Kefir.fromEvents(input, 'keyup'), | |
Kefir.fromEvents(input, 'input'), | |
Kefir.fromEvents(input, 'click'), | |
Kefir.fromEvents(input, 'focus') | |
]) | |
.takeUntilBy(elementDestroyedObservable) | |
.debounce(100) | |
.onValue(() => /* do some stuff */) |
View pasteText.js
function pasteText({replacementText, cursorContext, activeQuery}: {replacementText: string; cursorContext: CursorContext; activeQuery: string;}){ | |
// update text node content with replaced text | |
const lastIndex = cursorContext.textBeforeCursor.lastIndexOf(activeQuery); | |
cursorContext.textNode.textContent = cursorContext.textNodeContent.substring(0, lastIndex) + replacementText + cursorContext.textAfterCursor; | |
const selection = document.getSelection(); | |
if(!selection) return; | |
// put cursor at the end of the replaced text | |
const range = document.createRange(); |
View getCursorContext.js
/* @flow */ | |
/* this code heavily borrows from https://github.com/zurb/tribute */ | |
export type CursorContext = { | |
textNode: Node; | |
textNodeParent: Node; | |
textNodeContent: string; | |
textBeforeCursor: string; | |
textAfterCursor: string; |
View load.js
InboxSDK.load(2, 'YOUR_APP_ID_HERE', {suppressAddonTitle: 'YOUR_ADD_ON_TITLE_HERE'}).then(function(sdk){ | |
//more code | |
}); |
View compose.js
document.addEventListener('DOMNodeInserted', function(e){ | |
if(e.target.classList.contains('An')){ | |
addButton(e.target); | |
} | |
}); | |
$('.An').forEach(addButton); | |
function addButton(composeNode){ | |
var button = $('<div class="wG J-Z-I"><div class="J-J5-Ji J-Z-I-Kv-H"><div class="J-J5-Ji J-Z-I-J6-H"><div class="aA7 aaA aMZ"><img src="//composeIcon" /></div></div></div></div>'); | |
button.click(function(e){ |
View compose.js
/* | |
To inject a button into composes there's 5 main strategies an extension developer can use, each strategy has large tradeoffs | |
mainly around performance and ease of implementation. The result is that most extension developers opt for the | |
easy to implement but poor performing strategy. | |
*/ | |
/* | |
Strategy 1 |
NewerOlder