Skip to content

Commit

Permalink
Merge pull request #26 from DaniloNovakovic/v2
Browse files Browse the repository at this point in the history
v2.3.0 - Bookmarks & Folders are now always sorted
  • Loading branch information
DaniloNovakovic authored Oct 21, 2018
2 parents 6af3531 + 8ea08d3 commit 24a065e
Show file tree
Hide file tree
Showing 11 changed files with 159 additions and 40 deletions.
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "chrome-dynamic-bookmarks",
"version": "2.2.5",
"version": "2.3.0",
"description": "Chrome extension which dynamically updates bookmarks based on the specified regular expression.",
"scripts": {
"dev": "webpack --mode development",
Expand Down
6 changes: 4 additions & 2 deletions src/js/bookmarkManager/folderInfo.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,8 @@ import { displayBookmark } from './displayFunctions';
import {
findLeafNodes,
renderChildren,
createChildInfoId
createChildInfoId,
sortFolderInfoChildren
} from '../utils/folderInfo';
import * as dynBookmarks from '../lib/dynBookmarks';

Expand Down Expand Up @@ -97,6 +98,7 @@ function initFolderInfo() {
}
});
}
sortFolderInfoChildren();
});
}
});
Expand Down Expand Up @@ -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(
Expand Down
44 changes: 34 additions & 10 deletions src/js/bookmarkManager/treeView.js
Original file line number Diff line number Diff line change
@@ -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,
Expand All @@ -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);
Expand Down Expand Up @@ -64,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(() => {
Expand All @@ -90,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;
}
Expand All @@ -102,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')) {
Expand All @@ -115,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);
}
}
15 changes: 15 additions & 0 deletions src/js/bookmarkManager/treeViewComponents.js
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down
2 changes: 1 addition & 1 deletion src/js/components/Folder.js
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down
41 changes: 41 additions & 0 deletions src/js/lib/sortList.js
Original file line number Diff line number Diff line change
@@ -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(HTMLElement, HTMLElement)} callback - returns `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]);
} else {
shouldSwitch =
b[i].innerHTML.toLowerCase() > b[i + 1].innerHTML.toLowerCase();
}
if (shouldSwitch) {
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;
}
}
}
3 changes: 1 addition & 2 deletions src/js/popup/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -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();
Expand Down
5 changes: 3 additions & 2 deletions src/js/utils/bookmarkInfo.js
Original file line number Diff line number Diff line change
@@ -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 - {
Expand Down Expand Up @@ -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) {
Expand Down
73 changes: 54 additions & 19 deletions src/js/utils/folderInfo.js
Original file line number Diff line number Diff line change
@@ -1,3 +1,6 @@
import * as dbm from '../lib/dynBookmarks';
import sortList from '../lib/sortList';

/* Show / Hide export functionality */
export function hideFolderInfo() {
document.getElementById('folderInfo').classList.add('hide');
Expand Down Expand Up @@ -104,6 +107,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;
Expand All @@ -117,35 +121,29 @@ 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 {
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) => {
sortFolderInfoChildren();
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) {
Expand All @@ -159,3 +157,40 @@ 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');
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);
}
}
6 changes: 4 additions & 2 deletions src/js/utils/treeView.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
import options from '../config/config';
import * as dbm from '../lib/dynBookmarks';

const {
defaultFileIconColor,
defaultFolderIconColor,
Expand All @@ -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) {
Expand Down
2 changes: 1 addition & 1 deletion src/manifest.json
Original file line number Diff line number Diff line change
Expand Up @@ -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.3.0",
"permissions": ["tabs", "bookmarks", "storage"],
"background": {
"page": "background.html"
Expand Down

0 comments on commit 24a065e

Please sign in to comment.