").attr({ "value": i }).text(data[i].title));
}
@@ -36,27 +39,88 @@ function importBookmarks() {
var newData = $.parseJSON($("#importText").val());
} catch (err) {
console.error("Import failed: " + err.message);
- window.alert("Failed to import");
+ window.alert("Invalid Format");
return;
}
- setList(newData);
+ if (verifyBookmarks(newData)) {
+ setList(newData);
- $(".bookmarkGroup").remove();
- loadBookmarks();
- $("#importExportModal").modal("hide");
+ $("#importExportModal").modal("hide");
+ } else {
+ window.alert("Invalid Format");
+ }
}
function loadBookmarks() {
- var groups = getList();
+ $("#cardList").empty();
+ var openDBRequest = window.indexedDB.open("bookmarks");
- if (groups != null) {
- for (var i = 0; i < groups.length; i++) {
- var item = groups[i];
- buildCard(item.title, item.bookmarks);
+ openDBRequest.onsuccess = function (dbEvt) {
+ console.log("Opened database");
+ db = dbEvt.target.result;
+ bookmarkList = [];
+
+ db.transaction(["groupIndexes"], "readonly")
+ .objectStore("groupIndexes")
+ .getAll()
+ .onsuccess = function (indexEvt) {
+ var indexes = indexEvt.target.result;
+
+ // use a placholder because getting the group info is async
+ // and groups could finish loading in a different order
+ var cardList = $("#cardList");
+ for (var i = 0; i < indexes.length; i++) {
+ var placeholder = $("").attr("id", "group-" + i).appendTo(cardList);
+ buildGroup(indexes[i], placeholder);
+ }
+
+ db.close();
+ }
+ ;
+ }
+
+ openDBRequest.onerror = function (e) { console.log(e); }
+
+ openDBRequest.onupgradeneeded = function (e) {
+ // the database doesn't exist
+ console.log("Creating database");
+ db = e.target.result;
+
+ var data = window.localStorage.getItem("bookmarks");
+ if (data != null) {
+ console.log("Importing data from old version");
+ data = JSON.parse(data);
+ db.close();
+ setList(data);
+ window.localStorage.removeItem("bookmarks");
+ } else {
+ var groupStore = initDB(db);
+
+ // add example bookmarks
+ var exBookmarks = db.createObjectStore("Examples");
+ exBookmarks.createIndex("name", "name", { unique: false });
+ exBookmarks.createIndex("address", "address", { unique: false });
+ groupStore.add({ "title": "Examples", "groupIndex": 0 });
+
+ exBookmarks.add({ "name": "Github", "address": "https://github.com/" }, 0);
+ exBookmarks.add({ "name": "This project on Github", "address": "https://github.com/NeilBrommer/NewTabPage" }, 1);
+ exBookmarks.add({ "name": "Hacker News", "address": "https://news.ycombinator.com/" }, 2);
+ exBookmarks.add({ "name": "reddit", "address": "https://www.reddit.com/" }, 3);
}
- } else {
- $("#aboutModal").modal("show");
+ }
+}
+
+function buildGroup(groupInfo, placeholder) {
+ var groupTransaction = db.transaction([groupInfo.title], "readonly");
+ var groupStore = groupTransaction.objectStore(groupInfo.title);
+ var groupRequest = groupStore.getAll();
+ groupRequest.onsuccess = function (e) {
+ var bookmarks = e.target.result;
+
+ bookmarkList[groupInfo.groupIndex] = { "title": groupInfo.title, "bookmarks": bookmarks };
+
+ buildCard(groupInfo.title, bookmarks).appendTo(placeholder);
}
}
@@ -85,7 +149,7 @@ function buildCard(title, itemList) {
cardList.append(link);
}
- $("#cardList").append(card);
+ return card;
}
function selectGroupChanged(value) {
@@ -129,19 +193,92 @@ function setBackground(num, dark) {
$(".btn-dark").removeClass("btn-dark").addClass("btn-light");
}
-function getList() {
- return $.parseJSON(window.localStorage.getItem("bookmarks"));
-}
-
function getListString() {
- return window.localStorage.getItem("bookmarks");
+ return JSON.stringify(bookmarkList, null, 4);
}
-function setList(list) {
- if (typeof list == "string") {
- window.localStorage.setItem("bookmarks", list);
- } else {
- var stringified = JSON.stringify(list, null, 4);
- window.localStorage.setItem("bookmarks", stringified);
+function setList(data) {
+ // empty the DB and fill it with the new data
+ bookmarkList = data;
+
+ try {
+ indexedDB.deleteDatabase("bookmarks");
+ } catch (err) {
+ // it's OK if the DB doesn't exist
+ if (err.name != "NotFoundError") {
+ console.error(err);
+ return;
+ }
+ }
+ var openDBRequest = window.indexedDB.open("bookmarks", 1);
+
+ openDBRequest.onsuccess = function (e) {
+ db.close();
+ loadBookmarks();
+ }
+
+ openDBRequest.onerror = function (err) { console.error(err); }
+
+ openDBRequest.onupgradeneeded = function (e) {
+ console.log("filling db");
+ db = e.target.result;
+
+ var groupStore = initDB(db);
+
+ // create the object stores
+ for (var i = 0; i < data.length; i++) {
+ addGroup(data[i], groupStore, i);
+ }
}
}
+
+function initDB(db) {
+ // use a table to keep track of the order of groups
+ var groupStore = db.createObjectStore("groupIndexes", { keyPath: "title" });
+ groupStore.createIndex("title", "groupName", { unique: true });
+ groupStore.createIndex("groupIndex", "groupIndex", { unique: true });
+ return groupStore;
+}
+
+function addGroup(group, groupStore, index) {
+ var objStore = db.createObjectStore(group.title);
+ objStore.createIndex("name", "name", { unique: false });
+ objStore.createIndex("address", "address", { unique: false });
+ groupStore.add({ "title": group.title, "groupIndex": index });
+
+ var bookmarks = group.bookmarks;
+ for (var i = 0; i < bookmarks.length; i++) {
+ var bkmk = bookmarks[i];
+ objStore.add({ "name": bkmk.name, "address": bkmk.address }, i);
+ }
+}
+
+function verifyBookmarks(bookmarks) {
+ if (!Array.isArray(bookmarks))
+ return false;
+
+ for (var i = 0; i < bookmarks.length; i++) {
+ var item = bookmarks[i];
+
+ if (item == null || typeof item != "object")
+ return false;
+
+ if (item.title == null || typeof item.title != "string")
+ return false;
+
+ for (var j = 0; j < item.bookmarks.length; j++) {
+ var bkmk = item.bookmarks[j];
+
+ if (bkmk == null || typeof bkmk != "object")
+ return false;
+
+ if (bkmk.name == null || typeof bkmk.name != "string")
+ return false;
+
+ if (bkmk.address == null || typeof bkmk.address != "string")
+ return false;
+ }
+ }
+
+ return true;
+}