summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorAlon Levy <alon@pobox.com>2014-12-30 10:31:39 +0200
committerAlon Levy <alon@pobox.com>2015-01-05 13:23:57 +0200
commit54aa65a3dab029f28741875eb11a40adb06aff51 (patch)
tree872e9c2723644795d5ac633ec9ae7820f8e3f69d /src
parente1203339003c32eb646b199d2ae69b7082228cc9 (diff)
introduce commit_and_tx_diff__topo & commit_diff__topo
Multiple fixes / changes. _bugs introduced_ Changes: graph has two change functions (i.e. the internal API): - commit_and_tx_diff_topo This currently transmits, later will optimistically apply first - commit_diff__topo This only applies. Used on the ajax success path for commit_and_tx_diff__topo and when changes are only local. It signals on the diffBus - diffBus - a Bacon.Bus that all UI listens to - selection UI - node/link dialogs - main view (rz_core/update_view__graph) Small changes: whitespace rename filer->filter Completely broken: - text entry. should use commit_diff__topo Problems introduced: selection doesn't work correctly. editing not verified yet
Diffstat (limited to 'src')
-rw-r--r--src/client/model/graph.js246
-rw-r--r--src/client/rz_core.js95
-rw-r--r--src/client/textanalysis.js73
-rw-r--r--src/client/textanalysis.ui.js1
-rw-r--r--src/client/view/selection.js39
-rw-r--r--src/server/model/graph.py6
6 files changed, 231 insertions, 229 deletions
diff --git a/src/client/model/graph.js b/src/client/model/graph.js
index 12190cd7..b97fd117 100644
--- a/src/client/model/graph.js
+++ b/src/client/model/graph.js
@@ -9,7 +9,10 @@ function Graph() {
var nodes = [],
id_to_node_map = {},
+ id_to_node_index_map = {},
links = [],
+ id_to_link_map = {},
+ id_to_link_index_map = {},
diffBus = new Bacon.Bus();
// All operations done on the graph. When the server is used (i.e. always) this
@@ -29,18 +32,36 @@ function Graph() {
*/
this.addNode = function(spec) {
var node = this.__addNode(spec);
- if (node) {
- return node;
- }
+ return node;
+ }
+
+ var nodes_to_touched_links = function (node_id_set) {
+ var touched_links = [];
+
+ node_id_set.forEach(function (n_id) {
+ var n = id_to_node_map[n_id];
+ links.forEach(function (link) {
+ if ((link['__src'].equals(n)) || (link['__dst'].equals(n))) { // compare by id
+ touched_links.push(link);
+ }
+ });
+ });
+ console.dir(touched_links);
+
+ return touched_links.map(function(l){ return l.id; });
}
/**
*
* @param a topo_diff but that might be missing a few things, sanitize it first.
* all sanitation should be idempotent, but probably isn't.
+ *
+ * NOTE: currently this function transmits only. Later we want to optimistically
+ * first commit and then transmit.
*/
- this.commit_diff__topo = function (topo_diff, on_success, on_error) {
+ this.commit_and_tx_diff__topo = function (topo_diff) {
var name_to_node = {};
+ $.merge(topo_diff.link_set_rm, nodes_to_touched_links(topo_diff.node_set_rm));
topo_diff.node_set_add = topo_diff.node_set_add.map(function(n) {
if (n.id === undefined) {
var existing = findNodeByName(n.name);
@@ -83,16 +104,13 @@ function Graph() {
topo_diff.node_set_add = topo_diff.node_set_add.filter(function(n) {
return !hasNodeByName(n.name);
});
+
var graph_on_success = function(diff) {
- on_backend__diff(diff);
- if (on_success) {
- on_success(nodes);
- }
+ commit_diff__topo(diff);
}
var graph_on_error = function(error) {
- if (on_error) {
- on_error(error);
- }
+ console.log('error:');
+ console.dir(error);
}
console.log("COMMIT DIFF TOPO");
console.dir(topo_diff);
@@ -136,8 +154,7 @@ function Graph() {
}
util.assert(undefined != node.id, '__addNode: node id missing');
- nodes.push(node);
- id_to_node_map[node.id] = node;
+ _node_add_helper(node);
console.log('__addNode: node added: id: ' + node.id + ' state ' + node.state +
(rz_config.backend_enabled && peer_notify ? ' _commit_ ' : ''));
@@ -160,70 +177,45 @@ function Graph() {
}
this.__addNode = __addNode;
- this._remove_node_set = function(ns, peer_notify) {
-
- peer_notify = undefined === peer_notify ? true : peer_notify;
-
- var cascade_link_rm_set = [], // track cascading link removals
- has_non_temp = false;
- for (var j = 0; j < ns.length; j++) {
- var n = ns[j];
- var i = 0;
- if (n.state != 'temp') {
- has_non_temp = true;
- }
- while (i < links.length) {
- var link = links[i];
- if ((link['__src'].equals(n)) || (link['__dst'].equals(n))) { // compare by id
- links.splice(i, 1);
- cascade_link_rm_set.push(link);
- }
- else {
- i++;
- }
- }
- var index = findNodeIndex(n.id, n.state);
- if (index !== undefined) {
- nodes.splice(index, 1);
-
- util.assert(undefined != n.id, '_remove_node_set: node id missing');
- delete id_to_node_map[n.id];
- }
- }
+ var _node_remove_helper_by_index = function (i) {
+ var id = nodes[i].id;
+ delete id_to_node_index_map[id];
+ delete id_to_node_map[id];
+ nodes.splice(i, 1);
+ }
- cascade_link_rm_set.forEach(function(n){
- console.log('_remove_node_set: removed node: id: ' + n.id);
- });
+ var _node_add_helper = function (node) {
+ id_to_node_map[node.id] = node;
+ id_to_node_index_map[node.id] = nodes.length;
+ nodes.push(node);
+ util.assert(nodes[id_to_node_index_map[node.id]] === node, "master?");
+ }
- if (rz_config.backend_enabled && peer_notify && has_non_temp) {
- var topo_diff = model_diff.new_topo_diff({
- node_set_rm : ns.map(function(n){ return n.id; }),
- link_set_rm : cascade_link_rm_set.map(function(l){ return l.id; }),
- });
- var on_success = function(){
- // FIXME: handle possible outcomes:
- // - rm cascade of connected links
- };
- var on_error = function(){
- // TODO: add problem emblem to node
- };
- rz_api_backend.commit_diff__topo(topo_diff, on_success, on_error);
- }
+ var _link_remove_helper_by_index = function (i) {
+ var id = links[i].id;
+ delete id_to_link_index_map[id];
+ delete id_to_link_map[id];
+ links.splice(i, 1);
}
- this.removeNode = function(id) {
- var n = find_node__by_id(id);
- if (n === undefined) {
- console.log('bug: nonexistent node cannot be removed: ' + id);
- return;
- }
- this._remove_node_set([n]);
+ var _link_add_helper = function (link) {
+ id_to_link_map[link.id] = link;
+ id_to_link_index_map[link.id] = links.length;
+ links.push(link);
+ util.assert(links[id_to_link_index_map[link.id]] === link, "whaaa?");
}
- this.removeNodes = function(n_filer) {
- var ns = find_node_set_by_filer(n_filer);
- this._remove_node_set(ns);
+ var _remove_node_set = function(node_id_set) {
+ node_id_set.forEach(function (id) {
+ if (undefined === id_to_node_map[id]) {
+ console.log("warning: server returned an id we don't have " + id);
+ return;
+ }
+ _node_remove_helper_by_index(id_to_node_index_map[id]);
+ console.log('_remove_node_set: ' + id);
+ });
}
+ this._remove_node_set = _remove_node_set;
/**
*
@@ -417,7 +409,7 @@ function Graph() {
if (undefined == existing_link) {
- links.push(link);
+ _link_add_helper(link);
if (rz_config.backend_enabled && peer_notify){
var topo_diff = model_diff.new_topo_diff({
@@ -451,15 +443,15 @@ function Graph() {
attr_diff.add_link_attr_write(link.id, key, new_link_spec[key]);
}
- var on_ajax_success = function(id_to_link_map){
+ var on_ajax_success = function(id_to_link_map) {
var link_id = link.id; // original link id
- if (id_to_link_map[link_id].id != link_id){
+ if (id_to_link_map[link_id].id != link_id) {
// TODO: handle incoming ID update
util.assert(false, 'update_link: id attr change');
}
var ret_link = id_to_link_map[link_id];
- for (var key in ret_link){
+ for (var key in ret_link) {
if ('id' == key){
continue;
}
@@ -575,8 +567,17 @@ function Graph() {
return this._editProperty(id, state, 'type', newtype);
}
+ function new_attr_diff_prop_value(id, prop, value)
+ {
+ var diff = rz_diff.new_attr_diff();
+
+ diff.add_node_attr_write(id, prop, value);
+ return diff;
+ }
+
this._editProperty = function(id, state, prop, value) {
var n = find_node__by_id(id);
+
if (state !== null && state != n.state){
return false;
}
@@ -589,6 +590,7 @@ function Graph() {
console.log('warning: I hope robot is doing this not-through-backend property change');
}
n[prop] = value;
+ diffBus.push(new_attr_diff_prop_value(id, prop, value));
return true;
}
@@ -601,56 +603,34 @@ function Graph() {
}
}
- var linkGetIndexFromId = function(link_id) {
- for (var i = 0 ; i < links.length ; ++i) {
- if (links[i].id == link_id) {
- return i;
- }
- }
- }
+ this.removeLink = function(link) {
- this.removeLink = function(link, on_success, on_error) {
- var i;
+ util.assert(link.id !== undefined, 'bug: link without an id');
- if (link.id === undefined) {
- console.log('bug: link without an id');
- }
+ this._remove_link_set([link]);
+ }
- function graph_on_success(ret) {
- ret.link_rm.forEach(function (link_id) {
- links.splice(linkGetIndexFromId(link_id), 1);
- });
- if (on_success) {
- on_success();
+ var _remove_link_set = function(link_id_set) {
+ link_id_set.forEach(function (id) {
+ var index = id_to_link_index_map[id];
+ if (undefined === index) {
+ console.log("warning: server returned an id we don't have " + id);
+ return;
}
- }
+ _link_remove_helper_by_index(index);
+ console.log('_remove_link_set: ' + id);
+ });
+ }
+ this._remove_link_set = _remove_link_set;
- for (i = 0 ; i < links.length; ++i) {
- if (link.id !== undefined) {
- if (link.id === links[i].id) {
- if (link.state == 'temp') {
- links.splice(i, 1);
- if (on_success) {
- on_success();
- }
- } else {
- if (rz_config.backend_enabled) {
- var topo_diff = model_diff.new_topo_diff({
- link_set_rm: [link.id]
- });
- rz_api_backend.commit_diff__topo(topo_diff, graph_on_success, on_error);
- }
- }
- return;
- }
- } else {
- if (link.__src.id === links[i].__src.id && link.__dst.id === links[i].__dst.id) {
- links.splice(i, 1);
- return;
- }
- }
- }
- console.log('bug: attempt to remove non existant link');
+ this.removeNodes = function(state) {
+ var temp_node_ids = nodes.filter(function (n) { return n.state == state; })
+ .map(function (n) { return n.id; }),
+ topo_diff = model_diff.new_topo_diff({
+ node_set_rm : temp_node_ids,
+ });
+
+ this.commit_and_tx_diff__topo(topo_diff);
}
this.removeLinks = function(state) {
@@ -722,9 +702,9 @@ function Graph() {
}
/**
- * @param filer: must return true in order for node to be included in the returned set
+ * @param filter: must return true in order for node to be included in the returned set
*/
- var find_node_set_by_filer = function(filter) {
+ var find_node_set_by_filter = function(filter) {
var ret = [];
nodes.map(function(n){
if (true == filter(n)){
@@ -752,13 +732,6 @@ function Graph() {
return foundNodes;
}
- var findNodeIndex = function(id, state) {
- for (var i = 0; i < nodes.length; i++) {
- if ((id && nodes[i].id === id) || (state && nodes[i].state === state))
- return i;
- };
- }
-
function clear() {
nodes.length = 0;
links.length = 0;
@@ -801,15 +774,18 @@ function Graph() {
delete l_ptr.__src_id;
delete l_ptr.__dst_id;
var link_spec = l_ptr;
+ var link_spec = l_ptr;
var link = model_core.create_link_from_spec(src, dst, link_spec);
var l = addLink(link, false);
}
- function on_backend__diff(data) {
- data['node_set'].map(on_backend__node_add);
-
- data['link_set'].map(on_backend__link_add);
- diffBus.push(data);
+ function commit_diff__topo(diff) {
+ // done under protest
+ diff.node_set_add.map(on_backend__node_add);
+ diff.link_set_add.map(on_backend__link_add);
+ _remove_link_set(diff.link_set_rm);
+ _remove_node_set(diff.node_set_rm);
+ diffBus.push(diff);
}
/**
@@ -820,8 +796,8 @@ function Graph() {
// @ajax-trans
function load_from_backend(on_success) {
- function on_success__ajax(data) {
- on_backend__diff(data);
+ function on_success__ajax(diff) {
+ commit_diff__topo(diff);
undefined != on_success && on_success()
}
diff --git a/src/client/rz_core.js b/src/client/rz_core.js
index f0cfb8a9..91a25196 100644
--- a/src/client/rz_core.js
+++ b/src/client/rz_core.js
@@ -1,7 +1,7 @@
"use strict"
-define(['jquery', 'd3', 'consts', 'rz_bus', 'util', 'model/graph', 'model/core', 'view/helpers', 'view/view', 'rz_observer', 'view/selection', 'rz_config', 'rz_mesh'],
-function($, d3, consts, rz_bus, util, model_graph, model_core, view_helpers, view, rz_observer, selection, rz_config, rz_mesh) {
+define(['jquery', 'd3', 'consts', 'rz_bus', 'util', 'model/graph', 'model/core', 'view/helpers', 'view/view', 'rz_observer', 'view/selection', 'rz_config', 'rz_mesh', 'model/diff'],
+function($, d3, consts, rz_bus, util, model_graph, model_core, view_helpers, view, rz_observer, selection, rz_config, rz_mesh, model_diff) {
var addednodes = [],
vis,
@@ -76,15 +76,11 @@ var svgInput = (function() {
d = jelement.data().d;
if (e.which == 13 && newname != d.name) {
if (d.hasOwnProperty('__src')) {
- graph.update_link(d, {name: newname}, function() {
- update_view__graph(true);
- });
+ graph.update_link(d, {name: newname});
} else {
- graph.update_node(d, {name: newname}, function() {
- update_view__graph(true);
- });
- // TODO - 'updating' graphic
+ // TODO - display hourglass
// TODO - use promises to make A follows B readable.
+ graph.update_node(d, {name: newname});
}
}
hide();
@@ -221,9 +217,21 @@ var initDrawingArea = function () {
graph = new model_graph.Graph();
graph.diffBus.onValue(function (diff) {
- var relayout = false == rz_diff.is_attr_diff(diff);
+ var relayout = (false == model_diff.is_attr_diff(diff));
+ console.log('*************');
+ console.dir(diff);
update_view__graph(relayout);
});
+ // TODO: we are listening both on graph.diffBus and selection.selectionChangedBus,
+ // but the relation is actually:
+ //
+ // diffBus -> SelectedNodesBus -> us
+ // diffBus -> us
+ //
+ // we need to deduplicate this event
+ // but there is no coordination, resulting in double updates.
+ selection.selectionChangedBus.onValue(
+ function() { update_view__graph(false); });
var user_id = $('#user_id'),
user = user_id.text();
@@ -277,9 +285,7 @@ var initDrawingArea = function () {
// $('#canvas_d3').dblclick(canvas_handler_dblclick); - see #138
if (rz_config.backend_enabled){
- graph.load_from_backend( function(){
- update_view__graph(false);
- });
+ graph.load_from_backend();
}
}
@@ -333,8 +339,9 @@ function canvas_handler_dblclick(){
var n = model_core.create_node__set_random_id();
n.name = ''; // will be set by user
- graph.addNode(n);
- update_view__graph();
+ graph.commit_and_tx_diff__topo(model_diff.new_topo({
+ node_set_add : [n].map(model_util.adapt_format_write_node),
+ }));
var n_ve = locate_visual_element(n); // locate visual element
@@ -357,14 +364,15 @@ function canvas_handler_dblclick(){
/**
* update view: graph
*/
-function update_view__graph(no_relayout) {
+function update_view__graph(relayout) {
var node,
link,
link_g,
linktext,
nodetext,
unselected_link_group = document.querySelector('#link-group'),
- selected_link_group = document.querySelector('#selected-link-group');
+ selected_link_group = document.querySelector('#selected-link-group'),
+ relayout = relayout || true;
link = vis.selectAll("g.link")
.data(graph.links(), function(d) { return d.id; });
@@ -395,15 +403,10 @@ function update_view__graph(no_relayout) {
view.edge_info.on_delete(function () {
view.edge_info.hide();
- graph.removeLink(that.link, function() {
- update_view__graph(true);
- }, function() {
- console.log("error: could not remove link");
- });
+ graph.removeLink(that.link);
});
view.edge_info.show(d);
selection.update([src, dst]);
- update_view__graph(true);
});
link.attr("class", function(d, i){
@@ -621,12 +624,12 @@ function update_view__graph(no_relayout) {
force.nodes(graph.nodes())
.links(graph.links())
- if (no_relayout) {
+ if (relayout) {
+ force.alpha(0.1).start();
+ } else {
// XXX If we are stopped we need to update the text of the links at least,
// and this is the simplest way
tick();
- } else {
- force.alpha(0.1).start();
}
}
@@ -722,31 +725,39 @@ function tick(e) {
node.attr('visibility', 'visible');
}
-function showNodeInfo(d, i) {
- view.node_info.on_save(function(e, form_data) {
+function showNodeInfo(node, i) {
+ var closed = false;
- graph.update_node(d, form_data, function(){
- var old_type = d.type,
+ view.node_info.on_save(function(e, form_data) {
+ closed = true;
+ graph.update_node(node, form_data, function() {
+ var old_type = node.type,
new_type = form_data.type;
- if (new_type != old_type) {
- view.node_info.show(d);
- }
-
- view.node_info.hide();
- update_view__graph(true);
});
-
+ view.node_info.hide();
return false;
});
+ // FIXME - attribute diff, ignore uninteresting diffs via filtering
+ graph.diffBus.onValue(function () {
+ if (closed) {
+ return Bacon.noMore;
+ }
+ view.node_info.show(node);
+ });
+
view.node_info.on_delete(function() {
- graph.removeNode(d.id); // async
- update_view__graph(false);
+ var topo_diff = model_diff.new_topo_diff({
+ node_set_rm: [node.id]
+ });
+ console.log("closing node info");
+ closed = true;
view.node_info.hide();
+ graph.commit_and_tx_diff__topo(topo_diff);
});
- view.node_info.show(d);
+ view.node_info.show(node);
}
function svg_click_handler(e) {
@@ -760,7 +771,7 @@ function svg_click_handler(e) {
svgInput.hide();
selection.clear();
view.hide();
- update_view__graph(true);
+ update_view__graph(false);
}
return {
@@ -769,7 +780,7 @@ return {
load_from_json: function(result) {
graph.load_from_json(result);
recenterZoom();
- update_view__graph(false);
+ update_view__graph(true);
},
update_view__graph : update_view__graph,
}
diff --git a/src/client/textanalysis.js b/src/client/textanalysis.js
index 5e78f416..8ee4b05f 100644
--- a/src/client/textanalysis.js
+++ b/src/client/textanalysis.js
@@ -68,7 +68,8 @@ var typeStack = [];
var lastnode;
-var sugg = {}, // suggestions for autocompletion of node names
+var sugg_name = {},
+ id_to_name_map = {},
suggestions_options = new Bacon.Bus(); // TODO: Property: same as bus, but with initial value
var ANALYSIS_NODE_START = 'ANALYSIS_NODE_START';
@@ -80,16 +81,27 @@ function selectedType()
return nodetypes[typeindex];
}
-function autoSuggestAddName(name)
+/*
+ * id - undefined | node id
+ *
+ */
+function auto_suggest__update_name(name, id)
{
+ if (id !== undefined && id_to_name_map[id] !== undefined) {
+ delete sugg[id_to_name_map[id]];
+ id_to_name_map[id] = name;
+ }
/* note that name can contain spaces - this is ok. We might want to limit this though? */
sugg[name] = 1;
suggestions_options.push(sugg);
}
-function autoSuggestRemoveName(name)
+function auto_suggest_remove_name(name, id)
{
delete sugg[name];
+ if (id !== undefined) {
+ delete sugg[id];
+ }
suggestions_options.push(sugg);
}
@@ -349,7 +361,7 @@ var textAnalyser = function (newtext, finalize) {
if (finalize === true) {
typesetter = "perm";
for (n = 0; n < token_set_new_node_names.length; n++) {
- autoSuggestAddName(token_set_new_node_names[n]);
+ auto_suggest__update_name(token_set_new_node_names[n]);
}
} else {
typesetter = "temp";
@@ -467,7 +479,7 @@ var textAnalyser = function (newtext, finalize) {
}
} else {
// REINITIALISE GRAPH (DUMB BUT IT WORKS)
- graph.removeNodes(function(n){ return "temp" == n.state; });
+ graph.removeNodes("temp");
graph.removeLinks("temp");
if (!finalize) { // finalize done via topo diff below
@@ -504,22 +516,12 @@ var textAnalyser = function (newtext, finalize) {
// broadcast diff:
// - finalize?
// - broadcast_diff requested by caller
- var on_success = function (ret) {
- console.dir(ret);
- rz_core.update_view__graph();
- }
- var on_error = function () {
- console.log('askeeeeeeeeeeeeew');
- }
// drop bubble node
ret.node_set_add = ret.node_set_add.filter(function(n) { return n.type != 'bubble'; });
// drop and links
ret.link_set_add = ret.link_set_add.filter(function(l) { return l.name !== 'and'; });
- graph.commit_diff__topo(ret, on_success, on_error);
+ graph.commit_and_tx__topo(ret);
}
-
- // update graph if not going through backend
- rz_core.update_view__graph(!finalize && comp.graph_same);
};
if (finalize) {
@@ -532,24 +534,31 @@ var textAnalyser = function (newtext, finalize) {
function init(graph)
{
// deal with new nodes
- graph.diffBus.filter(function (diff) {
- return diff.nodes && diff.nodes.added;
- }).map(function (diff) {
- return diff.nodes.added.toLowerCase();
- }).onValue(autoSuggestAddName);
+ graph.diffBus
+ .filter(function (diff) {
+ return diff.node_set_add && diff.node_set_add.length > 0;
+ })
+ .map(".node_set_rm")
+ .flatMap(Bacon.fromArray)
+ .map(".name")
+ .map(function (name) { return name.toLowerCase(); })
+ .onValue(auto_suggest__update_name);
// deal with renamed links
- graph.diffBus.filter(function (diff) {
- return diff && diff.changed && diff.changed.links;
- }).map(function (diff) {
- return diff.changed.links;
- }).flatMap(Bacon.fromArray)
- .onValue(function (diff) {
- console.log('renamed link ' + diff.removed + ' -> ' + diff.added);
- autoSuggestRemoveName(diff.removed.toLowerCase());
- autoSuggestAddName(diff.added.toLowerCase());
- });
-
+ /*
+ // TODO renamed links - broken in server, DBO_attr_diff_commit doesn't return an Attr_Diff
+ graph.diffBus
+ .filter(function (diff) {
+ return diff && diff.link_set_rm && diff.link_set_rm.length > 0;
+ })
+ .map(".link_set_rm")
+ .flatMap(Bacon.fromArray)
+ .onValue(function (name) {
+ var name = TODO,
+ id = TODO;
+ auto_suggest__update_name(name, id);
+ });
+ */
// TODO renamed nodes, plus reuse part of the pipeline.
}
diff --git a/src/client/textanalysis.ui.js b/src/client/textanalysis.ui.js
index a3b3becf..b06978c1 100644
--- a/src/client/textanalysis.ui.js
+++ b/src/client/textanalysis.ui.js
@@ -107,7 +107,6 @@ function changeType(arg) {
typeselection.showChosenType(nodetype);
rz_core.graph.findCoordinates(id);
}
- rz_core.update_view__graph(true);
}
return {
diff --git a/src/client/view/selection.js b/src/client/view/selection.js
index 27842e11..9a87494a 100644
--- a/src/client/view/selection.js
+++ b/src/client/view/selection.js
@@ -1,17 +1,31 @@
-define(['rz_core'],
-function(rz_core) {
+define(['rz_core', 'Bacon'],
+function(rz_core, Bacon) {
function get_rz_core()
{
// circular dependency on rz_core, so require.js cannot solve it.
if (rz_core === undefined) {
rz_core = require('rz_core');
- rz_core.graph.diffBus.onValue(updateSelectionOnDiff);
+ listen_on_diff_bus(rz_core.graph.diffBus);
}
return rz_core;
}
-var selected_nodes = [];
+var selected_nodes = [],
+ selectionChangedBus = new Bacon.Bus();
+
+function listen_on_diff_bus(diffBus)
+{
+ diffBus
+ .filter(".node_set_rm")
+ .onValue(function (diff) {
+ var node_node_cmp = (function (a, b) { return a.id > b.id; }),
+ node_id_cmp = (function (a, b) { return a.id === b ? 0 : (a.id > b ? 1 : -1); });
+
+ updateSelectedNodesBus(sortedArrayDiff(selected_nodes.sort(node_node_cmp),
+ diff.node_set_rm.sort(), node_id_cmp));
+ });
+}
function sortedArrayDiff(a, b, a_cmp_b)
{
@@ -38,18 +52,10 @@ function sortedArrayDiff(a, b, a_cmp_b)
return ret;
}
-function updateSelectionOnDiff(diff)
+function updateSelectedNodesBus(new_selected_nodes)
{
- var node_node_cmp = (function (a, b) { return a.id > b.id; }),
- node_id_cmp = (function (a, b) { return a.id === b ? 0 : (a.id > b ? 1 : -1); });
-
- if (diff.nodes.removed === undefined || selected_nodes.length == 0) {
- return;
- }
- console.log("selection enter: " + String(selected_nodes.map(function(x) { return x.id; })));
- console.log("removed nodes enter: " + String(diff.nodes.removed));
- selected_nodes = sortedArrayDiff(selected_nodes.sort(node_node_cmp), diff.nodes.removed.sort(), node_id_cmp);
- console.log("selection exit: " + String(selected_nodes.map(function(x) { return x.id; })));
+ selected_nodes = new_selected_nodes;
+ selectionChangedBus.push(selected_nodes);
}
function byVisitors(node_selector, link_selector) {
@@ -66,7 +72,7 @@ function connectedComponent(nodes) {
link,
data;
- selected_nodes = nodes.map(function(x) { return x; });
+ updateSelectedNodesBus(nodes.map(function(x) { return x; }));
for (i = 0 ; i < connected.nodes.length ; ++i) {
data = connected.nodes[i];
@@ -137,6 +143,7 @@ return {
update: update,
selected_class: selected_class,
node_selected: node_selected,
+ selectionChangedBus: selectionChangedBus,
};
});
diff --git a/src/server/model/graph.py b/src/server/model/graph.py
index 87c0a8b0..76368a24 100644
--- a/src/server/model/graph.py
+++ b/src/server/model/graph.py
@@ -106,9 +106,9 @@ class Topo_Diff(object):
def to_json_dict(self):
ret = {k: getattr(self, k) for k in ['link_set_rm',
- 'node_set_rm',
- 'node_set_add',
- 'link_set_add'] }
+ 'node_set_rm',
+ 'node_set_add',
+ 'link_set_add'] }
return ret
def __init__(self, link_set_rm=[],