From 858e402df4e8b82179014c390a2f00d29832d545 Mon Sep 17 00:00:00 2001 From: Danilo Novakovic Date: Sat, 20 Oct 2018 21:17:31 +0200 Subject: [PATCH 1/5] utils now use dynBookmarks lib > small bugfix --- package.json | 2 +- src/js/bookmarkManager/treeView.js | 8 -------- src/js/popup/index.js | 3 +-- src/js/utils/bookmarkInfo.js | 5 +++-- src/js/utils/folderInfo.js | 6 ++++-- src/js/utils/treeView.js | 6 ++++-- src/manifest.json | 2 +- 7 files changed, 14 insertions(+), 18 deletions(-) diff --git a/package.json b/package.json index 3c9b0bf..a63507c 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "chrome-dynamic-bookmarks", - "version": "2.2.5", + "version": "2.2.6", "description": "Chrome extension which dynamically updates bookmarks based on the specified regular expression.", "scripts": { "dev": "webpack --mode development", diff --git a/src/js/bookmarkManager/treeView.js b/src/js/bookmarkManager/treeView.js index 7433a60..dad4929 100644 --- a/src/js/bookmarkManager/treeView.js +++ b/src/js/bookmarkManager/treeView.js @@ -1,7 +1,6 @@ import { section } from '../lib/react-clone'; import File from '../components/File'; import Folder from '../components/Folder'; -import options from '../config/config'; import { updateTreeColor } from '../utils/treeView'; import { createTree, @@ -14,13 +13,6 @@ import { import { displayFolderInfo, displayBookmark } from './displayFunctions'; import globalSelectHandler from './selectHandler'; -const { - defaultFileIconColor, - defaultFolderIconColor, - trackedFileIconColor, - trackedFolderIconColor -} = options; - document.addEventListener('DOMContentLoaded', () => { var sidenavs = document.querySelectorAll('.sidenav'); M.Sidenav.init(sidenavs); diff --git a/src/js/popup/index.js b/src/js/popup/index.js index 4de76ee..a2d5bba 100644 --- a/src/js/popup/index.js +++ b/src/js/popup/index.js @@ -19,9 +19,8 @@ document.addEventListener('DOMContentLoaded', function() { // extract values from form const title = event.target['bookmark_name'].value; let regExpString = event.target.regexp.value; - let regExp; try { - regExp = new RegExp(event.target.regexp.value); + new RegExp(event.target.regexp.value); } catch { formResponse.textContent = 'Invalid regular expression'; popupModalInstance.open(); diff --git a/src/js/utils/bookmarkInfo.js b/src/js/utils/bookmarkInfo.js index 3cc9065..dacf029 100644 --- a/src/js/utils/bookmarkInfo.js +++ b/src/js/utils/bookmarkInfo.js @@ -1,4 +1,5 @@ import { li, a } from '../lib/react-clone'; +import * as dbm from '../lib/dynBookmarks'; /** * Sets given properties to bookmarkInfo (undefined values will be ignored) * @param {object} props - { @@ -89,8 +90,8 @@ export function getBookmarkData(bookmarkId, done) { console.warn(chrome.runtime.lastError.message); } else { const bookmark = results[0]; - chrome.storage.sync.get(['dynBookmarks'], ({ dynBookmarks }) => { - let dynBook = dynBookmarks || {}; + dbm.findAll((err, dynBook) => { + if (err) console.warn(err); chrome.bookmarks.get(bookmark.parentId, (results) => { let parentTitle = null; if (chrome.runtime.lastError) { diff --git a/src/js/utils/folderInfo.js b/src/js/utils/folderInfo.js index d2fdf9c..3a500f5 100644 --- a/src/js/utils/folderInfo.js +++ b/src/js/utils/folderInfo.js @@ -1,3 +1,5 @@ +import * as dbm from '../lib/dynBookmarks'; + /* Show / Hide export functionality */ export function hideFolderInfo() { document.getElementById('folderInfo').classList.add('hide'); @@ -121,8 +123,8 @@ export function renderChildren(renderAll = false) { if (chrome.runtime.lastError) { console.warn(chrome.runtime.lastError.message); } else { - chrome.storage.sync.get(['dynBookmarks'], ({ dynBookmarks }) => { - const dynBook = dynBookmarks || {}; + dbm.findAll((err, dynBook) => { + if (err) console.warn(err); hideFolderInfoChildren(); for (let child of results) { findLeafNodes(child, (node) => { diff --git a/src/js/utils/treeView.js b/src/js/utils/treeView.js index 361448a..46e95db 100644 --- a/src/js/utils/treeView.js +++ b/src/js/utils/treeView.js @@ -1,4 +1,6 @@ import options from '../config/config'; +import * as dbm from '../lib/dynBookmarks'; + const { defaultFileIconColor, defaultFolderIconColor, @@ -7,8 +9,8 @@ const { } = options; export function updateTreeColor() { - chrome.storage.sync.get(['dynBookmarks'], ({ dynBookmarks }) => { - let dynBook = dynBookmarks || {}; + dbm.findAll((err, dynBook) => { + if (err) console.warn(err); chrome.bookmarks.getTree((results) => { const rootNode = results[0]; (function traverseTree(node) { diff --git a/src/manifest.json b/src/manifest.json index bd34f3f..93d18cf 100644 --- a/src/manifest.json +++ b/src/manifest.json @@ -2,7 +2,7 @@ "manifest_version": 2, "name": "Dynamic Bookmarks", "description": "Chrome extension which dynamically updates bookmarks based on the specified regular expression.", - "version": "2.2.5", + "version": "2.2.6", "permissions": ["tabs", "bookmarks", "storage"], "background": { "page": "background.html" From 9eb626680076d8fcd43b3d33db469808967ac551 Mon Sep 17 00:00:00 2001 From: Danilo Novakovic Date: Sun, 21 Oct 2018 08:56:29 +0200 Subject: [PATCH 2/5] folders and files are sorted properly on page load --- package.json | 2 +- src/js/bookmarkManager/treeViewComponents.js | 15 +++++++++++++++ src/manifest.json | 2 +- 3 files changed, 17 insertions(+), 2 deletions(-) diff --git a/package.json b/package.json index a63507c..9bd5af4 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "chrome-dynamic-bookmarks", - "version": "2.2.6", + "version": "2.3.0", "description": "Chrome extension which dynamically updates bookmarks based on the specified regular expression.", "scripts": { "dev": "webpack --mode development", diff --git a/src/js/bookmarkManager/treeViewComponents.js b/src/js/bookmarkManager/treeViewComponents.js index 881a3e5..798e320 100644 --- a/src/js/bookmarkManager/treeViewComponents.js +++ b/src/js/bookmarkManager/treeViewComponents.js @@ -28,6 +28,21 @@ export function createTree(node) { }); } else { let childEls = []; + node.children.sort((lhs, rhs) => { + let retVal = 0; + if (!lhs.children ^ !rhs.children) { + // only one is folder + retVal = !lhs.children ? 1 : -1; + } else { + // both or none are folders + if (lhs.title.toLowerCase() < rhs.title.toLowerCase()) { + retVal = -1; + } else { + retVal = 1; + } + } + return retVal; + }); for (let child of node.children) { let subTree = createTree(child); childEls.push(subTree); diff --git a/src/manifest.json b/src/manifest.json index 93d18cf..bcb1421 100644 --- a/src/manifest.json +++ b/src/manifest.json @@ -2,7 +2,7 @@ "manifest_version": 2, "name": "Dynamic Bookmarks", "description": "Chrome extension which dynamically updates bookmarks based on the specified regular expression.", - "version": "2.2.6", + "version": "2.3.0", "permissions": ["tabs", "bookmarks", "storage"], "background": { "page": "background.html" From b4e0650520d29bfdf61778c54deedc27f5b0d3a5 Mon Sep 17 00:00:00 2001 From: Danilo Novakovic Date: Sun, 21 Oct 2018 10:17:13 +0200 Subject: [PATCH 3/5] created sort html list function and made renderChildren more readable --- src/js/lib/sortList.js | 41 ++++++++++++++++++++++++++++++++++ src/js/utils/folderInfo.js | 45 ++++++++++++++++++++++++-------------- 2 files changed, 69 insertions(+), 17 deletions(-) create mode 100644 src/js/lib/sortList.js diff --git a/src/js/lib/sortList.js b/src/js/lib/sortList.js new file mode 100644 index 0000000..2681d33 --- /dev/null +++ b/src/js/lib/sortList.js @@ -0,0 +1,41 @@ +/** + * Sorts children of html element with given `id` + * @param {string} id - id of html element whose children will be sorted + * @param {function} callback - `true` if elements should swap, `false` if not (default: ascending) + */ +export default function sortList(id, callback) { + if (typeof id != 'string') { + return console.warn( + `failed to sort list ${id} (reason: invalid parameter listId)` + ); + } + var list, i, switching, b, shouldSwitch; + list = document.getElementById(id); + switching = true; + + while (switching) { + switching = false; + b = list.children; + + for (i = 0; i < b.length - 1; i++) { + shouldSwitch = false; + /* Check if the next item should + switch place with the current item: */ + if (typeof callback == 'function') { + shouldSwitch = !!callback(b[i], b[i + 1]); + break; + } else { + shouldSwitch = + b[i].innerHTML.toLowerCase() > b[i + 1].innerHTML.toLowerCase(); + break; + } + } + + if (shouldSwitch) { + /* If a switch has been marked, make the switch + and mark the switch as done: */ + b[i].parentNode.insertBefore(b[i + 1], b[i]); + switching = true; + } + } +} diff --git a/src/js/utils/folderInfo.js b/src/js/utils/folderInfo.js index 3a500f5..6802566 100644 --- a/src/js/utils/folderInfo.js +++ b/src/js/utils/folderInfo.js @@ -106,6 +106,7 @@ export function renderChildren(renderAll = false) { const folderId = renderAll === true ? '0' : childrenList.getAttribute('folderId'); + // extract search pattern from search-input let searchPattern; try { let searchInput = document.getElementById('search-input').value; @@ -119,35 +120,28 @@ export function renderChildren(renderAll = false) { const isUntrackedChecked = document.getElementById('untracked-checkbox') .checked; - chrome.bookmarks.getSubTree(folderId, (results) => { + chrome.bookmarks.getSubTree(folderId, (subTrees) => { if (chrome.runtime.lastError) { console.warn(chrome.runtime.lastError.message); } else { dbm.findAll((err, dynBook) => { if (err) console.warn(err); hideFolderInfoChildren(); - for (let child of results) { - findLeafNodes(child, (node) => { + for (let tree of subTrees) { + findLeafNodes(tree, (node) => { const childEl = document.getElementById(`child-info-${node.id}`); + // filter by search pattern if (childEl && searchPattern.test(childEl.textContent)) { const spans = childEl.querySelectorAll('span'); + + // filter by tracked/untracked if (dynBook[node.id] && isTrackedChecked) { - childEl.parentElement.classList.remove('hide'); - for (let span of spans) { - span.classList.replace( - defaultFileIconColor, - trackedFileIconColor - ); - } + showTracked(childEl); } else if (!dynBook[node.id] && isUntrackedChecked) { - childEl.parentElement.classList.remove('hide'); - for (let span of spans) { - span.classList.replace( - trackedFileIconColor, - defaultFileIconColor - ); - } + showUntracked(childEl); } + + // update information if bookmark is changed if (spans[0].textContent !== node.title) { spans[0].textContent = node.title; } else if (childEl.getAttribute('href') !== node.url) { @@ -161,3 +155,20 @@ export function renderChildren(renderAll = false) { } }); } + +/* Functions below are NOT exported, they are intended to make functions above more readable */ +function showTracked(childEl) { + const spans = childEl.querySelectorAll('span'); + childEl.parentElement.classList.remove('hide'); + replaceAllClass(spans, defaultFileIconColor, trackedFileIconColor); +} +function showUntracked(childEl) { + const spans = childEl.querySelectorAll('span'); + childEl.parentElement.classList.remove('hide'); + replaceAllClass(spans, trackedFileIconColor, defaultFileIconColor); +} +function replaceAllClass(nodesArray, oldClass, newClass) { + for (let node of nodesArray) { + node.classList.replace(oldClass, newClass); + } +} From 0f23cc02e775c00c8d6bb63abff1908484423645 Mon Sep 17 00:00:00 2001 From: Danilo Novakovic Date: Sun, 21 Oct 2018 12:20:01 +0200 Subject: [PATCH 4/5] folderInfo is now sorted always --- src/js/bookmarkManager/folderInfo.js | 6 ++++-- src/js/lib/sortList.js | 6 +++--- src/js/utils/folderInfo.js | 22 ++++++++++++++++++++++ 3 files changed, 29 insertions(+), 5 deletions(-) diff --git a/src/js/bookmarkManager/folderInfo.js b/src/js/bookmarkManager/folderInfo.js index 96f8227..f0c0561 100644 --- a/src/js/bookmarkManager/folderInfo.js +++ b/src/js/bookmarkManager/folderInfo.js @@ -4,7 +4,8 @@ import { displayBookmark } from './displayFunctions'; import { findLeafNodes, renderChildren, - createChildInfoId + createChildInfoId, + sortFolderInfoChildren } from '../utils/folderInfo'; import * as dynBookmarks from '../lib/dynBookmarks'; @@ -97,6 +98,7 @@ function initFolderInfo() { } }); } + sortFolderInfoChildren(); }); } }); @@ -141,7 +143,7 @@ function createFolderInfoChild( className: `truncate`, target: '_blank' }, - span({ className: `${color} text-darken-4` }, title), + span({ className: `${color} text-darken-4 child-info-title` }, title), span({ className: `${color} child-info-link` }, ` (${url})`) ), i( diff --git a/src/js/lib/sortList.js b/src/js/lib/sortList.js index 2681d33..486b25a 100644 --- a/src/js/lib/sortList.js +++ b/src/js/lib/sortList.js @@ -1,7 +1,7 @@ /** * Sorts children of html element with given `id` * @param {string} id - id of html element whose children will be sorted - * @param {function} callback - `true` if elements should swap, `false` if not (default: ascending) + * @param {function(HTMLElement, HTMLElement)} callback - returns `true` if elements should swap, `false` if not (default: ascending) */ export default function sortList(id, callback) { if (typeof id != 'string') { @@ -16,17 +16,17 @@ export default function sortList(id, callback) { while (switching) { switching = false; b = list.children; - for (i = 0; i < b.length - 1; i++) { shouldSwitch = false; /* Check if the next item should switch place with the current item: */ if (typeof callback == 'function') { shouldSwitch = !!callback(b[i], b[i + 1]); - break; } else { shouldSwitch = b[i].innerHTML.toLowerCase() > b[i + 1].innerHTML.toLowerCase(); + } + if (shouldSwitch) { break; } } diff --git a/src/js/utils/folderInfo.js b/src/js/utils/folderInfo.js index 6802566..8fd08af 100644 --- a/src/js/utils/folderInfo.js +++ b/src/js/utils/folderInfo.js @@ -1,4 +1,5 @@ import * as dbm from '../lib/dynBookmarks'; +import sortList from '../lib/sortList'; /* Show / Hide export functionality */ export function hideFolderInfo() { @@ -127,6 +128,7 @@ export function renderChildren(renderAll = false) { dbm.findAll((err, dynBook) => { if (err) console.warn(err); hideFolderInfoChildren(); + sortFolderInfoChildren(); for (let tree of subTrees) { findLeafNodes(tree, (node) => { const childEl = document.getElementById(`child-info-${node.id}`); @@ -156,6 +158,26 @@ export function renderChildren(renderAll = false) { }); } +export function sortFolderInfoChildren() { + sortList('folder-children-info', (lhs, rhs) => { + const lhsUrl = lhs.querySelector('.child-info-link').textContent; + const rhsUrl = rhs.querySelector('.child-info-link').textContent; + + const lhsTitle = lhs + .querySelector('.child-info-title') + .textContent.toLowerCase(); + const rhsTitle = rhs + .querySelector('.child-info-title') + .textContent.toLowerCase(); + + if (lhsTitle === rhsTitle) { + return lhsUrl > rhsUrl; + } else { + return lhsTitle > rhsTitle; + } + }); +} + /* Functions below are NOT exported, they are intended to make functions above more readable */ function showTracked(childEl) { const spans = childEl.querySelectorAll('span'); From 8ea08d39c05753383d94dfd3ef231093ac09ece2 Mon Sep 17 00:00:00 2001 From: Danilo Novakovic Date: Sun, 21 Oct 2018 13:48:47 +0200 Subject: [PATCH 5/5] tree view is now always sorted (on move, edit, and add) --- src/js/bookmarkManager/treeView.js | 36 ++++++++++++++++++++++++++++-- src/js/components/Folder.js | 2 +- 2 files changed, 35 insertions(+), 3 deletions(-) diff --git a/src/js/bookmarkManager/treeView.js b/src/js/bookmarkManager/treeView.js index dad4929..5ba014e 100644 --- a/src/js/bookmarkManager/treeView.js +++ b/src/js/bookmarkManager/treeView.js @@ -56,7 +56,8 @@ document.addEventListener('DOMContentLoaded', () => { onDragover: allowDrop, onDragstart: drag }); - parent.querySelector('ul').appendChild(newEl); + + appendSorted(parent.querySelector('ul'), newEl); // note: i wrapped this in timeout because storage is updated AFTER bookmark is created setTimeout(() => { @@ -82,6 +83,9 @@ document.addEventListener('DOMContentLoaded', () => { chrome.bookmarks.onChanged.addListener((id, changeInfo) => { if (changeInfo.title) { let elem = document.getElementById(id); + elem.setAttribute('name', changeInfo.title); + appendSorted(elem.parentElement, elem); + if (elem.classList.contains('folder')) { elem = elem.querySelector('.folder-header') || elem; } @@ -94,7 +98,7 @@ document.addEventListener('DOMContentLoaded', () => { const elem = document.getElementById(id); const parent = document.getElementById(moveInfo.parentId); if (parent.classList.contains('folder')) { - parent.querySelector('ul').appendChild(elem); + appendSorted(parent.querySelector('ul'), elem); } setTimeout(() => { if (elem.classList.contains('folder')) { @@ -107,3 +111,31 @@ document.addEventListener('DOMContentLoaded', () => { }, 100); }); }); + +function appendSorted(parent, element) { + if (!parent) return console.warn('parent in appendSorted is undefined'); + if (!element) return console.warn('element in appendSorted is undefined'); + let appended = false; + const elemName = element.getAttribute('name').toLowerCase(); + const isElemFolder = element.classList.contains('folder'); + for (let child of parent.children) { + try { + const childName = child.getAttribute('name').toLowerCase(); + const isChildFolder = child.classList.contains('folder'); + + if ( + (isElemFolder && !isChildFolder) || + (isElemFolder === isChildFolder && childName > elemName) + ) { + parent.insertBefore(element, child); + appended = true; + break; + } + } catch (err) { + console.warn(err); + } + } + if (!appended) { + parent.appendChild(element); + } +} diff --git a/src/js/components/Folder.js b/src/js/components/Folder.js index ec19713..83cb4a7 100644 --- a/src/js/components/Folder.js +++ b/src/js/components/Folder.js @@ -17,7 +17,7 @@ const Folder = (props, ...children) => { const folderName = name || 'unknown'; const iconColor = folderIconColor || defaultFolderIconColor; return div( - { className: 'folder', ...(id && { id }) }, + { className: 'folder', name: folderName, ...(id && { id }) }, header( { ...headerParams,