diff options
| author | Alon Levy <alon@pobox.com> | 2015-05-05 12:12:48 +0300 |
|---|---|---|
| committer | Alon Levy <alon@pobox.com> | 2015-05-05 13:54:23 +0300 |
| commit | fb67d553eaf8966d301b7c038e515cd771aa917a (patch) | |
| tree | a44203d76df4e13c390672aff6e3181f45a61baf /src/client/view | |
| parent | f56d42f3ed53c37e7c9cb5a88206431e1d12abbb (diff) | |
client: add distinct links selection
Diffstat (limited to 'src/client/view')
| -rw-r--r-- | src/client/view/graph_view.js | 6 | ||||
| -rw-r--r-- | src/client/view/selection.js | 176 |
2 files changed, 122 insertions, 60 deletions
diff --git a/src/client/view/graph_view.js b/src/client/view/graph_view.js index 6a9ba41b..634ff7a5 100644 --- a/src/client/view/graph_view.js +++ b/src/client/view/graph_view.js @@ -420,7 +420,7 @@ function GraphView(spec) { } function nodes__visible() { - return zen_mode ? selection.related() : nodes__filtered(); + return zen_mode ? selection.related_nodes() : nodes__filtered(); } function links__filtered() { @@ -428,7 +428,7 @@ function GraphView(spec) { } function links__visible() { - return zen_mode ? graph.find_links__by_nodes(selection.related()) : links__filtered(); + return zen_mode ? graph.find_links__by_nodes(selection.related_nodes()) : links__filtered(); } function node__transform(d) { @@ -1380,7 +1380,7 @@ function GraphView(spec) { parent_element[0][0].parentElement.addEventListener('mousedown', disable_zen_mode_auto_center); function center_on_selection_related() { - nodes__user_visible(selection.related(), true, 100 /* ms, duration of animation */); + nodes__user_visible(selection.related_nodes(), true, 100 /* ms, duration of animation */); } gv.link__pass_filter = link__pass_filter; diff --git a/src/client/view/selection.js b/src/client/view/selection.js index ec8984bc..e1d320e4 100644 --- a/src/client/view/selection.js +++ b/src/client/view/selection.js @@ -11,12 +11,14 @@ function selected_nodes_ids() { function Selection() { } -function new_selection(selected_nodes, related_nodes) +function new_selection(selected_nodes, related_nodes, selected_links, related_links) { var ret = new Selection(); ret.related_nodes = related_nodes; ret.selected_nodes = selected_nodes; + ret.related_links = related_links; + ret.selected_links = selected_links; return ret; } @@ -41,9 +43,16 @@ function get_main_graph_view() } var selected_nodes, // these are the nodes that are requested via update - related_nodes, // these are not directly selected but we want to show them to users selected_nodes__by_id, + related_nodes, // these are not directly selected but we want to show them to users related_nodes__by_id, + selected_links, // explicitly selected via external call + selected_links__by_id, + selected_links__by_node_id, + related_links, // implicitly selected via selected_links. note that links + // are considered related if both nodes are related as well. + related_links__by_id, + related_links__by_node_id, selectionChangedBus = new Bacon.Bus(); function listen_on_diff_bus(diffBus) @@ -54,11 +63,18 @@ function listen_on_diff_bus(diffBus) new_selected_nodes = selected_nodes.filter(function (n) { return get_main_graph().find_node__by_id(n.id) !== null; }); + new_selected_links = selected_links.filter(function (n) { + return get_main_graph().find_link__by_id(n.id) !== null; + }); new_related_nodes = related_nodes.filter(function (n) { return get_main_graph().find_node__by_id(n.id) !== null; }); + new_related_links = related_links.filter(function (n) { + return get_main_graph().find_link__by_id(n.id) !== null; + }); // reselect based on current graph - inner_select(new_selected_nodes, new_related_nodes); + inner_select(new_selected_nodes=new_selected_nodes, new_related_nodes=new_related_nodes, + new_selected_links=new_selected_links, new_related_links=new_related_links); }); } @@ -71,42 +87,54 @@ function nodes_to_id_dict(nodes) }, {}); } -function updateSelectedNodesBus(new_selected_nodes, new_related_nodes) +function links_to_id_dict(links) { - if (_.isEqual(selected_nodes, new_selected_nodes) && _.isEqual(related_nodes, new_related_nodes)) { - return; - } - selected_nodes = new_selected_nodes; - selected_nodes__by_id = nodes_to_id_dict(selected_nodes); - related_nodes = new_related_nodes; - related_nodes__by_id = nodes_to_id_dict(related_nodes); - selection_count_element.text(related_nodes.length > 0 ? '' + selected_nodes.length + ', ' + related_nodes.length : ''); - selectionChangedBus.push(new_selection(selected_nodes, related_nodes)); + return links.reduce( + function(d, v) { + d[v.id] = v; + return d; + }, {}); } -/* add nodes in nodes_b to a copy of nodes_a in order, skipping duplicates */ -function sum_nodes(nodes_a, nodes_b) +function links_to_node_id_dict(links) { - var set_a_id = _.object(nodes_a.map(function (n) { return [n.id, 1]; })), - ret = nodes_a.slice(0); - - for (var k in nodes_b) { - if (set_a_id[nodes_b[k].id] === undefined) { - ret.push(nodes_b[k]); - } - } - return ret; + return links.reduce( + function(d, v) { + d[v.__src.id] = v; + d[v.__dst.id] = v; + return d; + }, {}); } -function links_to_nodes(links) +function updateSelectedNodesBus(new_selected_nodes, new_related_nodes, new_selected_links, new_related_links) { - return _.flatten(_.map(links, function (link) { return [link.__src, link.__dst]; })); + var selection_empty = new_selected_nodes.length + new_selected_links.length == 0; + + if (_.isEqual(selected_nodes, new_selected_nodes) && _.isEqual(related_nodes, new_related_nodes) && + _.isEqual(selected_links, new_selected_links) && _.isEqual(related_links, new_related_links)) { + return; + } + selected_nodes = new_selected_nodes; + selected_nodes__by_id = nodes_to_id_dict(selected_nodes); + related_nodes = new_related_nodes; + related_nodes__by_id = nodes_to_id_dict(related_nodes); + selected_links = new_selected_links; + selected_links__by_id = links_to_id_dict(selected_links); + related_links = new_related_links; + related_links__by_id = links_to_id_dict(related_links); + selected_links__by_node_id = links_to_node_id_dict(selected_links); + related_links__by_node_id = links_to_node_id_dict(related_links); + selection_count_element.text( + selection_empty ? '' : + '' + selected_nodes.length + ', ' + related_nodes.length + ' | ' + + '' + selected_links.length + ', ' + related_links.length); + selectionChangedBus.push(new_selection(selected_nodes, related_nodes, selected_links, related_links)); } function byVisitors(node_selector, link_selector) { var new_selection = get_main_graph().find__by_visitors(node_selector, link_selector); - inner_select_nodes(sum_nodes(new_selection.nodes, links_to_nodes(new_selection.links))); + inner_select_nodes(new_selection.nodes, new_selection.links); } function _type_to_state(type) { @@ -135,7 +163,10 @@ function _select_nodes_helper(nodes, connected) { data.link.state = _type_to_state(data.type); }); nodes.forEach(function (n) { n.state = 'selected'; }); - return connected.nodes.map(function (d) { return d.node; }).concat(nodes.slice()); + return { + nodes: connected.nodes.map(function (d) { return d.node; }).concat(nodes.slice()), + links: connected.links.map(function (d) { return d.link; }), + } } function mutual_neighbours(nodes) { @@ -162,38 +193,51 @@ function neighbours(nodes) { var connected = get_main_graph().neighbourhood(nodes, 1); return _select_nodes_helper(nodes, connected); -} +}; var node_related = function(node) { - return related_nodes__by_id[node.id] !== undefined; -} + return related_nodes__by_id[node.id] !== undefined || + related_links__by_node_id[node.id] !== undefined; +}; var node_selected = function(node) { return selected_nodes__by_id[node.id] !== undefined; -} +}; var node_first_selected = function(node) { return selected_nodes && selected_nodes.length > 0 && node.id === selected_nodes[0].id; -} +}; var link_related = function(link) { - return node_related(link.__src) && node_related(link.__dst); + return related_links__by_id[link.id] !== undefined || + node_related(link.__src) && node_related(link.__dst); +}; + +var link_selected = function(link) { + return selected_links__by_id[link.id] !== undefined; +}; + +function empty_selection() { + return related_nodes.length == 0 && selected_nodes.length == 0 && + related_links.length == 0 && selected_links.length == 0; } var class__node = function(node, temporary) { - return (!temporary && (related_nodes.length > 0 || selected_nodes.length > 0)) ? + return (!temporary && !empty_selection()) ? (node_first_selected(node) ? 'first-selected' : (node_selected(node) ? 'selected' : (node_related(node) ? "related" : "notselected"))) : ""; } var class__link = function(link, temporary) { - return !temporary && related_nodes.length > 0 ? (link_related(link) ? "selected" : "notselected") : ""; + return !temporary && !empty_selection() ? + (link_selected(link) ? 'selected' : + (link_related(link) ? "related" : "notselected")) : ""; } var clear = function() { - updateSelectedNodesBus([], []); + updateSelectedNodesBus([], [], [], []); } function arr_compare(a1, a2) @@ -211,7 +255,10 @@ function arr_compare(a1, a2) var inner_select_nodes = function(nodes) { - inner_select(nodes, nodes.length == 1 ? neighbours(nodes) : mutual_neighbours(nodes)); + var related = nodes.length == 1 ? neighbours(nodes) : mutual_neighbours(nodes); + + console.dir(related); + inner_select(nodes, related.nodes, [], related.links); } var select_nodes = function(nodes) @@ -224,25 +271,24 @@ var select_nodes = function(nodes) } } -var inner_select = function(new_selected_nodes, new_related_nodes) +var inner_select = function(new_selected_nodes, new_related_nodes, new_selected_links, new_related_links) { - if (arr_compare(new_selected_nodes, selected_nodes) && arr_compare(new_related_nodes, related_nodes)) { + if (arr_compare(new_selected_nodes, selected_nodes) && arr_compare(new_related_nodes, related_nodes) && + arr_compare(new_selected_links, selected_links) && arr_compare(new_related_links, related_links)) { // no change return; } - updateSelectedNodesBus(new_selected_nodes, new_related_nodes); + updateSelectedNodesBus(new_selected_nodes, new_related_nodes, new_selected_links, new_related_links); } -function nodes_from_link(link) -{ - return [link.__src, link.__dst]; + +var all_related_nodes = function() { + return _.union(related_nodes, nodes_from_links(related_links)); } var select_link = function(link) { - var new_selected_nodes = nodes_from_link(link); - - inner_select(new_selected_nodes, new_selected_nodes); + inner_select([], nodes_from_links([link]), [link], [link]); } function invert(initial, inverted) @@ -252,11 +298,10 @@ function invert(initial, inverted) var invert_link = function(link) { - var link_nodes = nodes_from_link(link), - new_selected_nodes = invert(selected_nodes, link_nodes), - new_related_nodes = invert(related_nodes, link_nodes); + var new_selected_links = invert(selected_links, [link]), + new_related_links = _.union(invert(related_links, [link]), new_selected_links); - inner_select(new_selected_nodes, new_related_nodes); + inner_select(selected_nodes, all_related_nodes(), new_selected_links, new_related_links); } var invert_nodes = function(nodes) @@ -270,8 +315,10 @@ var setup_toolbar = function(main_graph, main_graph_view) main_graph.nodes__merge(selected_nodes_ids()); }, delete_selection = function() { - if (confirm(messages.delete_nodes_links_message(selected_nodes, []))) { - main_graph.nodes__delete(ids); + if (confirm(messages.delete_nodes_links_message(selected_nodes, selected_links))) { + // FIXME: atomic undo + main_graph.links__delete(_.map(selected_links, 'id')); + main_graph.nodes__delete(_.map(selected_nodes, 'id')); } }, link_fan_selection = function() { @@ -286,6 +333,7 @@ var setup_toolbar = function(main_graph, main_graph_view) merge_btn.asEventStream('click').onValue(merge_selection); delete_btn.asEventStream('click').onValue(delete_selection); link_fan_btn.asEventStream('click').onValue(link_fan_selection); + zen_mode_btn.asEventStream('click').onValue(main_graph_view.zen_mode__toggle); function show(e, visible) { if (visible) { @@ -302,19 +350,31 @@ var setup_toolbar = function(main_graph, main_graph_view) show(multiple_node_operations, visible); }); - // operations requiring 1 or more nodes - selectionChangedBus.map(function (selection) { return selection.selected_nodes.length > 0; }) + // operations requiring 1 or more node or link + selectionChangedBus + .map(function (selection) { + return selection.selected_nodes.length + selection.selected_links.length > 0; + }) .skipDuplicates() .onValue(function (visible) { - show(zen_mode_btn, visible); show(delete_btn, visible); }); - zen_mode_btn.asEventStream('click').onValue(main_graph_view.zen_mode__toggle); + // operations requiring 1 or more nodes + selectionChangedBus + .map(function (selection) { return selection.selected_nodes.length > 0; }) + .skipDuplicates() + .onValue(function (visible) { + show(zen_mode_btn, visible); + }); } var is_empty = function() { return selected_nodes && selected_nodes.length == 0; +}; + +var nodes_from_links = function(links) { + return _.flatten(_.map(links, function (l) { return [l.__src, l.__dst]; })); } // initialize @@ -338,6 +398,8 @@ return { selected_nodes: function() { return selected_nodes; }, related_nodes: function() { return related_nodes; }, + selected_links: function() { return selected_links; }, + related_links: function() { return related_links; }, }; }); |
