在第1部分中 ,我们介绍了一些新概念,并构建了扩展的框架版本,可以进行安装和测试。 然后, 第2部分将带我们通过一些辅助方法和错误处理,以及解析从Diigo中获得的结果并过滤出唯一标签。
在本系列的第3部分中,我们将使用迄今为止完成的所有工作来编写扩展主体。
制备
我清理了之前部分中制作的background.js
文件,因此继续并从Github中获取其内容。 它本质上是相同的,只是重新格式化并稍微进行了重组。
书签事件的侦听器
我们要做的第一件事是为书签事件添加一些侦听器。 具体来说,当书签创建,更改或删除发生时,我们希望Diigo知道它。
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
});
底部的两个侦听器只是占位符,因为Diigo尚不支持此功能。 我听说他们的API即将升级,因此我们还是把它们放在那里。
onCreated
侦听器首先检查所创建的书签节点是否具有父节点。 如果是的话,它将检查该父母的父母的名字-如果该名字是“ Tags”,我们知道我们拥有正确的文件夹,我们需要提交给Diigo。 现在,此功能假定您没有其他以“标签”为祖父母的双亲书签,但是从理论上讲,这可能会发生。 要对此进行检查,我们需要为Diigo主文件夹添加另一个父级检查,但我将留给您做家庭作业。
然后,我们使用两个参数调用doRequest
:创建的实际书签节点以及在其中创建的标签文件夹的名称。显然,我们需要此数据来告诉Diigo创建哪个书签以及提供哪个标签。 但是为什么doRequest
? 这不是我们的“ GET”功能吗? 是的-但您很快就会看到,我们将对其进行修改,以便它可以处理我们扩展的POST和GET操作。
接下来,我们需要将这些参数添加到我们的doRequest
函数中,并使它们对它们的存在或不存在做出反应,如下所示:
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
!== undefined) {
console.error(xml.status + ' ' + possibleErrors);
} 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!== undefined) {
console.error(xml.status + ' ' + possibleErrors);
} else {
console.error(possibleErrors.other);
console.error(xml.status);
}
}
}
};
}
};If the
bookmarknode
andtag
params are provided and valid, we execute the XHR request almost the same way as we did the original one for GETting the bookmarks, with one crucial difference – this time, we make it a POST request and add the title, tag, and bookmark name into the URL. That's all that's needed – now Diigo can accept our POST request and react accordingly. This is the beauty of RESTful API design.Root bookmarks
Now let's save all the BBS-root bookmarks. We already have them in an array from the initial looping in the
process
function, but we don't do anything with them. Let's change that.In Part 2, we made sure the "Diigo #BBS" folder exists. Once we're certain that it does, we can initiate the creation of the root bookmarks – they have a home we can put them in at that point.
Rewrite the part of the
process
function from this:var folderName = 'Diigo #BBS'; chrome.bookmarks.getFirstChildByTitle("1", folderName, function(value) { if (value === false) { chrome.bookmarks.create({ parentId: "1", title: folderName }, function (folder) { console.log(folderName + " not found and has been created at ID " + folder.id); }); } });
to
var folderName = 'Diigo #BBS'; chrome.bookmarks.getFirstChildByTitle("1", folderName, function(value) { 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); } });
As you can see, we added a new call to a
processTagsFolder
function. This function gets the "Diigo #BBS" folder passed in as the first parameter, and the array of all tags as the second. Seeing as this method executes either way – whether the "Diigo #BBS" folder pre-existed or not, we can put our root-bookmark-creation logic inside it./** * 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 }); } } }); }
In short, what we do here is fetch all the current root bookmarks, see if they're among the freshly downloaded ones and delete them if they're not (that means they've been untagged as bbs-root in Diigo), and finally, we add all the others. If you try it out, this should work wonderfully.
We also need to create the Tags folder if it doesn't exist. Add the following code right under the last bit:
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); } });
Obviously, we've made another function that gets called regardless of whether or not the Tags folder pre-existed. Let's define
processTags
.Processing Tags
/** * 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
]);
}
}
}
});// 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);
}
});
}
}The above function filters out the difference between the
AllTags
array (the list of tags we fetched fresh from Diigo and made unique) and the tag sub folders currently present in the "Tags" folder. This difference represents those folders in the Chrome Bookmark Bar which no longer have any members in the user's Diigo library. Thus, these folders are removed from Chrome.After the cleanup is done, the function iterates through the list of tags most recently downloaded from Diigo, and creates those sub folders, after which the
addAllBookmarksWithTag
function is called.Adding bookmarks to a tag subfolder
/** * 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 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); }); } } } }); }
Finally, we add the bookmarks to their respective tag folders. We first build an object containing current bookmark URLs from each folder as keys, and the bookmark nodes themselves as values.
The function iterates through the original result set, splits the tags and checks if the bookmark it's currently dealing with belongs into the current folder. The "/" trick is due to Diigo sometimes pushing random slashes onto URLs. We'll handle this in a followup "Optimizations" article. If the bookmark belongs into the folder, and is already inside, the function checks if the bookmark title needs updating. If so, it updates it. If the bookmark doesn't exist in the folder, and it should, then it is created.
Conclusion
At long last, we built the most of our extension. There's still some quirks to iron out, but most of the work is done. You can download the final version of
background.js
from Github .In Part 4, we'll focus on letting people log into the extension and use their own account, we'll allow adding of custom API keys in case of quota problems, and we'll optimize our code a bit. Stay tuned!
From: https://www.sitepoint.com/creating-chrome-extension-diigo-part-3/