Skip to content

Commit

Permalink
Merge pull request #1 from seeba8/feature_browser_action
Browse files Browse the repository at this point in the history
  • Loading branch information
Sebastian authored May 23, 2017
2 parents 1820977 + 2026c4f commit f3470ef
Show file tree
Hide file tree
Showing 13 changed files with 614 additions and 126 deletions.
20 changes: 13 additions & 7 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,15 +1,21 @@
# pinboard-omnibox
# Yet Another Pinboard Extension
Shows your [Pinboard](https://pinboard.in) bookmarks in the omnibar suggestions if "pin" is prepended.
Also, allows you to view, open and edit your bookmarks in your browser.

Current version: Haven't started counting yet.
Written in pure Javascript without external dependencies. Does not send any data anywhere, as far as I know (except to pinboard, of course).

Current version: 1.0

### Instructions
* To connect the add-on with your pinboard account, enter your API key in the settings page (about:addons). Do *not* enter your main password! The key will be stored in the add-on's local browser storage, and I have no idea if other add-ons / users / whoever can access that.
* To connect the add-on with your pinboard account, enter your API key in the options page (about:addons). Do *not* enter your main password! The key will be stored in the add-on's local browser storage, and I have no idea if other add-ons / users / whoever can access that.
* The API key can be found on https://pinboard.in/settings/password.

### Future plans
* Refine search to only tags, description or URL using another prefix after "pin", for example `pin u github` would look for entries where the URL contains `github`.
* Start searching only after 3 (?) characters.
* You can search through your bookmarks, create new ones and edit old ones via the button in the Action Menu (add it to the bar if it is not there)

### Thanks
Thanks to [lostsnow](https://github.com/lostsnow/pinboard-firefox) for the cool addon and for the bug motivating me to look into WebExtensions.

### Plans for the future
* Enable deleting of bookmarks
* Probably a lot of bugfixes
* Adapt it for Google Chrome, maybe?
* ...
223 changes: 142 additions & 81 deletions background.js
Original file line number Diff line number Diff line change
@@ -1,82 +1,96 @@
var pins = [];
var pins;
var options = {};

// Listeners
browser.runtime.onStartup.addListener(handleStartup);

//browser.runtime.onStartup.addListener(handleStartup);
browser.runtime.onInstalled.addListener(handleAddonInstalled);
browser.runtime.onMessage.addListener(handleMessage);
browser.storage.onChanged.addListener(handleStorageChanged);
browser.omnibox.onInputStarted.addListener(() => {
console.log(pins);
});
browser.omnibox.onInputChanged.addListener(handleInputChanged);
browser.omnibox.onInputEntered.addListener(handleInputEntered);
browser.tabs.onUpdated.addListener(handleTabUpdated);

// Provide help text to the user.
browser.omnibox.setDefaultSuggestion({
description: `Search your pinboard bookmarks`
description: `Search your pinboard bookmarks`
});

function handleAddonInstalled(){
handleStartup();

function handleAddonInstalled() {
options = {
"urlPrefix": "u",
"tagPrefix": "t",
"titlePrefix": "n",
"toReadPrefix": "r",
"showBookmarked": true
"urlPrefix": "u",
"tagPrefix": "t",
"titlePrefix": "n",
"toReadPrefix": "r",
"showBookmarked": true,
"changeActionbarIcon": true
};
browser.storage.local.set({"options": options});
browser.storage.local.set({ "options": options });
browser.storage.local.get(null).then((res) => {
if(!!res.apikey && res.pins.length == 0){
if (!!res.apikey && res.pins.size == 0) {
updatePinData();
}
else if(!!res.pins && res.pins.length > 0){
else if (!!res.pins && res.pins.size > 0) {
updatePinVariable();
}

})

}
// Update the pins on startup of the browser
function handleStartup(){
function handleStartup() {
updatePinData();
loadOptions();
updatePinVariable();
}

function loadOptions(){
browser.storage.local.get("options").then((res)=>{
function loadOptions() {
browser.storage.local.get("options").then((res) => {
options = res.options;
if (options.changeActionbarIcon) {
browser.browserAction.setIcon({
path: {
16: "img/pinboard-icon-grey.svg",
32: "img/pinboard-icon-grey.svg"
}
});
}
else {
browser.browserAction.setIcon({
path: {
16: "img/pinboard-icon.svg",
32: "img/pinboard-icon.svg"
}
});
}
});
}

// Only update pin data when the api key was modified
function handleStorageChanged(changes, area){
console.log(changes);
if(Object.keys(changes).includes("apikey")){
function handleStorageChanged(changes, area) {
if (Object.keys(changes).includes("apikey")) {
updatePinData();
}
else if(Object.keys(changes).includes("pins")){
else if (Object.keys(changes).includes("pins")) {
updatePinVariable();
}
else if(Object.keys(changes).includes("options")){
else if (Object.keys(changes).includes("options")) {
loadOptions();
}
}

function updatePinVariable(){
function updatePinVariable() {
browser.storage.local.get("pins").then((res) => {
pins = res["pins"];
console.log("Updated pin variable");
pins = new Map(res["pins"]);
});
}

function isUpdateAvailable(){
function isUpdateAvailable() {
browser.storage.local.get(["apikey", "lastsync"]).then((token) => {
let headers = new Headers({"Accept": "application/json"});
let init = {method: 'GET', headers};
let request = new Request("https://api.pinboard.in/v1/posts/update?auth_token="+token.apikey+"&format=json", init);
fetch(request).then((response) =>{
let headers = new Headers({ "Accept": "application/json" });
let init = { method: 'GET', headers };
let request = new Request("https://api.pinboard.in/v1/posts/update?auth_token=" + token.apikey + "&format=json", init);
fetch(request).then((response) => {
response.json().then((json) => {
return (Date(json.update_time) > token.lastsync);
})
Expand All @@ -86,112 +100,128 @@ function isUpdateAvailable(){

// Reloads all bookmarks from pinboard. Should be optimized to get a delta...
// Should listen to return codes
function updatePinData(){
function updatePinData() {
browser.storage.local.get(["apikey", "lastsync", "pins"]).then((token) => {
if(!token.apikey || token.apikey == "" || (!!token.lastsync && new Date(token.lastsync) > Date.now() - 1000*60*10)){
if (!token.apikey || token.apikey == "" || (!!token.lastsync && new Date(token.lastsync) > Date.now() - 1000 * 60 * 10)) {
console.log("Not syncing, either no API key or last sync less than 10 minutes ago.");
updatePinVariable();
return;
}

if(!!token.pins && token.pins.length > 0 && !!token.lastsync && !isUpdateAvailable()){
//pins.length, because we are in the token, where the pins are stored as Array, not Map
if (!!token.pins && token.pins.length > 0 && !!token.lastsync && !isUpdateAvailable()) {
console.log("Not syncing, no update available");
updatePinVariable();
return;
}
let request = null;
let headers = new Headers({"Accept": "application/json"});
let init = {method: 'GET', headers};
if(!token.lastsync || token.pins.length == 0){
request = new Request("https://api.pinboard.in/v1/posts/all?auth_token="+token.apikey+"&format=json", init);
let headers = new Headers({ "Accept": "application/json" });
let init = { method: 'GET', headers };
//pins.length, because we are in the token, where the pins are stored as Array, not Map
if (!token.lastsync || token.pins.length == 0) {
request = new Request("https://api.pinboard.in/v1/posts/all?auth_token=" + token.apikey + "&format=json", init);
console.log("Loading pins from scratch!");
}
else {
request = new Request("https://api.pinboard.in/v1/posts/all?auth_token="+token.apikey+"&format=json&fromdt="+
new Date(token.lastsync).toISOString(), init);
request = new Request("https://api.pinboard.in/v1/posts/all?auth_token=" + token.apikey + "&format=json&fromdt=" +
new Date(token.lastsync).toISOString(), init);
}
browser.storage.local.set({lastsync:Date.now()});
browser.storage.local.set({ lastsync: Date.now() });
fetch(request).then((response) => {
response.json().then((json) => {
browser.storage.local.set({pins: json});
let pinsMap = new Map();
json.forEach((pin) => {
pinsMap.set(pin.href, {
href: pin.href,
description: pin.description,
tags: pin.tags,
time: pin.time,
toread: pin.toread
});
});
browser.storage.local.set({ pins: Array.from(pinsMap.entries()) });
console.log("Sync successful, pins updated");
});
});
});
}

// Update the suggestions whenever the input is changed.
function handleInputChanged(text, addSuggestions){
/* const toReadRegex = new Regex("(^\w\s)?"+options.toReadPrefix+"\w?\s.*","gm");
text = text.toLowerCase();
let toReadPrefix = text.search(toReadRegex);
*/
function handleInputChanged(text, addSuggestions) {
/* const toReadRegex = new Regex("(^\w\s)?"+options.toReadPrefix+"\w?\s.*","gm");
text = text.toLowerCase();
let toReadPrefix = text.search(toReadRegex);
*/
let searchArea = [];
let hasPrefix = false;
let toRead = false;
if(text.startsWith(options.tagPrefix + " ")){
if (text.startsWith(options.tagPrefix + " ")) {
searchArea.push("tags");
hasPrefix = true;
hasPrefix = true;
}
else if(text.startsWith(options.urlPrefix + " ")){
else if (text.startsWith(options.urlPrefix + " ")) {
searchArea.push("href");
hasPrefix = true;
}
else if(text.startsWith(options.titlePrefix + " ")){
else if (text.startsWith(options.titlePrefix + " ")) {
searchArea.push("description");
hasPrefix = true;
}
else {
searchArea = ["tags", "href", "description"];
}
if(text.startsWith(options.toReadPrefix + " ")){
if (text.startsWith(options.toReadPrefix + " ")) {
hasPrefix = true;
toRead = true;
}
if(hasPrefix){
text = text.slice(text.indexOf(" ")+1);
if (hasPrefix) {
text = text.slice(text.indexOf(" ") + 1);
}
console.log("Searching for: "+text);
let selectedPins = [];
pins.forEach((pin) => {
for (var [key, pin] of pins) {
searchArea.forEach((filter) => {
if(pin[filter].toLowerCase().includes(text)){
if(!toRead || pin["toread"]=="yes"){
if (pin[filter].toLowerCase().includes(text)) {
if (!toRead || pin["toread"] == "yes") {
selectedPins.push(pin);
}
}
});
});
createSuggestions(selectedPins,text).then(addSuggestions);
}
createSuggestions(selectedPins, text).then(addSuggestions);
}

// Open the page based on how the user clicks on a suggestion.
function handleInputEntered(text, disposition){
function handleInputEntered(text, disposition) {
let url = text;
const regex = /^(http:\/\/|https:\/\/|ftp:|mailto:|file:|javascript:|feed:).+$/iu;
let m;
if ((m = regex.exec(text)) === null) {
url = "https:\/\/pinboard.in/search/?query=" + encodeURIComponent(url) + "&mine=Search+Mine";
}
switch (disposition) {
case "currentTab":
browser.tabs.update({url});
browser.tabs.update({ url });
break;
case "newForegroundTab":
browser.tabs.create({url});
browser.tabs.create({ url });
break;
case "newBackgroundTab":
browser.tabs.create({url, active: false});
browser.tabs.create({ url, active: false });
break;
}
}
}

//Create the array with the searchbar suggestions
function createSuggestions(pins, searchtext){
function createSuggestions(pins, searchtext) {
return new Promise(resolve => {
let suggestions = []
let suggestionsOnEmptyResults = [{
content: "https://pinboard.in/search/?query="+encodeURIComponent(searchtext),
content: "https://pinboard.in/search/?query=" + encodeURIComponent(searchtext),
description: "No results found, go to Pinboard search"
}];
if(!pins || pins.length == 0){
if (!pins || pins.size == 0) {
return resolve(suggestionsOnEmptyResults);
}
pins.forEach(function(pin){
pins.forEach(function (pin) {
suggestions.push({
content: pin.href,
description: pin.description
Expand All @@ -201,17 +231,48 @@ function createSuggestions(pins, searchtext){
})
}

function handleTabUpdated(tabId, changeInfo, tab){
if(!options.showBookmarked){
function checkDisplayBookmarked(url, tabId) {
if (pins.has(url)) {
if (options.showBookmarked) {
browser.pageAction.show(tabId);
}
if (options.changeActionbarIcon) {
browser.browserAction.setIcon({
path: {
16: "img/pinboard-icon-blue.svg",
32: "img/pinboard-icon-blue.svg"
},
tabId: tabId
});
}
}
else {
if (options.changeActionbarIcon) {
browser.browserAction.setIcon({
path: {
16: "img/pinboard-icon-grey.svg",
32: "img/pinboard-icon-grey.svg"
},
tabId: tabId
});
}
}
}

function handleTabUpdated(tabId, changeInfo, tab) {
if (!options.showBookmarked && !options.changeActionbarIcon) {
return;
}
console.log(options);
if(changeInfo.status == "complete"){
console.log("looking for a fitting bookmark");
pins.forEach((pin) => {
if(pin.href == tab.url){
browser.pageAction.show(tab.id);
}
if (changeInfo.status == "loading") {
checkDisplayBookmarked(tab.url, tabId);
}
}

function handleMessage(request, sender, sendResponse) {
if (request.callFunction == "checkDisplayBookmarked" && !!request.url) {
browser.tabs.query({ active: true }).then((tab) => {
tab = tab[0];
checkDisplayBookmarked(request.url, tab.id);
});
}
}
Binary file removed img/pinboard-19.png
Binary file not shown.
Binary file removed img/pinboard-38.png
Binary file not shown.
5 changes: 5 additions & 0 deletions img/pinboard-icon-blue.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
5 changes: 5 additions & 0 deletions img/pinboard-icon-grey.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading

0 comments on commit f3470ef

Please sign in to comment.