Skip to content

Commit

Permalink
Began shifting tree.js to functions of new model
Browse files Browse the repository at this point in the history
Getting ever closer to #35!
  • Loading branch information
Chris White committed Jan 28, 2018
1 parent f818caf commit 8909f29
Show file tree
Hide file tree
Showing 4 changed files with 110 additions and 8 deletions.
5 changes: 5 additions & 0 deletions tabfern/src/view/item_details.js
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,11 @@
[ //keys
'win_id', // from Chrome
'node_id', // from jstree
'ordered_url_hash',
// Hash of the URLs of its tabs, in order. Used for
// determining whether a window is open. NOTE: if a user
// opens two windows with exactly the same set of tabs,
// whichever one already has that ordered_url_hash will keep it.
],
[ //other data
'win', // the actual Window record from chrome
Expand Down
81 changes: 76 additions & 5 deletions tabfern/src/view/model.js
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,8 @@

(function (root, factory) {
let imports=['jquery','jstree','loglevel', 'view/const',
'view/item_details', 'view/item_tree', 'justhtmlescape'];
'view/item_details', 'view/item_tree', 'justhtmlescape',
'buffer', 'blake2s'];

if (typeof define === 'function' && define.amd) {
// AMD
Expand All @@ -38,7 +39,8 @@
}
root.tabfern_item = factory(...requirements);
}
}(this, function ($, _unused_jstree_placeholder_, log, K, D, T, Esc ) {
}(this, function ($, _unused_jstree_placeholder_, log, K, D, T, Esc,
Buffer, BLAKE2s) {
"use strict";

function loginfo(...args) { log.info('TabFern view/item.js: ', ...args); };
Expand Down Expand Up @@ -86,11 +88,12 @@

/// Mark window item #val as unsaved.
/// @param val {Object} the item
/// @param adjust_title {Boolean=true} Add unsaved markers if truthy
/// @return {Boolean} true on success; false on error
module.mark_win_as_unsaved = function(val) {
module.mark_win_as_unsaved = function(val, adjust_title=true) {
if(!val || val.ty !== K.IT_WIN || !val.node_id) return false;
val.keep = K.WIN_NOKEEP;
if(val.raw_title !== null) {
if(adjust_title && (val.raw_title !== null)) {
val.raw_title = module.remove_unsaved_markers(val.raw_title) +
' (Unsaved)';
}
Expand Down Expand Up @@ -309,6 +312,59 @@
// "Rez" and "Erase" are adding/removing items, to distinguish them
// from creating and destroying Chrome widgets.

// Helper routines ///////////////////////////////////////////////// {{{1

// Hash the strings in #strs together. All strings are encoded in utf8
// before hashing.
// @param strs {mixed} a string or array of strings.
// @return {String} the hash, as a string of hex chars
module.hashStrings = function(strs) {
if(!Array.isArray(strs)) strs = [strs];
let blake = new BLAKE2s(32);
for(let str of strs) {
let databuf = new Uint8Array(Buffer.from(str, 'utf8'));
blake.update(databuf);
}
return blake.hexDigest();
} //hashString

// Update the given node's ordered_url_hash to reflect its current children.
// @return {Boolean} True if the ordered_url_hash was set; false if it
// wasn't. On false return, the ordered_url_hash
// will have been set to a falsy value.
module.updateOrderedURLHash = function(vornyParent) {
let {val: parent_val, node_id: parent_node_id} =
module.vn_by_vorny(vornyParent, K.IT_WIN);
let parent_node = T.treeobj.get_node(parent_node_id);
if(!parent_val || !parent_node_id || !parent_node) return false;

let child_urls = [];
for(let child_node_id of parent_node.children) {
let child_url = D.tabs.by_node_id(child_node_id, 'raw_url');
if(!child_url) { // rather than inconsistent state, just clear it
D.windows.change_key(parent_val, 'ordered_url_hash', null);
return false;
}
child_urls.push(child_url);
}

let ordered_url_hash = module.hashStrings(child_urls);

// Check if a different window already has that hash. If so, that
// window keeps that hash.
let other_win_val = D.windows.by_ordered_url_hash(ordered_url_hash);
if(other_win_val) {
D.windows.change_key(parent_val, 'ordered_url_hash', null);
// This window will no longer participate in merge detection.
return false;
} else {
D.windows.change_key(parent_val, 'ordered_url_hash', ordered_url_hash);
}

return true;
}; //updateOrderedURLHash()

// }}}1
// Querying the model ////////////////////////////////////////////// {{{1

/// Get a {val, node_id} pair (vn) from one of those (vorny).
Expand Down Expand Up @@ -514,6 +570,10 @@
let node = T.treeobj.get_node(node_id);
if(!node) return false;

T.treeobj.open_node(node_id);
// We always open nodes for presently-open windows. However, this
// won't work if no tabs have been added yet.

D.windows.change_key(val, 'win_id', cwin.id);
// node_id unchanged
val.win = cwin;
Expand All @@ -532,6 +592,7 @@

/// Attach a Chrome tab to an existing tab item.
/// Updates the item, but does not touch the Chrome tab.
/// Also updates the parent's val.ordered_url_hash.
/// @param tab_vorny {mixed} The item
/// @param ctab {Chrome Tab} The open tab
/// @return {Boolean} true on success; false on error
Expand Down Expand Up @@ -566,6 +627,8 @@
module.refresh_label(node_id);
module.refresh_tab_icon(val); // since favicon may have changed

module.updateOrderedURLHash(node.parent);

return true;
} //markTabAsOpen

Expand Down Expand Up @@ -613,6 +676,8 @@

let {val, node_id} = module.vn_by_vorny(tab_vorny, K.IT_TAB);
if(!val || !node_id) return false;
let node = T.treeobj.get_node(node_id);
if(!node) return false;

if(!val.isOpen || !val.tab) {
log.error({'Refusing to re-mark already-closed tab as closed':val});
Expand Down Expand Up @@ -648,12 +713,18 @@
/// @return {Boolean} true on success; false on error
module.eraseTab = function(tab_vorny) {
let {val, node_id} = module.vn_by_vorny(tab_vorny, K.IT_TAB);
if(!val || !node_id) return false;
let node = T.treeobj.get_node(node_id);
if(!val || !node_id || !node) return false;

let parent_node_id = node.parent;

D.tabs.remove_value(val);
// So any events that are triggered won't try to look for a
// nonexistent tab.
T.treeobj.delete_node(node_id);

module.updateOrderedURLHash(parent_node_id);

return true;
} //eraseTab

Expand Down
25 changes: 22 additions & 3 deletions tabfern/src/view/tree.js
Original file line number Diff line number Diff line change
Expand Up @@ -359,6 +359,7 @@ function saveTree(save_ephemeral_windows = true, cbk = undefined)

result_win.raw_title = win_val.raw_title;
result_win.tabs = [];
result_win.ordered_url_hash = win_val.ordered_url_hash || undefined;
if(is_ephemeral) result_win.ephemeral = true;
// Don't bother putting it in if we don't need it.

Expand Down Expand Up @@ -919,12 +920,22 @@ function addWindowNodeActions(win_node_id)
/// @returns the tree-node ID, or undefined on error.
function createNodeForWindow(cwin, keep)
{
if(!cwin || !cwin.id) return;

// Don't put our own popup window in the list
if( cwin.id && (cwin.id === my_winid) ) return;

let {node_id, val} = M.makeItemForWindow(cwin, keep);
let is_first = (!!cwin && getBoolSetting(CFG_NEW_WINS_AT_TOP));
let {node_id, val} = M.vnRezWin(is_first); //M.makeItemForWindow(cwin, keep);
if(!node_id) return; //sanity check

M.markWinAsOpen(val, cwin);
if(keep) {
M.remember(node_id, false);
} else {
M.mark_win_as_unsaved(val, false);
}

addWindowNodeActions(node_id);

if(cwin.tabs) { // new windows may have no tabs
Expand All @@ -934,6 +945,9 @@ function createNodeForWindow(cwin, keep)
}
}

T.treeobj.open_node(node_id);
// TODO move this into M.vnRezTab once createNodeForTab calls it.

return node_id;
} //createNodeForWindow

Expand All @@ -950,6 +964,8 @@ function createNodeForClosedWindowV1(win_data_v1)
// TODO need to not mark it keep if it's ephemeral and still open.
let {node_id, val} = M.makeItemForWindow();

// TODO restore ordered_url_hash

// Mark recovered windows
if(is_ephemeral) {
M.add_subtype(node_id, K.NST_RECOVERED);
Expand Down Expand Up @@ -1062,6 +1078,7 @@ var loadSavedWindowsFromData = (function(){
/// Each win is {raw_title: "foo", tabs: [tab, tab, ...]}
/// A V1 win may optionally include:
/// - ephemeral:<truthy> (default false) to mark ephemeral windows.
/// - ordered_url_hash {String}
/// Each tab is {raw_title: "foo", raw_url: "bar"}
/// A V1 tab may optionally include:
/// - bordered:<truthy> (default false) to mark windows with borders
Expand Down Expand Up @@ -3145,8 +3162,10 @@ function addOpenWindowsToTree(done, winarr)
// TODO update per createNodeForClosedWindowV1 --- it will
// still be marked KEEP.
M.del_subtype(existing_win.node.id, K.NST_RECOVERED);
existing_win.val.raw_title =
existing_win.val.raw_title.replace(/ (Recovered)$/,'');
if(existing_win.val.raw_title) {
existing_win.val.raw_title =
existing_win.val.raw_title.replace(/ (Recovered)$/,'');
}

T.treeobj.open_node(existing_win.node);
T.treeobj.redraw_node(existing_win.node);
Expand Down
7 changes: 7 additions & 0 deletions tabfern/test/spec/spec-view-model.js
Original file line number Diff line number Diff line change
Expand Up @@ -151,10 +151,17 @@ describe('view/model', function() {
expect(this.tab_val.isOpen).toBe(true);
expect($node.find('a').first().text()).toMatch(/^Jasmine\b/);

let parent_val = D.windows.by_win_id(ctab.windowId);
expect(parent_val).toBeTruthy();
since('The parent window should have an ordered_url_hash now')
.expect(parent_val.ordered_url_hash).toBeTruthy();
done();
});
});

// TODO test opening a second window with the same tab URL, and making sure
// the first window keeps the corresponding ordered_url_hash.

it('can mark a tab as closed',()=>{
let $node = $('#'+this.tab_node_id).eq(0);
expect($node.hasClass('tfs-open')).toBe(true);
Expand Down

0 comments on commit 8909f29

Please sign in to comment.