diff options
| author | Alon Levy <alon@pobox.com> | 2014-11-24 20:30:09 +0200 |
|---|---|---|
| committer | Alon Levy <alon@pobox.com> | 2014-11-24 20:30:09 +0200 |
| commit | f2ce89be26456be0fba9b298a19e98b83482170d (patch) | |
| tree | d4cc432d2f8023859bfa91278484b250fa8eaf50 | |
| parent | 224b9b40ef7ac3b560c28eaef8dea0473d362e71 (diff) | |
add search
The added input lets you select by *oring* multiple space less
expressions. i.e. 'a b' will find all nodes with name containing either
'a' or 'b'. Not the specification, will be remedied later (i.e. maybe
tomorrow, who knows).
excuse the bulk commit, this does 3 things:
splits off selection logic into src/view/selection
- handles setting state explicityl on Node's, still bad.
- bad at least the 'selected' state (i.e. boolean) is in one place.
- common point for clicking on nodes/edges and selecting view selection
input
makes graph.getConnectedNodesAndLinks accept an array and not just a
single node
implements the search functionality outlined in the first paragraph.
| -rw-r--r-- | src/main.js | 16 | ||||
| -rw-r--r-- | src/model/graph.js | 106 | ||||
| -rw-r--r-- | src/rz_core.js | 99 | ||||
| -rw-r--r-- | src/view/selection.js | 80 |
4 files changed, 186 insertions, 115 deletions
diff --git a/src/main.js b/src/main.js index b4f71001..feefcf57 100644 --- a/src/main.js +++ b/src/main.js @@ -1,5 +1,5 @@ -define(['textanalysis.ui', 'textanalysis', 'buttons', 'history', 'drag_n_drop', 'robot', 'model/core', 'rz_config', 'rz_core'], -function(textanalysis_ui, textanalysis, buttons, history, drag_n_drop, robot, model_core, rz_config, rz_core) { +define(['textanalysis.ui', 'textanalysis', 'buttons', 'history', 'drag_n_drop', 'robot', 'model/core', 'rz_config', 'rz_core', 'view/selection'], +function(textanalysis_ui, textanalysis, buttons, history, drag_n_drop, robot, model_core, rz_config, rz_core, selection) { function getParameterByName(name) { name = name.replace(/[\[]/, "\\[").replace(/[\]]/, "\\]"); @@ -43,6 +43,18 @@ function(textanalysis_ui, textanalysis, buttons, history, drag_n_drop, $('#search').focus(); } }; + // TODO: move me somewhere + $('#search').on('input', function(e) { + var text = this.value, + r = new RegExp(text.replace(' ', '|')); // TODO fails for quotes + console.log('search: ' + text); + if (text.length > 0) { + selection.byVisitors(function (n) { return n.name.match(r); }); + } else { + selection.clear(); + } + rz_core.update_view__graph(false); + }); // TODO: interaction between the hack above and this model_core.init(rz_config); textanalysis.init(); diff --git a/src/model/graph.js b/src/model/graph.js index 6d13a83e..a71ba4c5 100644 --- a/src/model/graph.js +++ b/src/model/graph.js @@ -118,7 +118,7 @@ function Graph() { * TODO: implement for d !== 1 * */ - this.getConnectedNodesAndLinks = function(n, d) { + this.getConnectedNodesAndLinks = function(chosen_nodes, d) { var i = 0, j = 0, adjacentnode, @@ -126,9 +126,7 @@ function Graph() { link2, ret = {'nodes':[], 'links':[]}; - $(".debug").html(n.state); - - if (n === undefined) { + if (chosen_nodes === undefined) { console.log('getConnectedNodesAndLinks: bug: called with undefined node'); return; } @@ -137,38 +135,44 @@ function Graph() { } d = d || 1; + if (chosen_nodes.length === undefined) { + console.log('getConnectedNodesAndLinks: expected array'); + } + while (i < links.length) { link = links[i]; - // XXX: using name comparison because n might be stale - if (compareNames(link.__src.name, n.name)) { - adjacentnode = findNode(link.__dst.id, null); - if (adjacentnode.state !== "temp") { - ret.nodes.push({type: 'exit', node: adjacentnode}); - } - ret.links.push({type: 'exit', link: link}); + chosen_nodes.forEach(function (n) { + // XXX: using name comparison because n might be stale + if (compareNames(link.__src.name, n.name)) { + adjacentnode = findNode(link.__dst.id, null); + if (adjacentnode.state !== "temp") { + ret.nodes.push({type: 'exit', node: adjacentnode}); + } + ret.links.push({type: 'exit', link: link}); - if (link.__dst.type === "chainlink") { - while (j < links.length) { - link2 = links[j]; - if (link.__dst.id === link2.__dst.id && - link2.__dst.type === "chainlink" && - link2.__dst.state !== "temp") { - adjacentnode = findNode(link2.__src.id, null); - if (adjacentnode.state !== "temp") { - ret.nodes.push({type: 'enter', node: adjacentnode}); + if (link.__dst.type === "chainlink") { + while (j < links.length) { + link2 = links[j]; + if (link.__dst.id === link2.__dst.id && + link2.__dst.type === "chainlink" && + link2.__dst.state !== "temp") { + adjacentnode = findNode(link2.__src.id, null); + if (adjacentnode.state !== "temp") { + ret.nodes.push({type: 'enter', node: adjacentnode}); + } + ret.links.push({type: 'enter', link: link2}); } - ret.links.push({type: 'enter', link: link2}); + j++; } - j++; } + j=0; } - j=0; - } - if (compareNames(links[i].__dst.name, n.name)) { - adjacentnode = findNode(links[i].__src.id, null); - if (adjacentnode.state !== "temp") adjacentnode.state = "enter"; - links[i].state = "enter"; - } + if (compareNames(links[i].__dst.name, n.name)) { + adjacentnode = findNode(links[i].__src.id, null); + if (adjacentnode.state !== "temp") adjacentnode.state = "enter"; + links[i].state = "enter"; + } + }); i++; } return ret; @@ -634,6 +638,50 @@ function Graph() { var get_links = function() { return links; }; this.links = get_links; + function setRegularState() { + var x, node, link, s; + + for (x in nodes) { + node = nodes[x]; + s = node.state; + if (s === 'chosen' || s === 'enter' || s === 'exit') { + node.state = 'perm'; + } + } + for (x in links) { + link = links[x]; + s = link.state; + if (s === 'chosen' || s === 'enter' || s === 'exit') { + link.state = 'perm'; + } + } + } + this.setRegularState = setRegularState; + + this.findByVisitors = function(node_visitor, link_visitor) { + var n_length = nodes.length, + l_length = links.length, + selected = [], + i, + node, + link, + state; + + if (!node_visitor) { + return; + } + + for (i = 0 ; i < n_length; ++i) { + node = nodes[i]; + if (node.state == 'temp') { + continue; + } + if (node_visitor(node)) { + selected.push(node); + } + } + return selected; + } } return { diff --git a/src/rz_core.js b/src/rz_core.js index 755f3364..83de9033 100644 --- a/src/rz_core.js +++ b/src/rz_core.js @@ -1,7 +1,7 @@ "use strict" -define(['jquery', 'd3', 'consts', 'signal', 'util', 'model/graph', 'model/core', 'view/helpers', 'view/view', 'rz_observer'], -function($, d3, consts, signal, util, model_graph, model_core, view_helpers, view, rz_observer) { +define(['jquery', 'd3', 'consts', 'signal', 'util', 'model/graph', 'model/core', 'view/helpers', 'view/view', 'rz_observer', 'view/selection'], +function($, d3, consts, signal, util, model_graph, model_core, view_helpers, view, rz_observer, selection) { var addednodes = []; @@ -24,10 +24,6 @@ var drag; var force; -// TODO hide inside a selection object? behavior? probably a good idea. route selection to it, -// let it change state on nodes & links and query it for selection activity. (between 'select' and 'unselect') -var selection; // boolean, true if there is a current selection - function recenterZoom() { vis.attr("transform", "translate(0,0)scale(1)"); } @@ -175,14 +171,6 @@ function canvas_handler_dblclick(){ }); } -var node_selected = function(node) { - return node.state == 'chosen' || node.state == 'enter' || node.state == 'exit'; -} - -var selected_class = function(node) { - return selection ? (node_selected(node) ? "selected" : "notselected") : ""; -} - /** * update view: graph */ @@ -212,7 +200,7 @@ function update_view__graph(no_relayout) { .attr("markerHeight", 4) .attr("orient", "auto") .attr("class", function(d) { - return selected_class(d); + return selection.selected_class(d); }) .append("svg:path") .attr("d", "M0,-5L10,0L0,5"); @@ -237,7 +225,7 @@ function update_view__graph(no_relayout) { view.edge_info.hide(); }); view.edge_info.show(d); - removeHighlight(); + selection.clear(); highlight(src); highlight(dst); src.state = 'chosen'; @@ -248,12 +236,12 @@ function update_view__graph(no_relayout) { link.attr("class", function(d, i){ var temp_and = (d.name && d.name.replace(/ /g,"")=="and" && d.state==="temp") ? "temp_and" : ""; - return ["graph link", temp_and, selected_class(d)].join(' '); + return ["graph link", temp_and, selection.selected_class(d)].join(' '); }); link.selectAll('path.link') .attr('class', function(d) { - return [d.state, selected_class(d), "link graph"].join(' '); + return [d.state, selection.selected_class(d), "link graph"].join(' '); }); link.exit().remove(); @@ -268,7 +256,7 @@ function update_view__graph(no_relayout) { linktext.enter() .append("text") .attr("class", function(d) { - return ["linklabel graph", selected_class(d)].join(' '); + return ["linklabel graph", selection.selected_class(d)].join(' '); }) .attr("text-anchor", "middle") .on("click", function(d, i) { @@ -310,7 +298,7 @@ function update_view__graph(no_relayout) { } if (d.state !== "temp"){ editNode(this, d, i); - removeHighlight(); + selection.clear(); showInfo(this.node, i); } }) @@ -320,7 +308,7 @@ function update_view__graph(no_relayout) { this.node = d; }) .attr('class', function(d) { - return ['node', selected_class(d)].join(' '); + return ['node', selection.selected_class(d)].join(' '); }); nodetext = nodeEnter.insert("text") @@ -360,10 +348,10 @@ function update_view__graph(no_relayout) { } d3.event.stopPropagation(); if(d.state!=="temp") { - removeHighlight(); + selection.clear(); showInfo(d, i); } else { - removeHighlight(); + selection.clear(); } update_view__graph(true); }); @@ -390,7 +378,7 @@ function update_view__graph(no_relayout) { }) .on("click", function(d, i) { if(d.state!=="temp") { - removeHighlight(); + selection.clear(); showInfo(d, i); } }); @@ -580,66 +568,9 @@ function tick(e) { node.attr('visibility', 'visible'); } -function removeHighlight() { - // TODO: stop manipulating state - var nodes = graph.nodes(), - links = graph.links(), - k = 0, j = 0; - - selection = false; - while (k < nodes.length) { - if (nodes[k]['state'] === "enter" || nodes[k]['state'] === "exit" || nodes[k]['state'] === "chosen") { - nodes[k]['state'] = "perm"; - } - k++; - } - while (j < links.length) { - links[j]['state'] = "perm"; - j++; - } -} - -function highlight(n) -{ - var n, - connected = graph.getConnectedNodesAndLinks(n, 1), - i, - node, - link, - data; - - selection = true; - n.state = 'chosen'; - - for (i = 0 ; i < connected.nodes.length ; ++i) { - data = connected.nodes[i]; - node = data.node; - switch (data.type) { - case 'exit': - node.state = 'exit'; - break; - case 'enter': - node.state = 'enter'; - break; - }; - } - for (i = 0 ; i < connected.links.length ; ++i) { - data = connected.links[i]; - link = data.link; - switch (data.type) { - case 'exit': - link.state = 'exit'; - break; - case 'enter': - link.state = 'enter'; - break; - }; - } -} - function showInfo(d, i) { if (d.state !== "chosen" && d.state !== 'temp') { - highlight(d); + selection.connectedComponent([d]); view.node_info.show(d); view.node_info.on_submit(function() { if (d.type === "deliverable") { @@ -658,7 +589,7 @@ function showInfo(d, i) { } }); } else { - removeHighlight(); + selection.clear(); view.node_info.hide(); } update_view__graph(true); @@ -669,7 +600,7 @@ function mousedown() { $('.editinfo').css('left', 0); $('.editlinkinfo').css('top', -100); $('.editlinkinfo').css('left', 0); - removeHighlight(); + selection.clear(); view.hide(); update_view__graph(true); } diff --git a/src/view/selection.js b/src/view/selection.js new file mode 100644 index 00000000..57f2443d --- /dev/null +++ b/src/view/selection.js @@ -0,0 +1,80 @@ +define(['rz_core'], +function(rz_core) { + +function get_rz_core() +{ + // circular dependency on rz_core, so require.js cannot solve it. + if (rz_core === undefined) { + rz_core = require('rz_core'); + } + return rz_core; +} + +var selection = false; + +function byVisitors(node_selector, link_selector) { + selection = true; + var selected_nodes = get_rz_core().graph.findByVisitors(node_selector, link_selector); + + clear(); + connectedComponent(selected_nodes); +} + +function connectedComponent(nodes) { + var nodes, + connected = get_rz_core().graph.getConnectedNodesAndLinks(nodes, 1), + i, + node, + link, + data; + + selection = true; + nodes.forEach(function (n) { n.state = 'chosen'; }); + + for (i = 0 ; i < connected.nodes.length ; ++i) { + data = connected.nodes[i]; + node = data.node; + switch (data.type) { + case 'exit': + node.state = 'exit'; + break; + case 'enter': + node.state = 'enter'; + break; + }; + } + for (i = 0 ; i < connected.links.length ; ++i) { + data = connected.links[i]; + link = data.link; + switch (data.type) { + case 'exit': + link.state = 'exit'; + break; + case 'enter': + link.state = 'enter'; + break; + }; + } +} + +var node_selected = function(node) { + return node.state == 'chosen' || node.state == 'enter' || node.state == 'exit' || node.state == 'selected'; +} + +var selected_class = function(node) { + return selection ? (node_selected(node) ? "selected" : "notselected") : ""; +} + +var clear = function() { + selection = false; + get_rz_core().graph.setRegularState(); +} + +return { + 'byVisitors': byVisitors, + 'connectedComponent': connectedComponent, + 'clear': clear, + 'selected_class': selected_class, +}; + +}); |
