Add basic capabilities
stores and loads bookmarks from localStorage, import/export capabilities
This commit is contained in:
		
							parent
							
								
									bee398bcef
								
							
						
					
					
						commit
						5798bad50a
					
				
							
								
								
									
										34
									
								
								.gitignore
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										34
									
								
								.gitignore
									
									
									
									
										vendored
									
									
										Normal file
									
								
							|  | @ -0,0 +1,34 @@ | ||||||
|  | 
 | ||||||
|  | bootstrap/css/ | ||||||
|  | 
 | ||||||
|  | bootstrap/js/ | ||||||
|  | 
 | ||||||
|  | js/jquery-3\.2\.1\.js | ||||||
|  | 
 | ||||||
|  | js/jquery-3\.2\.1\.min\.js | ||||||
|  | 
 | ||||||
|  | js/jquery-3\.2\.1\.min\.map | ||||||
|  | 
 | ||||||
|  | js/popper\.js | ||||||
|  | 
 | ||||||
|  | img/0\.png | ||||||
|  | 
 | ||||||
|  | img/1\.png | ||||||
|  | 
 | ||||||
|  | img/2\.png | ||||||
|  | 
 | ||||||
|  | img/3\.png | ||||||
|  | 
 | ||||||
|  | img/4\.png | ||||||
|  | 
 | ||||||
|  | img/5\.png | ||||||
|  | 
 | ||||||
|  | img/6\.png | ||||||
|  | 
 | ||||||
|  | img/7\.png | ||||||
|  | 
 | ||||||
|  | img/8\.png | ||||||
|  | 
 | ||||||
|  | js/popper\.js\.map | ||||||
|  | 
 | ||||||
|  | bookmarkList\.json | ||||||
							
								
								
									
										0
									
								
								bootstrap/.gitkeep
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										0
									
								
								bootstrap/.gitkeep
									
									
									
									
									
										Normal file
									
								
							
							
								
								
									
										
											BIN
										
									
								
								favicon.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										
											BIN
										
									
								
								favicon.png
									
									
									
									
									
										Normal file
									
								
							
										
											Binary file not shown.
										
									
								
							| After Width: | Height: | Size: 2.9 KiB | 
							
								
								
									
										0
									
								
								img/.gitkeep
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										0
									
								
								img/.gitkeep
									
									
									
									
									
										Normal file
									
								
							
							
								
								
									
										177
									
								
								index.html
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										177
									
								
								index.html
									
									
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,177 @@ | ||||||
|  | <!doctype html> | ||||||
|  | <html lang="en"> | ||||||
|  | 
 | ||||||
|  | <head> | ||||||
|  | 	<meta charset="utf-8"> | ||||||
|  | 	<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no"> | ||||||
|  | 	<meta name="description" content="A new tab page"> | ||||||
|  | 	<meta name="author" content="Neil Brommer"> | ||||||
|  | 	<link rel="icon" href="favicon.png"> | ||||||
|  | 
 | ||||||
|  | 	<title>New Start</title> | ||||||
|  | 
 | ||||||
|  | 	<!-- Bootstrap core CSS --> | ||||||
|  | 	<link href="bootstrap/css/bootstrap.min.css" rel="stylesheet"> | ||||||
|  | 
 | ||||||
|  | 	<!-- Custom styles --> | ||||||
|  | 	<link href="main.css" rel="stylesheet"> | ||||||
|  | </head> | ||||||
|  | 
 | ||||||
|  | <body> | ||||||
|  | 
 | ||||||
|  | 	<nav class="navbar navbar-expand-md navbar-dark bg-dark mb-4"> | ||||||
|  | 		<!-- <a class="navbar-brand" href=".">Start</a >--> | ||||||
|  | 		<button class="navbar-toggler" type="button" data-toggle="collapse" data-target="#navbarCollapse" aria-controls="navbarCollapse" | ||||||
|  | 		 aria-expanded="false" aria-label="Toggle navigation"> | ||||||
|  | 			<span class="navbar-toggler-icon"></span> | ||||||
|  | 		</button> | ||||||
|  | 		<div class="collapse navbar-collapse" id="navbarCollapse"> | ||||||
|  | 			<ul class="navbar-nav mr-auto"> | ||||||
|  | 				<!-- Keep this to push elements to the right --> | ||||||
|  | 			</ul> | ||||||
|  | 			<form class="form-inline mt-2 mt-md-0"> | ||||||
|  | 				<button class="btn btn-light ml-sm-2" type="button" data-Toggle="modal" data-target="#aboutModal">About</button> | ||||||
|  | 				<button class="btn btn-light ml-sm-2" type="button" data-Toggle="modal" data-target="#importExportModal">Import/Export</button> | ||||||
|  | 				<button class="btn btn-light ml-sm-2" type="button">Edit</button> | ||||||
|  | 				<button class="btn btn-light ml-sm-2" type="button" data-toggle="modal" data-target="#newBookmarkModal">Add</button> | ||||||
|  | 			</form> | ||||||
|  | 		</div> | ||||||
|  | 	</nav> | ||||||
|  | 
 | ||||||
|  | 	<div id="wrapper" class="container"> | ||||||
|  | 		<div id="cardList" class="card-columns"> | ||||||
|  | 
 | ||||||
|  | 		</div> | ||||||
|  | 	</div> | ||||||
|  | 
 | ||||||
|  | 	<div id="newBookmarkModal" class="modal fade" tabindex="-1" role="dialog"> | ||||||
|  | 		<div class="modal-dialog" role="document"> | ||||||
|  | 			<div class="modal-content"> | ||||||
|  | 				<div class="modal-header"> | ||||||
|  | 					<h5 class="modal-title">Add Bookmark</h5> | ||||||
|  | 					<button type="button" class="close" data-dismiss="modal" aria-label="Close"> | ||||||
|  | 						<span aria-hidden="true">×</span> | ||||||
|  | 					</button> | ||||||
|  | 				</div> | ||||||
|  | 				<div class="modal-body"> | ||||||
|  | 					<div class="form-group"> | ||||||
|  | 						<label for="newBookmarkName">Name</label> | ||||||
|  | 						<input id="newBookmarkName" type="text" class="form-control"> | ||||||
|  | 					</div> | ||||||
|  | 					<div class="form-group"> | ||||||
|  | 						<label for="newBookmarkURL">Address</label> | ||||||
|  | 						<input id="newBookmarkURL" type="url" class="form-control"> | ||||||
|  | 					</div> | ||||||
|  | 					<div class="form-group"> | ||||||
|  | 						<label for="newBookmarkGroup">Group</label> | ||||||
|  | 						<select id="newBookmarkGroup" name="Group" class="form-control" onchange="selectGroupChanged(this.value)"> | ||||||
|  | 							<option value="-">New Group</option> | ||||||
|  | 						</select> | ||||||
|  | 					</div> | ||||||
|  | 					<div id="createGroup" class="form-group newGroup"> | ||||||
|  | 						<label for="newBookMarkGroupNew">New Group</label> | ||||||
|  | 						<input id="newBookMarkGroupNew" type="text" class="form-control"> | ||||||
|  | 					</div> | ||||||
|  | 				</div> | ||||||
|  | 				<div class="modal-footer"> | ||||||
|  | 					<button type="button" class="btn btn-primary">Add</button> | ||||||
|  | 					<button type="button" class="btn btn-secondary" data-dismiss="modal">Close</button> | ||||||
|  | 				</div> | ||||||
|  | 			</div> | ||||||
|  | 		</div> | ||||||
|  | 	</div> | ||||||
|  | 
 | ||||||
|  | 	<div id="importExportModal" class="modal fade" tabindex="-1" role="dialog"> | ||||||
|  | 		<div class="modal-dialog" role="document"> | ||||||
|  | 			<div class="modal-content"> | ||||||
|  | 				<div class="modal-header"> | ||||||
|  | 					<h5 class="modal-title">Import/Export</h5> | ||||||
|  | 					<button type="button" class="close" data-dismiss="modal" aria-label="Close"> | ||||||
|  | 						<span aria-hidden="true">×</span> | ||||||
|  | 					</button> | ||||||
|  | 				</div> | ||||||
|  | 				<div class="modal-body"> | ||||||
|  | 					<div class="form-group"> | ||||||
|  | 						<label for="exportText">Export</label> | ||||||
|  | 						<textarea id="exportText" class="form-control" rows="4"></textarea> | ||||||
|  | 					</div> | ||||||
|  | 					<div class="form-group"> | ||||||
|  | 						<label for="importText">Import</label> | ||||||
|  | 						<textarea id="importText" class="form-control" rows="4"></textarea> | ||||||
|  | 					</div> | ||||||
|  | 				</div> | ||||||
|  | 				<div class="modal-footer"> | ||||||
|  | 					<button type="button" id="btnImport" class="btn btn-primary">Import</button> | ||||||
|  | 					<button type="button" class="btn btn-secondary" data-dismiss="modal">Close</button> | ||||||
|  | 				</div> | ||||||
|  | 			</div> | ||||||
|  | 		</div> | ||||||
|  | 	</div> | ||||||
|  | 
 | ||||||
|  | 	<div id="aboutModal" class="modal fade" tabindex="-1" role="dialog"> | ||||||
|  | 		<div class="modal-dialog" role="document"> | ||||||
|  | 			<div class="modal-content"> | ||||||
|  | 				<div class="modal-header"> | ||||||
|  | 					<h5 class="modal-title">About</h5> | ||||||
|  | 					<button type="button" class="close" data-dismiss="modal" aria-label="Close"> | ||||||
|  | 						<span aria-hidden="true">×</span> | ||||||
|  | 					</button> | ||||||
|  | 				</div> | ||||||
|  | 				<div class="modal-body"> | ||||||
|  | 					<p> | ||||||
|  | 						<a href="https://github.com/NeilBrommer/NewTabPage">Project on GitHub</a> | ||||||
|  | 					</p> | ||||||
|  | 					<p> | ||||||
|  | 						This page keeps a list of bookmarks separated into groups. It's intended to be used a a new tab page. | ||||||
|  | 					</p> | ||||||
|  | 					<p> | ||||||
|  | 						It uses localStorage to store the list of bookmarks rather than storing them server side. | ||||||
|  | 					</p> | ||||||
|  | 					<p> | ||||||
|  | 						The format for the import/export JSON is: | ||||||
|  | 					</p> | ||||||
|  | 					<pre> | ||||||
|  | [ | ||||||
|  | 	{ | ||||||
|  | 		"title": "Group title", | ||||||
|  | 		"bookmarks": [ | ||||||
|  | 			{ | ||||||
|  | 				"name": "Bookmark 1", | ||||||
|  | 				"address": "http://example.com" | ||||||
|  | 			}, | ||||||
|  | 			{ | ||||||
|  | 				"name": "Bookmark 2", | ||||||
|  | 				"address": "http://example.net" | ||||||
|  | 			} | ||||||
|  | 		] | ||||||
|  | 	}, | ||||||
|  | 	{ | ||||||
|  | 		"title": "Group 2", | ||||||
|  | 		"bookmarks": [ | ||||||
|  | 			{ | ||||||
|  | 				"name": "Bookmark 3", | ||||||
|  | 				"address": "http://example.org" | ||||||
|  | 			} | ||||||
|  | 		] | ||||||
|  | 	} | ||||||
|  | ] | ||||||
|  | 					</pre> | ||||||
|  | 				</div> | ||||||
|  | 				<div class="modal-footer"> | ||||||
|  | 					<button type="button" class="btn btn-secondary" data-dismiss="modal">Close</button> | ||||||
|  | 				</div> | ||||||
|  | 			</div> | ||||||
|  | 		</div> | ||||||
|  | 	</div> | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | 	<!-- Bootstrap core JavaScript | ||||||
|  |     ================================================== --> | ||||||
|  | 	<!-- Placed at the end of the document so the pages load faster --> | ||||||
|  | 	<script src="js/jquery-3.2.1.min.js"></script> | ||||||
|  | 	<script src="js/popper.js"></script> | ||||||
|  | 	<script src="bootstrap/js/bootstrap.min.js"></script> | ||||||
|  | 	<script src="js/main.js"></script> | ||||||
|  | </body> | ||||||
|  | 
 | ||||||
|  | </html> | ||||||
							
								
								
									
										145
									
								
								js/main.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										145
									
								
								js/main.js
									
									
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,145 @@ | ||||||
|  | $(document).ready(function () { | ||||||
|  | 	calcBackground(); | ||||||
|  | 	loadBookmarks(); | ||||||
|  | 
 | ||||||
|  | 	$("#importExportModal").on("shown.bs.modal", function () { | ||||||
|  | 		var data = getListString(); | ||||||
|  | 		if (data != null) | ||||||
|  | 			$("#exportText").text(data); | ||||||
|  | 		else | ||||||
|  | 			$("#exportText").text("[]"); | ||||||
|  | 	}); | ||||||
|  | 
 | ||||||
|  | 	$("#newBookmarkModal").on("shown.bs.modal", function () { | ||||||
|  | 		var combo = $("#newBookmarkGroup"); | ||||||
|  | 		var data = getList(); | ||||||
|  | 		for (var i = 0; i < data.length; i++) { | ||||||
|  | 			combo.append($("<option></option>").attr({ "value": i }).text(data[i].title)); | ||||||
|  | 		} | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | 		// TODO complete adding bookmarks
 | ||||||
|  | 		window.alert("Adding bookmarks is still incomplete"); | ||||||
|  | 		$("#newBookmarkModal").modal("hide"); | ||||||
|  | 	}); | ||||||
|  | 
 | ||||||
|  | 	$("#btnImport").click(importBookmarks); | ||||||
|  | 	$("#exportText").click(function () { | ||||||
|  | 		$("#exportText").select(); | ||||||
|  | 	}); | ||||||
|  | }); | ||||||
|  | 
 | ||||||
|  | function importBookmarks() { | ||||||
|  | 	try { | ||||||
|  | 		var newData = $.parseJSON($("#importText").val()); | ||||||
|  | 	} catch (err) { | ||||||
|  | 		console.error("Import failed: " + err.message); | ||||||
|  | 		window.alert("Failed to import"); | ||||||
|  | 		return; | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	setList(newData); | ||||||
|  | 
 | ||||||
|  | 	$(".bookmarkGroup").remove(); | ||||||
|  | 	loadBookmarks(); | ||||||
|  | 	$("#importExportModal").modal("hide"); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | function loadBookmarks() { | ||||||
|  | 	var groups = getList(); | ||||||
|  | 
 | ||||||
|  | 	if (groups != null) { | ||||||
|  | 		for (var i = 0; i < groups.length; i++) { | ||||||
|  | 			var item = groups[i]; | ||||||
|  | 			buildCard(item.title, item.bookmarks); | ||||||
|  | 		} | ||||||
|  | 	} else { | ||||||
|  | 		$("#aboutModal").modal("show"); | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | function buildCard(title, itemList) { | ||||||
|  | 	var card = $(document.createElement("div")); | ||||||
|  | 	card.attr({ "id": "group-" + title, "class": "card bookmarkGroup" }); | ||||||
|  | 
 | ||||||
|  | 	var cardHead = $(document.createElement("div")); | ||||||
|  | 	cardHead.attr({ "class": "card-header" }); | ||||||
|  | 	cardHead.text(title); | ||||||
|  | 	card.append(cardHead); | ||||||
|  | 
 | ||||||
|  | 	var cardList = $(document.createElement("div")); | ||||||
|  | 	cardList.attr({ "class": "list-group list-group-flush" }); | ||||||
|  | 	card.append(cardList); | ||||||
|  | 
 | ||||||
|  | 	for (var i = 0; i < itemList.length; i++) { | ||||||
|  | 		var item = itemList[i]; | ||||||
|  | 		var link = $(document.createElement("a")); | ||||||
|  | 		link.attr({ | ||||||
|  | 			"class": "list-group-item list-group-item-action", | ||||||
|  | 			"href": item.address | ||||||
|  | 		}); | ||||||
|  | 		link.text(item.name); | ||||||
|  | 
 | ||||||
|  | 		cardList.append(link); | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	$("#cardList").append(card); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | function selectGroupChanged(value) { | ||||||
|  | 	if (value == "-") | ||||||
|  | 		$("#createGroup").show(); | ||||||
|  | 	else | ||||||
|  | 		$("#createGroup").hide(); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | function calcBackground() { | ||||||
|  | 	var now = new Date(); | ||||||
|  | 	var hours = now.getHours(); | ||||||
|  | 	var mins = now.getMinutes(); | ||||||
|  | 	var total = (hours * 60) + mins; | ||||||
|  | 
 | ||||||
|  | 	// 24*60 = 1440
 | ||||||
|  | 	// 2:40 between each step
 | ||||||
|  | 	if (total > 300 && total < 460) // 5:00 - 7:40
 | ||||||
|  | 		setBackground(1, false); | ||||||
|  | 	else if (total > 460 && total < 620) | ||||||
|  | 		setBackground(2, false); | ||||||
|  | 	else if (total > 620 && total < 780) | ||||||
|  | 		setBackground(3, false); | ||||||
|  | 	else if (total > 780 && total < 940) | ||||||
|  | 		setBackground(4, false); | ||||||
|  | 	else if (total > 940 && total < 1100) | ||||||
|  | 		setBackground(5, false); | ||||||
|  | 	else if (total > 1100 && total < 1260) | ||||||
|  | 		setBackground(6, true); | ||||||
|  | 	else if (total > 1260 && total < 1420) | ||||||
|  | 		setBackground(7, true); | ||||||
|  | 	else | ||||||
|  | 		setBackground(8, true); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | function setBackground(num, dark) { | ||||||
|  | 	$("body").css("background-image", "url(img/" + num + ".png)"); | ||||||
|  | 	if (dark) | ||||||
|  | 		$(".btn-light").removeClass("btn-light").addClass("btn-dark"); | ||||||
|  | 	else | ||||||
|  | 		$(".btn-dark").removeClass("btn-dark").addClass("btn-light"); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | function getList() { | ||||||
|  | 	return $.parseJSON(window.localStorage.getItem("bookmarks")); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | function getListString() { | ||||||
|  | 	return window.localStorage.getItem("bookmarks"); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | 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); | ||||||
|  | 	} | ||||||
|  | } | ||||||
							
								
								
									
										68
									
								
								main.css
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										68
									
								
								main.css
									
									
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,68 @@ | ||||||
|  | body { | ||||||
|  | 	width: 100%; | ||||||
|  | 	height: 100%; | ||||||
|  | 	background: white no-repeat center center fixed; | ||||||
|  |     background-size: cover; | ||||||
|  | 
 | ||||||
|  |     tab-size: 4; | ||||||
|  |     -moz-tab-size: 4; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | #wrapper { | ||||||
|  | 	width: 60%; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | .card-header { | ||||||
|  | 	background-color: #FEA63C; | ||||||
|  | 	color: white; | ||||||
|  | 	font-size: 16px; | ||||||
|  | 	font-weight: 600; | ||||||
|  | 	height: 32px; | ||||||
|  | 	padding-top: 4px; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | .list-group-item { | ||||||
|  | 	height: 32px; | ||||||
|  | 	padding-top: 4px; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | .card { | ||||||
|  |     border: 0px; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | .navbar { | ||||||
|  |     background: transparent !important; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | /* change the number of columns in card-columns */ | ||||||
|  | @media (min-width: 34em) { | ||||||
|  |     .card-columns { | ||||||
|  |         -webkit-column-count: 1; | ||||||
|  |         -moz-column-count: 1; | ||||||
|  |         column-count: 1; | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | @media (min-width: 48em) { | ||||||
|  |     .card-columns { | ||||||
|  |         -webkit-column-count: 1; | ||||||
|  |         -moz-column-count: 1; | ||||||
|  |         column-count: 1; | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | @media (min-width: 62em) { | ||||||
|  |     .card-columns { | ||||||
|  |         -webkit-column-count: 2; | ||||||
|  |         -moz-column-count: 2; | ||||||
|  |         column-count: 2; | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | @media (min-width: 75em) { | ||||||
|  |     .card-columns { | ||||||
|  |         -webkit-column-count: 2; | ||||||
|  |         -moz-column-count: 2; | ||||||
|  |         column-count: 2; | ||||||
|  |     } | ||||||
|  | } | ||||||
		Loading…
	
		Reference in a new issue