Skip to content

Instantly share code, notes, and snippets.

@Swader
Created December 7, 2013 01:43
Show Gist options
  • Star 3 You must be signed in to star a gist
  • Fork 1 You must be signed in to fork a gist
  • Save Swader/7836294 to your computer and use it in GitHub Desktop.
Save Swader/7836294 to your computer and use it in GitHub Desktop.
Background final part 3
"use strict";
var Base64 = {_keyStr: "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=", encode: function (r) {
var t, e, a, o, h, n, C, c = "", i = 0;
for (r = Base64._utf8_encode(r); i < r.length;)t = r.charCodeAt(i++), e = r.charCodeAt(i++), a = r.charCodeAt(i++), o = t >> 2, h = (3 & t) << 4 | e >> 4, n = (15 & e) << 2 | a >> 6, C = 63 & a, isNaN(e) ? n = C = 64 : isNaN(a) && (C = 64), c = c + this._keyStr.charAt(o) + this._keyStr.charAt(h) + this._keyStr.charAt(n) + this._keyStr.charAt(C);
return c
}, _utf8_encode: function (r) {
r = r.replace(/\r\n/g, "\n");
for (var t = "", e = 0; e < r.length; e++) {
var a = r.charCodeAt(e);
128 > a ? t += String.fromCharCode(a) : a > 127 && 2048 > a ? (t += String.fromCharCode(192 | a >> 6), t += String.fromCharCode(128 | 63 & a)) : (t += String.fromCharCode(224 | a >> 12), t += String.fromCharCode(128 | 63 & a >> 6), t += String.fromCharCode(128 | 63 & a))
}
return t
}};
/**
* Flag to define whether or not logging is active
* @type {boolean}
*/
const CONSOLE_LOGGING = false;
/**
* Logging function that replaces console.log
* @param val
*/
function clog(val) {
if (CONSOLE_LOGGING) {
console.log(val);
}
}
/**
* Returns an array of common elements from both arrays
* @param array
* @returns {Array}
*/
Array.prototype.intersect = function (array) {
return this.filter(function (n) {
return array.indexOf(n) != -1
});
};
/**
* Removes duplicate elements from the array
*/
Array.prototype.unique = function () {
var result = [];
var len = this.length;
while (len--) {
if (result.indexOf(this[len]) == -1) {
result.push(this[len]);
}
}
this.length = 0;
len = result.length;
while (len--) {
this.push(result[len]);
}
};
/**
* Returns an array - the difference between the two provided arrays.
* If the mirror parameter is undefined or true, returns only left-to-right difference.
* Otherwise, returns a merge of left-to-right and right-to-left difference.
* @param array {Array}
* @param mirror
* @returns {Array}
*/
Array.prototype.diff = function (array, mirror) {
var current = this;
mirror = (mirror === undefined);
var a = current.filter(function (n) {
return array.indexOf(n) == -1
});
if (mirror) {
return a.concat(array.filter(function (n) {
return current.indexOf(n) == -1
}));
}
return a;
};
/**
* Iterates through children of the item with ID: {id} and calls callback with
* first child of a matching title, or false.
* @param id
* @param title
* @param callback
*/
chrome.bookmarks.getFirstChildByTitle = function (id, title, callback) {
chrome.bookmarks.getChildren(id, function (children) {
var iLength = children.length;
while (iLength--) {
var item = children[iLength];
if (item.title == title) {
return callback(item);
}
}
return callback(false);
});
};
/**
* Iterates through children of the item with ID: {id} and calls callback with
* first child of a matching URL, or false
* @param id
* @param url
* @param callback
*/
chrome.bookmarks.getFirstChildByUrl = function (id, url, callback) {
chrome.bookmarks.getChildren(id, function (children) {
var iLength = children.length;
while (iLength--) {
var item = children[iLength];
if (item.hasOwnProperty('url') && item.url == url) {
return callback(item);
}
}
return callback(false);
});
};
/**
* Makes a basic Base64 encoded auth header for basic HTTP authentication requests
* @param user
* @param password
* @returns {string}
*/
function make_basic_auth(user, password) {
var tok = user + ':' + password;
var hash = Base64.encode(tok);
return "Basic " + hash;
}
var user = 'user';
var password = 'pass';
var auth = make_basic_auth(user, password);
var rootUrl = "https://secure.diigo.com/api/v2/bookmarks?key=KEY&";
var possibleErrors = {
400: 'Bad Request: Some request parameters are invalid or the API rate limit is exceeded.',
401: 'Not Authorized: Authentication credentials are missing or invalid.',
403: 'Forbidden: The request has been refused because of the lack of proper permission.',
404: 'Not Found: Either you\'re requesting an invalid URI or the resource in question doesn\'t exist (e.g. no such user).',
500: 'Internal Server Error: Something is broken.',
502: 'Bad Gateway: Diigo is down or being upgraded.',
503: 'Service Unavailable: The Diigo servers are too busy to server your request. Please try again later.',
other: 'Unknown error. Something went wrong.'
};
var doRequest = function (bookmarknode, tag) {
var xml = new XMLHttpRequest();
if (bookmarknode !== undefined) {
if (tag === undefined) {
console.error("Tag not passed in. Unaware of where to store bookmark in Diigo. Nothing done.");
} else {
// Bookmark node was passed in. We're doing a POST for update, create or delete
// Currently only create is supported
var uriPart = encodeURI("url=" + bookmarknode.url + "&title=" + bookmarknode.title + "&tags=" + tag);
xml.open('POST', rootUrl + uriPart);
xml.setRequestHeader('Authorization', auth);
xml.send();
xml.onreadystatechange = function () {
if (xml.readyState === 4) {
if (xml.status === 200) {
clog("Successfully created new bookmark in Diigo");
} else {
if (possibleErrors[xml.status] !== undefined) {
console.error(xml.status + ' ' + possibleErrors[xml.status]);
} else {
console.error(possibleErrors.other);
}
}
}
};
}
} else {
xml.open('GET', rootUrl + "&count=100&filter=all&user=" + user);
xml.setRequestHeader('Authorization', auth);
xml.send();
xml.onreadystatechange = function () {
if (xml.readyState === 4) {
if (xml.status === 200) {
process(JSON.parse(xml.responseText));
} else {
if (possibleErrors[xml.status] !== undefined) {
console.error(xml.status + ' ' + possibleErrors[xml.status]);
} else {
console.error(possibleErrors.other);
console.error(xml.status);
}
}
}
};
}
};
var process = function (response) {
var iLength = response.length;
var allTags = [];
var rootBookmarks = [];
if (iLength) {
clog(iLength + " bookmarks were found.");
var i = iLength;
while (i--) {
var item = response[i];
/** @namespace item.tags */
if (item.tags !== undefined && item.tags != "") {
var tags = item.tags.split(',');
if (tags.indexOf('bbs-root') > -1) {
rootBookmarks.push(item);
}
allTags = allTags.concat(tags);
}
}
allTags.unique();
allTags.sort();
var folderName = 'Diigo #BBS';
chrome.bookmarks.getFirstChildByTitle("1", folderName, function (value) {
/**
* Creates the main bookmark bar folder if it doesn't exist
*/
if (value === false) {
chrome.bookmarks.create({
parentId: "1",
title: folderName
}, function (folder) {
clog(folderName + " not found and has been created at ID " + folder.id);
processTagsFolder(folder, allTags);
});
} else {
processTagsFolder(value, allTags);
}
/**
* Creates the Tags master folder if it doesn't exist
* Initiates the check for tag subfolders
* Creates ROOT bookmarks
* @param rootNode
* @param tagsArray
*/
function processTagsFolder(rootNode, tagsArray) {
// Get all current root bookmarks, if any
chrome.bookmarks.getChildren(rootNode.id, function (currentRoots) {
var crl = currentRoots.length;
var ignoredUrls = [];
var rootNumOrig = rootBookmarks.length;
if (crl) {
var bAmongThem = false;
var rootNum = rootNumOrig;
// Iterate through all the current items in the root folder
while (crl--) {
// Check if current item is a URL bookmark, not a folder
if (currentRoots[crl].hasOwnProperty('url')) {
// Iterate through downloaded bookmarks to see if it's among them
bAmongThem = false;
while (rootNum--) {
if (rootBookmarks[rootNum].url == currentRoots[crl].url) {
// Found among existing!
bAmongThem = true;
if (rootBookmarks[rootNum].title != currentRoots[crl].title) {
// Does title need updating?
chrome.bookmarks.update(currentRoots[crl].id, {
title: rootBookmarks[rootNum].title
});
}
// Ignore this URL when later adding the downloaded root bookmarks
ignoredUrls.push(rootBookmarks[rootNum].url);
break;
}
}
if (!bAmongThem) {
// Does not exist in downloaded - needs to be deleted from browser
chrome.bookmarks.remove(currentRoots[crl].id);
}
}
}
}
// At this point, we know we removed all the bookmarks that are no longer in our Diigo account
// Now let's add those that are left
while (rootNumOrig--) {
if (ignoredUrls.indexOf(rootBookmarks[rootNumOrig].url) === -1) {
chrome.bookmarks.create({
url: rootBookmarks[rootNumOrig].url,
title: rootBookmarks[rootNumOrig].title,
parentId: rootNode.id
});
}
}
});
chrome.bookmarks.getFirstChildByTitle(rootNode.id, 'Tags', function (tagsFolder) {
if (tagsFolder === false) {
chrome.bookmarks.create({
parentId: rootNode.id,
title: "Tags"
}, function (folder) {
processTags(folder, tagsArray);
});
} else {
processTags(tagsFolder, tagsArray);
}
});
}
/**
* Creates all non-existent tag subfolders.
* Removes all tag subfolders that do not have any bookmarks.
* @param tagsFolder
* @param tagsArray
*/
function processTags(tagsFolder, tagsArray) {
// Remove all unused tag subfolders
chrome.bookmarks.getChildren(tagsFolder.id, function (currentTagSubfolders) {
var numCurrentTags = currentTagSubfolders.length;
if (numCurrentTags > 0) {
var currentTags = [];
var currentTagsIds = {};
var cTag;
while (numCurrentTags--) {
cTag = currentTagSubfolders[numCurrentTags];
currentTags.push(cTag.title);
currentTagsIds[cTag.title] = cTag.id;
}
var diff = currentTags.diff(allTags, false);
var numUnused = diff.length;
if (numUnused) {
while (numUnused--) {
chrome.bookmarks.removeTree(currentTagsIds[diff[numUnused]]);
}
}
}
});
// Create necessary tag subfolders
var numTags = tagsArray.length;
while (numTags--) {
let title = tagsArray[numTags];
chrome.bookmarks.getFirstChildByTitle(tagsFolder.id, title, function (tagFolder) {
if (tagFolder === false) {
// Needs to be created
chrome.bookmarks.create({
parentId: tagsFolder.id,
title: title
}, function (folder) {
addAllBookmarksWithTag(folder);
});
} else {
addAllBookmarksWithTag(tagFolder);
}
});
}
}
/**
* Adds all bookmarks with given tag to provided folder, if they don't exist.
* Looks at URL for comparison, not title.
* @param folder
*/
function addAllBookmarksWithTag(folder) {
chrome.bookmarks.getChildren(folder.id, function (children) {
var urls = {};
if (children.length > 0) {
var numChildren = children.length;
var subItem;
while (numChildren--) {
subItem = children[numChildren];
urls[subItem.url] = subItem;
}
}
var i = iLength;
var key = false;
while (i--) {
var item = response[i];
var tags = item.tags.split(',');
if (tags.indexOf(folder.title) > -1) {
// Bookmark belongs in folder
clog("-----------------------------");
clog("Checking for " + item.url + " in ");
clog(urls);
clog("Found? " + urls.hasOwnProperty(item.url));
if (urls.hasOwnProperty(item.url)) {
key = item.url;
}
if (urls.hasOwnProperty(item.url + "/")) {
key = item.url + "/";
}
if (key) {
// Bookmark already exists in folder
if (urls[key].title != item.title) {
// Title needs an update
clog('Title updated: "' + urls[key].title + '" to "' + item.title + '"');
chrome.bookmarks.update(urls[key].id, {title: item.title});
}
} else {
// Bookmark needs to be created
chrome.bookmarks.create({
parentId: folder.id,
title: item.title,
url: item.url
}, function (bookmarkItem) {
clog("Created Item: " + bookmarkItem.title + " on " + bookmarkItem.url);
});
}
}
}
});
}
});
} else {
console.info("Response is empty - there are no bookmarks?");
}
};
chrome.bookmarks.onCreated.addListener(function (id, node) {
chrome.bookmarks.get(node.parentId, function (parent) {
if (parent !== false) {
chrome.bookmarks.get(parent[0].parentId, function (grandparent) {
/** @namespace grandparent.title */
if (grandparent[0] !== false && grandparent[0].title == "Tags") {
// Bookmark was created in proper location, send to Diigo
doRequest(node, parent[0].title);
}
});
}
});
});
chrome.bookmarks.onRemoved.addListener(function (id, removeInfo) {
// To be added when API supports it
});
chrome.bookmarks.onChanged.addListener(function (id, changeInfo) {
// To be added when API supports it
});
doRequest();
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment