From b3fd9ff4d24931d04407d585c676f637c3cf7304 Mon Sep 17 00:00:00 2001 From: Marcus Poeckelmann <marcus.poeckelmann@informatik.uni-halle.de> Date: Wed, 1 Mar 2023 16:48:12 +0100 Subject: [PATCH] v2.3: added drag'n'drop functionality to change the order of names --- LICENSE | 2 +- README | 2 +- catview.css | 4 +- catview.js | 698 +++++++++++++++++++++++++++++++----------- demo/demo_bottom.html | 1 + demo/demo_left.html | 1 + demo/demo_right.html | 1 + demo/demo_top.html | 1 + 8 files changed, 521 insertions(+), 189 deletions(-) mode change 100755 => 100644 catview.js diff --git a/LICENSE b/LICENSE index bef0602..7e0cff1 100644 --- a/LICENSE +++ b/LICENSE @@ -1,6 +1,6 @@ The MIT License (MIT) -Copyright (c) 2015 ongoing Marcus Pöckelmann +Copyright (c) 2015 and ongoing Marcus Pöckelmann Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/README b/README index c58f20c..2ece30a 100644 --- a/README +++ b/README @@ -1,6 +1,6 @@ Thank you for using CATview - the Colored & Aligned Texts view. -This is version 2.2. +This is version 2.3. For information about the features and usage of CATview, please visit: https://catview.uzi.uni-halle.de diff --git a/catview.css b/catview.css index 7caa6af..4d5e55f 100644 --- a/catview.css +++ b/catview.css @@ -1,7 +1,7 @@ /* The MIT License (MIT) - Copyright (c) 2015-2021 Marcus Pöckelmann + Copyright (c) 2015 and ongoing Marcus Pöckelmann Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal @@ -78,3 +78,5 @@ #CATview .edge-name-right { text-align: right; border-radius: 8px 0 0 8px; } /* tl tr br bl*/ #CATview .edge-name-bottom { text-align: center; border-radius: 8px 8px 0 0; } /* tl tr br bl*/ #CATview .edge-name-left { text-align: left; border-radius: 0 8px 8px 0; } /* tl tr br bl*/ + +#CATview .draggable text{ cursor: grab; } diff --git a/catview.js b/catview.js old mode 100755 new mode 100644 index c1a143e..9217e33 --- a/catview.js +++ b/catview.js @@ -1,7 +1,7 @@ /* The MIT License (MIT) - Copyright (c) 2015 ongoing Marcus Pöckelmann + Copyright (c) 2015 and ongoing Marcus Pöckelmann Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal @@ -23,7 +23,7 @@ */ // CATview - the Colored & Aligned Texts view -// version 2.2 +// version 2.3 const CATview = new function() { this.debug = false; @@ -32,7 +32,7 @@ const CATview = new function() { if(CATview.debug) console.log('CATview.initialize'); - CATview.version = '2.2'; + CATview.version = '2.3'; // id of the parent container that will include CATview CATview.parent_id = 'CATview'; @@ -46,8 +46,8 @@ const CATview = new function() { CATview.height_svg = document.getElementById('CATview').offsetHeight; // CATviews orientation - CATview.orientation = ['left', 'right', 'bottom'].indexOf(_orientation) == -1 ? 'top' : _orientation; - CATview.vertical = (CATview.orientation == 'left' || CATview.orientation == 'right'); + CATview.orientation = ['left', 'right', 'bottom'].indexOf(_orientation) === -1 ? 'top' : _orientation; + CATview.vertical = (CATview.orientation === 'left' || CATview.orientation === 'right'); CATview.x_inverted = (typeof _x_inverted === 'undefined') ? false : _x_inverted; // setting: default order of edges (false → left/top, true → right/bottom) CATview.y_inverted = (typeof _y_inverted === 'undefined') ? false : _y_inverted; // setting: default order of witnesses (false → top/left, true → bottom/right) @@ -70,6 +70,7 @@ const CATview = new function() { CATview.rect_v_margin = null; CATview.rect_stroke = null; CATview.rect_border = "stroke: #2f2f86;"; + CATview.enable_scroll_spy = true; CATview.scroll_spy_pos = 1; // default position of the scroll spy CATview.display_remaining_edges = false; // setting: should remaining alignment edges be displayed CATview.threshold = 50; // setting: for which degree of difference remaining alignment edges should be displayed @@ -107,7 +108,7 @@ const CATview = new function() { CATview.edge_names = []; // array of the edges' names, displayed on mouse over // scales to map the elements to the available pixels - CATview.scale_edges = null; // map [from, to] + CATview.scale_edges = [0.5,1]; //null; // map [from, to] CATview.scale_names = null; // map the names CATview.scale_edges_original = null; // map [0, n-1] // a scale for the color @@ -116,6 +117,20 @@ const CATview = new function() { CATview.use_equality_color = true; //CATview.color = d3.scaleLinear().domain([0, 1]).range(["#b8b8b8", "#000"]); //CATview.color = d3.scaleLinear().domain([0, 1]).range(["#7CCF7C", "c0c"]); + + CATview.drag_enabled = false; // is drag'n'drop allowed + CATview.drag_callback = null; // called after a successful name swap + CATview.drag_names_order = null; + // the current order of names (might differ from the original order due to drag'n'drop) + CATview.drag_order_name2pos = null; // array that maps: name index → position index + // some helper variables across the events + CATview.drag_axis_attr = CATview.vertical? "x" : "y"; + CATview.drag_positions = []; + CATview.drag_old_pos = null; + CATview.drag_new_pos = null; + CATview.drag_do_end = false; + CATview.drag_threshold = 0.1; + CATview.drag_mode = 'insert'; // 'insert' || 'swap' }; // method to set margins and content width/height according to the current orientation of CATview and its axis @@ -128,12 +143,12 @@ const CATview = new function() { CATview.margin.top = CATview.x_inverted ? CATview.space_for_tool_icons : CATview.space_for_names_axis; CATview.margin.bottom = CATview.x_inverted ? CATview.space_for_names_axis : CATview.space_for_tool_icons; // handle place of the edges axis according to the (left or right) orientation of CATview - CATview.margin.left = CATview.orientation == 'left' ? CATview.space_for_other : CATview.space_for_egdes_axis; - CATview.margin.right = CATview.orientation == 'left' ? CATview.space_for_egdes_axis : CATview.space_for_other; + CATview.margin.left = CATview.orientation === 'left' ? CATview.space_for_other : CATview.space_for_egdes_axis; + CATview.margin.right = CATview.orientation === 'left' ? CATview.space_for_egdes_axis : CATview.space_for_other; } else { // handle place of the edges axis according to the (top or bottom) orientation of CATview - CATview.margin.top = CATview.orientation == 'top' ? CATview.space_for_other : CATview.space_for_egdes_axis; - CATview.margin.bottom = CATview.orientation == 'top' ? CATview.space_for_egdes_axis : CATview.space_for_other; + CATview.margin.top = CATview.orientation === 'top' ? CATview.space_for_other : CATview.space_for_egdes_axis; + CATview.margin.bottom = CATview.orientation === 'top' ? CATview.space_for_egdes_axis : CATview.space_for_other; // handle place of names axis and tool icons in respect to the edges axis direction for horizontal orientation of CATview CATview.margin.left = CATview.x_inverted ? CATview.space_for_tool_icons : CATview.space_for_names_axis; CATview.margin.right = CATview.x_inverted ? CATview.space_for_names_axis : CATview.space_for_tool_icons; @@ -144,14 +159,14 @@ const CATview = new function() { CATview.height_content = CATview.height_svg - CATview.margin.top - CATview.margin.bottom; }; // method to set scales according to the current orientation of CATview and its axis - this.set_scale_x = function(_from, _to){ + this.set_scale_edges = function(_from, _to){ if(CATview.debug) - console.log('CATview.set_scale_y'); + console.log('CATview.set_scale_edges'); if(CATview.vertical){ // vertical orientation (left, right) if(CATview.x_inverted){ - // inverted egdes axis (starting bottom) + // inverted edges axis (starting bottom) CATview.scale_edges = d3.scaleLinear().domain([_from, _to]).range([CATview.height_content, 0]); } else { // normal edges axis (starting top) @@ -160,7 +175,7 @@ const CATview = new function() { } else { // horizontal orientation (top, bottom) if(CATview.x_inverted){ - // inverted egdes axis (starting right) + // inverted edges axis (starting right) CATview.scale_edges = d3.scaleLinear().domain([_from, _to]).range([CATview.width_content, 0]); } else { // normal edges axis (starting left) @@ -168,9 +183,9 @@ const CATview = new function() { } } }; - this.set_scale_y = function(){ + this.set_scale_names = function(){ if(CATview.debug) - console.log('CATview.set_scale_y'); + console.log('CATview.set_scale_names'); if(CATview.vertical){ // vertical orientation (left, right) @@ -203,6 +218,12 @@ const CATview = new function() { if(CATview.debug) console.log(data); CATview.names = data.names; + // default order of names + CATview.drag_order_name2pos = []; + for(let i = 0; i < CATview.names.length; i++){ + CATview.drag_order_name2pos.push(i); + } + CATview.edges = data.edges; CATview.remaining_edges = data.remaining_edges; CATview.extra_segments = data.extra_segments; @@ -259,7 +280,7 @@ const CATview = new function() { for (let j = CATview.search_results.length - 1; j >= 0; j--) { if (CATview.search_results[j][0] > col) CATview.search_results[j][0] -= 1; - else if (CATview.search_results[j][0] == col) + else if (CATview.search_results[j][0] === col) CATview.search_results.splice(j, 1); } @@ -272,7 +293,7 @@ const CATview = new function() { } } - CATview.set_scale_x(0, CATview.edges.length + 1) + CATview.set_scale_edges(0, CATview.edges.length + 1) CATview.scale_edges_original = CATview.scale_edges.copy(); CATview.refresh_content(CATview.from, CATview.to); return true; @@ -297,12 +318,12 @@ const CATview = new function() { let names_offset = 0; if(CATview.names.length > 0){ CATview.svg.append("g") - .attr('id', 'CATview_prerendered_names') - .selectAll("text") - .data(CATview.names) - .enter().append("text") - .text(function (d) {return d}) - .attr('font-size', CATview.font_size_y); + .attr('id', 'CATview_prerendered_names') + .selectAll("text") + .data(CATview.names) + .enter().append("text") + .text(function (d) {return d}) + .attr('font-size', CATview.font_size_y); // get the dimension of the longest text and add 2 pixels padding let names = document.querySelectorAll('#CATview_prerendered_names text'); for(let i = 0; i < names.length; i++){ @@ -319,16 +340,19 @@ const CATview = new function() { CATview.set_margins(); // initialize the scales - CATview.set_scale_x(0, CATview.edges.length + 1) + CATview.set_scale_edges(0, CATview.edges.length + 1) CATview.scale_edges_original = CATview.scale_edges.copy(); - CATview.set_scale_y() + CATview.set_scale_names(); // add some zebra stripes CATview.svg.append("g").attr("class", "zebra"); CATview.draw_zebra_stripes(); // add the names-axis - CATview.svg.append("g").attr("class", "axis names-axis"); + if(CATview.drag_enabled) + CATview.svg.append("g").attr("class", "axis names-axis draggable"); + else + CATview.svg.append("g").attr("class", "axis names-axis"); CATview.draw_names_axis(); // add the group of tool icons @@ -337,8 +361,8 @@ const CATview = new function() { // add a group for the content and its zoom behavior CATview.content = CATview.svg.append("g") - .attr("class", "content") - .attr("transform", "translate(" + CATview.margin.left + "," + CATview.margin.top + ")"); + .attr("class", "content") + .attr("transform", "translate(" + CATview.margin.left + "," + CATview.margin.top + ")"); // add the zooming behavior CATview.zoom = d3.zoom().on("zoom", CATview.zooming); @@ -347,9 +371,9 @@ const CATview = new function() { // the zoom needs an element over the contents' complete size CATview.content.append('rect') - .attr('width', CATview.width_content) - .attr('height', CATview.height_content) - .attr('style', 'fill-opacity: 0.0;'); + .attr('width', CATview.width_content) + .attr('height', CATview.height_content) + .attr('style', 'fill-opacity: 0.0;'); // add the edges-axis CATview.content.append("g").attr("class", "axis edges-axis"); @@ -364,7 +388,7 @@ const CATview = new function() { // set default interval of data to be shown and draw the content CATview.from_pixel = 0; - CATview.to_pixel = CATview.vertical == true ? CATview.height_content : CATview.width_content; + CATview.to_pixel = CATview.vertical === true ? CATview.height_content : CATview.width_content; CATview.refresh_content(1, CATview.edges.length); // append the brush, if it was enabled prior the call of draw_svg @@ -376,23 +400,24 @@ const CATview = new function() { }; // define the interval of edges and refresh the content afterwards - this.refresh_content = function(from, to){ + this.refresh_content = function(_from, _to){ if(CATview.debug) - console.log('CATview.refresh_content(' + from + ', ' + to + ')'); + console.log('CATview.refresh_content(' + _from + ', ' + _to + ')'); if(CATview.content){ - if (from != null) CATview.from = from; - if (from == 0) CATview.from = 1; - if (to != null) CATview.to = to; - if (to > CATview.edges.length) CATview.to = CATview.edges.length; + if (_from !== null) CATview.from = _from; + if (_from === 0) CATview.from = 1; + if (_to !== null) CATview.to = _to; + if (_to > CATview.edges.length) CATview.to = CATview.edges.length; // refresh the edges-scale (with one bin offset) - CATview.set_scale_x(Math.max(CATview.from - 1, 0), Math.min(CATview.to + 1, CATview.edges.length + 1)) + CATview.set_scale_edges(Math.max(CATview.from - 1, 0), Math.min(CATview.to + 1, CATview.edges.length + 1)); CATview.draw_alignment(); CATview.draw_extra_segments(); CATview.draw_search_results(); - CATview.draw_scroll_spy(); - CATview.draw_remaining_edges(); + if(CATview.enable_scroll_spy) + CATview.draw_scroll_spy(); + //CATview.draw_remaining_edges(); } else return false; @@ -425,8 +450,8 @@ const CATview = new function() { pos_y = CATview.height_content; } edges_axis - .attr("transform", "translate(" + pos_x + "," + pos_y + ")") - .call(CATview.edgesAxis); + .attr("transform", "translate(" + pos_x + "," + pos_y + ")") + .call(CATview.edgesAxis); }; this.draw_names_axis = function(){ if(CATview.debug) @@ -444,22 +469,31 @@ const CATview = new function() { else names_axis.attr("transform", "translate(" + CATview.margin.left + ", " + CATview.margin.top + ")"); + // default names order (may not initialised yet or number of names are not up-to-date) + if(CATview.drag_order_name2pos === null || CATview.drag_order_name2pos.length != CATview.names.length){ + CATview.drag_order_name2pos = []; + for(let i = 0; i < CATview.names.length; i++){ + CATview.drag_order_name2pos.push(i); + } + } + // add the names let anchor = 'end'; if(CATview.vertical){ - if((CATview.orientation == 'left' && CATview.x_inverted) || (CATview.orientation == 'right' && CATview.x_inverted == false)) + if((CATview.orientation === 'left' && CATview.x_inverted) || (CATview.orientation === 'right' && !CATview.x_inverted)) anchor = 'start'; - names_axis.selectAll("text") .data(CATview.names) .enter().append("text") .text(function (d) {return d}) - .attr("x", function(d, i) {return CATview.scale_names(i)}) + .attr("x", function(d, i) {return CATview.scale_names(CATview.drag_order_name2pos[i])}) + .attr("original", function(d, i) {return i}) + .attr("current", function(d, i) {return CATview.drag_order_name2pos[i]}) .attr("style", "text-anchor: " + anchor + ";") .attr('font-size', CATview.font_size_y) .attr("transform", function(d, i) { // rotate for 90 degrees than move the names a bit to the right to make them centered - return 'rotate(' + (CATview.orientation == 'left' ? 90 : -90) + ', ' + CATview.scale_names(i) + ', 0)translate(0,' + (CATview.font_size_y/2) + ')'; + return 'rotate(' + (CATview.orientation === 'left' ? 90 : -90) + ', ' + CATview.scale_names(CATview.drag_order_name2pos[i]) + ', 0)translate(0,' + (CATview.font_size_y/2) + ')'; }); } else { if(CATview.x_inverted) @@ -468,10 +502,290 @@ const CATview = new function() { .data(CATview.names) .enter().append("text") .text(function (d) {return d}) - .attr("y", function(d, i) {return CATview.scale_names(i) + 5 }) // a little offset due to font size + .attr("y", function(d, i) {return CATview.scale_names(CATview.drag_order_name2pos[i]) + 5 }) // a little offset due to font size + .attr("original", function(d, i) {return i}) + .attr("current", function(d, i) {return CATview.drag_order_name2pos[i]}) .attr("style", "text-anchor: " + anchor + ";") .attr('font-size', CATview.font_size_y); } + + // enable drag'n'drop of names (and their associated edges) + if(CATview.drag_enabled){ + // which axis the draggable names are placed + CATview.drag_axis_attr = CATview.vertical? "x" : "y"; + // get the valid positions from dragging names + CATview.drag_positions = []; + d3.selectAll("g.axis.names-axis>text").each(function(d,i) { CATview.drag_positions.push( parseFloat(d3.select(this).attr(CATview.drag_axis_attr)));}); + // and sort them in ascending order + CATview.drag_positions.sort(function(a, b){ return a-b; }); + + // define the drag events + var drag_handler = d3.drag() + .on("start", function(){ + let drag_object = d3.select(this); + CATview.drag_old_pos = parseInt(drag_object.attr('current')); + + d3.select("g.axis.names-axis") + .append("text") + .text(drag_object.text()) + .attr("id", "ghost") + .style("visibility", "hidden") + .style("opacity", 0.5) + .style("text-anchor", "middle") + .attr("transform", "rotate("+(CATview.orientation === "left"? "90" : CATview.orientation === "right"? "-90" : "0") + ")" ); + + }) + .on("drag", function(d){ + // track the current grabbed name label + d3.select("#ghost") + .style("visibility", "visible") + .style("cursor", "grabbing") + .attr("x", d3.event.x) + .attr("y", d3.event.y); + + if (!CATview.vertical){ + d3.select("#ghost") + .style("visibility", "visible") + .style("cursor", "grabbing") + .attr("x", d3.event.x) + .attr("y", d3.event.y); + } + else { + if (CATview.orientation === "left"){ + d3.select("#ghost") + .style("visibility", "visible") + .style("cursor", "grabbing") + .attr("x", d3.event.y) + .attr("y", (-1)*d3.event.x); + } + else { + d3.select("#ghost") + .style("visibility", "visible") + .style("cursor", "grabbing") + .attr("x", (-1)*d3.event.y) + .attr("y", d3.event.x); + } + } + + // if current mouse pos differs enough from valid pos, then choose + // the nearest neighbour and switch positions with it if there is a neighbour + if (Math.abs((CATview.vertical? d3.event.x : d3.event.y) - CATview.drag_positions[CATview.drag_old_pos]) > + CATview.drag_threshold * Math.abs(CATview.drag_positions[0]-CATview.drag_positions[1])){ + // only when the attributes here have been updated, we should do a drag-end event. + // if not, this results in an error when double clicking on an element, after a first + // drag has been applied + CATview.drag_do_end = true; + + // we don't need the direction, we just need to identify the position of the dragged element + // with the one with the closest coordinates to the mouse cursor + + // select the element with the closest y to current mouse position + // for this calculate all differences of the valid positions to the mouse cursor + // and take the index with lowest distance. + let distances = CATview.drag_positions.map(function(el){return Math.abs(el-(CATview.vertical? d3.event.x : d3.event.y));}); + // get the index with minimal distance + let minimum = Math.min(...distances); // ... → translates the array to a list of values + let idx_min = distances.indexOf(minimum); // just returns first index that matches argument + + // now we can estimate the targets position + CATview.drag_new_pos = idx_min; + } + }) + .on("end", function(d){ + // only swap on end of dragging! + // swap the dragged elements and the target elements position: + d3.select("#ghost").remove(); + + if(!CATview.drag_do_end) + return; + + let old_pos = CATview.drag_old_pos + let new_pos = CATview.drag_new_pos + let objects = {} + // get all name objects + for(let pos = 0; pos < CATview.names.length; pos++){ + objects[pos] = d3.select("g.axis.names-axis>text[current=\"" + pos + "\"]"); + } + + // collects all information necessary to move the names (and their associated segments) + let names_to_move = []; + // add the dragged name + names_to_move.push({ + 'object': objects[old_pos], + 'from': old_pos, + 'to': new_pos, + 'new_position': CATview.drag_positions[new_pos], // move_pos[move_pos.length-1] + 'transform': objects[new_pos].attr("transform") // of name on target position + }); + // add other names depending on the drag mode: + if(CATview.drag_mode === 'swap'){ + // for swap mode → add the name at the target position + names_to_move.push({ + 'object': objects[new_pos], + 'from': new_pos, + 'to': old_pos, + 'new_position': CATview.drag_positions[old_pos], + 'transform': objects[old_pos].attr("transform") // of name on target position + }); + } + else{ + // for insert mode = a ring swap → add the names between the old and new position of the dragged name + if(old_pos < new_pos){ + for (let pos = new_pos; pos > old_pos; pos--){ + names_to_move.push({ + 'object': objects[pos], + 'from': pos, + 'to': pos - 1, + 'new_position': CATview.drag_positions[pos - 1], + 'transform': objects[pos - 1].attr("transform") + }); + } + } + else{ // new_pos < old_pos + for (let pos = new_pos; pos < old_pos; pos++){ + names_to_move.push({ + 'object': objects[pos], + 'from': pos, + 'to': pos + 1, + 'new_position': CATview.drag_positions[pos + 1], + 'transform': objects[pos + 1].attr("transform") + }); + } + } + + } + + // perform the movment of the names and their associated segments + names_to_move.forEach(function(name, i){ + // move the name + name.object.transition() + .attr(CATview.drag_axis_attr, name.new_position) + .attr("transform", name.transform); // add transformation of the text (rotation in vertical orientation) + + // remember the new current position + name.object.attr('current', name.to); + // and update the names order + let original = parseInt(name.object.attr('original')); + CATview.drag_order_name2pos[original] = name.to; + + // console.log(' ' + name.object.text() + ': ' + name.from + ' → ' + name.to); + + // move the associated segments + let segments = d3.selectAll("rect.rect-segment[data-segment-index$=\"_"+original+"\"],rect.search_result[data-segment-index$=\"_"+original+"\"]"); + // set the x or y attribute to the one at the position before (in move_pos) + let add_to_source = CATview.drag_positions[name.to] - CATview.drag_positions[name.from]; + // segments can differ in height and therefore y-value (in vertical orientation) + // so we need to add the same amount instead of setting the y value the same for all + segments.each( function(){ + d3.select(this) + .transition() + .attr(CATview.drag_axis_attr, parseFloat(d3.select(this).attr(CATview.drag_axis_attr)) + add_to_source); + }); + }); + + // drag finished + CATview.drag_do_end = false; + + if (CATview.drag_callback != null) { + return CATview.drag_callback(old_pos_idx, new_pos_idx); + } + + }); + + // select the name labels and listen for drag-events + let inner_texts = d3.selectAll("g.axis.names-axis>text"); + drag_handler(inner_texts); + } + }; + + this.enable_drag = function(_mode){ + if(CATview.debug) + console.log('CATview.enable_drag'); + + if(_mode === 'swap') + CATview.drag_mode = 'swap' + else + CATview.drag_mode = 'insert' // default + + CATview.drag_enabled = true; + + if(CATview.svg !== null && CATview.svg.select(".names-axis").size() === 1 ){ + CATview.svg.append("g").attr("class", "axis names-axis "); + let axis = document.querySelector('.names-axis'); + axis.classList.add('draggable'); + CATview.draw_names_axis(); + } + + return true; + }; + this.disable_drag = function(){ + if(CATview.debug) + console.log('CATview.disable_drag'); + + CATview.drag_enabled = false; + + if(CATview.svg !== null && CATview.svg.select(".names-axis").size() === 1 ){ + CATview.svg.append("g").attr("class", "axis names-axis "); + let axis = document.querySelector('.names-axis'); + axis.classList.remove('draggable'); + CATview.draw_names_axis(); + } + + return true; + }; + // _name2pos = array that maps: name index → position index, e.g. [3,1,2,0] (swap first and last text) + this.set_names_order = function(_name2pos){ + if(CATview.debug) + console.log('CATview.set_names_order'); + + // TODO validate data + CATview.drag_order_name2pos = _name2pos; + + if(CATview.drag_enabled){ + CATview.draw_names_axis(); + CATview.refresh_content(CATview.from, CATview.to); + } + + return true; + }; + this.reset_names_order = function(){ + if(CATview.debug) + console.log('CATview.reset_names_order'); + + let names_order = {}; + for(let i = 0; i < CATview.names.length; i++) + names_order[i] = i; + + return CATview.set_names_order(names_order); + }; + // returns an array that maps: name index → position index, e.g. [3,1,2,0] (swap first and last text) + this.get_names_order = function(){ + if(CATview.debug) + console.log('CATview.get_names_order'); + + return CATview.drag_order_name2pos; + }; + this.set_drag_callback = function(drag_callback) { + if(CATview.debug) + console.log('CATview.set_drag_callback'); + + CATview.drag_callback = drag_callback; + return true; + }; + + this.toggle_drag_mode = function(){ + if(CATview.debug) + console.log('CATview.toggle_drag_mode'); + + if(CATview.drag_mode === 'insert') + CATview.drag_mode = 'swap' + else + CATview.drag_mode = 'insert' + + // redraw the names axis + CATview.draw_names_axis(); + + return CATview.drag_mode; }; // draws the zebra stripes (gray lines in the background for every second text) @@ -500,9 +814,9 @@ const CATview = new function() { .attr("class", "rect-zebra") .attr("width", width ) .attr("height", height) - .attr('x', CATview.vertical == true ? function(d, i) {return CATview.scale_names(i) - width / 2.0 + CATview.margin.left} : offset) - .attr('y', CATview.vertical == true ? offset : function(d, i) {return CATview.scale_names(i) - height / 2.0 + CATview.margin.top}) - .attr("fill", function(d,i) { return i%2==1 ? "#e8e8e8":"transparent";}) + .attr('x', CATview.vertical === true ? function(d, i) {return CATview.scale_names(i) - width / 2.0 + CATview.margin.left} : offset) + .attr('y', CATview.vertical === true ? offset : function(d, i) {return CATview.scale_names(i) - height / 2.0 + CATview.margin.top}) + .attr("fill", function(d,i) { return i%2===1 ? "#e8e8e8":"transparent";}) }; // draws the icons for tools included in CATview.tools @@ -513,11 +827,11 @@ const CATview = new function() { let toolicons = CATview.svg.select(".tool-icons"); toolicons.selectAll('*').remove(); // remove the current content - let margin = 3; // extra margin around the tool icon conatiner + let margin = 3; // extra margin around the tool icon container let width, height; // available space for the tool icon container // place the container for the tool icons - if(CATview.vertical == true){ + if(CATview.vertical === true){ width = CATview.width_svg - CATview.margin.left - CATview.margin.right - 2 * margin; height = CATview.space_for_tool_icons - 2 * margin; if(CATview.x_inverted) @@ -565,7 +879,7 @@ const CATview = new function() { for(let i = 0; i < CATview.tools.length; i++){ let x, y; - if(CATview.vertical == true){ + if(CATview.vertical === true){ x = i * (fontsize + padding) + fontsize/2; y = fontsize/2; } else { @@ -574,17 +888,17 @@ const CATview = new function() { } toolicons.append('text') - .attr("class", "tool-icon") - .attr("x", x) - .attr("y", y) - .attr("style", "text-anchor: middle; dominant-baseline: middle;") - .attr("font-family","FontAwesome") - //.attr("textLength", fontsize) - //.attr("lengthAdjust","spacingAndGlyphs") - .text(String.fromCharCode(parseInt(CATview.tools[i][0], 16))) - .attr('font-size', fontsize - 4) - .attr("transform", (function(){ return 'rotate(' + CATview.tools[i][2] + ' ' + x + ' ' + y + ')' })) - .on("click", CATview.tools[i][1]); + .attr("class", "tool-icon") + .attr("x", x) + .attr("y", y) + .attr("style", "text-anchor: middle; dominant-baseline: middle;") + .attr("font-family","FontAwesome") + //.attr("textLength", fontsize) + //.attr("lengthAdjust","spacingAndGlyphs") + .text(String.fromCharCode(parseInt(CATview.tools[i][0], 16))) + .attr('font-size', fontsize - 4) + .attr("transform", (function(){ return 'rotate(' + CATview.tools[i][2] + ' ' + x + ' ' + y + ')' })) + .on("click", CATview.tools[i][1]); } } }; @@ -601,9 +915,9 @@ const CATview = new function() { let edges_temp = CATview.edges.map(function(d, i) { return [d, i + 1];}).filter(function(d) { return CATview.from <= d[1] && d[1] <= CATview.to; }); // get the max. width and height that is available for a segment - let segment_width = Math.floor(CATview.width_content/(CATview.vertical == true ? CATview.names.length : edges_temp.length)); + let segment_width = Math.floor(CATview.width_content/(CATview.vertical === true ? CATview.names.length : edges_temp.length)); if(segment_width < 1) segment_width = 1; - let segment_height = Math.floor(CATview.height_content/(CATview.vertical == true ? edges_temp.length : CATview.names.length)); + let segment_height = Math.floor(CATview.height_content/(CATview.vertical === true ? edges_temp.length : CATview.names.length)); if(segment_height < 1) segment_height = 1; // set width, height, margin and stroke for a rectangle @@ -615,50 +929,52 @@ const CATview = new function() { // create a grouping row for each edge let rows = CATview.content.select(".alignment").selectAll("g.row_witness") - .data(edges_temp) - .enter().append("g") - .attr("class", "row_witness") - .attr("transform", function(d) { return "translate(" + - (CATview.vertical == true ? (-CATview.rect_width/2 + ", " + (CATview.scale_edges(parseInt(d[1]))-(CATview.rect_height/2))) : - (CATview.scale_edges(parseInt(d[1]))-(CATview.rect_width/2)) + ", " + (-CATview.rect_height/2)) - + ")"; }) - .on('mouseenter', function(d, i){CATview.show_edge_name(i)}) - .on('mouseleave', function(){ CATview.hide_edge_name()}); + .data(edges_temp) + .enter().append("g") + .attr("class", "row_witness") + .attr("transform", function(d) { return "translate(" + + (CATview.vertical == true ? (-CATview.rect_width/2 + ", " + (CATview.scale_edges(parseInt(d[1]))-(CATview.rect_height/2))) : + (CATview.scale_edges(parseInt(d[1]))-(CATview.rect_width/2)) + ", " + (-CATview.rect_height/2)) + + ")"; }) + .on('mouseenter', function(d, i){CATview.show_edge_name(i)}) + .on('mouseleave', function(){ CATview.hide_edge_name()}); // draw the rects for the witnesses in each row rows.selectAll("rect") - .data(function(d) { return d[0].map(function(d2, j) {return [j, (d[1] - 1), d2];}).filter( function(d2) { return d2[2] != "-1"; } ); }) - .enter().append("rect") - .attr("class", "rect-segment") - .on("click", function(d){ - CATview.click_on_edge_callback(d[0], d[1]); - }) - .attr('cursor', 'pointer') - .attr("width", CATview.rect_width) - .attr("height", CATview.rect_height) - //.attr("ry", rect_corner) // rounded Corners - .attr(CATview.vertical == true ? "x" : "y", function(d) { return CATview.scale_names(d[0])}) - .attr("style", function(d) { - return CATview.rect_stroke + CATview.rect_border + "fill: " + - (parseFloat(d[2]) == 0.0 && CATview.use_equality_color ? CATview.equality_color : CATview.scale_color(parseFloat(d[2]))); - }); + .data(function(d) { return d[0].map(function(d2, j) {return [j, (d[1] - 1), d2];}).filter( function(d2) { return d2[2] != "-1"; } ); }) + .enter().append("rect") + .attr("class", "rect-segment") + .attr("data-segment-index", function(d) { return d[1] + "_" + d[0];}) + .on("click", function(d){ + CATview.click_on_edge_callback(d[0], d[1]); + }) + .attr('cursor', 'pointer') + .attr("width", CATview.rect_width) + .attr("height", CATview.rect_height) + //.attr("ry", rect_corner) // rounded Corners + .attr(CATview.vertical == true ? "x" : "y", function(d) { return CATview.scale_names(CATview.drag_order_name2pos[d[0]])}) + .attr("style", function(d) { + return CATview.rect_stroke + CATview.rect_border + "fill: " + + (parseFloat(d[2]) == 0.0 && CATview.use_equality_color ? CATview.equality_color : CATview.scale_color(parseFloat(d[2]))); + }); + // update the edges-axis CATview.edgesAxis.scale(CATview.scale_edges); CATview.content.select(".edges-axis").call(CATview.edgesAxis); CATview.content.select(".edges-axis").selectAll("text") - .attr("style", "text-anchor: middle;") - .attr("transform", function() { - // rotate for 90 degrees than move the positions a bit to make them centered - let transform = ''; - switch (CATview.orientation) - { - case "left": transform = 'translate(3, 0)'; break; - case "right": transform = 'translate(-3, 0)'; break; - default: break; - } - return transform; - }) - .attr('font-size', CATview.font_size_x); + .attr("style", "text-anchor: middle;") + .attr("transform", function() { + // rotate for 90 degrees than move the positions a bit to make them centered + let transform = ''; + switch (CATview.orientation) + { + case "left": transform = 'translate(3, 0)'; break; + case "right": transform = 'translate(-3, 0)'; break; + default: break; + } + return transform; + }) + .attr('font-size', CATview.font_size_x); } else return false; @@ -722,8 +1038,8 @@ const CATview = new function() { if(CATview.debug) console.log('CATview.hide_edge_name'); CATview.edge_name.transition() - .duration(200) - .style("opacity", 0); + .duration(200) + .style("opacity", 0); }; // draws the scroll spy as orange bar @@ -731,29 +1047,29 @@ const CATview = new function() { if(CATview.debug) console.log('CATview.draw_scroll_spy: ' + index); - if (arguments.length == 1) CATview.scroll_spy_pos = parseInt(index) + 1; + if (arguments.length === 1) CATview.scroll_spy_pos = parseInt(index) + 1; if(CATview.content){ CATview.content.selectAll("rect.scroll_spy").remove(); if(CATview.from <= CATview.scroll_spy_pos && CATview.scroll_spy_pos <= CATview.to) { - if(CATview.vertical == true){ + if(CATview.vertical === true){ let height = CATview.rect_height + 2 * (CATview.rect_v_margin/2); CATview.content.insert("rect", ".edges-axis") - .attr("class", "scroll_spy") - .attr("width", CATview.width_content) - .attr("height", height) - .attr("x", 0) - .attr("y", CATview.scale_edges(CATview.scroll_spy_pos) - (height/2)); + .attr("class", "scroll_spy") + .attr("width", CATview.width_content) + .attr("height", height) + .attr("x", 0) + .attr("y", CATview.scale_edges(CATview.scroll_spy_pos) - (height/2)); } else{ let width = CATview.rect_width + 2 * (CATview.rect_h_margin/2); CATview.content.insert("rect", ".edges-axis") - .attr("class", "scroll_spy") - .attr("width", width) - .attr("height", CATview.height_content) - .attr("x", CATview.scale_edges(CATview.scroll_spy_pos) - (width/2)) - .attr("y", "0"); + .attr("class", "scroll_spy") + .attr("width", width) + .attr("height", CATview.height_content) + .attr("x", CATview.scale_edges(CATview.scroll_spy_pos) - (width/2)) + .attr("y", "0"); } } } @@ -787,11 +1103,11 @@ const CATview = new function() { edges[1].forEach(function (edge) { if (CATview.from <= edge[0][1] + 1 && edge[0][1] + 1 <= CATview.to && CATview.from <= edge[1][1] + 1 && edge[1][1] + 1 <= CATview.to) { CATview.content.select(".remaining_edges").append('line') - .attr("class", "remaining") - .attr('x1', CATview.scale_edges(edge[0][1] + 1)) - .attr('y1', CATview.scale_names(edge[0][0])) - .attr('x2', CATview.scale_edges(edge[1][1] + 1)) - .attr('y2', CATview.scale_names(edge[1][0])); + .attr("class", "remaining") + .attr('x1', CATview.scale_edges(edge[0][1] + 1)) + .attr('y1', CATview.scale_names(edge[0][0])) + .attr('x2', CATview.scale_edges(edge[1][1] + 1)) + .attr('y2', CATview.scale_names(edge[1][0])); } }); @@ -839,11 +1155,14 @@ const CATview = new function() { .attr('cursor', 'pointer') .attr("width", CATview.rect_width - 1) .attr("height", CATview.rect_height - 2) - .attr(CATview.vertical == true ? "x" : "y", function(d) { return CATview.scale_names(segment[1]) + 1}) + .attr(CATview.vertical == true ? "x" : "y", function(d) { + return CATview.scale_names(CATview.drag_order_name2pos[segment[1]]) + 1 + }) .attr("style", function(d) { return 'fill:transparent; stroke-width:2; stroke-dasharray:3,1; stroke:' + - (parseFloat(segment[2]) == 0.0 && CATview.use_equality_color ? CATview.equality_color : CATview.scale_color(parseFloat(segment[2]))); - }); + (parseFloat(segment[2]) === 0.0 && CATview.use_equality_color ? CATview.equality_color : CATview.scale_color(parseFloat(segment[2]))); + }) + .attr("data-segment-index",segment[0] + "_" + segment[1]); }); } } @@ -871,7 +1190,7 @@ const CATview = new function() { if(CATview.search_results){ - if(CATview.search_mode == 'seg'){ + if(CATview.search_mode === 'seg'){ // draw highlight-rectangles over the segment-rectangles // first filter the hits according to the zoom let hits = CATview.search_results.filter(function(d){return (CATview.from <= d[0] + 1 && d[0] +1 <= CATview.to)}); @@ -882,19 +1201,20 @@ const CATview = new function() { function (a, b) { return a.concat(b);}); CATview.content.select(".search_results_foreground").selectAll("rect") - .data(hits) - .enter().append("rect") - .attr('class', 'search_result') - .on("click", function (d) {CATview.click_on_edge_callback(d[1], d[0] - 1);}) - .attr('cursor', 'pointer') - .attr("width", CATview.rect_width) - .attr("height", CATview.rect_height) - .attr("x", function (d) {return CATview.vertical == true ? - (CATview.scale_names(d[1]) - CATview.rect_width / 2) : (CATview.scale_edges(d[0]) - CATview.rect_width / 2); - }) - .attr("y", function (d) {return CATview.vertical == true ? - (CATview.scale_edges(d[0]) - CATview.rect_height / 2) : (CATview.scale_names(d[1]) - CATview.rect_height / 2); - }); + .data(hits) + .enter().append("rect") + .attr('class', 'search_result') + .on("click", function (d) {CATview.click_on_edge_callback(d[1], d[0] - 1);}) + .attr('cursor', 'pointer') + .attr("width", CATview.rect_width) + .attr("height", CATview.rect_height) + .attr("x", function (d) {return CATview.vertical == true ? + (CATview.scale_names(CATview.drag_order_name2pos[d[1]]) - CATview.rect_width / 2) : (CATview.scale_edges(d[0]) - CATview.rect_width / 2); + }) + .attr("y", function (d) {return CATview.vertical == true ? + (CATview.scale_edges(d[0]) - CATview.rect_height / 2) : (CATview.scale_names(CATview.drag_order_name2pos[d[1]]) - CATview.rect_height / 2); + }) + .attr("data-segment-index", function(d){return (d[0]-1) + "_" + d[1] }); } } else{ @@ -903,24 +1223,24 @@ const CATview = new function() { let edge = CATview.search_results[i][0] + 1; // test whether the search hit is within the currently shown excerpt of the alignment if(CATview.from <= edge && edge <= CATview.to){ - if(CATview.vertical == true){ + if(CATview.vertical === true){ let height = CATview.rect_height + 2; CATview.content.select(".search_results_background").append('rect') - .attr('class', 'search_result') - .attr("width", CATview.width_content - 4) - .attr("height", height) - .attr("x", 2) - .attr("y", CATview.scale_edges(edge) - (height/2)); + .attr('class', 'search_result') + .attr("width", CATview.width_content - 4) + .attr("height", height) + .attr("x", 2) + .attr("y", CATview.scale_edges(edge) - (height/2)); } else{ let width = CATview.rect_width + 2; CATview.content.select(".search_results_background").append('rect') - .attr('class', 'search_result') - .attr("width", width) - .attr("height", CATview.height_content - 4) - .attr("x", CATview.scale_edges(edge) - (width/2)) - .attr("y", 2); + .attr('class', 'search_result') + .attr("width", width) + .attr("height", CATview.height_content - 4) + .attr("x", CATview.scale_edges(edge) - (width/2)) + .attr("y", 2); } } } @@ -941,7 +1261,7 @@ const CATview = new function() { if(CATview.content){ CATview.set_margins(); - CATview.set_scale_x(0, CATview.edges.length + 1); + CATview.set_scale_edges(0, CATview.edges.length + 1); CATview.scale_edges_original = CATview.scale_edges.copy(); // relocate the content container CATview.svg.select(".content").attr("transform", "translate(" + CATview.margin.left + "," + CATview.margin.top + ")"); @@ -949,7 +1269,7 @@ const CATview = new function() { CATview.draw_edges_axis(); CATview.draw_names_axis(); CATview.draw_tool_icons(); - CATview.refresh_content(); + CATview.refresh_content(CATview.from, CATview.to); } else return false; @@ -960,15 +1280,15 @@ const CATview = new function() { if(CATview.debug) console.log('CATview.invert_names_axis'); - CATview.y_inverted = !CATview.y_inverted; + let name2pos = CATview.get_names_order(); // map: name index → position index + let pos2name = Array(name2pos.length); // create map: position index → name index + for(let i = 0; i < name2pos.length; i++) + pos2name[name2pos[i]] = i; + pos2name.reverse(); // invert the positions + for(let i = 0; i < pos2name.length; i++) // remap to: name index → position index + name2pos[pos2name[i]] = i; - if(CATview.content){ - CATview.set_scale_y(); - CATview.draw_names_axis(); - CATview.refresh_content(); - } - else - return false; + return CATview.set_names_order(name2pos); }; // set the maximum zooming level (enable or disable zooming) @@ -996,23 +1316,23 @@ const CATview = new function() { console.log('CATview.zooming'); let new_scale = d3.event.transform.k; - let new_translate = CATview.vertical == true ? (d3.event.transform.y/new_scale) : (d3.event.transform.x/new_scale); + let new_translate = CATview.vertical === true ? (d3.event.transform.y/new_scale) : (d3.event.transform.x/new_scale); if(CATview.content){ let delta_translate = 0; let scale_from = 0; let scale_to = 0; - let current_size = CATview.vertical == true ? CATview.height_content : CATview.width_content; + let current_size = CATview.vertical === true ? CATview.height_content : CATview.width_content; // if the scale factor has changed - if (new_scale != CATview.scale) { + if (new_scale !== CATview.scale) { // calculate the change of the size let delta_size = (current_size / new_scale) - (current_size / CATview.scale); // distribute the change of the size among the left/top and right/bottom site due to the relative mouse center - scale_from = delta_size * ((CATview.vertical == true ? d3.mouse(this)[1] : d3.mouse(this)[0]) / current_size); + scale_from = delta_size * ((CATview.vertical === true ? d3.mouse(this)[1] : d3.mouse(this)[0]) / current_size); scale_to = delta_size - scale_from; } - else if(CATview.display_brush == false){ + else if(CATview.display_brush === false){ // calculate the relative translation (only if the brush function is disabled) delta_translate = CATview.translate - new_translate; } @@ -1051,7 +1371,7 @@ const CATview = new function() { new_from = new_to; new_to = swap; } - if(new_from != CATview.from || new_to != CATview.to) { + if(new_from !== CATview.from || new_to !== CATview.to) { // refresh the content only if edges within the new zoom have changed CATview.refresh_content(new_from, new_to); } @@ -1067,7 +1387,7 @@ const CATview = new function() { } CATview.brush.move(d3.select('.brush'), [from, to].map(CATview.scale_edges)) // TODO prevent brushing listeners - } + } } else return false; @@ -1086,7 +1406,7 @@ const CATview = new function() { if(CATview.debug) console.log('CATview.enable_brush'); - if(CATview.display_brush == false){ + if(CATview.display_brush === false){ CATview.display_brush = true; // add the brush to CATview if(CATview.content) @@ -1105,7 +1425,7 @@ const CATview = new function() { if(CATview.debug) console.log('CATview.disable_brush'); - if(CATview.display_brush == true) { + if(CATview.display_brush === true) { // reset the brush's attributes CATview.display_brush = false; CATview.brush_from_edge = -1; @@ -1128,15 +1448,15 @@ const CATview = new function() { console.log('CATview.draw_brush'); if(CATview.content){ - if(CATview.display_brush == true){ + if(CATview.display_brush === true){ // remove previous brush CATview.content.select(".brush").remove(); // add the brush to CATview - if(CATview.vertical == true) { + if(CATview.vertical === true) { CATview.brush = d3.brushY() - .on("brush", CATview.brushing) - .on("end", CATview.brush_end); + .on("brush", CATview.brushing) + .on("end", CATview.brush_end); if(CATview.x_inverted) CATview.brush.extent([[CATview.scale_names.range()[0], CATview.scale_edges.range()[1]], [CATview.scale_names.range()[1], CATview.scale_edges.range()[0]]]); @@ -1154,8 +1474,8 @@ const CATview = new function() { } CATview.content.append("g") - .attr("class", "brush") - .call(CATview.brush); + .attr("class", "brush") + .call(CATview.brush); } return true; } @@ -1179,7 +1499,7 @@ const CATview = new function() { this.brushing = function() { if(CATview.debug) console.log('CATview.brushing'); - + // handle inverted edges axis let from = CATview.x_inverted ? 1 : 0; @@ -1190,7 +1510,7 @@ const CATview = new function() { let from_edge = Math.round(CATview.scale_edges.invert(d3.event.selection[from]) - CATview.brush_offset_from - 0.001) -1; if(from_edge < CATview.from - 1) from_edge = CATview.from - 1; else if(from_edge >= CATview.to) from_edge = CATview.to - 1; - + // add the current range to the from position to get the to position let to_edge = from_edge + CATview.brush_range - 1; @@ -1200,7 +1520,7 @@ const CATview = new function() { if(to_edge - from_edge >= 0) { // check whether the interval has changed - if (from_edge != CATview.brush_from_edge || to_edge != CATview.brush_to_edge){ // || from_name != CATview.brush_from_name || to_name != CATview.brush_to_name) { + if (from_edge !== CATview.brush_from_edge || to_edge !== CATview.brush_to_edge){ // || from_name != CATview.brush_from_name || to_name != CATview.brush_to_name) { // save the current interval CATview.brush_from_edge = from_edge; CATview.brush_to_edge = to_edge; @@ -1242,9 +1562,9 @@ const CATview = new function() { // calculated the number of rectangles within the window let brush_range = to_edge - from_edge + 1; - + // intervene if range is zero - if(brush_range == 0){ + if(brush_range === 0){ console.log('intervene') to_edge = from_edge; brush_range = 1; @@ -1253,10 +1573,10 @@ const CATview = new function() { // smoothly redraw the brush to discrete positions (begin and end of the rectangles) if(CATview.x_inverted){ d3.select(this).transition().call(d3.event.target.move, - [to_edge + 1.0 + CATview.brush_offset_to, from_edge + 1.0 + CATview.brush_offset_from].map(CATview.scale_edges)); + [to_edge + 1.0 + CATview.brush_offset_to, from_edge + 1.0 + CATview.brush_offset_from].map(CATview.scale_edges)); }else{ d3.select(this).transition().call(d3.event.target.move, - [from_edge + 1.0 + CATview.brush_offset_from, to_edge + 1.0 + CATview.brush_offset_to].map(CATview.scale_edges)); + [from_edge + 1.0 + CATview.brush_offset_from, to_edge + 1.0 + CATview.brush_offset_to].map(CATview.scale_edges)); } // save new values if there was a change @@ -1321,7 +1641,7 @@ const CATview = new function() { this.add_tool_toggle_search_mode = function(index){ return CATview.add_tool('f002', function(){ - CATview.search_mode = (CATview.search_mode == 'seg' ? 'col' : 'seg'); + CATview.search_mode = (CATview.search_mode === 'seg' ? 'col' : 'seg'); CATview.draw_search_results();}, index, 0); @@ -1450,12 +1770,18 @@ const CATview = new function() { // enable/disable console output to debug this.toggle_debug = function(debug){ if(debug != null && typeof(debug) == "boolean") - CATview.debug = !debug; + CATview.debug = !debug; CATview.debug = !CATview.debug; return true; }; + // parse a vaue to float and set precision to avoid rounding errors + this.to_float = function(_value){ + return parseFloat(_value); + return parseFloat(parseFloat(_value).toFixed(5)); + } + // todo // - may: method to set search_mode → toggle_search_mode(_mode) // - may: method to zoom → zoom_to(_factor) diff --git a/demo/demo_bottom.html b/demo/demo_bottom.html index 849851c..1aa9682 100644 --- a/demo/demo_bottom.html +++ b/demo/demo_bottom.html @@ -81,6 +81,7 @@ "Duis at…", "Ut varius…", "Quisque tincidunt…", "Curabitur ac…", "Vivamus vulputate…", "Aliquam sagittis…", "Nunc ut…", "Curabitur placerat…", "Aenean molestie…", "Donec quis…", "Aliquam sagittis…", "Duis nunc…", "Pellentesque habitant…", "Tincidunt dignissim…", "Nullam non…" ]); + CATview.enable_drag(); CATview.add_tool_invert_names_axis(); // invert-names-axis tool CATview.add_tool_invert_edges_axis(); // invert-edges-axis tool CATview.add_tool_toggle_search_mode(); // switch-highlight-mode-of-search-results tool diff --git a/demo/demo_left.html b/demo/demo_left.html index 8d19397..5e0eabb 100644 --- a/demo/demo_left.html +++ b/demo/demo_left.html @@ -82,6 +82,7 @@ "Duis at…", "Ut varius…", "Quisque tincidunt…", "Curabitur ac…", "Vivamus vulputate…", "Aliquam sagittis…", "Nunc ut…", "Curabitur placerat…", "Aenean molestie…", "Donec quis…", "Aliquam sagittis…", "Duis nunc…", "Pellentesque habitant…", "Tincidunt dignissim…", "Nullam non…" ]); + CATview.enable_drag(); CATview.add_tool_invert_names_axis(); // invert-names-axis tool CATview.add_tool_invert_edges_axis(); // invert-edges-axis tool CATview.add_tool_toggle_search_mode(); // switch-highlight-mode-of-search-results tool diff --git a/demo/demo_right.html b/demo/demo_right.html index 8bcb7ab..4d751e5 100644 --- a/demo/demo_right.html +++ b/demo/demo_right.html @@ -82,6 +82,7 @@ "Duis at…", "Ut varius…", "Quisque tincidunt…", "Curabitur ac…", "Vivamus vulputate…", "Aliquam sagittis…", "Nunc ut…", "Curabitur placerat…", "Aenean molestie…", "Donec quis…", "Aliquam sagittis…", "Duis nunc…", "Pellentesque habitant…", "Tincidunt dignissim…", "Nullam non…" ]); + CATview.enable_drag(); CATview.add_tool_invert_names_axis(); // invert-names-axis tool CATview.add_tool_invert_edges_axis(); // invert-edges-axis tool CATview.add_tool_toggle_search_mode(); // switch-highlight-mode-of-search-results tool diff --git a/demo/demo_top.html b/demo/demo_top.html index 0548081..7661647 100644 --- a/demo/demo_top.html +++ b/demo/demo_top.html @@ -82,6 +82,7 @@ "Duis at…", "Ut varius…", "Quisque tincidunt…", "Curabitur ac…", "Vivamus vulputate…", "Aliquam sagittis…", "Nunc ut…", "Curabitur placerat…", "Aenean molestie…", "Donec quis…", "Aliquam sagittis…", "Duis nunc…", "Pellentesque habitant…", "Tincidunt dignissim…", "Nullam non…" ]); + CATview.enable_drag('insert'); CATview.add_tool_invert_names_axis(); // invert-names-axis tool CATview.add_tool_invert_edges_axis(); // invert-edges-axis tool CATview.add_tool_toggle_search_mode(); // switch-highlight-mode-of-search-results tool -- GitLab