From fbee23bcb3aaba6902079919f0a2bb87f862412d Mon Sep 17 00:00:00 2001 From: Rees Simmons Date: Mon, 3 Oct 2016 14:26:24 -0400 Subject: [PATCH 1/9] Updating to drag and drop branch, only keeping the stable componenets --- nengo_gui/static/ace.js | 3 +- nengo_gui/static/components/netgraph.css | 2 - nengo_gui/static/components/netgraph.js | 29 +- nengo_gui/static/components/netgraph_item.js | 43 +- nengo_gui/static/drag_and_drop.css | 56 ++ nengo_gui/static/drag_and_drop.js | 564 +++++++++++++++++++ nengo_gui/static/side_menu.css | 29 +- nengo_gui/static/side_menu.js | 59 +- nengo_gui/templates/page.html | 152 +++-- 9 files changed, 838 insertions(+), 99 deletions(-) create mode 100644 nengo_gui/static/drag_and_drop.css create mode 100644 nengo_gui/static/drag_and_drop.js diff --git a/nengo_gui/static/ace.js b/nengo_gui/static/ace.js index 07ca9b8a..fdffd361 100644 --- a/nengo_gui/static/ace.js +++ b/nengo_gui/static/ace.js @@ -32,7 +32,8 @@ Nengo.Ace = function (uid, args) { var code_div = document.createElement('div') code_div.id = 'editor' $('#rightpane').append(code_div); - this.editor = ace.edit('editor') + this.editor = ace.edit('editor'); + this.editor.$blockScrolling = Infinity; this.editor.getSession().setMode("ace/mode/python"); this.editor.gotoLine(1); this.marker = null; diff --git a/nengo_gui/static/components/netgraph.css b/nengo_gui/static/components/netgraph.css index 583b64b0..f2b2ed22 100644 --- a/nengo_gui/static/components/netgraph.css +++ b/nengo_gui/static/components/netgraph.css @@ -17,7 +17,6 @@ g.net rect{ .netgraph g text { text-anchor: middle; dominant-baseline: text-before-edge; - cursor: default !important; } .netgraph g text:active { @@ -62,4 +61,3 @@ rect.view{ text-anchor: middle; dominant-baseline: text-before-edge; } - diff --git a/nengo_gui/static/components/netgraph.js b/nengo_gui/static/components/netgraph.js index 8b50075c..544c991d 100644 --- a/nengo_gui/static/components/netgraph.js +++ b/nengo_gui/static/components/netgraph.js @@ -16,6 +16,12 @@ Nengo.NetGraph = function(parent, args) { this.offsetX = 0; // global x,y pan offset this.offsetY = 0; + // override the random position chosen by the server for a new item. + // This is used by the drag-and-drop system to ensure that the newly + // created items appear at the position they were dropped at. + this.override_positions = {}; + this.override_sizes = {} + var scale = 1.0; Object.defineProperty(this, 'scale', { // global scaling factor @@ -127,7 +133,7 @@ Nengo.NetGraph = function(parent, args) { this.width = $(this.svg).width(); this.height = $(this.svg).height(); - + this.tool_height = $(toolbar.toolbar).height(); /** three separate layers, so that expanded networks are at the back, @@ -348,9 +354,9 @@ Nengo.NetGraph.prototype.on_message = function(event) { item.y = data.pos[1]; item.width = data.size[0]; item.height = data.size[1]; - + item.redraw(); - + this.scaleMiniMap(); } else if (data.type === 'config') { @@ -373,7 +379,8 @@ Nengo.NetGraph.prototype.on_message = function(event) { if (item === undefined) { item = this.svg_conns[data.uid]; } - + delete this.override_positions[data.uid]; + delete this.override_sizes[data.uid]; item.remove(); } else if (data.type === 'reconnect') { @@ -447,6 +454,12 @@ Nengo.NetGraph.prototype.create_object = function(info) { var item_mini = new Nengo.NetGraphItem(this, info, true); this.minimap_objects[info.uid] = item_mini; + if(this.override_positions.hasOwnProperty(info.uid)){ + info.pos[0] = this.override_positions[info.uid][0]; + info.pos[1] = this.override_positions[info.uid][1]; + info.size[0] = this.override_sizes[info.uid][0]; + info.size[1] = this.override_sizes[info.uid][1]; + } var item = new Nengo.NetGraphItem(this, info, false, item_mini); this.svg_objects[info.uid] = item; @@ -461,7 +474,7 @@ Nengo.NetGraph.prototype.create_object = function(info) { Nengo.NetGraph.prototype.create_connection = function(info) { var conn_mini = new Nengo.NetGraphConnection(this, info, true); this.minimap_conns[info.uid] = conn_mini; - + var conn = new Nengo.NetGraphConnection(this, info, false, conn_mini); this.svg_conns[info.uid] = conn; }; @@ -480,7 +493,7 @@ Nengo.NetGraph.prototype.on_resize = function(event) { var new_width = item.get_screen_width() / this.scale; var new_height = item.get_screen_height() / this.scale; item.width = new_width/(2*width); - item.height = new_height/(2*height); + item.height = new_height/(2*height); } } } @@ -583,7 +596,7 @@ Nengo.NetGraph.prototype.create_minimap = function () { this.mm_width = $(this.minimap).width(); this.mm_height = $(this.minimap).height(); - + // default display minimap this.mm_display = true; this.toggleMiniMap(); @@ -665,7 +678,7 @@ Nengo.NetGraph.prototype.scaleMiniMap = function () { * main viewport and scale the viewbox to reflect that. */ Nengo.NetGraph.prototype.scaleMiniMapViewBox = function () { if (!this.mm_display) { return; } - + var mm_w = this.mm_width var mm_h = this.mm_height diff --git a/nengo_gui/static/components/netgraph_item.js b/nengo_gui/static/components/netgraph_item.js index 63a239cd..169b16a5 100644 --- a/nengo_gui/static/components/netgraph_item.js +++ b/nengo_gui/static/components/netgraph_item.js @@ -12,7 +12,7 @@ */ Nengo.NetGraphItem = function(ng, info, minimap, mini_item) { var self = this; - + this.ng = ng; this.type = info.type; this.uid = info.uid; @@ -22,6 +22,9 @@ Nengo.NetGraphItem = function(ng, info, minimap, mini_item) { this.fixed_width = null; this.fixed_height = null; this.dimensions = info.dimensions; + if(info.type == 'ens'){ + this.n_neurons = info.n_neurons; + } this.minimap = minimap; this.html_node = info.html; if (minimap == false) { @@ -32,7 +35,7 @@ Nengo.NetGraphItem = function(ng, info, minimap, mini_item) { this.g_networks = ng.g_networks_mini; this.g_items = ng.g_items_mini; } - + var width = info.size[0]; Object.defineProperty(this, 'width', { get: function() { @@ -40,7 +43,7 @@ Nengo.NetGraphItem = function(ng, info, minimap, mini_item) { }, set: function(val) { width = val; - + if (!this.minimap) { this.mini_item.width = val } @@ -53,7 +56,7 @@ Nengo.NetGraphItem = function(ng, info, minimap, mini_item) { }, set: function(val) { height = val; - + if (!this.minimap) { this.mini_item.height = val } @@ -66,7 +69,7 @@ Nengo.NetGraphItem = function(ng, info, minimap, mini_item) { }, set: function(val) { x = val; - + if (!this.minimap) { this.mini_item.x = val } @@ -79,13 +82,13 @@ Nengo.NetGraphItem = function(ng, info, minimap, mini_item) { }, set: function(val) { y = val; - + if (!this.minimap) { this.mini_item.y = val } } }); - + /** if this is a network, the children list is the set of NetGraphItems * and NetGraphConnections that are inside this network */ this.children = []; @@ -119,6 +122,7 @@ Nengo.NetGraphItem = function(ng, info, minimap, mini_item) { var g = this.ng.createSVGElement('g'); this.g = g; this.g_items.appendChild(g); + g.setAttribute('data-id',this.uid); g.classList.add(this.type); this.area = this.ng.createSVGElement('rect'); @@ -164,7 +168,7 @@ Nengo.NetGraphItem = function(ng, info, minimap, mini_item) { g.appendChild(this.area); this.redraw(); - + interact.margin(10); if (!this.minimap) { @@ -197,6 +201,7 @@ Nengo.NetGraphItem = function(ng, info, minimap, mini_item) { onend: function(event) { var item = self.ng.svg_objects[uid]; item.constrain_position(); + self.ng.override_positions[uid] = [item.x,item.y]; self.ng.notify({act:"pos", uid:uid, x:item.x, y:item.y}); item.redraw(); @@ -283,7 +288,7 @@ Nengo.NetGraphItem = function(ng, info, minimap, mini_item) { } item.redraw(); - + if (self.depth === 1) { self.ng.scaleMiniMap(); } @@ -292,6 +297,7 @@ Nengo.NetGraphItem = function(ng, info, minimap, mini_item) { var item = self.ng.svg_objects[uid]; item.constrain_position(); item.redraw(); + self.ng.override_sizes[uid] = [item.width,item.height]; self.ng.notify({act:"pos_size", uid:uid, x:item.x, y:item.y, width:item.width, height:item.height}); @@ -410,6 +416,7 @@ Nengo.NetGraphItem.prototype.generate_menu = function () { function() {self.create_graph('SpaSimilarity', self.sp_targets[0]);}]); } // TODO: Enable input and output value plots for basal ganglia network + items.push(['Delete Component',function(){Nengo.vpl.delete_component(self.uid)}]); items.push(['Details ...', function() {self.create_modal();}]); return items; }; @@ -581,7 +588,7 @@ Nengo.NetGraphItem.prototype.remove = function() { if (this.depth == 1) { this.ng.scaleMiniMap(); } - + if (!this.minimap) { this.mini_item.remove(); } @@ -616,10 +623,10 @@ Nengo.NetGraphItem.prototype.constrain_position = function() { if (this.parent !== null) { this.width = Math.min(0.5, this.width); this.height = Math.min(0.5, this.height); - + this.x = Math.min(this.x, 1.0-this.width); this.x = Math.max(this.x, this.width); - + this.y = Math.min(this.y, 1.0-this.height); this.y = Math.max(this.y, this.height); } @@ -732,7 +739,7 @@ Nengo.NetGraphItem.prototype.redraw_size = function() { Nengo.NetGraphItem.prototype.get_screen_width = function() { if (this.minimap && !this.ng.mm_display) { return 1; } - + if (this.fixed_width !== null) { return this.fixed_width; } @@ -754,7 +761,7 @@ Nengo.NetGraphItem.prototype.get_screen_width = function() { Nengo.NetGraphItem.prototype.get_screen_height = function() { if (this.minimap && !this.ng.mm_display) { return 1; } - + if (this.fixed_height !== null) { return this.fixed_height; } @@ -782,9 +789,9 @@ Nengo.NetGraphItem.prototype.redraw = function() { this.redraw_children(); this.redraw_child_connections(); this.redraw_connections(); - - if (!this.minimap && this.ng.mm_display) { - this.mini_item.redraw() + + if (!this.minimap && this.ng.mm_display) { + this.mini_item.redraw() } } @@ -793,7 +800,7 @@ Nengo.NetGraphItem.prototype.redraw = function() { Nengo.NetGraphItem.prototype.get_screen_location = function() { // FIXME this should probably use this.ng.get_scaled_width and this.ng.get_scaled_height if (this.minimap && !this.ng.mm_display) { return [1, 1]; } - + if (this.minimap == false) { var w = this.ng.width * this.ng.scale; var h = this.ng.height * this.ng.scale; diff --git a/nengo_gui/static/drag_and_drop.css b/nengo_gui/static/drag_and_drop.css new file mode 100644 index 00000000..f5c0e8a3 --- /dev/null +++ b/nengo_gui/static/drag_and_drop.css @@ -0,0 +1,56 @@ +.drag_drop_menu{ + height:100%; + background: #ddd; + color:inherit; + overflow: hidden; +} + +.off svg{ + cursor: pointer; + background: inherit; + fill: rgb(180,180,180); +} +.on svg{ + cursor: pointer; + background: #AAA; + fill: #DDD; +} + + +#mode_list{ + position: relative; + top: 0; + left: 0; + width: 100%; + color: inherit; + padding: 30px; + z-index: 99999; + overflow-y: scroll; +} + +.mode{ + position: relative; + float: left; + left: 15%; + width: 70%; + text-align: center; + -webkit-user-select: none; /* Chrome/Safari */ + -moz-user-select: none; /* Firefox */ +} +.off > svg:hover{ + cursor: pointer; + background-color: #BBB; + fill: #DDD; +} + +.node_effect_good:hover > rect:nth-child(2){ + fill: #00FF00 !important; +} + +.node_effect_bad:hover > rect:nth-child(2){ + fill: #FF0000 !important; +} + +.ens_effect_good:hover > .ensemble{ + fill: #00FF00 !important; +} diff --git a/nengo_gui/static/drag_and_drop.js b/nengo_gui/static/drag_and_drop.js new file mode 100644 index 00000000..e8c02aa4 --- /dev/null +++ b/nengo_gui/static/drag_and_drop.js @@ -0,0 +1,564 @@ +Nengo.VPL = function(){ + var self = this; + //Setting Click handlers for toggle buttons + $("#mode_list").height($(document).height()*0.85); + $(document).keyup(function(e) { + if (e.keyCode === 27) self.unselct_component(); // esc + }); + + $("#Node > svg").on('click',function(){ + self.component_toggle('Node'); + }); + + $("#Ensemble > svg").on('click',function(){ + self.component_toggle('Ensemble'); + }); + + $("#Network > svg").on('click',function(){ + self.component_toggle('Network'); + }); + + $("#Connection > svg").on('click',function(){ + self.component_toggle('Connection'); + }); +} +//Activates type mode for creating components +Nengo.VPL.prototype.component_toggle = function(type) { + var ng = Nengo.netgraph; + var self = this; + if(interact($("#netgraph")[0]).ondragmove != null){ + drag_func = interact($("#netgraph")[0]).ondragmove; + } + if($("#"+type).attr('class') == 'mode off'){ + self.unselct_component(); + $("#"+type).attr('class','mode on'); + } else{ + self.unselct_component(); + return; + } + if($("#"+type).attr('class') == 'mode on'){ + + + var cur_obj = ""; + var obj_class = ""; + var obj_name = ""; + if(type != 'Connection'){ + $('#netgraph').css('cursor','crosshair','important'); + self.create_component(type); + } else if(type == 'Connection'){ + $('#netgraph').css('cursor','cell','important'); + self.create_connection(); + } + } +} + + +Nengo.VPL.prototype.create_component = function(type){ + var ng = Nengo.netgraph; + var self = this; + var cur_obj, obj_class,obj_name; + var cursor_uid = "cursor."+type; + var name_convert = {"Node":"node","Ensemble":"ens","Network":"net"} + var element = document.getElementById('main'); + element.style['box-shadow'] = "inset 0 0 30px #00FF00"; + self.toggle_item_drag(false); + + $(document).mousedown(function(event) { + if(event.button == 0){}else{return;} + + cur_obj = $($(event.target).parent()[0]); + obj_class = cur_obj.attr('class'); + obj_name = cur_obj.attr('data-id'); + + /** Creates cursor shape. */ + if (event.pageX - $("#main").offset().left < 0 || + event.pageX - $("#main").offset().left > $("#main").width() - 80 || + event.pageY - $("#main").offset().top < 0 || + event.pageY - $("#main").offset().top > $("#main").height() - 80) { + return; + } + var cursor_start = self.compute_position(event); + var cursor_width, cursor_height, cursor_end, final_pos, final_size; + var mousemoved = false; + + ng.create_object({'pos':[-1000,-1000],'size': + self.compute_size(type,{"parent_network":""}),'uid':cursor_uid, + 'type':name_convert[type],'parent':null,'label':""}); + + var cursor_item = ng.svg_objects[cursor_uid]; + cursor_item.shape.style['fill-opacity'] = 0.4; + cursor_item.width = 0; + cursor_item.height = 0; + cursor_item.x = cursor_start[0]; + cursor_item.y = cursor_start[1]; + + $(document).mousemove(function(evt) { + + mousemoved = true; + + cursor_end = self.compute_position(evt); + cursor_width = Math.abs(cursor_end[0] - cursor_start[0]); + cursor_height = Math.abs(cursor_end[1] - cursor_start[1]); + cursor_item.x = cursor_start[0] + (cursor_end[0] - cursor_start[0])/2; + cursor_item.y = cursor_start[1] + (cursor_end[1] - cursor_start[1])/2 + cursor_item.width = cursor_width/2; + cursor_item.height = cursor_height/2; + cursor_item.redraw(); + final_pos = [cursor_item.x,cursor_item.y]; + final_size = [cursor_item.width,cursor_item.height]; + return false; + }); + + $(document).one('mouseup', function() { + ng.on_message({"data":JSON.stringify({"type":"remove","uid":cursor_uid})}); + + + /**Checks if click is in a valid place and if the object + * clicked on is a network. */ + if(mousemoved == true){ + if (event.pageX - $("#main").offset().left > 0 && + event.pageX - $("#main").offset().left < $("#main").width() - 80 && + event.pageY - $("#main").offset().top > 0 && + event.pageY - $("#main").offset().top < $("#main").height() - 80) { + if(obj_class.indexOf('net') != -1){ + event.parent_network = obj_name; + } else{ + event.parent_network = ""; + } + if(cursor_width > 0.08 && cursor_height > 0.08){ + var obj_prop = {}; + obj_prop.pos = self.compute_position({"parent_network":event.parent_network, + "component_pos":final_pos}); + obj_prop.size = self.compute_size(type,{"parent_network":event.parent_network, + "component_size":final_size}); + obj_prop.parent_network = event.parent_network; + console.log(obj_prop); + var comp_name = self.add_component(type,obj_prop); + var checkExist = setInterval(function() { + if (ng.svg_objects[comp_name] != null) { + self.toggle_item_drag(false); + ng.notify({act:"pos_size", uid:comp_name, x:final_pos[0], + y:final_pos[1], width: final_size[0], + height: final_size[1]}); + clearInterval(checkExist); + } + }, 100); // check every 100ms + } + } + $(document).unbind('mousemove mouseup'); + } + + }); + + // Using return false prevents browser's default, + // often unwanted mousemove actions (drag & drop) + }); +} + +Nengo.VPL.prototype.create_connection = function(){ + var cur_obj = ""; + var obj_class = ""; + var obj_name = ""; + var self = this; + var ng = Nengo.netgraph; + /** Creates dashed line to signify potential connection. */ + self.create_dash_line(); + + var objects = {}; + objects.uids = []; + objects.classes = []; + $('.node').attr('class','node node_effect_good'); + $('.ens').attr('class','ens ens_effect_good'); + + /** On click and not drag. */ + $('#netgraph').on('mousedown', function (event) { + if(event.button == 2){ + return; + } + $('#netgraph').on('mouseup mousemove', function(evt) { + if (evt.type === 'mouseup') { + cur_obj = $($(event.target).parent()[0]); + obj_class = cur_obj.attr('class').split(" ")[0]; + obj_name = cur_obj.attr('data-id'); + + /** Adds first part of potential connection if + * it is an ensemble or node. */ + if(obj_class != null && (obj_class == 'ens' || + obj_class == 'node')){ + objects.uids.push(obj_name); + objects.classes.push(obj_class); + + } + /** Cancels connection if misclicked. */ + else if (objects.uids.length == 1){ + self.unselct_component(); + return; + } + + if(objects.uids.length == 1){ + $('.node').attr('class','node node_effect_bad'); + } + + if(objects.uids.length == 1){ + var obj_pos; + var over_obj_pos; + var over_obj_svg; + var over_obj_name; + var over_obj_class; + var rel_pos = []; + $("#main").on('mousemove',function(event){ + /** Updates cursor connection line's + * position. */ + obj_pos = ng.svg_objects[objects.uids[0]].get_screen_location(); + over_obj_svg = $($(event.target).parent()[0]); + over_obj_name = over_obj_svg.attr('data-id'); + if(over_obj_svg.attr('class') != null){ + over_obj_class = over_obj_svg.attr('class').split(" ")[0]; + } + + $(".temp_line").attr('x1',obj_pos[0]); + $(".temp_line").attr('y1',obj_pos[1]); + if(over_obj_class != null && (over_obj_class == 'ens'|| + over_obj_class == 'node')){ + rel_pos = []; + over_obj_pos = ng.svg_objects[over_obj_name].get_screen_location(); + + rel_pos[0] = over_obj_pos[0]-0.07*(over_obj_pos[0]-obj_pos[0]); + rel_pos[1] = over_obj_pos[1]-0.07*(over_obj_pos[1]-obj_pos[1]); + + $(".temp_line").attr('x2',(rel_pos[0])); + $(".temp_line").attr('y2',(rel_pos[1])); + + }else{ + $(".temp_line").attr('x2',(event.pageX-$(this).offset().left)); + $(".temp_line").attr('y2',((event.pageY-$(this).offset().top))); + } + }); + } + if(objects.uids.length == 2) { + /** Completes Connection, adds appropriate + * code to editor and resets line. */ + self.add_connection(objects.uids[0],objects.uids[1]); + objects = {}; + objects.uids = []; + objects.classes = []; + $("#main").unbind('mousemove'); + $(".temp_group").remove(); + $('.node').attr('class','node node_effect_good'); + $('.ens').attr('class','ens ens_effect_good'); + self.create_dash_line(); + } + } + $('#netgraph').off('mouseup mousemove'); + }); + }); +} + +Nengo.VPL.prototype.unselct_component = function(type){ + var ng = Nengo.netgraph; + this.toggle_item_drag(true); + + $(".mode").attr('class','mode off'); + $('#netgraph').css('cursor',''); + $('.node').attr('class','node'); + $('.ens').attr('class','ens'); + $('#netgraph').unbind('mousedown'); + $('#netgraph').unbind('click'); + $(".temp_group").remove(); + $("#main").unbind('mousemove'); + $(document).unbind('mousedown'); + var element = document.getElementById('main'); + element.style['box-shadow'] = ""; + + + if(ng.svg_objects["cursor.Node"] != null){ + ng.on_message({"data":JSON.stringify({"type":"remove","uid":"cursor.Node"})}); + } else if(ng.svg_objects["cursor.Ensemble"] != null){ + ng.on_message({"data":JSON.stringify({"type":"remove","uid":"cursor.Ensemble"})}); + } else if(ng.svg_objects["cursor.Network"] != null){ + ng.on_message({"data":JSON.stringify({"type":"remove","uid":"cursor.Network"})}); + } +} + +Nengo.VPL.prototype.add_connection = function(obj1,obj2){ + var ng = Nengo.netgraph; + var item1 = ng.svg_objects[obj1]; + var item2 = ng.svg_objects[obj2]; + var dim_out = item1.dimensions; + var dim_in = 0; + + if(item2.type == "node" && item2.passthrough == null){ + dim_in = 0; + } else{ + dim_in = item2.dimensions; + } + + var component_code = "nengo.Connection("+obj1+", "+obj2+")\n"; + if (dim_out > dim_in){ + component_code = "nengo.Connection("+obj1+"[:"+dim_in+"], "+obj2+")\n"; + } else if (dim_out < dim_in) { + component_code = "nengo.Connection("+obj1+", "+obj2+"[:"+dim_out+"])\n"; + } + + if(dim_in != 0){ + var tab = " "; + var editor = ace.edit('editor'); + var last_line = editor.session.getLength(); + + editor.gotoLine(last_line); + editor.insert(tab + component_code); + } +} + +Nengo.VPL.prototype.add_component = function(type, info) { + var tab = " "; + var ng = Nengo.netgraph; + var editor = ace.edit('editor'); + var last_line = editor.session.getLength(); + var obj_names = ng.svg_objects; + var size = [] + var component_code = ""; + var component_name = ""; + var code_str = ""; + var name_switch = {"Ensemble":"ensemble","Node":"node","Network":"network"}; + var parent_net = ng.svg_objects[info.parent_network]; + + if(info.parent_network == "" || info.parent_network == "cursor.Network"){ + component_name = this.open_name(name_switch[type]); + } else{ + component_name = this.open_name(info.parent_network+"."+name_switch[type]); + } + + if (type == "Ensemble") { + code_str = " = nengo.Ensemble(n_neurons=50, dimensions=1)\n"; + } else if (type == "Node") { + code_str = " = nengo.Node([0])\n"; + } else if (type == "Network") { + code_str = " = nengo.Network()\n"; + } + component_code = component_name + code_str; + ng.override_sizes[component_name] = info.size; + ng.override_positions[component_name] = info.pos; + console.log(info.size+" "+info.pos); + if(info.parent_network == "" || info.parent_network == "cursor.Network"){ + editor.gotoLine(last_line); + editor.insert(tab + component_code); + } else{ + var selection = editor.find("with "+info.parent_network+":"); + if(selection != null){ + editor.replace("with "+info.parent_network+":\n"+tab+tab+ + component_code.substring(0,component_code.length-1)); + } else{ + editor.gotoLine(last_line); + editor.insert(tab+"with "+info.parent_network+":\n"+tab+tab+ + component_code+tab+tab+"pass\n"); + } + } + return component_name; +} + +Nengo.VPL.prototype.compute_position = function(event) { + var ng = Nengo.netgraph; + var pos = []; + //Handles mouse position Transform + var w = ng.get_scaled_width(); + var offsetX = ng.offsetX * w; + var h = ng.get_scaled_height(); + var offsetY = ng.offsetY * h; + var page_offset = $("#main").offset(); + var parent, ratio_x, ratio_y; + var parent_pos = [1,1]; + if(event.component_pos == "" || event.component_pos == null){ + pos[0] = ((event.pageX - page_offset.left) - offsetX) / w; + pos[1] = ((event.pageY - page_offset.top) - offsetY) / h; + } else{ + pos = event.component_pos; + } + if(event.parent_network != "" && event.parent_network != null && + event.parent_network != "cursor.Network"){ + + var parent = ng.svg_objects[event.parent_network]; + var parent_size = this.compute_network_size(parent); + parent.expand(); + parent_pos[0] = (parent.get_screen_location()[0] - offsetX)/w; + parent_pos[1] = (parent.get_screen_location()[1] - offsetY)/h; + pos[0] = ((pos[0] - parent_pos[0])/parent_size[0] + 1)/2; + pos[1] = ((pos[1] - parent_pos[1])/parent_size[1] + 1)/2; + } + return pos; +} + +Nengo.VPL.prototype.compute_size = function(type,event){ + var ng = Nengo.netgraph; + var size = []; + var parent_net = ng.svg_objects[event.parent_network]; + var component_size = event.component_size; + var parent_size = []; + if(component_size == null){ + if (type == "Ensemble") { + size= [0.06/ng.scale,0.05/ng.scale]; + } else if (type == "Node") { + size = [0.03/ng.scale,0.05/ng.scale]; + } else if (type == "Network") { + size = [0.1/ng.scale,0.18/ng.scale]; + } + }else{ + size = [component_size[0],component_size[1]]; + } + if(event.parent_network != "" && event.parent_network != "cursor.Network"){ + size= [size[0]/2,size[1]/2]; + parent_size = this.compute_network_size(parent_net); + size = [size[0]/parent_size[0],size[1]/parent_size[1]]; + } + return size; +} + +Nengo.VPL.prototype.compute_network_size = function (parent_net){ + var parent_size = [1,1]; + if (parent_net.parent != null) { + parent_size[0] = parent_net.width*2; + parent_size[1] = parent_net.height*2; + parent_net = parent_net.parent; + while(parent_net.parent != null){ + parent_size[0] *= parent_net.width*2; + parent_size[1] *= parent_net.height*2; + parent_net = parent_net.parent; + } + } + parent_size = [parent_net.width*parent_size[0], + parent_net.height*parent_size[1]]; + return parent_size; +} + +Nengo.VPL.prototype.open_name = function(name) { + var num = 1; + var editor = ace.edit('editor'); + while (editor.find(name + num + " =") != null) { + num ++; + } + return name + num; +} + + +Nengo.VPL.prototype.create_dash_line = function(){ + var svg = d3.select('#netgraph'); + var temp_g = svg.append('svg:g').attr("class","temp_group"); + var line = temp_g.append('svg:line') + .attr('stroke','#000000') + .attr('stroke-linecap','line') + .attr('stroke-dasharray',"20, 10") + .attr('stroke-width','10') + .attr('class','temp_line') + .attr('marker-end', 'url(#marker_arrow)'); + var marker = temp_g.append('svg:marker') + .attr('id', 'marker_arrow') + .attr('markerHeight', 6) + .attr('markerWidth', 6) + .attr('markerUnits', 'strokeWidth') + .attr('orient', 'auto') + .attr('refX', 0) + .attr('refY', 0) + .attr('viewBox', '-5 -5 10 10') + .append('svg:path') + .attr('d', 'M 0,0 m -5,-5 L 5,0 L -5,5 Z') + .attr('fill', '#000000') + +} + +Nengo.VPL.prototype.toggle_item_drag = function(toggle){ + interact($("#netgraph")[0]).draggable({ + enabled: toggle // explicitly disable dragging + }); + for(var item in Nengo.netgraph.svg_objects){ + interact(Nengo.netgraph.svg_objects[item].g).draggable({ + enabled: toggle // explicitly disable dragging + }); + interact(Nengo.netgraph.svg_objects[item].area).resizable({ + enabled: toggle // explicitly disable dragging + }); + } +} + +Nengo.VPL.prototype.delete_component = function(uid){ + var editor = ace.edit("editor"); + var re = new RegExp("(?:[^\.])"+uid+"\\s*="); + var component = Nengo.netgraph.svg_objects[uid]; + var start_with,end_with,net_children; + var self = this; + + this.delete_connections(uid); + var code_line = editor.find(re); + console.log(re); + while(code_line != null){ + editor.removeLines(code_line.start.row); + code_line = editor.find(re); + } + + if(component.type == "net"){ + var net_children = component.children; + for(var x = 0; x < net_children.length; x++){ + this.delete_component(net_children[x].uid); + } + + start_with = editor.find("with "+uid); + if(start_with != null){ + editor.removeLines(start_with.start.row); + end_with = editor.find("pass"); + editor.removeLines(end_with.start.row); + } + } +} + +Nengo.VPL.prototype.delete_connections = function(uid){ + var editor = ace.edit("editor"); + var re = new RegExp("nengo\.Connection\s*.*(?:[^\.])"+uid) + var code_line = editor.find(re); + while(code_line){ + editor.removeLines(code_line.start.row); + code_line = editor.find(re); + } +} + +Nengo.VPL.prototype.config_component = function(uid){ + var self = this; + var editor = ace.edit("editor"); + var re = new RegExp("(?:[^\.])"+uid+"\\s*="); + var code_line = editor.find(re); + var component = Nengo.netgraph.svg_objects[uid]; + Nengo.modal.component_config(uid); + Nengo.modal.show(); + Nengo.modal.footer('ok_cancel', + function(e) { + var n_number = $("#config-neuron-number").val(); + var dim_number = $("#config-dimension").val(); + var Range = require("ace/range").Range; + var tab = " "; + var tabs; + Nengo.netgraph.svg_objects[uid].n_neurons = n_number; + Nengo.netgraph.svg_objects[uid].dimensions = dim_number; + + if(component.parent == null){tabs = tab;} + else{tabs = tab+tab;} + + editor.session.replace(new Range(code_line.start.row, 0, code_line.start.row, Number.MAX_VALUE), + tabs+uid+" = nengo.Ensemble(n_neurons="+n_number+",dimensions="+dim_number+")"); + self.delete_connections(uid); + var conn_in = component.conn_in; + var conn_out = component.conn_out; + for(var x = 0; x < conn_in.length; x++){ + self.add_connection(conn_in[x].pre.uid,conn_in[x].post.uid); + } + for(var x = 0; x < conn_out.length; x++){ + self.add_connection(conn_out[x].post.uid,conn_out[x].pre.uid); + } + $('#OK').attr('data-dismiss', 'modal'); + }, + function () { + $('#cancel-button').attr('data-dismiss', 'modal'); + } + ); +} + +$(document).ready(function(){ + Nengo.vpl = new Nengo.VPL(); +}); diff --git a/nengo_gui/static/side_menu.css b/nengo_gui/static/side_menu.css index 693cbe18..1df84298 100644 --- a/nengo_gui/static/side_menu.css +++ b/nengo_gui/static/side_menu.css @@ -1,20 +1,26 @@ .sidenav-container { - height: calc(100% - 120px); - width: 250px; - position: fixed; + -webkit-flex: 0 0 auto; + flex: 0 0 auto; + + display: -webkit-flex; + display: flex; + -webkit-flex-direction: column; + flex-direction: column; + box-sizing: border-box; + width: 200px; + position: relative;; z-index: 9999; - top: 34px; + display: none; left: 0; background-color: #eee; overflow-x: hidden; overflow-y: hidden; - transition: 0.5s; - border-right: 1px solid #4D4D4D; - font-size: 0px; + transition: 0s; + border-right: solid #666 5px; color: #333; font-family: arial; - transform: translate(-250px); + transform: translate(0px); } .sidenav-container .tab-content{ @@ -88,7 +94,7 @@ float:right; font-size: 14px; } -.accordion-content {display: none;} +.accordion-content {display: none} .accordion-content.default {display: block;} @@ -97,14 +103,15 @@ position: relative; top: 0; left: 0; - width: 250px; + width: 100%; height: 0px; border-bottom-right-radius: 4px; color: inherit; padding: 0.2em; background-color: None; z-index: 99999; - overflow: auto; + overflow-y: auto; + overflow-x: hidden; cursor: pointer !important; border-top: solid #999 1px; } diff --git a/nengo_gui/static/side_menu.js b/nengo_gui/static/side_menu.js index e4710863..b0db8b62 100644 --- a/nengo_gui/static/side_menu.js +++ b/nengo_gui/static/side_menu.js @@ -14,15 +14,32 @@ Nengo.SideMenu = function() { self.menu_width = document.getElementsByClassName("sidenav-container")[0] .offsetWidth; - // Gathers all the menu tabs from the HTML for the Event Handlers self.tabs = $(".tab-content"); + self.current_tab = -1; - self.top_buttons = ['#Open_file_button', '#Config_menu']; + self.top_buttons = ['#Open_file_button', '#Component_menu', '#Config_menu']; self.initialize_button_handlers(); + self.width = $(".sidenav-container").width(); + self.height = $("#vmiddle").height(); //----EVENT HANDLERS---- + interact('.sidenav-container') + .resizable({ + edges: { left: false, right: true, bottom: false, top: false} + }) + .on('resizemove', function (event) { + if(self.width > 200 || event.deltaRect.right > 0){ + self.width += event.deltaRect.right; + + $(".sidenav-container").width(self.width); + + $(self.tabs[self.current_tab]).width(self.width); + } + Nengo.netgraph.on_resize(); + }); + // file_browser $('#Open_file_button')[0].addEventListener('click', function() { if (!$(this).hasClass('deactivated') && file_browser_open == false) { @@ -46,19 +63,15 @@ Nengo.SideMenu = function() { var accord_num = document.getElementsByClassName('accordion-container') .length + 1; - for (var x = 1; x < accord_num; x++) { - $('#accordion-container' + String(x)) - .find('.accordion-toggle') - .click(function() { + $('.accordion-container').find('.accordion-toggle').click(function() { - // Expand or collapse this panel - $(this).next().slideToggle(); + // Expand or collapse this panel + $(this).next().slideToggle(); - // Hide the other panels - $(".accordion-content").not($(this).next()).slideUp(); + // Hide the other panels + $(".accordion-content").not($(this).next()).slideUp(); - }); - } + }); $(window).on('resize', function() {self.on_resize();}); self.on_resize(); @@ -89,12 +102,13 @@ Nengo.SideMenu.prototype.toggle_side_nav = function() { if (self.menu_open === false) { trans_val = "0"; self.menu_open = true; + element.style.display = "flex"; } else { - trans_val = String(-self.menu_width); + trans_val = String(-self.width); self.menu_open = false; + element.style.display = "none"; } - - element.style.transform = "translate(" + trans_val + "px)"; + Nengo.netgraph.on_resize(); }; Nengo.SideMenu.prototype.show_side_nav = function() { @@ -114,6 +128,9 @@ Nengo.SideMenu.prototype.hide_side_nav = function() { Nengo.SideMenu.prototype.menu_tab_click = function(it, pos_num, close_if_selected) { var self = this; var trans_func = function() { + + self.current_tab = pos_num; + if ($(it).hasClass('deactivated')) { return; } @@ -123,7 +140,14 @@ Nengo.SideMenu.prototype.menu_tab_click = function(it, pos_num, close_if_selecte } else { self.show_side_nav(); var element = document.getElementById("Menu_content"); - var trans_val = String(-self.menu_width * pos_num); + $(self.tabs[self.current_tab]).width(self.width); + $(".sidenav-container").width(self.width); + + var trans_width = 0; + for(var x = 0; x < pos_num; x++){ + trans_width += $(self.tabs[x]).width(); + } + var trans_val = String(-trans_width); element.style.transform = "translate(" + trans_val + "px)"; self.focus_reset(); $(it).addClass("selected"); @@ -141,6 +165,7 @@ Nengo.SideMenu.prototype.focus_reset = function() { /** This lets you browse the files available on the server */ Nengo.SideMenu.prototype.file_browser = function() { + var self = this; sim.ws.send('browse'); fb = $('#filebrowser'); @@ -152,7 +177,7 @@ Nengo.SideMenu.prototype.file_browser = function() { window.location.assign('/?filename=' + file); } ); - + $("#filebrowser").height(self.height-5); }; /** Export the layout to the SVG in Downloads folder **/ diff --git a/nengo_gui/templates/page.html b/nengo_gui/templates/page.html index d9f053f2..129629d6 100644 --- a/nengo_gui/templates/page.html +++ b/nengo_gui/templates/page.html @@ -19,11 +19,56 @@ + +
+ +
+
@@ -36,7 +81,70 @@
-
+ + + + + + + + +
+
+ + + + + + + + + + + +

Ensemble

+
+ +
+ + + + + +

Node

+
+ +
+ + + + + + +

Network

+
+ +
+ + + + + + + + + +

Connection

+
+
+
+
+

Configure Settings

@@ -77,47 +185,6 @@
-
- -
-
@@ -204,6 +271,7 @@ + From 71f881c313d2f1121f4fbd75cd1c6adea17e9021 Mon Sep 17 00:00:00 2001 From: Rees Simmons Date: Mon, 3 Oct 2016 15:48:47 -0400 Subject: [PATCH 2/9] Added config modal --- nengo_gui/static/components/netgraph_item.js | 1 + nengo_gui/static/drag_and_drop.js | 2 +- nengo_gui/static/modal.js | 31 ++++++++++++++++++++ 3 files changed, 33 insertions(+), 1 deletion(-) diff --git a/nengo_gui/static/components/netgraph_item.js b/nengo_gui/static/components/netgraph_item.js index 169b16a5..984064c5 100644 --- a/nengo_gui/static/components/netgraph_item.js +++ b/nengo_gui/static/components/netgraph_item.js @@ -396,6 +396,7 @@ Nengo.NetGraphItem.prototype.generate_menu = function () { items.push(['Spikes', function() {self.create_graph('Raster');}]); items.push(['Voltages', function() {self.create_graph('Voltage');}]); items.push(['Firing pattern', function() {self.create_graph('SpikeGrid');}]); + items.push(['Configure',function(){Nengo.vpl.config_component(self.uid)}]); } if (this.type == 'node') { items.push(['Slider', function() {self.create_graph('Slider');}]); diff --git a/nengo_gui/static/drag_and_drop.js b/nengo_gui/static/drag_and_drop.js index e8c02aa4..db569ace 100644 --- a/nengo_gui/static/drag_and_drop.js +++ b/nengo_gui/static/drag_and_drop.js @@ -549,7 +549,7 @@ Nengo.VPL.prototype.config_component = function(uid){ self.add_connection(conn_in[x].pre.uid,conn_in[x].post.uid); } for(var x = 0; x < conn_out.length; x++){ - self.add_connection(conn_out[x].post.uid,conn_out[x].pre.uid); + self.add_connection(conn_out[x].pre.uid,conn_out[x].post.uid); } $('#OK').attr('data-dismiss', 'modal'); }, diff --git a/nengo_gui/static/modal.js b/nengo_gui/static/modal.js index 2a532701..52bb3942 100644 --- a/nengo_gui/static/modal.js +++ b/nengo_gui/static/modal.js @@ -794,6 +794,37 @@ Nengo.Modal.prototype.make_conn_path_dropdown_list = function($container, others } } +Nengo.Modal.prototype.component_config = function(uid){ + this.clear_body(); + var component = Nengo.netgraph.svg_objects[uid]; + var name_switch = {"ens":"Ensemble","node":"Node"}; + this.title(name_switch[component.type]+" "+uid+"'s properties.'"); + var $form = $('
').appendTo(this.$body); + $('
' + + '' + + '
' + + '' + + '#' + + '
' + + '' + + '
' + + '' + + '#' + + '
' + + '
').appendTo($form); + $("#config-dimension").val(component.dimensions); + $("#config-neuron-number").val(component.n_neurons); +} + Nengo.modal = new Nengo.Modal($('.modal').first()); //Change the global defaults of the modal validator From c20af6314c2e5b1e9765ac365e3cae7f48161646 Mon Sep 17 00:00:00 2001 From: Rees Simmons Date: Thu, 6 Oct 2016 16:41:07 -0400 Subject: [PATCH 3/9] Added ability to delete connections directly, fixed a few bugs concerning code creation --- nengo_gui/static/components/netgraph_conn.js | 27 ++- nengo_gui/static/drag_and_drop.js | 220 ++++++++++--------- 2 files changed, 139 insertions(+), 108 deletions(-) diff --git a/nengo_gui/static/components/netgraph_conn.js b/nengo_gui/static/components/netgraph_conn.js index b137102c..c3817f85 100644 --- a/nengo_gui/static/components/netgraph_conn.js +++ b/nengo_gui/static/components/netgraph_conn.js @@ -57,6 +57,18 @@ Nengo.NetGraphConnection = function(ng, info, minimap, mini_conn) { /** create the line and its arrowhead marker */ this.g = ng.createSVGElement('g'); + this.menu = new Nengo.Menu(this.ng.parent); + var self = this; + $(this.g).bind('contextmenu', function(event) { + event.preventDefault(); + event.stopPropagation(); + if (self.menu.visible_any()) { + self.menu.hide_any(); + } else { + self.menu.show(event.clientX, event.clientY, + self.generate_menu()); + } + }); this.create_line(); @@ -190,7 +202,7 @@ Nengo.NetGraphConnection.prototype.find_post = function() { Nengo.NetGraphConnection.prototype.set_pres = function(pres) { this.pres = pres; this.set_pre(this.find_pre()); - + if (!this.minimap) { this.mini_conn.set_pres(pres); } @@ -198,7 +210,7 @@ Nengo.NetGraphConnection.prototype.set_pres = function(pres) { Nengo.NetGraphConnection.prototype.set_posts = function(posts) { this.posts = posts; this.set_post(this.find_post()); - + if (!this.minimap) { this.mini_conn.set_posts(posts); } @@ -236,7 +248,7 @@ Nengo.NetGraphConnection.prototype.remove = function() { this.removed = true; delete this.ng.svg_conns[this.uid]; - + if (!this.minimap) { this.mini_conn.remove(); } @@ -341,7 +353,7 @@ Nengo.NetGraphConnection.prototype.redraw = function() { this.marker.setAttribute('transform', 'translate(' + mx + ',' + my + ') rotate('+ angle +')'); } - + if (!this.minimap && this.ng.mm_display) { this.mini_conn.redraw(); } @@ -372,3 +384,10 @@ Nengo.NetGraphConnection.prototype.intersect_length = function(theta, alpha, wid return [x,y]; } + +Nengo.NetGraphConnection.prototype.generate_menu = function(){ + var self = this; + var items = []; + items.push(['Delete Connection',function(){Nengo.vpl.delete_connection(self.pre.uid,self.post.uid)}]); + return items; +} diff --git a/nengo_gui/static/drag_and_drop.js b/nengo_gui/static/drag_and_drop.js index db569ace..c00a039f 100644 --- a/nengo_gui/static/drag_and_drop.js +++ b/nengo_gui/static/drag_and_drop.js @@ -1,34 +1,34 @@ Nengo.VPL = function(){ var self = this; - //Setting Click handlers for toggle buttons $("#mode_list").height($(document).height()*0.85); $(document).keyup(function(e) { if (e.keyCode === 27) self.unselct_component(); // esc }); - $("#Node > svg").on('click',function(){ - self.component_toggle('Node'); - }); + //Setting Click handlers for toggle buttons + $("#Node > svg").on('click',function(){ + self.component_toggle('Node'); + }); - $("#Ensemble > svg").on('click',function(){ - self.component_toggle('Ensemble'); - }); + $("#Ensemble > svg").on('click',function(){ + self.component_toggle('Ensemble'); + }); - $("#Network > svg").on('click',function(){ - self.component_toggle('Network'); - }); + $("#Network > svg").on('click',function(){ + self.component_toggle('Network'); + }); - $("#Connection > svg").on('click',function(){ - self.component_toggle('Connection'); - }); + $("#Connection > svg").on('click',function(){ + self.component_toggle('Connection'); + }); } -//Activates type mode for creating components + +/** Activates type mode for creating components */ Nengo.VPL.prototype.component_toggle = function(type) { var ng = Nengo.netgraph; var self = this; - if(interact($("#netgraph")[0]).ondragmove != null){ - drag_func = interact($("#netgraph")[0]).ondragmove; - } + + /** Activates and deactivates modes, accordingly */ if($("#"+type).attr('class') == 'mode off'){ self.unselct_component(); $("#"+type).attr('class','mode on'); @@ -36,12 +36,13 @@ Nengo.VPL.prototype.component_toggle = function(type) { self.unselct_component(); return; } - if($("#"+type).attr('class') == 'mode on'){ - + /** Activates component or connection mode */ + if($("#"+type).attr('class') == 'mode on'){ var cur_obj = ""; var obj_class = ""; var obj_name = ""; + if(type != 'Connection'){ $('#netgraph').css('cursor','crosshair','important'); self.create_component(type); @@ -52,7 +53,8 @@ Nengo.VPL.prototype.component_toggle = function(type) { } } - +/** Creates the interaction, click events, and visuals for drawing and creation + of components. type - one of ['Node','Ensemble'.'Network']*/ Nengo.VPL.prototype.create_component = function(type){ var ng = Nengo.netgraph; var self = this; @@ -60,17 +62,25 @@ Nengo.VPL.prototype.create_component = function(type){ var cursor_uid = "cursor."+type; var name_convert = {"Node":"node","Ensemble":"ens","Network":"net"} var element = document.getElementById('main'); + + /** Adds green border and deactivates click and drag events. */ element.style['box-shadow'] = "inset 0 0 30px #00FF00"; self.toggle_item_drag(false); $(document).mousedown(function(event) { - if(event.button == 0){}else{return;} + /** Cancels/deactivates click event if it is a right click. */ + if(event.button == 0){} + else if(event.button == 2){ + self.unselct_component(); + return; + } + else{return;} cur_obj = $($(event.target).parent()[0]); obj_class = cur_obj.attr('class'); obj_name = cur_obj.attr('data-id'); - /** Creates cursor shape. */ + /** Checks if user click is within the netgraph. */ if (event.pageX - $("#main").offset().left < 0 || event.pageX - $("#main").offset().left > $("#main").width() - 80 || event.pageY - $("#main").offset().top < 0 || @@ -81,17 +91,22 @@ Nengo.VPL.prototype.create_component = function(type){ var cursor_width, cursor_height, cursor_end, final_pos, final_size; var mousemoved = false; + if(obj_class.indexOf('net') != -1){ + event.parent_network = obj_name; + } else{ + event.parent_network = null; + } + + /** Creates temporary cursor component. */ ng.create_object({'pos':[-1000,-1000],'size': - self.compute_size(type,{"parent_network":""}),'uid':cursor_uid, + [0,0],'uid':cursor_uid, 'type':name_convert[type],'parent':null,'label':""}); - var cursor_item = ng.svg_objects[cursor_uid]; cursor_item.shape.style['fill-opacity'] = 0.4; - cursor_item.width = 0; - cursor_item.height = 0; cursor_item.x = cursor_start[0]; cursor_item.y = cursor_start[1]; + /** Activates if the event is a click and drag. */ $(document).mousemove(function(evt) { mousemoved = true; @@ -103,55 +118,52 @@ Nengo.VPL.prototype.create_component = function(type){ cursor_item.y = cursor_start[1] + (cursor_end[1] - cursor_start[1])/2 cursor_item.width = cursor_width/2; cursor_item.height = cursor_height/2; + cursor_item.constrain_position(); cursor_item.redraw(); final_pos = [cursor_item.x,cursor_item.y]; final_size = [cursor_item.width,cursor_item.height]; return false; }); + /** On drag end */ $(document).one('mouseup', function() { ng.on_message({"data":JSON.stringify({"type":"remove","uid":cursor_uid})}); - - /**Checks if click is in a valid place and if the object - * clicked on is a network. */ if(mousemoved == true){ - if (event.pageX - $("#main").offset().left > 0 && - event.pageX - $("#main").offset().left < $("#main").width() - 80 && - event.pageY - $("#main").offset().top > 0 && - event.pageY - $("#main").offset().top < $("#main").height() - 80) { - if(obj_class.indexOf('net') != -1){ - event.parent_network = obj_name; - } else{ - event.parent_network = ""; - } - if(cursor_width > 0.08 && cursor_height > 0.08){ - var obj_prop = {}; - obj_prop.pos = self.compute_position({"parent_network":event.parent_network, - "component_pos":final_pos}); - obj_prop.size = self.compute_size(type,{"parent_network":event.parent_network, - "component_size":final_size}); - obj_prop.parent_network = event.parent_network; - console.log(obj_prop); - var comp_name = self.add_component(type,obj_prop); - var checkExist = setInterval(function() { - if (ng.svg_objects[comp_name] != null) { - self.toggle_item_drag(false); - ng.notify({act:"pos_size", uid:comp_name, x:final_pos[0], - y:final_pos[1], width: final_size[0], - height: final_size[1]}); - clearInterval(checkExist); - } - }, 100); // check every 100ms - } + /** Checks if component is larger than minimum size. */ + if(cursor_width > 0.08 && cursor_height > 0.08){ + /** Converts all properties from the cursor component + * to the new component. */ + var obj_prop = {}; + obj_prop.pos = self.compute_position({"parent_network":event.parent_network, + "component_pos":final_pos}); + obj_prop.size = self.compute_size(type,{"parent_network":event.parent_network, + "component_size":final_size}); + obj_prop.parent_network = event.parent_network; + + var comp_name = self.add_component(type,obj_prop); + /** Checks every 100ms until the component shows up in + * the netgraph and notifies the server. */ + var checkExist = setInterval(function() { + if (ng.svg_objects[comp_name] != null) { + var new_comp = ng.svg_objects[comp_name]; + self.toggle_item_drag(false); + new_comp.constrain_position(); + ng.redraw(); + ng.override_positions[comp_name] = [new_comp.x,new_comp.y]; + ng.override_sizes[comp_name] = [new_comp.width,new_comp.height]; + ng.notify({act:"pos_size", uid:comp_name, x:new_comp.x, + y:new_comp.y, width: new_comp.width, + height: new_comp.height}); + + + clearInterval(checkExist); + } + }, 100); } $(document).unbind('mousemove mouseup'); } - }); - - // Using return false prevents browser's default, - // often unwanted mousemove actions (drag & drop) }); } @@ -170,7 +182,7 @@ Nengo.VPL.prototype.create_connection = function(){ $('.node').attr('class','node node_effect_good'); $('.ens').attr('class','ens ens_effect_good'); - /** On click and not drag. */ + /** On main click and not drag. */ $('#netgraph').on('mousedown', function (event) { if(event.button == 2){ return; @@ -199,6 +211,7 @@ Nengo.VPL.prototype.create_connection = function(){ $('.node').attr('class','node node_effect_bad'); } + /** After the first component is selected. */ if(objects.uids.length == 1){ var obj_pos; var over_obj_pos; @@ -212,6 +225,7 @@ Nengo.VPL.prototype.create_connection = function(){ obj_pos = ng.svg_objects[objects.uids[0]].get_screen_location(); over_obj_svg = $($(event.target).parent()[0]); over_obj_name = over_obj_svg.attr('data-id'); + if(over_obj_svg.attr('class') != null){ over_obj_class = over_obj_svg.attr('class').split(" ")[0]; } @@ -254,6 +268,8 @@ Nengo.VPL.prototype.create_connection = function(){ }); } +/** Deactivates the various styling and event handlers activated by the +* component toggle. */ Nengo.VPL.prototype.unselct_component = function(type){ var ng = Nengo.netgraph; this.toggle_item_drag(true); @@ -269,17 +285,9 @@ Nengo.VPL.prototype.unselct_component = function(type){ $(document).unbind('mousedown'); var element = document.getElementById('main'); element.style['box-shadow'] = ""; - - - if(ng.svg_objects["cursor.Node"] != null){ - ng.on_message({"data":JSON.stringify({"type":"remove","uid":"cursor.Node"})}); - } else if(ng.svg_objects["cursor.Ensemble"] != null){ - ng.on_message({"data":JSON.stringify({"type":"remove","uid":"cursor.Ensemble"})}); - } else if(ng.svg_objects["cursor.Network"] != null){ - ng.on_message({"data":JSON.stringify({"type":"remove","uid":"cursor.Network"})}); - } } +/** Creates and inserts the appropriate code for the type of connection */ Nengo.VPL.prototype.add_connection = function(obj1,obj2){ var ng = Nengo.netgraph; var item1 = ng.svg_objects[obj1]; @@ -293,28 +301,31 @@ Nengo.VPL.prototype.add_connection = function(obj1,obj2){ dim_in = item2.dimensions; } - var component_code = "nengo.Connection("+obj1+", "+obj2+")\n"; + var component_code = "nengo.Connection("+obj1+", "+obj2+")"; if (dim_out > dim_in){ - component_code = "nengo.Connection("+obj1+"[:"+dim_in+"], "+obj2+")\n"; + component_code = "nengo.Connection("+obj1+"[:"+dim_in+"], "+obj2+")"; } else if (dim_out < dim_in) { - component_code = "nengo.Connection("+obj1+", "+obj2+"[:"+dim_out+"])\n"; + component_code = "nengo.Connection("+obj1+", "+obj2+"[:"+dim_out+"])"; } if(dim_in != 0){ var tab = " "; var editor = ace.edit('editor'); - var last_line = editor.session.getLength(); + var last_line = editor.session.getLength() + 1; - editor.gotoLine(last_line); - editor.insert(tab + component_code); + editor.session.insert({"row":editor.session.getLength()+1,"column":0} + ,"\n"); + editor.session.insert({"row":editor.session.getLength()+1,"column":0} + ,tab + component_code); } } +/** Creates and inserts the appropriate code for the type of connection */ Nengo.VPL.prototype.add_component = function(type, info) { var tab = " "; var ng = Nengo.netgraph; var editor = ace.edit('editor'); - var last_line = editor.session.getLength(); + var last_line = editor.session.getLength() + 1; var obj_names = ng.svg_objects; var size = [] var component_code = ""; @@ -323,40 +334,43 @@ Nengo.VPL.prototype.add_component = function(type, info) { var name_switch = {"Ensemble":"ensemble","Node":"node","Network":"network"}; var parent_net = ng.svg_objects[info.parent_network]; - if(info.parent_network == "" || info.parent_network == "cursor.Network"){ + if(info.parent_network == null || info.parent_network == "cursor.Network"){ component_name = this.open_name(name_switch[type]); } else{ component_name = this.open_name(info.parent_network+"."+name_switch[type]); } if (type == "Ensemble") { - code_str = " = nengo.Ensemble(n_neurons=50, dimensions=1)\n"; + code_str = " = nengo.Ensemble(n_neurons=50, dimensions=1)"; } else if (type == "Node") { - code_str = " = nengo.Node([0])\n"; + code_str = " = nengo.Node([0])"; } else if (type == "Network") { - code_str = " = nengo.Network()\n"; + code_str = " = nengo.Network()"; } component_code = component_name + code_str; ng.override_sizes[component_name] = info.size; ng.override_positions[component_name] = info.pos; - console.log(info.size+" "+info.pos); - if(info.parent_network == "" || info.parent_network == "cursor.Network"){ - editor.gotoLine(last_line); - editor.insert(tab + component_code); + + if(info.parent_network == null || info.parent_network == "cursor.Network"){ + editor.session.insert({"row":last_line+1,"column":0},"\n"); + editor.session.insert({"row":last_line+1,"column":0},tab + component_code); } else{ var selection = editor.find("with "+info.parent_network+":"); if(selection != null){ editor.replace("with "+info.parent_network+":\n"+tab+tab+ - component_code.substring(0,component_code.length-1)); + component_code); } else{ - editor.gotoLine(last_line); - editor.insert(tab+"with "+info.parent_network+":\n"+tab+tab+ - component_code+tab+tab+"pass\n"); + editor.session.insert({"row":editor.session.getLength()+1,"column":0} + ,"\n"); + editor.session.insert({"row":last_line+1,"column":0}, + tab+"with "+info.parent_network+":\n"+tab+tab+ + component_code+"\n"+tab+tab+"pass"); } } return component_name; } +/** Computes the position of a component based on the click event. */ Nengo.VPL.prototype.compute_position = function(event) { var ng = Nengo.netgraph; var pos = []; @@ -388,24 +402,15 @@ Nengo.VPL.prototype.compute_position = function(event) { return pos; } +/** Computes the size of a component based on the click event. */ Nengo.VPL.prototype.compute_size = function(type,event){ var ng = Nengo.netgraph; var size = []; var parent_net = ng.svg_objects[event.parent_network]; var component_size = event.component_size; var parent_size = []; - if(component_size == null){ - if (type == "Ensemble") { - size= [0.06/ng.scale,0.05/ng.scale]; - } else if (type == "Node") { - size = [0.03/ng.scale,0.05/ng.scale]; - } else if (type == "Network") { - size = [0.1/ng.scale,0.18/ng.scale]; - } - }else{ - size = [component_size[0],component_size[1]]; - } - if(event.parent_network != "" && event.parent_network != "cursor.Network"){ + size = [component_size[0],component_size[1]]; + if(event.parent_network != null && event.parent_network != "cursor.Network"){ size= [size[0]/2,size[1]/2]; parent_size = this.compute_network_size(parent_net); size = [size[0]/parent_size[0],size[1]/parent_size[1]]; @@ -413,6 +418,7 @@ Nengo.VPL.prototype.compute_size = function(type,event){ return size; } +/** Computes the size of a network in the global coordinate system. */ Nengo.VPL.prototype.compute_network_size = function (parent_net){ var parent_size = [1,1]; if (parent_net.parent != null) { @@ -465,6 +471,7 @@ Nengo.VPL.prototype.create_dash_line = function(){ } +/** Activates/deactivates drag/resize events. */ Nengo.VPL.prototype.toggle_item_drag = function(toggle){ interact($("#netgraph")[0]).draggable({ enabled: toggle // explicitly disable dragging @@ -479,6 +486,7 @@ Nengo.VPL.prototype.toggle_item_drag = function(toggle){ } } +/** Deletes code that is used to create a nengo component */ Nengo.VPL.prototype.delete_component = function(uid){ var editor = ace.edit("editor"); var re = new RegExp("(?:[^\.])"+uid+"\\s*="); @@ -488,7 +496,7 @@ Nengo.VPL.prototype.delete_component = function(uid){ this.delete_connections(uid); var code_line = editor.find(re); - console.log(re); + while(code_line != null){ editor.removeLines(code_line.start.row); code_line = editor.find(re); @@ -510,8 +518,12 @@ Nengo.VPL.prototype.delete_component = function(uid){ } Nengo.VPL.prototype.delete_connections = function(uid){ + this.delete_connection(uid,""); +} + +Nengo.VPL.prototype.delete_connection = function(uid1,uid2){ var editor = ace.edit("editor"); - var re = new RegExp("nengo\.Connection\s*.*(?:[^\.])"+uid) + var re = new RegExp("nengo\.Connection\s*.*(?:[^\.])"+uid1+".*"+uid2); var code_line = editor.find(re); while(code_line){ editor.removeLines(code_line.start.row); From 5fdf8e9d52cf99fbccd6b4c2ae83faae2dfd9f09 Mon Sep 17 00:00:00 2001 From: Rees Simmons Date: Tue, 11 Oct 2016 20:04:08 -0400 Subject: [PATCH 4/9] Added line numbers as a property of nengo componets/connections --- nengo_gui/components/netgraph.py | 3 +++ nengo_gui/exec_env.py | 29 +++++++++++++++++++++++++++++ 2 files changed, 32 insertions(+) diff --git a/nengo_gui/components/netgraph.py b/nengo_gui/components/netgraph.py index efebcbf2..973d9e08 100644 --- a/nengo_gui/components/netgraph.py +++ b/nengo_gui/components/netgraph.py @@ -543,6 +543,9 @@ def get_extra_info(self, obj): info['sp_targets'] = ( nengo_gui.components.spa_plot.SpaPlot.applicable_targets(obj)) + + # the line number information is injected via nengo_gui.exec_env + info['line_number'] = obj._line_number return info def send_pan_and_zoom(self, client): diff --git a/nengo_gui/exec_env.py b/nengo_gui/exec_env.py index eb3a34ae..27cc65a4 100644 --- a/nengo_gui/exec_env.py +++ b/nengo_gui/exec_env.py @@ -6,6 +6,8 @@ import sys from nengo.utils.compat import StringIO +import nengo + # list of Simulators to check for known_modules = ['nengo', 'nengo_ocl', 'nengo_distilled', @@ -45,6 +47,22 @@ def __init__(self, *args, **kwargs): return DummySimulator +# create a wrapper class for NengoObjects that records the line number +# they were created on +def make_line_number_class(cls): + class LineNumberClass(cls): + _original_class = cls + def __init__(self, *args, **kwargs): + for fn, ln, func, line in reversed(traceback.extract_stack()): + if fn == compiled_filename: + self._line_number = ln + break + else: + self._line_number = None + super(LineNumberClass, self).__init__(*args, **kwargs) + return LineNumberClass + + # thread local storage for storing whether we are executing a script flag = threading.local() @@ -110,6 +128,12 @@ def __enter__(self): self.simulators[mod] = mod.Simulator mod.Simulator = make_dummy(mod.Simulator) + # install wrapper classes to keep track of creation line numbers + for name in ['Node', 'Network', 'Connection', 'Ensemble']: + setattr(nengo, name, + make_line_number_class(getattr(nengo, name))) + + def __exit__(self, exc_type, exc_value, traceback): for mod, cls in self.simulators.items(): mod.Simulator = cls @@ -121,3 +145,8 @@ def __exit__(self, exc_type, exc_value, traceback): if self.added_directory is not None: sys.path.remove(self.added_directory) self.added_directory = None + + # remove line number wrapper classes + for name in ['Node', 'Network', 'Connection', 'Ensemble']: + setattr(nengo, name, + getattr(nengo, name)._original_class) From 784d474e3f6bbcadc85a24b43424a5647f1875e6 Mon Sep 17 00:00:00 2001 From: Rees Simmons Date: Fri, 21 Oct 2016 12:35:39 -0400 Subject: [PATCH 5/9] Major Changes to side palette including style change and 2 new modes, cursor and remove --- nengo_gui/static/drag_and_drop.css | 61 ++++-- nengo_gui/static/drag_and_drop.js | 312 +++++++++++++++++++---------- nengo_gui/static/side_menu.css | 2 +- nengo_gui/static/side_menu.js | 4 +- nengo_gui/templates/page.html | 122 +++++------ 5 files changed, 311 insertions(+), 190 deletions(-) diff --git a/nengo_gui/static/drag_and_drop.css b/nengo_gui/static/drag_and_drop.css index f5c0e8a3..a88ed036 100644 --- a/nengo_gui/static/drag_and_drop.css +++ b/nengo_gui/static/drag_and_drop.css @@ -3,17 +3,22 @@ background: #ddd; color:inherit; overflow: hidden; + } -.off svg{ +.off{ cursor: pointer; background: inherit; fill: rgb(180,180,180); + -webkit-transition-duration: 0.4s; /* Safari */ + transition-duration: 0.2s; } -.on svg{ +.on{ cursor: pointer; - background: #AAA; + background-color: #BBB; fill: #DDD; + color: #000; + box-shadow: inset 0 0 5px #666; } @@ -23,34 +28,54 @@ left: 0; width: 100%; color: inherit; - padding: 30px; z-index: 99999; - overflow-y: scroll; + padding-left: 20px; } .mode{ position: relative; float: left; - left: 15%; - width: 70%; - text-align: center; + border-radius: 5px; + width: 110px; + height: 90px; + margin: 10px; + border: 1px solid black; -webkit-user-select: none; /* Chrome/Safari */ -moz-user-select: none; /* Firefox */ } -.off > svg:hover{ - cursor: pointer; - background-color: #BBB; - fill: #DDD; +.mode > div{ + text-align: center; + position: relative; + margin: 0 auto; + width: 100%; + height: 100%; +} +.mode > div >svg{ + width: 60%; +} + +.mode > div > p{ + position: absolute; + bottom: -5px; + margin-left: auto; + margin-right: auto; + left: 0; + right: 0; +} +.off:hover{ + background-color: #BBB; + fill: #DDD; + color: #000; } -.node_effect_good:hover > rect:nth-child(2){ - fill: #00FF00 !important; +.node_effect_good > rect:nth-child(2){ + fill: #5DFC0A !important; } -.node_effect_bad:hover > rect:nth-child(2){ - fill: #FF0000 !important; +.node_effect_bad > rect:nth-child(2){ + fill: #FD0000 !important; } -.ens_effect_good:hover > .ensemble{ - fill: #00FF00 !important; +.ens_effect_good > .ensemble{ + fill: #5DFC0A !important; } diff --git a/nengo_gui/static/drag_and_drop.js b/nengo_gui/static/drag_and_drop.js index c00a039f..0bcd1069 100644 --- a/nengo_gui/static/drag_and_drop.js +++ b/nengo_gui/static/drag_and_drop.js @@ -1,24 +1,31 @@ Nengo.VPL = function(){ - var self = this; - $("#mode_list").height($(document).height()*0.85); - $(document).keyup(function(e) { - if (e.keyCode === 27) self.unselct_component(); // esc - }); + var self = this; + $("#mode_list").height($(document).height()*0.85); + $(document).keyup(function(e) { + if (e.keyCode === 27) self.unselect_component("Pointer"); // esc + }); + self.contextmenu = null; //Setting Click handlers for toggle buttons - $("#Node > svg").on('click',function(){ + $("#Delete").on('click',function(){ + self.component_toggle('Delete'); + }) + $("#Pointer").on('click',function(){ + self.unselect_component("Pointer"); + }) + $("#Node").on('click',function(){ self.component_toggle('Node'); }); - $("#Ensemble > svg").on('click',function(){ + $("#Ensemble").on('click',function(){ self.component_toggle('Ensemble'); }); - $("#Network > svg").on('click',function(){ + $("#Network").on('click',function(){ self.component_toggle('Network'); }); - $("#Connection > svg").on('click',function(){ + $("#Connection").on('click',function(){ self.component_toggle('Connection'); }); } @@ -30,10 +37,10 @@ Nengo.VPL.prototype.component_toggle = function(type) { /** Activates and deactivates modes, accordingly */ if($("#"+type).attr('class') == 'mode off'){ - self.unselct_component(); + self.unselect_component(); $("#"+type).attr('class','mode on'); } else{ - self.unselct_component(); + self.unselect_component("Pointer"); return; } @@ -42,13 +49,20 @@ Nengo.VPL.prototype.component_toggle = function(type) { var cur_obj = ""; var obj_class = ""; var obj_name = ""; + var element = document.getElementById('main'); + /** Adds green border and deactivates click and drag events. */ + element.style['box-shadow'] = "inset 0 0 30px #00FF00"; + self.toggle_item_drag(false); - if(type != 'Connection'){ - $('#netgraph').css('cursor','crosshair','important'); + if(type != 'Connection' && type != "Delete"){ + $('#netgraph').css('cursor','crosshair'); self.create_component(type); } else if(type == 'Connection'){ - $('#netgraph').css('cursor','cell','important'); + $('#netgraph').css('cursor','cell'); self.create_connection(); + } else if(type == 'Delete'){ + $('#netgraph').css('cursor','pointer'); + self.delete_mode(); } } } @@ -63,18 +77,12 @@ Nengo.VPL.prototype.create_component = function(type){ var name_convert = {"Node":"node","Ensemble":"ens","Network":"net"} var element = document.getElementById('main'); - /** Adds green border and deactivates click and drag events. */ - element.style['box-shadow'] = "inset 0 0 30px #00FF00"; - self.toggle_item_drag(false); - $(document).mousedown(function(event) { /** Cancels/deactivates click event if it is a right click. */ - if(event.button == 0){} - else if(event.button == 2){ - self.unselct_component(); + if(event.button == 2){ + self.unselect_component("Pointer"); return; } - else{return;} cur_obj = $($(event.target).parent()[0]); obj_class = cur_obj.attr('class'); @@ -168,121 +176,158 @@ Nengo.VPL.prototype.create_component = function(type){ } Nengo.VPL.prototype.create_connection = function(){ + var self = this; var cur_obj = ""; var obj_class = ""; var obj_name = ""; - var self = this; var ng = Nengo.netgraph; + var mousemove = false; /** Creates dashed line to signify potential connection. */ + self.delete_dashed_line(); self.create_dash_line(); var objects = {}; objects.uids = []; objects.classes = []; - $('.node').attr('class','node node_effect_good'); - $('.ens').attr('class','ens ens_effect_good'); + self.show_connectable(true,true); /** On main click and not drag. */ - $('#netgraph').on('mousedown', function (event) { + $(document).mousedown(function(event) { if(event.button == 2){ + self.unselect_component("Pointer"); return; } - $('#netgraph').on('mouseup mousemove', function(evt) { - if (evt.type === 'mouseup') { - cur_obj = $($(event.target).parent()[0]); - obj_class = cur_obj.attr('class').split(" ")[0]; - obj_name = cur_obj.attr('data-id'); - - /** Adds first part of potential connection if - * it is an ensemble or node. */ - if(obj_class != null && (obj_class == 'ens' || - obj_class == 'node')){ - objects.uids.push(obj_name); - objects.classes.push(obj_class); - - } - /** Cancels connection if misclicked. */ - else if (objects.uids.length == 1){ - self.unselct_component(); - return; - } - - if(objects.uids.length == 1){ - $('.node').attr('class','node node_effect_bad'); - } - - /** After the first component is selected. */ - if(objects.uids.length == 1){ - var obj_pos; - var over_obj_pos; - var over_obj_svg; - var over_obj_name; - var over_obj_class; - var rel_pos = []; - $("#main").on('mousemove',function(event){ - /** Updates cursor connection line's - * position. */ - obj_pos = ng.svg_objects[objects.uids[0]].get_screen_location(); - over_obj_svg = $($(event.target).parent()[0]); - over_obj_name = over_obj_svg.attr('data-id'); - - if(over_obj_svg.attr('class') != null){ - over_obj_class = over_obj_svg.attr('class').split(" ")[0]; - } - - $(".temp_line").attr('x1',obj_pos[0]); - $(".temp_line").attr('y1',obj_pos[1]); - if(over_obj_class != null && (over_obj_class == 'ens'|| - over_obj_class == 'node')){ - rel_pos = []; - over_obj_pos = ng.svg_objects[over_obj_name].get_screen_location(); - - rel_pos[0] = over_obj_pos[0]-0.07*(over_obj_pos[0]-obj_pos[0]); - rel_pos[1] = over_obj_pos[1]-0.07*(over_obj_pos[1]-obj_pos[1]); - - $(".temp_line").attr('x2',(rel_pos[0])); - $(".temp_line").attr('y2',(rel_pos[1])); - - }else{ - $(".temp_line").attr('x2',(event.pageX-$(this).offset().left)); - $(".temp_line").attr('y2',((event.pageY-$(this).offset().top))); - } - }); - } - if(objects.uids.length == 2) { - /** Completes Connection, adds appropriate - * code to editor and resets line. */ - self.add_connection(objects.uids[0],objects.uids[1]); - objects = {}; - objects.uids = []; - objects.classes = []; - $("#main").unbind('mousemove'); - $(".temp_group").remove(); - $('.node').attr('class','node node_effect_good'); - $('.ens').attr('class','ens ens_effect_good'); - self.create_dash_line(); - } + cur_obj = $($(event.target).parent()[0]); + if(cur_obj.attr('class') != null){ + obj_class = cur_obj.attr('class').split(" ")[0]; + } + obj_name = cur_obj.attr('data-id'); + var rel_pos = []; + var obj_pos; + var over_obj_pos; + + /** Adds first part of potential connection if + * it is an ensemble or node. */ + if(obj_class != null && (obj_class == 'ens' || + obj_class == 'node')){ + objects.uids.push(obj_name); + objects.classes.push(obj_class); + } + if(objects.uids.length == 0){ + return; + } else{ + self.show_connectable(true,false); + } + $(document).mousemove(function(evt) { + mousemove = true; + /** After the first component is selected. */ + rel_pos = []; + obj_pos = []; + over_obj_pos = []; + var over_obj_svg; + var over_obj_name; + var over_obj_class; + + /** Updates cursor connection line's + * position. */ + var page_offset = $("#main").offset(); + obj_pos = ng.svg_objects[objects.uids[0]].get_screen_location(); + over_obj_svg = $($(evt.target).parent()[0]); + over_obj_name = over_obj_svg.attr('data-id'); + if(over_obj_svg.attr('class') != null){ + over_obj_class = over_obj_svg.attr('class').split(" ")[0]; + } + + $(".temp_line").attr('x1',obj_pos[0]); + $(".temp_line").attr('y1',obj_pos[1]); + if(over_obj_class != null && (over_obj_class == 'ens'|| + over_obj_class == 'node')){ + rel_pos = []; + over_obj_pos = ng.svg_objects[over_obj_name].get_screen_location(); + + rel_pos[0] = over_obj_pos[0]-0.07*(over_obj_pos[0]-obj_pos[0]); + rel_pos[1] = over_obj_pos[1]-0.07*(over_obj_pos[1]-obj_pos[1]); + + $(".temp_line").attr('x2',(rel_pos[0])); + $(".temp_line").attr('y2',(rel_pos[1])); + + }else{ + rel_pos[0] = evt.pageX-page_offset.left; + rel_pos[1] = evt.pageY-page_offset.top; + $(".temp_line").attr('x2',rel_pos[0]); + $(".temp_line").attr('y2',rel_pos[1]); + } + }); + $(document).one('mouseup', function(evt) { + /** Completes Connection, adds appropriate + * code to editor and resets line. */ + cur_obj = $($(evt.target).parent()[0]); + obj_class = cur_obj.attr('class').split(" ")[0]; + obj_name = cur_obj.attr('data-id'); + + /** Adds first part of potential connection if + * it is an ensemble or node. */ + if(rel_pos != false){ + if(obj_class != null && (obj_class == 'ens' || + obj_class == 'node')){ + objects.uids.push(obj_name); + objects.classes.push(obj_class); + } + if(objects.uids.length == 2){ + self.add_connection(objects.uids[0],objects.uids[1]); + } } - $('#netgraph').off('mouseup mousemove'); + objects = {}; + objects.uids = []; + objects.classes = []; + self.delete_dashed_line(); + self.create_dash_line(); + self.show_connectable(true,true); + $(document).unbind('mousemove mouseup'); }); }); } +Nengo.VPL.prototype.delete_mode = function(){ + var self = this; + var cur_obj = ""; + var obj_class = ""; + var obj_name = ""; + $(document).mousemove(function(event) { + if(event.button == 2){ + self.unselect_component("Pointer"); + return; + } + cur_obj = $($(event.target).parent()[0]); + if(cur_obj.attr('class') != null){ + obj_class = cur_obj.attr('class').split(" ")[0]; + } + obj_name = cur_obj.attr('data-id'); + if(obj_name != null){ + $("#netgraph").css('cursor','pointer'); + }else{ + $("#netgraph").css('cursor',''); + } + $(document).click(function(evt){ + if(obj_name != null){ + self.delete_component(obj_name); + } + }); + }); +} /** Deactivates the various styling and event handlers activated by the * component toggle. */ -Nengo.VPL.prototype.unselct_component = function(type){ +Nengo.VPL.prototype.unselect_component = function(default_mode){ var ng = Nengo.netgraph; this.toggle_item_drag(true); - $(".mode").attr('class','mode off'); $('#netgraph').css('cursor',''); - $('.node').attr('class','node'); - $('.ens').attr('class','ens'); - $('#netgraph').unbind('mousedown'); - $('#netgraph').unbind('click'); - $(".temp_group").remove(); - $("#main").unbind('mousemove'); - $(document).unbind('mousedown'); + if(default_mode == "Pointer"){ + $("#"+default_mode).attr('class','mode on'); + } + this.show_connectable(false,false); + this.delete_dashed_line(); + $(document).unbind('mousedown mousemove mouseup click'); var element = document.getElementById('main'); element.style['box-shadow'] = ""; } @@ -445,6 +490,33 @@ Nengo.VPL.prototype.open_name = function(name) { return name + num; } +Nengo.VPL.prototype.show_connectable = function(toggle,allowed_connect){ + var node_style; + if(toggle == false){ + $(".node").attr("class","node"); + $(".ens").attr("class","ens"); + $(".node").unbind("mouseover mouseout"); + $(".ens").unbind("mouseover mouseout"); + return; + } + else{ + if(allowed_connect == true){ + node_style = 'node node_effect_good'; + } else{ + node_style = 'node node_effect_bad'; + } + $(".node").mouseover(function(){ + $(this).attr('class',node_style); + }).mouseout(function(){ + $(this).attr('class','node'); + }) + $(".ens").mouseover(function(){ + $(this).attr('class','ens ens_effect_good'); + }).mouseout(function(){ + $(this).attr('class','ens'); + }); + } +} Nengo.VPL.prototype.create_dash_line = function(){ var svg = d3.select('#netgraph'); @@ -471,8 +543,26 @@ Nengo.VPL.prototype.create_dash_line = function(){ } +Nengo.VPL.prototype.delete_dashed_line = function(){ + if($(".temp_group") != null){ + $(".temp_group").remove(); + } +} + /** Activates/deactivates drag/resize events. */ Nengo.VPL.prototype.toggle_item_drag = function(toggle){ + var self = this; + if(self.contextmenu == null && jQuery._data($("#netgraph")[0],"events")['contextmenu'] != null){ + self.contextmenu = jQuery._data( $("#netgraph")[0], "events" )['contextmenu'][0].handler + } + if(toggle){ + setTimeout(function(){ + $("#netgraph").bind("contextmenu",self.contextmenu); + },100); + } else{ + $("#netgraph").bind("contextmenu",function(event){ + event.preventDefault()}); + } interact($("#netgraph")[0]).draggable({ enabled: toggle // explicitly disable dragging }); diff --git a/nengo_gui/static/side_menu.css b/nengo_gui/static/side_menu.css index 1df84298..2d0c771d 100644 --- a/nengo_gui/static/side_menu.css +++ b/nengo_gui/static/side_menu.css @@ -8,7 +8,7 @@ -webkit-flex-direction: column; flex-direction: column; box-sizing: border-box; - width: 200px; + width: 180px; position: relative;; z-index: 9999; display: none; diff --git a/nengo_gui/static/side_menu.js b/nengo_gui/static/side_menu.js index b0db8b62..99cab9eb 100644 --- a/nengo_gui/static/side_menu.js +++ b/nengo_gui/static/side_menu.js @@ -22,7 +22,7 @@ Nengo.SideMenu = function() { self.initialize_button_handlers(); self.width = $(".sidenav-container").width(); self.height = $("#vmiddle").height(); - + self.minWidth = 180; //----EVENT HANDLERS---- interact('.sidenav-container') @@ -30,7 +30,7 @@ Nengo.SideMenu = function() { edges: { left: false, right: true, bottom: false, top: false} }) .on('resizemove', function (event) { - if(self.width > 200 || event.deltaRect.right > 0){ + if(self.width > self.minWidth || event.deltaRect.right > 0){ self.width += event.deltaRect.right; $(".sidenav-container").width(self.width); diff --git a/nengo_gui/templates/page.html b/nengo_gui/templates/page.html index 129629d6..29b49404 100644 --- a/nengo_gui/templates/page.html +++ b/nengo_gui/templates/page.html @@ -81,73 +81,80 @@
- - - +
- - - - -
-
- - - - - - - - - - - -

Ensemble

-
+
+
+ + +

Move/Resize

+
+
-
- - - - - -

Node

-
+
+
+ + + + + + + + + + +

Ensemble

+
+
-
+
+
+ + + + + +

Node

+
+
- - - - - -

Network

-
+
+
+ + + + + +

Network

+
+
-
+
+
+ + + + + + +

Connection

+
+
- - - - - - - - -

Connection

-
+
+
+ + +

Remove

+
+
-

Configure Settings

+

Settings

@@ -161,13 +168,13 @@
-

Download Simulation Data

+

Simulation Data

-

Export layout as SVG

+

Layout as SVG

@@ -180,7 +187,6 @@

-
From 8545f0a266e65a5be9654b6c0045c6b729268130 Mon Sep 17 00:00:00 2001 From: Rees Simmons Date: Thu, 10 Nov 2016 10:12:36 -0500 Subject: [PATCH 6/9] changes to drag and drop and introduction of configuration files --- nengo_gui/static/components/netgraph.css | 16 ++ nengo_gui/static/components/netgraph.js | 9 +- nengo_gui/static/components/netgraph_conn.js | 5 +- nengo_gui/static/components/netgraph_item.js | 2 +- nengo_gui/static/drag_and_drop.css | 79 +++++-- nengo_gui/static/drag_and_drop.js | 71 +++--- .../static/lib/css/bootstrap-slider.min.css | 41 ++++ .../static/lib/js/bootstrap-slider.min.js | 5 + nengo_gui/static/modal.css | 2 - nengo_gui/static/modal.js | 4 +- nengo_gui/static/nengo.js | 23 ++ nengo_gui/static/vpl_config.css | 26 +++ nengo_gui/static/vpl_config.js | 206 ++++++++++++++++++ nengo_gui/templates/page.html | 121 +++++----- 14 files changed, 486 insertions(+), 124 deletions(-) create mode 100644 nengo_gui/static/lib/css/bootstrap-slider.min.css create mode 100644 nengo_gui/static/lib/js/bootstrap-slider.min.js create mode 100644 nengo_gui/static/vpl_config.css create mode 100644 nengo_gui/static/vpl_config.js diff --git a/nengo_gui/static/components/netgraph.css b/nengo_gui/static/components/netgraph.css index f2b2ed22..fe25c395 100644 --- a/nengo_gui/static/components/netgraph.css +++ b/nengo_gui/static/components/netgraph.css @@ -61,3 +61,19 @@ rect.view{ text-anchor: middle; dominant-baseline: text-before-edge; } + +.node:hover > rect{ + filter: url(#drop-shadow); +} + +.ens:hover > g{ + filter: url(#drop-shadow); +} + +.net:hover > rect{ + filter: url(#drop-shadow); +} + +.conn:hover{ + filter: url(#drop-shadow); +} diff --git a/nengo_gui/static/components/netgraph.js b/nengo_gui/static/components/netgraph.js index 544c991d..8d5649d8 100644 --- a/nengo_gui/static/components/netgraph.js +++ b/nengo_gui/static/components/netgraph.js @@ -163,6 +163,9 @@ Nengo.NetGraph = function(parent, args) { defs.appendChild(s); this.svg.insertBefore(defs, this.svg.firstChild); + Nengo.create_filter([1,1,1],2,"drop-shadow"); + Nengo.create_filter([2,0,0],1,"red-drop-shadow"); + Nengo.create_filter([0,2,0],1,"green-drop-shadow"); /** connect to server */ this.ws = Nengo.create_websocket(args.uid); @@ -309,8 +312,10 @@ Nengo.NetGraph = function(parent, args) { if (self.menu.visible_any()) { self.menu.hide_any(); } else { - self.menu.show(event.clientX, event.clientY, - self.generate_menu()); + if(Nengo.vpl.edit_mode == false){ + self.menu.show(event.clientX, event.clientY, + self.generate_menu()); + } } }); diff --git a/nengo_gui/static/components/netgraph_conn.js b/nengo_gui/static/components/netgraph_conn.js index c3817f85..92937e0c 100644 --- a/nengo_gui/static/components/netgraph_conn.js +++ b/nengo_gui/static/components/netgraph_conn.js @@ -57,6 +57,7 @@ Nengo.NetGraphConnection = function(ng, info, minimap, mini_conn) { /** create the line and its arrowhead marker */ this.g = ng.createSVGElement('g'); + this.g.classList.add('conn'); this.menu = new Nengo.Menu(this.ng.parent); var self = this; $(this.g).bind('contextmenu', function(event) { @@ -388,6 +389,8 @@ Nengo.NetGraphConnection.prototype.intersect_length = function(theta, alpha, wid Nengo.NetGraphConnection.prototype.generate_menu = function(){ var self = this; var items = []; - items.push(['Delete Connection',function(){Nengo.vpl.delete_connection(self.pre.uid,self.post.uid)}]); + items.push(['Delete Connection',function(){ + Nengo.vpl.delete_connection(self.pre.uid,self.post.uid); + }]); return items; } diff --git a/nengo_gui/static/components/netgraph_item.js b/nengo_gui/static/components/netgraph_item.js index 984064c5..80400d5c 100644 --- a/nengo_gui/static/components/netgraph_item.js +++ b/nengo_gui/static/components/netgraph_item.js @@ -396,7 +396,7 @@ Nengo.NetGraphItem.prototype.generate_menu = function () { items.push(['Spikes', function() {self.create_graph('Raster');}]); items.push(['Voltages', function() {self.create_graph('Voltage');}]); items.push(['Firing pattern', function() {self.create_graph('SpikeGrid');}]); - items.push(['Configure',function(){Nengo.vpl.config_component(self.uid)}]); + items.push(['Configure',function(){Nengo.vpl_config.ensemble_config(self.uid)}]); } if (this.type == 'node') { items.push(['Slider', function() {self.create_graph('Slider');}]); diff --git a/nengo_gui/static/drag_and_drop.css b/nengo_gui/static/drag_and_drop.css index a88ed036..18c02538 100644 --- a/nengo_gui/static/drag_and_drop.css +++ b/nengo_gui/static/drag_and_drop.css @@ -9,16 +9,24 @@ .off{ cursor: pointer; background: inherit; - fill: rgb(180,180,180); + fill: #DDD; -webkit-transition-duration: 0.4s; /* Safari */ transition-duration: 0.2s; } + +.off:hover{ + transform: scale(1.2); + fill: #DDD; + color: #000; +} + .on{ + transition-duration: 0.2s; cursor: pointer; - background-color: #BBB; + background-color: #CCC; fill: #DDD; color: #000; - box-shadow: inset 0 0 5px #666; + box-shadow: inset 0 0 5px #000; } @@ -29,17 +37,15 @@ width: 100%; color: inherit; z-index: 99999; - padding-left: 20px; + padding: 2px; } .mode{ position: relative; + margin: 2px; float: left; - border-radius: 5px; - width: 110px; - height: 90px; - margin: 10px; - border: 1px solid black; + width: 80px; + height: 80px; -webkit-user-select: none; /* Chrome/Safari */ -moz-user-select: none; /* Firefox */ } @@ -51,31 +57,62 @@ height: 100%; } .mode > div >svg{ - width: 60%; + width: 100%; } .mode > div > p{ position: absolute; - bottom: -5px; + bottom: -13%; margin-left: auto; margin-right: auto; left: 0; right: 0; + display: none; + font-size: 12px; } -.off:hover{ - background-color: #BBB; - fill: #DDD; - color: #000; + +.mode > div > span{ + position: absolute; + margin-left: auto; + margin-right: auto; + left: 0; + right: 0; + top: 25%; + font-size: 2em; +} + +.mode-group { + overflow: hidden; +} + +.mode-group > p { + border-bottom: 1px solid black; +} + +.node_effect_good > rect{ + filter: url(#green-drop-shadow) !important; +} + +.node_effect_bad > rect{ + filter: url(#red-drop-shadow) !important; +} + +.ens_effect_good > g{ + filter: url(#green-drop-shadow) !important; +} + +.node.red_hover:hover rect { + filter: url(#red-drop-shadow) !important; } -.node_effect_good > rect:nth-child(2){ - fill: #5DFC0A !important; +.ens.red_hover:hover > g{ + filter: url(#red-drop-shadow) !important; } -.node_effect_bad > rect:nth-child(2){ - fill: #FD0000 !important; +.net.red_hover:hover > rect{ + filter: url(#red-drop-shadow) !important; } -.ens_effect_good > .ensemble{ - fill: #5DFC0A !important; +.conn.red_hover:hover { + filter: url(#red-drop-shadow); } diff --git a/nengo_gui/static/drag_and_drop.js b/nengo_gui/static/drag_and_drop.js index 0bcd1069..89fca4d8 100644 --- a/nengo_gui/static/drag_and_drop.js +++ b/nengo_gui/static/drag_and_drop.js @@ -1,5 +1,6 @@ Nengo.VPL = function(){ var self = this; + self.edit_mode = false; $("#mode_list").height($(document).height()*0.85); $(document).keyup(function(e) { if (e.keyCode === 27) self.unselect_component("Pointer"); // esc @@ -11,7 +12,7 @@ Nengo.VPL = function(){ self.component_toggle('Delete'); }) $("#Pointer").on('click',function(){ - self.unselect_component("Pointer"); + self.unselect_component('Pointer'); }) $("#Node").on('click',function(){ self.component_toggle('Node'); @@ -51,9 +52,8 @@ Nengo.VPL.prototype.component_toggle = function(type) { var obj_name = ""; var element = document.getElementById('main'); /** Adds green border and deactivates click and drag events. */ - element.style['box-shadow'] = "inset 0 0 30px #00FF00"; + element.style['box-shadow'] = "inset 0 0 30px #5da3ff"; self.toggle_item_drag(false); - if(type != 'Connection' && type != "Delete"){ $('#netgraph').css('cursor','crosshair'); self.create_component(type); @@ -61,6 +61,10 @@ Nengo.VPL.prototype.component_toggle = function(type) { $('#netgraph').css('cursor','cell'); self.create_connection(); } else if(type == 'Delete'){ + $('.node').attr('class','node red_hover'); + $('.ens').attr('class','ens red_hover'); + $('.net').attr('class','net red_hover'); + $('.conn').attr('class','conn red_hover'); $('#netgraph').css('cursor','pointer'); self.delete_mode(); } @@ -150,6 +154,7 @@ Nengo.VPL.prototype.create_component = function(type){ obj_prop.parent_network = event.parent_network; var comp_name = self.add_component(type,obj_prop); + console.log(comp_name); /** Checks every 100ms until the component shows up in * the netgraph and notifies the server. */ var checkExist = setInterval(function() { @@ -262,7 +267,9 @@ Nengo.VPL.prototype.create_connection = function(){ /** Completes Connection, adds appropriate * code to editor and resets line. */ cur_obj = $($(evt.target).parent()[0]); - obj_class = cur_obj.attr('class').split(" ")[0]; + if(cur_obj.attr('class') != null){ + obj_class = cur_obj.attr('class').split(" ")[0]; + } obj_name = cur_obj.attr('data-id'); /** Adds first part of potential connection if @@ -294,10 +301,10 @@ Nengo.VPL.prototype.delete_mode = function(){ var obj_class = ""; var obj_name = ""; $(document).mousemove(function(event) { - if(event.button == 2){ - self.unselect_component("Pointer"); - return; - } + $('.node').attr('class','node red_hover'); + $('.ens').attr('class','ens red_hover'); + $('.net').attr('class','net red_hover'); + $('.conn').attr('class','conn red_hover'); cur_obj = $($(event.target).parent()[0]); if(cur_obj.attr('class') != null){ obj_class = cur_obj.attr('class').split(" ")[0]; @@ -309,12 +316,17 @@ Nengo.VPL.prototype.delete_mode = function(){ $("#netgraph").css('cursor',''); } $(document).click(function(evt){ + if(event.button == 2){ + self.unselect_component("Pointer"); + return; + } if(obj_name != null){ self.delete_component(obj_name); } }); }); } + /** Deactivates the various styling and event handlers activated by the * component toggle. */ Nengo.VPL.prototype.unselect_component = function(default_mode){ @@ -495,6 +507,8 @@ Nengo.VPL.prototype.show_connectable = function(toggle,allowed_connect){ if(toggle == false){ $(".node").attr("class","node"); $(".ens").attr("class","ens"); + $(".net").attr("class","net"); + $(".conn").attr("class","conn"); $(".node").unbind("mouseover mouseout"); $(".ens").unbind("mouseover mouseout"); return; @@ -552,16 +566,14 @@ Nengo.VPL.prototype.delete_dashed_line = function(){ /** Activates/deactivates drag/resize events. */ Nengo.VPL.prototype.toggle_item_drag = function(toggle){ var self = this; - if(self.contextmenu == null && jQuery._data($("#netgraph")[0],"events")['contextmenu'] != null){ - self.contextmenu = jQuery._data( $("#netgraph")[0], "events" )['contextmenu'][0].handler - } if(toggle){ setTimeout(function(){ - $("#netgraph").bind("contextmenu",self.contextmenu); + self.edit_mode = false; },100); } else{ - $("#netgraph").bind("contextmenu",function(event){ - event.preventDefault()}); + setTimeout(function(){ + self.edit_mode = true; + },100); } interact($("#netgraph")[0]).draggable({ enabled: toggle // explicitly disable dragging @@ -621,38 +633,13 @@ Nengo.VPL.prototype.delete_connection = function(uid1,uid2){ } } -Nengo.VPL.prototype.config_component = function(uid){ +Nengo.VPL.prototype.config_connection = function(uid1,uid2){ var self = this; var editor = ace.edit("editor"); - var re = new RegExp("(?:[^\.])"+uid+"\\s*="); - var code_line = editor.find(re); - var component = Nengo.netgraph.svg_objects[uid]; - Nengo.modal.component_config(uid); + Nengo.modal.config_connection_modal(); Nengo.modal.show(); Nengo.modal.footer('ok_cancel', function(e) { - var n_number = $("#config-neuron-number").val(); - var dim_number = $("#config-dimension").val(); - var Range = require("ace/range").Range; - var tab = " "; - var tabs; - Nengo.netgraph.svg_objects[uid].n_neurons = n_number; - Nengo.netgraph.svg_objects[uid].dimensions = dim_number; - - if(component.parent == null){tabs = tab;} - else{tabs = tab+tab;} - - editor.session.replace(new Range(code_line.start.row, 0, code_line.start.row, Number.MAX_VALUE), - tabs+uid+" = nengo.Ensemble(n_neurons="+n_number+",dimensions="+dim_number+")"); - self.delete_connections(uid); - var conn_in = component.conn_in; - var conn_out = component.conn_out; - for(var x = 0; x < conn_in.length; x++){ - self.add_connection(conn_in[x].pre.uid,conn_in[x].post.uid); - } - for(var x = 0; x < conn_out.length; x++){ - self.add_connection(conn_out[x].pre.uid,conn_out[x].post.uid); - } $('#OK').attr('data-dismiss', 'modal'); }, function () { @@ -662,5 +649,5 @@ Nengo.VPL.prototype.config_component = function(uid){ } $(document).ready(function(){ - Nengo.vpl = new Nengo.VPL(); + Nengo.vpl = new Nengo.VPL(); }); diff --git a/nengo_gui/static/lib/css/bootstrap-slider.min.css b/nengo_gui/static/lib/css/bootstrap-slider.min.css new file mode 100644 index 00000000..1fecc069 --- /dev/null +++ b/nengo_gui/static/lib/css/bootstrap-slider.min.css @@ -0,0 +1,41 @@ +/*! ======================================================= + VERSION 9.4.1 +========================================================= */ +/*! ========================================================= + * bootstrap-slider.js + * + * Maintainers: + * Kyle Kemp + * - Twitter: @seiyria + * - Github: seiyria + * Rohit Kalkur + * - Twitter: @Rovolutionary + * - Github: rovolution + * + * ========================================================= + * + * bootstrap-slider is released under the MIT License + * Copyright (c) 2016 Kyle Kemp, Rohit Kalkur, and contributors + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, + * copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following + * conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + * + * ========================================================= */.slider{display:inline-block;vertical-align:middle;position:relative}.slider.slider-horizontal{width:210px;height:20px}.slider.slider-horizontal .slider-track{height:10px;width:100%;margin-top:-5px;top:50%;left:0}.slider.slider-horizontal .slider-selection,.slider.slider-horizontal .slider-track-low,.slider.slider-horizontal .slider-track-high{height:100%;top:0;bottom:0}.slider.slider-horizontal .slider-tick,.slider.slider-horizontal .slider-handle{margin-left:-10px}.slider.slider-horizontal .slider-tick.triangle,.slider.slider-horizontal .slider-handle.triangle{position:relative;top:50%;transform:translateY(-50%);border-width:0 10px 10px 10px;width:0;height:0;border-bottom-color:#0480be;margin-top:0}.slider.slider-horizontal .slider-tick-container{white-space:nowrap;position:absolute;top:0;left:0;width:100%}.slider.slider-horizontal .slider-tick-label-container{white-space:nowrap;margin-top:20px}.slider.slider-horizontal .slider-tick-label-container .slider-tick-label{padding-top:4px;display:inline-block;text-align:center}.slider.slider-vertical{height:210px;width:20px}.slider.slider-vertical .slider-track{width:10px;height:100%;left:25%;top:0}.slider.slider-vertical .slider-selection{width:100%;left:0;top:0;bottom:0}.slider.slider-vertical .slider-track-low,.slider.slider-vertical .slider-track-high{width:100%;left:0;right:0}.slider.slider-vertical .slider-tick,.slider.slider-vertical .slider-handle{margin-top:-10px}.slider.slider-vertical .slider-tick.triangle,.slider.slider-vertical .slider-handle.triangle{border-width:10px 0 10px 10px;width:1px;height:1px;border-left-color:#0480be;margin-left:0}.slider.slider-vertical .slider-tick-label-container{white-space:nowrap}.slider.slider-vertical .slider-tick-label-container .slider-tick-label{padding-left:4px}.slider.slider-disabled .slider-handle{background-image:-webkit-linear-gradient(top,#dfdfdf 0,#bebebe 100%);background-image:-o-linear-gradient(top,#dfdfdf 0,#bebebe 100%);background-image:linear-gradient(to bottom,#dfdfdf 0,#bebebe 100%);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffdfdfdf',endColorstr='#ffbebebe',GradientType=0)}.slider.slider-disabled .slider-track{background-image:-webkit-linear-gradient(top,#e5e5e5 0,#e9e9e9 100%);background-image:-o-linear-gradient(top,#e5e5e5 0,#e9e9e9 100%);background-image:linear-gradient(to bottom,#e5e5e5 0,#e9e9e9 100%);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffe5e5e5',endColorstr='#ffe9e9e9',GradientType=0);cursor:not-allowed}.slider input{display:none}.slider .tooltip.top{margin-top:-36px}.slider .tooltip-inner{white-space:nowrap;max-width:none}.slider .hide{display:none}.slider-track{position:absolute;cursor:pointer;background-image:-webkit-linear-gradient(top,#f5f5f5 0,#f9f9f9 100%);background-image:-o-linear-gradient(top,#f5f5f5 0,#f9f9f9 100%);background-image:linear-gradient(to bottom,#f5f5f5 0,#f9f9f9 100%);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#fff5f5f5',endColorstr='#fff9f9f9',GradientType=0);-webkit-box-shadow:inset 0 1px 2px rgba(0,0,0,0.1);box-shadow:inset 0 1px 2px rgba(0,0,0,0.1);border-radius:4px}.slider-selection{position:absolute;background-image:-webkit-linear-gradient(top,#f9f9f9 0,#f5f5f5 100%);background-image:-o-linear-gradient(top,#f9f9f9 0,#f5f5f5 100%);background-image:linear-gradient(to bottom,#f9f9f9 0,#f5f5f5 100%);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#fff9f9f9',endColorstr='#fff5f5f5',GradientType=0);-webkit-box-shadow:inset 0 -1px 0 rgba(0,0,0,0.15);box-shadow:inset 0 -1px 0 rgba(0,0,0,0.15);-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box;border-radius:4px}.slider-selection.tick-slider-selection{background-image:-webkit-linear-gradient(top,#89cdef 0,#81bfde 100%);background-image:-o-linear-gradient(top,#89cdef 0,#81bfde 100%);background-image:linear-gradient(to bottom,#89cdef 0,#81bfde 100%);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff89cdef',endColorstr='#ff81bfde',GradientType=0)}.slider-track-low,.slider-track-high{position:absolute;background:transparent;-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box;border-radius:4px}.slider-handle{position:absolute;top:0;width:20px;height:20px;background-color:#337ab7;background-image:-webkit-linear-gradient(top,#149bdf 0,#0480be 100%);background-image:-o-linear-gradient(top,#149bdf 0,#0480be 100%);background-image:linear-gradient(to bottom,#149bdf 0,#0480be 100%);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff149bdf',endColorstr='#ff0480be',GradientType=0);filter:none;-webkit-box-shadow:inset 0 1px 0 rgba(255,255,255,.2),0 1px 2px rgba(0,0,0,.05);box-shadow:inset 0 1px 0 rgba(255,255,255,.2),0 1px 2px rgba(0,0,0,.05);border:0 solid transparent}.slider-handle.round{border-radius:50%}.slider-handle.triangle{background:transparent none}.slider-handle.custom{background:transparent none}.slider-handle.custom::before{line-height:20px;font-size:20px;content:'\2605';color:#726204}.slider-tick{position:absolute;width:20px;height:20px;background-image:-webkit-linear-gradient(top,#f9f9f9 0,#f5f5f5 100%);background-image:-o-linear-gradient(top,#f9f9f9 0,#f5f5f5 100%);background-image:linear-gradient(to bottom,#f9f9f9 0,#f5f5f5 100%);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#fff9f9f9',endColorstr='#fff5f5f5',GradientType=0);-webkit-box-shadow:inset 0 -1px 0 rgba(0,0,0,0.15);box-shadow:inset 0 -1px 0 rgba(0,0,0,0.15);-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box;filter:none;opacity:.8;border:0 solid transparent}.slider-tick.round{border-radius:50%}.slider-tick.triangle{background:transparent none}.slider-tick.custom{background:transparent none}.slider-tick.custom::before{line-height:20px;font-size:20px;content:'\2605';color:#726204}.slider-tick.in-selection{background-image:-webkit-linear-gradient(top,#89cdef 0,#81bfde 100%);background-image:-o-linear-gradient(top,#89cdef 0,#81bfde 100%);background-image:linear-gradient(to bottom,#89cdef 0,#81bfde 100%);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff89cdef',endColorstr='#ff81bfde',GradientType=0);opacity:1} \ No newline at end of file diff --git a/nengo_gui/static/lib/js/bootstrap-slider.min.js b/nengo_gui/static/lib/js/bootstrap-slider.min.js new file mode 100644 index 00000000..b926993a --- /dev/null +++ b/nengo_gui/static/lib/js/bootstrap-slider.min.js @@ -0,0 +1,5 @@ +/*! ======================================================= + VERSION 9.4.1 +========================================================= */ +"use strict";var _typeof="function"==typeof Symbol&&"symbol"==typeof Symbol.iterator?function(a){return typeof a}:function(a){return a&&"function"==typeof Symbol&&a.constructor===Symbol?"symbol":typeof a},windowIsDefined="object"===("undefined"==typeof window?"undefined":_typeof(window));!function(a){if("function"==typeof define&&define.amd)define(["jquery"],a);else if("object"===("undefined"==typeof module?"undefined":_typeof(module))&&module.exports){var b;try{b=require("jquery")}catch(c){b=null}module.exports=a(b)}else window&&(window.Slider=a(window.jQuery))}(function(a){var b="slider",c="bootstrapSlider";windowIsDefined&&!window.console&&(window.console={}),windowIsDefined&&!window.console.log&&(window.console.log=function(){}),windowIsDefined&&!window.console.warn&&(window.console.warn=function(){});var d;return function(a){function b(){}function c(a){function c(b){b.prototype.option||(b.prototype.option=function(b){a.isPlainObject(b)&&(this.options=a.extend(!0,this.options,b))})}function e(b,c){a.fn[b]=function(e){if("string"==typeof e){for(var g=d.call(arguments,1),h=0,i=this.length;i>h;h++){var j=this[h],k=a.data(j,b);if(k)if(a.isFunction(k[e])&&"_"!==e.charAt(0)){var l=k[e].apply(k,g);if(void 0!==l&&l!==k)return l}else f("no such method '"+e+"' for "+b+" instance");else f("cannot call methods on "+b+" prior to initialization; attempted to call '"+e+"'")}return this}var m=this.map(function(){var d=a.data(this,b);return d?(d.option(e),d._init()):(d=new c(this,e),a.data(this,b,d)),a(this)});return!m||m.length>1?m:m[0]}}if(a){var f="undefined"==typeof console?b:function(a){console.error(a)};return a.bridget=function(a,b){c(b),e(a,b)},a.bridget}}var d=Array.prototype.slice;c(a)}(a),function(a){function e(b,c){function d(a,b){var c="data-slider-"+b.replace(/_/g,"-"),d=a.getAttribute(c);try{return JSON.parse(d)}catch(e){return d}}this._state={value:null,enabled:null,offset:null,size:null,percentage:null,inDrag:!1,over:!1},this.ticksCallbackMap={},this.handleCallbackMap={},"string"==typeof b?this.element=document.querySelector(b):b instanceof HTMLElement&&(this.element=b),c=c?c:{};for(var e=Object.keys(this.defaultOptions),f=0;f0)for(var s=0;s0){for(this.ticksContainer=document.createElement("div"),this.ticksContainer.className="slider-tick-container",f=0;f0)for(this.tickLabelContainer=document.createElement("div"),this.tickLabelContainer.className="slider-tick-label-container",f=0;f0&&(this.options.max=Math.max.apply(Math,this.options.ticks),this.options.min=Math.min.apply(Math,this.options.ticks)),Array.isArray(this.options.value)?(this.options.range=!0,this._state.value=this.options.value):this.options.range?this._state.value=[this.options.value,this.options.max]:this._state.value=this.options.value,this.trackLow=k||this.trackLow,this.trackSelection=j||this.trackSelection,this.trackHigh=l||this.trackHigh,"none"===this.options.selection&&(this._addClass(this.trackLow,"hide"),this._addClass(this.trackSelection,"hide"),this._addClass(this.trackHigh,"hide")),this.handle1=m||this.handle1,this.handle2=n||this.handle2,p===!0)for(this._removeClass(this.handle1,"round triangle"),this._removeClass(this.handle2,"round triangle hide"),f=0;f0){for(var d,e,f,g=0,h=1;hthis.options.max?this.options.max:k},toPercentage:function(a){if(this.options.max===this.options.min)return 0;if(this.options.ticks_positions.length>0){for(var b,c,d,e=0,f=0;f0?this.options.ticks[f-1]:0,d=f>0?this.options.ticks_positions[f-1]:0,c=this.options.ticks[f],e=this.options.ticks_positions[f];break}if(f>0){var g=(a-b)/(c-b);return d+g*(e-d)}}return 100*(a-this.options.min)/(this.options.max-this.options.min)}},logarithmic:{toValue:function(a){var b=0===this.options.min?0:Math.log(this.options.min),c=Math.log(this.options.max),d=Math.exp(b+(c-b)*a/100);return d=this.options.min+Math.round((d-this.options.min)/this.options.step)*this.options.step,dthis.options.max?this.options.max:d},toPercentage:function(a){if(this.options.max===this.options.min)return 0;var b=Math.log(this.options.max),c=0===this.options.min?0:Math.log(this.options.min),d=0===a?0:Math.log(a);return 100*(d-c)/(b-c)}}};d=function(a,b){return e.call(this,a,b),this},d.prototype={_init:function(){},constructor:d,defaultOptions:{id:"",min:0,max:10,step:1,precision:0,orientation:"horizontal",value:5,range:!1,selection:"before",tooltip:"show",tooltip_split:!1,handle:"round",reversed:!1,enabled:!0,formatter:function(a){return Array.isArray(a)?a[0]+" : "+a[1]:a},natural_arrow_keys:!1,ticks:[],ticks_positions:[],ticks_labels:[],ticks_snap_bounds:0,ticks_tooltip:!1,scale:"linear",focus:!1,tooltip_position:null,labelledby:null,rangeHighlights:[]},getElement:function(){return this.sliderElem},getValue:function(){return this.options.range?this._state.value:this._state.value[0]},setValue:function(a,b,c){a||(a=0);var d=this.getValue();this._state.value=this._validateInputValue(a);var e=this._applyPrecision.bind(this);this.options.range?(this._state.value[0]=e(this._state.value[0]),this._state.value[1]=e(this._state.value[1]),this._state.value[0]=Math.max(this.options.min,Math.min(this.options.max,this._state.value[0])),this._state.value[1]=Math.max(this.options.min,Math.min(this.options.max,this._state.value[1]))):(this._state.value=e(this._state.value),this._state.value=[Math.max(this.options.min,Math.min(this.options.max,this._state.value))],this._addClass(this.handle2,"hide"),"after"===this.options.selection?this._state.value[1]=this.options.max:this._state.value[1]=this.options.min),this.options.max>this.options.min?this._state.percentage=[this._toPercentage(this._state.value[0]),this._toPercentage(this._state.value[1]),100*this.options.step/(this.options.max-this.options.min)]:this._state.percentage=[0,0,100],this._layout();var f=this.options.range?this._state.value:this._state.value[0];return this._setDataVal(f),b===!0&&this._trigger("slide",f),d!==f&&c===!0&&this._trigger("change",{oldValue:d,newValue:f}),this},destroy:function(){this._removeSliderEventHandlers(),this.sliderElem.parentNode.removeChild(this.sliderElem),this.element.style.display="",this._cleanUpEventCallbacksMap(),this.element.removeAttribute("data"),a&&(this._unbindJQueryEventHandlers(),this.$element.removeData("slider"))},disable:function(){return this._state.enabled=!1,this.handle1.removeAttribute("tabindex"),this.handle2.removeAttribute("tabindex"),this._addClass(this.sliderElem,"slider-disabled"),this._trigger("slideDisabled"),this},enable:function(){return this._state.enabled=!0,this.handle1.setAttribute("tabindex",0),this.handle2.setAttribute("tabindex",0),this._removeClass(this.sliderElem,"slider-disabled"),this._trigger("slideEnabled"),this},toggle:function(){return this._state.enabled?this.disable():this.enable(),this},isEnabled:function(){return this._state.enabled},on:function(a,b){return this._bindNonQueryEventHandler(a,b),this},off:function(b,c){a?(this.$element.off(b,c),this.$sliderElem.off(b,c)):this._unbindNonQueryEventHandler(b,c)},getAttribute:function(a){return a?this.options[a]:this.options},setAttribute:function(a,b){return this.options[a]=b,this},refresh:function(){return this._removeSliderEventHandlers(),e.call(this,this.element,this.options),a&&a.data(this.element,"slider",this),this},relayout:function(){return this._resize(),this._layout(),this},_removeSliderEventHandlers:function(){if(this.handle1.removeEventListener("keydown",this.handle1Keydown,!1),this.handle2.removeEventListener("keydown",this.handle2Keydown,!1),this.options.ticks_tooltip){for(var a=this.ticksContainer.getElementsByClassName("slider-tick"),b=0;b=0?c:this.attributes["aria-valuenow"].value,e=parseInt(d,10);b.value[0]=e,b.percentage[0]=a.options.ticks_positions[e],a._setToolTipOnMouseOver(b),a._showTooltip()};return b.addEventListener("mouseenter",d,!1),d},addMouseLeave:function(a,b){var c=function(){a._hideTooltip()};return b.addEventListener("mouseleave",c,!1),c}}},_layout:function(){var a;if(a=this.options.reversed?[100-this._state.percentage[0],this.options.range?100-this._state.percentage[1]:this._state.percentage[1]]:[this._state.percentage[0],this._state.percentage[1]],this.handle1.style[this.stylePos]=a[0]+"%",this.handle1.setAttribute("aria-valuenow",this._state.value[0]),this.handle2.style[this.stylePos]=a[1]+"%",this.handle2.setAttribute("aria-valuenow",this._state.value[1]),this.rangeHighlightElements.length>0&&Array.isArray(this.options.rangeHighlights)&&this.options.rangeHighlights.length>0)for(var b=0;b0){var g="vertical"===this.options.orientation?"height":"width",h="vertical"===this.options.orientation?"marginTop":"marginLeft",i=this._state.size/(this.options.ticks.length-1);if(this.tickLabelContainer){var j=0;if(0===this.options.ticks_positions.length)"vertical"!==this.options.orientation&&(this.tickLabelContainer.style[h]=-i/2+"px"),j=this.tickLabelContainer.offsetHeight;else for(k=0;kj&&(j=this.tickLabelContainer.childNodes[k].offsetHeight);"horizontal"===this.options.orientation&&(this.sliderElem.style.marginBottom=j+"px")}for(var k=0;k=a[0]&&l<=a[1]&&this._addClass(this.ticks[k],"in-selection"):"after"===this.options.selection&&l>=a[0]?this._addClass(this.ticks[k],"in-selection"):"before"===this.options.selection&&l<=a[0]&&this._addClass(this.ticks[k],"in-selection"),this.tickLabels[k]&&(this.tickLabels[k].style[g]=i+"px","vertical"!==this.options.orientation&&void 0!==this.options.ticks_positions[k]?(this.tickLabels[k].style.position="absolute",this.tickLabels[k].style[this.stylePos]=l+"%",this.tickLabels[k].style[h]=-i/2+"px"):"vertical"===this.options.orientation&&(this.tickLabels[k].style.marginLeft=this.sliderElem.offsetWidth+"px",this.tickLabelContainer.style.marginTop=this.sliderElem.offsetWidth/2*-1+"px"))}}var m;if(this.options.range){m=this.options.formatter(this._state.value),this._setText(this.tooltipInner,m),this.tooltip.style[this.stylePos]=(a[1]+a[0])/2+"%","vertical"===this.options.orientation?this._css(this.tooltip,"margin-top",-this.tooltip.offsetHeight/2+"px"):this._css(this.tooltip,"margin-left",-this.tooltip.offsetWidth/2+"px"),"vertical"===this.options.orientation?this._css(this.tooltip,"margin-top",-this.tooltip.offsetHeight/2+"px"):this._css(this.tooltip,"margin-left",-this.tooltip.offsetWidth/2+"px");var n=this.options.formatter(this._state.value[0]);this._setText(this.tooltipInner_min,n);var o=this.options.formatter(this._state.value[1]);this._setText(this.tooltipInner_max,o),this.tooltip_min.style[this.stylePos]=a[0]+"%","vertical"===this.options.orientation?this._css(this.tooltip_min,"margin-top",-this.tooltip_min.offsetHeight/2+"px"):this._css(this.tooltip_min,"margin-left",-this.tooltip_min.offsetWidth/2+"px"),this.tooltip_max.style[this.stylePos]=a[1]+"%","vertical"===this.options.orientation?this._css(this.tooltip_max,"margin-top",-this.tooltip_max.offsetHeight/2+"px"):this._css(this.tooltip_max,"margin-left",-this.tooltip_max.offsetWidth/2+"px")}else m=this.options.formatter(this._state.value[0]),this._setText(this.tooltipInner,m),this.tooltip.style[this.stylePos]=a[0]+"%","vertical"===this.options.orientation?this._css(this.tooltip,"margin-top",-this.tooltip.offsetHeight/2+"px"):this._css(this.tooltip,"margin-left",-this.tooltip.offsetWidth/2+"px");if("vertical"===this.options.orientation)this.trackLow.style.top="0",this.trackLow.style.height=Math.min(a[0],a[1])+"%",this.trackSelection.style.top=Math.min(a[0],a[1])+"%",this.trackSelection.style.height=Math.abs(a[0]-a[1])+"%",this.trackHigh.style.bottom="0",this.trackHigh.style.height=100-Math.min(a[0],a[1])-Math.abs(a[0]-a[1])+"%";else{this.trackLow.style.left="0",this.trackLow.style.width=Math.min(a[0],a[1])+"%",this.trackSelection.style.left=Math.min(a[0],a[1])+"%",this.trackSelection.style.width=Math.abs(a[0]-a[1])+"%",this.trackHigh.style.right="0",this.trackHigh.style.width=100-Math.min(a[0],a[1])-Math.abs(a[0]-a[1])+"%";var p=this.tooltip_min.getBoundingClientRect(),q=this.tooltip_max.getBoundingClientRect();"bottom"===this.options.tooltip_position?p.right>q.left?(this._removeClass(this.tooltip_max,"bottom"),this._addClass(this.tooltip_max,"top"),this.tooltip_max.style.top="",this.tooltip_max.style.bottom="22px"):(this._removeClass(this.tooltip_max,"top"),this._addClass(this.tooltip_max,"bottom"),this.tooltip_max.style.top=this.tooltip_min.style.top,this.tooltip_max.style.bottom=""):p.right>q.left?(this._removeClass(this.tooltip_max,"top"),this._addClass(this.tooltip_max,"bottom"),this.tooltip_max.style.top="18px"):(this._removeClass(this.tooltip_max,"bottom"),this._addClass(this.tooltip_max,"top"),this.tooltip_max.style.top=this.tooltip_min.style.top)}},_createHighlightRange:function(a,b){return this._isHighlightRange(a,b)?a>b?{start:b,size:a-b}:{start:a,size:b-a}:null},_isHighlightRange:function(a,b){return a>=0&&100>=a&&b>=0&&100>=b?!0:!1},_resize:function(a){this._state.offset=this._offset(this.sliderElem),this._state.size=this.sliderElem[this.sizePos],this._layout()},_removeProperty:function(a,b){a.style.removeProperty?a.style.removeProperty(b):a.style.removeAttribute(b)},_mousedown:function(a){if(!this._state.enabled)return!1;this._state.offset=this._offset(this.sliderElem),this._state.size=this.sliderElem[this.sizePos];var b=this._getPercentage(a);if(this.options.range){var c=Math.abs(this._state.percentage[0]-b),d=Math.abs(this._state.percentage[1]-b);this._state.dragged=d>c?0:1,this._adjustPercentageForRangeSliders(b)}else this._state.dragged=0;this._state.percentage[this._state.dragged]=b,this._layout(),this.touchCapable&&(document.removeEventListener("touchmove",this.mousemove,!1),document.removeEventListener("touchend",this.mouseup,!1)),this.mousemove&&document.removeEventListener("mousemove",this.mousemove,!1),this.mouseup&&document.removeEventListener("mouseup",this.mouseup,!1),this.mousemove=this._mousemove.bind(this),this.mouseup=this._mouseup.bind(this),this.touchCapable&&(document.addEventListener("touchmove",this.mousemove,!1),document.addEventListener("touchend",this.mouseup,!1)),document.addEventListener("mousemove",this.mousemove,!1),document.addEventListener("mouseup",this.mouseup,!1),this._state.inDrag=!0;var e=this._calculateValue();return this._trigger("slideStart",e),this._setDataVal(e),this.setValue(e,!1,!0),this._pauseEvent(a),this.options.focus&&this._triggerFocusOnHandle(this._state.dragged),!0},_touchstart:function(a){if(void 0===a.changedTouches)return void this._mousedown(a);var b=a.changedTouches[0];this.touchX=b.pageX,this.touchY=b.pageY},_triggerFocusOnHandle:function(a){0===a&&this.handle1.focus(),1===a&&this.handle2.focus()},_keydown:function(a,b){if(!this._state.enabled)return!1;var c;switch(b.keyCode){case 37:case 40:c=-1;break;case 39:case 38:c=1}if(c){if(this.options.natural_arrow_keys){var d="vertical"===this.options.orientation&&!this.options.reversed,e="horizontal"===this.options.orientation&&this.options.reversed;(d||e)&&(c=-c)}var f=this._state.value[a]+c*this.options.step;return this.options.range&&(f=[a?this._state.value[0]:f,a?f:this._state.value[1]]),this._trigger("slideStart",f),this._setDataVal(f),this.setValue(f,!0,!0),this._setDataVal(f),this._trigger("slideStop",f),this._layout(),this._pauseEvent(b),!1}},_pauseEvent:function(a){a.stopPropagation&&a.stopPropagation(),a.preventDefault&&a.preventDefault(),a.cancelBubble=!0,a.returnValue=!1},_mousemove:function(a){if(!this._state.enabled)return!1;var b=this._getPercentage(a);this._adjustPercentageForRangeSliders(b),this._state.percentage[this._state.dragged]=b,this._layout();var c=this._calculateValue(!0);return this.setValue(c,!0,!0),!1},_touchmove:function(a){if(void 0!==a.changedTouches){var b=a.changedTouches[0],c=b.pageX-this.touchX,d=b.pageY-this.touchY;this._state.inDrag||("vertical"===this.options.orientation&&5>=c&&c>=-5&&(d>=15||-15>=d)?this._mousedown(a):5>=d&&d>=-5&&(c>=15||-15>=c)&&this._mousedown(a))}},_adjustPercentageForRangeSliders:function(a){if(this.options.range){var b=this._getNumDigitsAfterDecimalPlace(a);b=b?b-1:0;var c=this._applyToFixedAndParseFloat(a,b);0===this._state.dragged&&this._applyToFixedAndParseFloat(this._state.percentage[1],b)c&&(this._state.percentage[1]=this._state.percentage[0],this._state.dragged=0)}},_mouseup:function(){if(!this._state.enabled)return!1;this.touchCapable&&(document.removeEventListener("touchmove",this.mousemove,!1),document.removeEventListener("touchend",this.mouseup,!1)),document.removeEventListener("mousemove",this.mousemove,!1),document.removeEventListener("mouseup",this.mouseup,!1),this._state.inDrag=!1,this._state.over===!1&&this._hideTooltip();var a=this._calculateValue(!0);return this._layout(),this._setDataVal(a),this._trigger("slideStop",a),!1},_calculateValue:function(a){var b;if(this.options.range?(b=[this.options.min,this.options.max],0!==this._state.percentage[0]&&(b[0]=this._toValue(this._state.percentage[0]),b[0]=this._applyPrecision(b[0])),100!==this._state.percentage[1]&&(b[1]=this._toValue(this._state.percentage[1]),b[1]=this._applyPrecision(b[1]))):(b=this._toValue(this._state.percentage[0]),b=parseFloat(b),b=this._applyPrecision(b)),a){for(var c=[b,1/0],d=0;d').appendTo(this.$body); $('
' + + '' + - '
' + + '
' + '' + '#' + '
' + + '' + '
' + diff --git a/nengo_gui/static/nengo.js b/nengo_gui/static/nengo.js index 7407c202..31a1507a 100644 --- a/nengo_gui/static/nengo.js +++ b/nengo_gui/static/nengo.js @@ -119,3 +119,26 @@ Nengo.draw_legend = function(parent, labels, color_func, uid) { return legend_svg; }; + +Nengo.create_filter = function(colors,stdDev,id){ + var filter = d3.select('#netgraph').append("filter") + .attr("id", id) + .attr("height", "200%") + .attr("width", "200%"); + filter.append("feGaussianBlur") + .attr('in',"SourceGraphic") + .attr("stdDeviation", stdDev) + .attr("result", "coloredBlur"); + filter.append('feColorMatrix') + .attr('in',"coloredBlur") + .attr('type',"matrix") + .attr('values',"0 0 0 "+colors[0]+" 0 0 0 0 "+colors[1]+" 0 0 0 0 "+ + colors[2]+" 0 0 0 0 1.5 0") + .attr("result","coloredBlur"); + + var feMerge = filter.append("feMerge"); + feMerge.append("feMergeNode") + .attr("in", "coloredBlur") + feMerge.append("feMergeNode") + .attr("in", "SourceGraphic"); +} diff --git a/nengo_gui/static/vpl_config.css b/nengo_gui/static/vpl_config.css new file mode 100644 index 00000000..a25fdab7 --- /dev/null +++ b/nengo_gui/static/vpl_config.css @@ -0,0 +1,26 @@ +#graph_container{ + position: relative; + width: 500px; + height: 270px; + margin-left: auto; + margin-right: auto; + left: 0px; +} +.controls > label{ + margin-right: 10px !important; +} +#ens_dimension{ + width: 100px +} + +#radius_val { + width: 60px; + margin-right: 25px; +} + +#ex1C .slider-selection { + background: grey; +} +#ex1C .slider-handle { + background: #5da3ff; +} diff --git a/nengo_gui/static/vpl_config.js b/nengo_gui/static/vpl_config.js new file mode 100644 index 00000000..526778b4 --- /dev/null +++ b/nengo_gui/static/vpl_config.js @@ -0,0 +1,206 @@ +Nengo.VPLConfig = function(){ + this.$form = $(''+ + '
'+ + '
'+ + ''+ + ''+ + '
'+ + '
'+ + '
'+ + '
'+ + ''+ + '
'+ + ''+ + ''+ + '
'+ + '
'+ + '
'+ + ''+ + ''+ + '
'+ +''); + this.graph_container = $(); + this.graph_w = 500; + this.radius = 1; + self.radius_slider; +} + +Nengo.VPLConfig.prototype.ensemble_modal = function(){ + var self = this; + + Nengo.modal.clear_body(); + Nengo.modal.show(); + Nengo.modal.title('Edit Ensemble'); + self.$form.appendTo(Nengo.modal.$body); + this.graph_container.appendTo(this.$form); + self.redraw_graph("#graph_container",[1,2,3],[[1,2,3],[4,5,6]],'radius','frequency') + self.radius_slider = new Slider('#ex1',{ + min: 0.1, + max: 10, + tooltip: "hide", + value: 1, + step: 0.1, + id: "ex1C" + }) + self.radius_slider.on("slide",function(){ + $("#radius_val").val($("#ex1").val()); + self.radius = $("#ex1").val(); + var x_axis = []; + for(var x = 1; x <= 3; x++){ + x_axis.push((x/3)*self.radius); + } + self.redraw_graph("#graph_container",x_axis,[[1,2,3],[4,5,6]],'radius','frequency') + }); + $("#radius_val").on("change",function(){ + var max_val = self.radius_slider.getAttribute("max"); + var value = parseInt($(this).val(),10); + if (value > max_val){ + self.radius_slider.setAttribute("max",value); + } + self.radius_slider.setValue(value); + }) +} + +Nengo.VPLConfig.prototype.redraw_graph = function(selector, x, ys, x_label, y_label){ + var self = this; + $("#graph_container > svg").remove(); + self.graph_container.appendTo(this.$form); + self.multiline_plot(selector, x, ys, x_label, y_label); +} + +Nengo.VPLConfig.prototype.multiline_plot = function(selector, x, ys, x_label, y_label) { + + var margin = {left: 30, top: 0, right: 0, bottom: 30}; + var w = 500 - margin.left - margin.right; + var h = 220 - margin.bottom - margin.top; + var graph_w = w + margin.left + margin.right; + var graph_h = h + margin.bottom + margin.top; + var text_offset = 15; + + var scale_x = d3.scale.linear() + .domain([ x[0], x[x.length - 1] ]) + .range([margin.left, w - margin.right]); + var scale_y = d3.scale.linear() + .domain([d3.min(ys, function(y){ return d3.min(y); }) - 0.01, + d3.max(ys, function(y){ return d3.max(y); }) + 0.01]) + .range([h+margin.top, margin.top]); + + // Add an SVG element with the desired dimensions and margin. + var svg = d3.select(selector).append("svg") + .attr("viewBox","0 0 "+graph_w+" "+graph_h) + .attr("width","100%") + .attr("height","100%"); + + // create the axes + var xAxis = d3.svg.axis() + .scale(scale_x) + .orient("bottom") + .ticks(9); + svg.append("g") + .attr("class", "axis axis_x unselectable") + .attr("transform", "translate(0," + (h+margin.top) + ")") + .call(xAxis); + + var yAxisLeft = d3.svg.axis() + .scale(scale_y) + .ticks(5) + .orient("left"); + var yAxisRight = d3.svg.axis() + .scale(scale_x) + .scale(scale_y) + .ticks(5) + .orient("right"); + svg.append("g") + .attr("class", "axis axis_y unselectable") + .attr("transform", "translate(" + margin.left + ",0)") + .call(yAxisLeft); + svg.append("g") + .attr("class", "axis axis_y unselectable") + .attr("transform", "translate(" + w + ",0)") + .call(yAxisRight); + + // label the axes + if (x_label !== "") { + svg.append("text") + .attr("class", "x label") + .attr("text-anchor", "middle") + .attr("x", graph_w / 2) + .attr("y", text_offset + graph_h - margin.bottom / 2) + .text(x_label); + } + + if (y_label !== "") { + svg.append("text") + .attr("class", "y label") + .attr("text-anchor", "middle") + .attr("x", -graph_h/2) + .attr("y", -text_offset + margin.left / 2) + .attr("dy", ".75em") + .attr("transform", "rotate(-90)") + .text(y_label); + } + + // add the lines + var colors = Nengo.make_colors(ys.length); + + var line = d3.svg.line() + .x(function(d, i) { return scale_x(x[i]); }) + .y(function(d) { return scale_y(d); }) + + svg.append("g") + .selectAll("path") + .data(ys) + .enter() + .append("path") + .attr("d", line) + .attr("class", "line") + .style("stroke", function(d, i) { return colors[i]; }); +} + +Nengo.VPLConfig.prototype.ensemble_config = function(uid){ + var vpl = Nengo.vpl; + var editor = ace.edit("editor"); + var re = new RegExp("(?:[^\.])"+uid+"\\s*="); + var code_line = editor.find(re); + var component = Nengo.netgraph.svg_objects[uid]; + Nengo.modal.component_config(uid); + Nengo.modal.show(); + Nengo.modal.footer('ok_cancel', + function(e) { + var n_number = $("#config-neuron-number").val(); + var dim_number = $("#config-dimension").val(); + var Range = require("ace/range").Range; + var tab = " "; + var tabs; + Nengo.netgraph.svg_objects[uid].n_neurons = n_number; + Nengo.netgraph.svg_objects[uid].dimensions = dim_number; + + if(component.parent == null){tabs = tab;} + else{tabs = tab+tab;} + + editor.session.replace(new Range(code_line.start.row, 0, code_line.start.row, Number.MAX_VALUE), + tabs+uid+" = nengo.Ensemble(n_neurons="+n_number+",dimensions="+dim_number+")"); + vpl.delete_connections(uid); + var conn_in = component.conn_in; + var conn_out = component.conn_out; + for(var x = 0; x < conn_in.length; x++){ + vpl.add_connection(conn_in[x].pre.uid,conn_in[x].post.uid); + } + for(var x = 0; x < conn_out.length; x++){ + vpl.add_connection(conn_out[x].pre.uid,conn_out[x].post.uid); + } + $('#OK').attr('data-dismiss', 'modal'); + }, + function () { + $('#cancel-button').attr('data-dismiss', 'modal'); + } + ); +} + +$(document).ready(function(){ + Nengo.vpl_config = new Nengo.VPLConfig(); +}); diff --git a/nengo_gui/templates/page.html b/nengo_gui/templates/page.html index 29b49404..e691c7f7 100644 --- a/nengo_gui/templates/page.html +++ b/nengo_gui/templates/page.html @@ -17,9 +17,11 @@ + + @@ -82,70 +84,79 @@
- -
-
- - -

Move/Resize

+
+

Controls

+
+
+ + +

Move

+
-
-
-
- - - - - - - - - - -

Ensemble

+
+
+ + +

Remove

+
- -
-
- - - - - -

Node

+
+

Components

+
+
+ + + + + + + + + + +

Ensemble

+
-
-
-
- - - - - -

Network

+
+
+ + + + + +

Node

+
-
-
-
- - - - - - -

Connection

+
+
+ + + + + + + + + + +

Network

+
-
-
-
- - -

Remove

+
+
+ + + + + + +

Connection

+
@@ -247,6 +258,7 @@ + @@ -278,6 +290,7 @@ + From e249e7f674bf221f3e67b5dbe0a77ae618f18585 Mon Sep 17 00:00:00 2001 From: Rees Simmons Date: Thu, 10 Nov 2016 16:02:30 -0500 Subject: [PATCH 7/9] Changing form inputs --- nengo_gui/static/vpl_config.css | 24 +++++++++---- nengo_gui/static/vpl_config.js | 60 +++++++++++++++++++++------------ 2 files changed, 55 insertions(+), 29 deletions(-) diff --git a/nengo_gui/static/vpl_config.css b/nengo_gui/static/vpl_config.css index a25fdab7..c9fc2559 100644 --- a/nengo_gui/static/vpl_config.css +++ b/nengo_gui/static/vpl_config.css @@ -7,20 +7,30 @@ left: 0px; } .controls > label{ - margin-right: 10px !important; + margin-right: 15px !important; } #ens_dimension{ width: 100px } -#radius_val { - width: 60px; - margin-right: 25px; +.slider { + margin-right: 15px; } -#ex1C .slider-selection { - background: grey; +#ensModalForm .form-control { + height: 25px !important; + width: 40px !important; + padding: 2px; } -#ex1C .slider-handle { + +.slider-selection { background: #5da3ff; } + +.slider-track-high { + background: #EEE; +} + +.slider-handle { + background: #A6A6A6; +} diff --git a/nengo_gui/static/vpl_config.js b/nengo_gui/static/vpl_config.js index 526778b4..0bbc3ccc 100644 --- a/nengo_gui/static/vpl_config.js +++ b/nengo_gui/static/vpl_config.js @@ -1,6 +1,6 @@ Nengo.VPLConfig = function(){ this.$form = $('
'+ - '
'+ + '
'+ '
'+ ''+ ''+ @@ -8,11 +8,6 @@ Nengo.VPLConfig = function(){ '
'+ '
'+ '
'+ - ''+ - '
'+ - ''+ - ''+ - '
'+ '
'+ '
'+ ''+ @@ -23,10 +18,13 @@ Nengo.VPLConfig = function(){ ''+ '
'+ ''); + this.neuron_params = {} + this.neuron_params['LIF_default'] = {} + this.neuron_params['LIF_custom'] = {} this.graph_container = $(); this.graph_w = 500; this.radius = 1; - self.radius_slider; + this.sliders = {}; } Nengo.VPLConfig.prototype.ensemble_modal = function(){ @@ -36,35 +34,53 @@ Nengo.VPLConfig.prototype.ensemble_modal = function(){ Nengo.modal.show(); Nengo.modal.title('Edit Ensemble'); self.$form.appendTo(Nengo.modal.$body); + // var tabs = Nengo.modal.tabbed_body([{id: 'params', title: 'Parameters'}, + // {id: 'model', title: 'Model'}]); this.graph_container.appendTo(this.$form); self.redraw_graph("#graph_container",[1,2,3],[[1,2,3],[4,5,6]],'radius','frequency') - self.radius_slider = new Slider('#ex1',{ + self.create_slider("#radius_controls","ex1","Radius",{ min: 0.1, - max: 10, + max: 2, tooltip: "hide", value: 1, step: 0.1, - id: "ex1C" + id: "ex1C", }) - self.radius_slider.on("slide",function(){ - $("#radius_val").val($("#ex1").val()); - self.radius = $("#ex1").val(); - var x_axis = []; - for(var x = 1; x <= 3; x++){ - x_axis.push((x/3)*self.radius); - } - self.redraw_graph("#graph_container",x_axis,[[1,2,3],[4,5,6]],'radius','frequency') +} + +Nengo.VPLConfig.prototype.create_slider = function(parent_selector,new_id,label,options){ + var self = this; + var control_group = $('
') + .appendTo($(parent_selector)); + var label_tag = $('') + .appendTo($(control_group)); + var slider_input = $('') + .appendTo($(control_group)); + var slide_val = $('') + .appendTo($(control_group)); + + + self.sliders[new_id] = new Slider('#'+new_id,options); + self.sliders[new_id].on("slide",function(){ + $(slide_val).val(self.sliders[new_id].getValue()); }); - $("#radius_val").on("change",function(){ - var max_val = self.radius_slider.getAttribute("max"); + $(slide_val).on("change",function(){ + var max_val = self.sliders[new_id].getAttribute("max"); var value = parseInt($(this).val(),10); if (value > max_val){ - self.radius_slider.setAttribute("max",value); + self.sliders[new_id].setAttribute("max",value); } - self.radius_slider.setValue(value); + self.sliders[new_id].setValue(value); }) + // var x_axis = []; + // for(var x = 1; x <= 3; x++){ + // x_axis.push((x/3)*self.radius); + // } + // self.redraw_graph("#graph_container",x_axis,[[1,2,3],[4,5,6]],'radius','frequency') } + Nengo.VPLConfig.prototype.redraw_graph = function(selector, x, ys, x_label, y_label){ var self = this; $("#graph_container > svg").remove(); From 7093469a87cd30e6f848af5d3c6f101aaa1a531b Mon Sep 17 00:00:00 2001 From: Rees Simmons Date: Thu, 17 Nov 2016 14:08:26 -0500 Subject: [PATCH 8/9] Cleaned up syntax for drag_and_drop files --- nengo_gui/static/components/netgraph_item.js | 1 - nengo_gui/static/drag_and_drop.css | 3 +- nengo_gui/static/drag_and_drop.js | 26 ++--- nengo_gui/static/modal.js | 33 ------- nengo_gui/static/side_menu.css | 2 +- nengo_gui/static/side_menu.js | 2 +- nengo_gui/static/vpl_config.css | 18 ++-- nengo_gui/static/vpl_config.js | 99 +++++++++++++------- nengo_gui/templates/page.html | 6 +- 9 files changed, 89 insertions(+), 101 deletions(-) diff --git a/nengo_gui/static/components/netgraph_item.js b/nengo_gui/static/components/netgraph_item.js index 80400d5c..169b16a5 100644 --- a/nengo_gui/static/components/netgraph_item.js +++ b/nengo_gui/static/components/netgraph_item.js @@ -396,7 +396,6 @@ Nengo.NetGraphItem.prototype.generate_menu = function () { items.push(['Spikes', function() {self.create_graph('Raster');}]); items.push(['Voltages', function() {self.create_graph('Voltage');}]); items.push(['Firing pattern', function() {self.create_graph('SpikeGrid');}]); - items.push(['Configure',function(){Nengo.vpl_config.ensemble_config(self.uid)}]); } if (this.type == 'node') { items.push(['Slider', function() {self.create_graph('Slider');}]); diff --git a/nengo_gui/static/drag_and_drop.css b/nengo_gui/static/drag_and_drop.css index 18c02538..77f53a2f 100644 --- a/nengo_gui/static/drag_and_drop.css +++ b/nengo_gui/static/drag_and_drop.css @@ -3,7 +3,6 @@ background: #ddd; color:inherit; overflow: hidden; - } .off{ @@ -29,7 +28,6 @@ box-shadow: inset 0 0 5px #000; } - #mode_list{ position: relative; top: 0; @@ -56,6 +54,7 @@ width: 100%; height: 100%; } + .mode > div >svg{ width: 100%; } diff --git a/nengo_gui/static/drag_and_drop.js b/nengo_gui/static/drag_and_drop.js index 89fca4d8..271feb01 100644 --- a/nengo_gui/static/drag_and_drop.js +++ b/nengo_gui/static/drag_and_drop.js @@ -1,12 +1,12 @@ Nengo.VPL = function(){ var self = this; self.edit_mode = false; + $("#mode_list").height($(document).height()*0.85); $(document).keyup(function(e) { if (e.keyCode === 27) self.unselect_component("Pointer"); // esc }); - self.contextmenu = null; //Setting Click handlers for toggle buttons $("#Delete").on('click',function(){ self.component_toggle('Delete'); @@ -179,7 +179,8 @@ Nengo.VPL.prototype.create_component = function(type){ }); }); } - +/** Creates the interaction, click events, and visuals for drawing and creation + of connections.*/ Nengo.VPL.prototype.create_connection = function(){ var self = this; var cur_obj = ""; @@ -295,6 +296,8 @@ Nengo.VPL.prototype.create_connection = function(){ }); } +/** Creates the interaction, click events, and visuals for drawing and creation + of deleting objects.*/ Nengo.VPL.prototype.delete_mode = function(){ var self = this; var cur_obj = ""; @@ -493,6 +496,7 @@ Nengo.VPL.prototype.compute_network_size = function (parent_net){ return parent_size; } +/** Finds the next available uid for a type of component */ Nengo.VPL.prototype.open_name = function(name) { var num = 1; var editor = ace.edit('editor'); @@ -502,6 +506,7 @@ Nengo.VPL.prototype.open_name = function(name) { return name + num; } +/** Adds css to show which connections are valid and which aren't */ Nengo.VPL.prototype.show_connectable = function(toggle,allowed_connect){ var node_style; if(toggle == false){ @@ -619,10 +624,12 @@ Nengo.VPL.prototype.delete_component = function(uid){ } } +/** Deletes all connections to a specific component */ Nengo.VPL.prototype.delete_connections = function(uid){ this.delete_connection(uid,""); } +/** Deletes a specific connection between two components */ Nengo.VPL.prototype.delete_connection = function(uid1,uid2){ var editor = ace.edit("editor"); var re = new RegExp("nengo\.Connection\s*.*(?:[^\.])"+uid1+".*"+uid2); @@ -633,21 +640,6 @@ Nengo.VPL.prototype.delete_connection = function(uid1,uid2){ } } -Nengo.VPL.prototype.config_connection = function(uid1,uid2){ - var self = this; - var editor = ace.edit("editor"); - Nengo.modal.config_connection_modal(); - Nengo.modal.show(); - Nengo.modal.footer('ok_cancel', - function(e) { - $('#OK').attr('data-dismiss', 'modal'); - }, - function () { - $('#cancel-button').attr('data-dismiss', 'modal'); - } - ); -} - $(document).ready(function(){ Nengo.vpl = new Nengo.VPL(); }); diff --git a/nengo_gui/static/modal.js b/nengo_gui/static/modal.js index 7c276d93..2a532701 100644 --- a/nengo_gui/static/modal.js +++ b/nengo_gui/static/modal.js @@ -794,39 +794,6 @@ Nengo.Modal.prototype.make_conn_path_dropdown_list = function($container, others } } -Nengo.Modal.prototype.component_config = function(uid){ - this.clear_body(); - var component = Nengo.netgraph.svg_objects[uid]; - var name_switch = {"ens":"Ensemble","node":"Node"}; - this.title(name_switch[component.type]+" "+uid+"'s properties.'"); - var $form = $('
').appendTo(this.$body); - $('
' + - - '' + - '
' + - '' + - '#' + - '
' + - - '' + - '
' + - '' + - '#' + - '
' + - '
').appendTo($form); - $("#config-dimension").val(component.dimensions); - $("#config-neuron-number").val(component.n_neurons); -} - Nengo.modal = new Nengo.Modal($('.modal').first()); //Change the global defaults of the modal validator diff --git a/nengo_gui/static/side_menu.css b/nengo_gui/static/side_menu.css index 2d0c771d..124c9aee 100644 --- a/nengo_gui/static/side_menu.css +++ b/nengo_gui/static/side_menu.css @@ -23,7 +23,7 @@ transform: translate(0px); } -.sidenav-container .tab-content{ +.sidenav-container .tab-contents{ font-size: 15px; float: left; width: 250px; diff --git a/nengo_gui/static/side_menu.js b/nengo_gui/static/side_menu.js index 99cab9eb..d8c357ec 100644 --- a/nengo_gui/static/side_menu.js +++ b/nengo_gui/static/side_menu.js @@ -15,7 +15,7 @@ Nengo.SideMenu = function() { self.menu_width = document.getElementsByClassName("sidenav-container")[0] .offsetWidth; // Gathers all the menu tabs from the HTML for the Event Handlers - self.tabs = $(".tab-content"); + self.tabs = $(".tab-contents"); self.current_tab = -1; self.top_buttons = ['#Open_file_button', '#Component_menu', '#Config_menu']; diff --git a/nengo_gui/static/vpl_config.css b/nengo_gui/static/vpl_config.css index c9fc2559..6788b5ae 100644 --- a/nengo_gui/static/vpl_config.css +++ b/nengo_gui/static/vpl_config.css @@ -6,23 +6,19 @@ margin-right: auto; left: 0px; } -.controls > label{ - margin-right: 15px !important; -} -#ens_dimension{ - width: 100px -} - -.slider { - margin-right: 15px; -} -#ensModalForm .form-control { +#ensModalForm input.form-control { height: 25px !important; width: 40px !important; padding: 2px; + margin-right: 15px; +} + +#ensModalForm .controls { + margin-bottom: 8px; } + .slider-selection { background: #5da3ff; } diff --git a/nengo_gui/static/vpl_config.js b/nengo_gui/static/vpl_config.js index 0bbc3ccc..92f164d0 100644 --- a/nengo_gui/static/vpl_config.js +++ b/nengo_gui/static/vpl_config.js @@ -1,27 +1,26 @@ Nengo.VPLConfig = function(){ - this.$form = $(''+ - '
'+ - '
'+ - ''+ - ''+ - '
'+ - '
'+ - '
'+ - '
'+ + this.$param_form = $(''+ + '
'+ '
'+ '
'+ - ''+ - ''+ '
'+ ''); + this.$model_form = $('
'+ + '
'+ + ' '+ + ''+ + '
'+ + '
'+ + '
'+ + '
') this.neuron_params = {} - this.neuron_params['LIF_default'] = {} - this.neuron_params['LIF_custom'] = {} - this.graph_container = $(); + this.neuron_params['LIF'] = {tau_rc: {min:0,max:1,default:0.02,step:0.01}, + tau_ref: {min:0,max:0.01,default:0.002,step:0.001}, + min_voltage: {min:0,max:10,default:0,step:1}}; + this.graph_container = $('
'); this.graph_w = 500; this.radius = 1; this.sliders = {}; @@ -31,43 +30,79 @@ Nengo.VPLConfig.prototype.ensemble_modal = function(){ var self = this; Nengo.modal.clear_body(); - Nengo.modal.show(); - Nengo.modal.title('Edit Ensemble'); - self.$form.appendTo(Nengo.modal.$body); - // var tabs = Nengo.modal.tabbed_body([{id: 'params', title: 'Parameters'}, - // {id: 'model', title: 'Model'}]); - this.graph_container.appendTo(this.$form); + Nengo.modal.show() + Nengo.modal.title('Edit Ensemble');; + var tabs = Nengo.modal.tabbed_body([{id: 'params', title: 'Parameters'}, + {id: 'model', title: 'Neuron Model'}]); + this.graph_container.prependTo(".tab-content"); + self.$param_form.appendTo(tabs.params); + self.$model_form.appendTo(tabs.model); + + $("#ens_model").on("change",function(){ + var optionSelected = $("option:selected", this); + var value = this.value; + + for(var item in self.neuron_params[value]){ + console.log(item); + self.create_slider("#model_controls",item,item,{ + min: self.neuron_params[value][item]['min'], + max: self.neuron_params[value][item]['max'], + tooltip: "hide", + value: self.neuron_params[value][item]['default'], + step: self.neuron_params[value][item]['step'], + }); + } + }); self.redraw_graph("#graph_container",[1,2,3],[[1,2,3],[4,5,6]],'radius','frequency') - self.create_slider("#radius_controls","ex1","Radius",{ + self.create_slider("#param_controls","ens_dimension","Dimension",{ + min: 1, + max: 10, + tooltip: "hide", + value: 1, + step: 1, + id: "ens_dimensionC", + }); + self.create_slider("#param_controls","ex1","Radius",{ min: 0.1, max: 2, tooltip: "hide", value: 1, step: 0.1, id: "ex1C", - }) + }); + self.create_slider("#param_controls","neuron_num","Neuron #",{ + min: 1, + max: 200, + tooltip: "hide", + value: 50, + step: 1, + id: "neuron_numC", + }); + + } Nengo.VPLConfig.prototype.create_slider = function(parent_selector,new_id,label,options){ var self = this; var control_group = $('
') .appendTo($(parent_selector)); - var label_tag = $('') - .appendTo($(control_group)); - var slider_input = $('') + var label_tag = $('') .appendTo($(control_group)); var slide_val = $('') + '_val" placeholder="1">') + .appendTo($(control_group)); + var slider_input = $('') .appendTo($(control_group)); self.sliders[new_id] = new Slider('#'+new_id,options); + slide_val.val(self.sliders[new_id].getValue()); self.sliders[new_id].on("slide",function(){ $(slide_val).val(self.sliders[new_id].getValue()); }); $(slide_val).on("change",function(){ var max_val = self.sliders[new_id].getAttribute("max"); - var value = parseInt($(this).val(),10); + var value = parseFloat($(this).val(),10); if (value > max_val){ self.sliders[new_id].setAttribute("max",value); } @@ -84,7 +119,7 @@ Nengo.VPLConfig.prototype.create_slider = function(parent_selector,new_id,label, Nengo.VPLConfig.prototype.redraw_graph = function(selector, x, ys, x_label, y_label){ var self = this; $("#graph_container > svg").remove(); - self.graph_container.appendTo(this.$form); + self.graph_container.prependTo(".tab-content"); self.multiline_plot(selector, x, ys, x_label, y_label); } diff --git a/nengo_gui/templates/page.html b/nengo_gui/templates/page.html index e691c7f7..f56f392a 100644 --- a/nengo_gui/templates/page.html +++ b/nengo_gui/templates/page.html @@ -78,11 +78,11 @@ -
+
-
+

Controls

@@ -161,7 +161,7 @@
-
+
From a5bb6033ac6017a88b312d458992b5734a896970 Mon Sep 17 00:00:00 2001 From: Rees Simmons Date: Thu, 17 Nov 2016 14:21:19 -0500 Subject: [PATCH 9/9] Fixed server side error --- nengo_gui/components/netgraph.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/nengo_gui/components/netgraph.py b/nengo_gui/components/netgraph.py index 973d9e08..416d2edc 100644 --- a/nengo_gui/components/netgraph.py +++ b/nengo_gui/components/netgraph.py @@ -545,7 +545,8 @@ def get_extra_info(self, obj): nengo_gui.components.spa_plot.SpaPlot.applicable_targets(obj)) # the line number information is injected via nengo_gui.exec_env - info['line_number'] = obj._line_number + if hasattr(obj,"_line_number"): + info['line_number'] = obj._line_number return info def send_pan_and_zoom(self, client):