X-Git-Url: http://git.shadowcat.co.uk/gitweb/gitweb.cgi?a=blobdiff_plain;f=root%2Fjs%2Frelationship.js;h=32869ee80f2dc84130dd4b70587cc5ec8b58f739;hb=45ee3b9658be56ccae5c1e7ad04c64fcf719ebd0;hp=d569c16cfa6b1963d59657e8b9bd1bf5205e9a6d;hpb=13aa153c0992277c0f96ec5acf0ef42f825c207b;p=scpubgit%2Fstemmaweb.git diff --git a/root/js/relationship.js b/root/js/relationship.js index d569c16..32869ee 100644 --- a/root/js/relationship.js +++ b/root/js/relationship.js @@ -1,5 +1,11 @@ +var MARGIN=30; +var svg_root = null; +var svg_root_element = null; +var start_element_height = 0; +var reltypes = {}; + function getTextPath() { - var currpath = window.location.pathname + var currpath = window.location.pathname; // Get rid of trailing slash if( currpath.lastIndexOf('/') == currpath.length - 1 ) { currpath = currpath.slice( 0, currpath.length - 1) @@ -25,6 +31,11 @@ function getRelationshipURL() { return path_parts[0] + '/' + path_parts[1] + '/relationships'; } +// Make an XML ID into a valid selector +function jq(myid) { + return '#' + myid.replace(/(:|\.)/g,'\\$1'); +} + function svgEnlargementLoaded() { //Give some visual evidence that we are working $('#loading_overlay').show(); @@ -37,11 +48,55 @@ function svgEnlargementLoaded() { { 'top': lo_height / 2 - $("#loading_message").height() / 2, 'left': lo_width / 2 - $("#loading_message").width() / 2 }); //Set viewbox widht and height to widht and height of $('#svgenlargement svg'). + $('#update_workspace_button').data('locked', false); + $('#update_workspace_button').css('background-position', '0px 44px'); //This is essential to make sure zooming and panning works properly. $('#svgenlargement ellipse').attr( {stroke:'green', fill:'#b3f36d'} ); var graph_svg = $('#svgenlargement svg'); var svg_g = $('#svgenlargement svg g')[0]; + if (!svg_g) return; svg_root = graph_svg.svg().svg('get').root(); + + // Find the real root and ignore any text nodes + for (i = 0; i < svg_root.childNodes.length; ++i) { + if (svg_root.childNodes[i].nodeName != '#text') { + svg_root_element = svg_root.childNodes[i]; + break; + } + } + + // This might be a little application specific and should be pushed to the backend + // but... This collapes corrector hands when they agree with the original + // e.g., P46-*,P46-C becomes simply P46 + $(svg_root).find('text').each(function () { + var t = $(this).text(); + var ws = t.split(','); + for (i = 0; i < ws.length; ++i) { + if (ws[i].indexOf('-*')> -1) { + var collapse = false; + var main = $.trim(ws[i].substring(0,ws[i].indexOf('-'))); + for (j = 0; j < ws.length; ++j) { + if (i != j && ws[j].indexOf('-')> -1) { + var pair = $.trim(ws[j].substring(0,ws[j].indexOf('-'))); + if (main == pair) { + collapse = true; + ws[j] = ''; + } + } + } + if (collapse) ws[i] = main; + } + } + t = ''; + for (i = 0; i < ws.length; ++i) { + if (ws[i].length) { + if (t.length>0) t+=', '; + t+=ws[i]; + } + } + $(this).text(t); + }); + svg_root.viewBox.baseVal.width = graph_svg.attr( 'width' ); svg_root.viewBox.baseVal.height = graph_svg.attr( 'height' ); //Now set scale and translate so svg height is about 150px and vertically centered in viewbox. @@ -55,7 +110,7 @@ function svgEnlargementLoaded() { var transform = 'rotate(0) scale(' + scale + ') translate(4 ' + translate + ')'; svg_g.setAttribute('transform', transform); //used to calculate min and max zoom level: - start_element_height = $("#svgenlargement .node title:contains('START#')").siblings('ellipse')[0].getBBox().height; + start_element_height = $('#__START__ ellipse')[0].getBBox().height; add_relations( function() { $('#loading_overlay').hide(); }); } @@ -89,9 +144,7 @@ function add_relations( callback_fn ) { } function get_ellipse( node_id ) { - return $('#svgenlargement .node').children('title').filter( function(index) { - return $(this).text() == node_id; - }).siblings('ellipse'); + return $( jq( node_id ) + ' ellipse'); } function get_node_obj( node_id ) { @@ -102,12 +155,6 @@ function get_node_obj( node_id ) { return node_ellipse.data( 'node_obj' ); } -function get_edge( edge_id ) { - return $('#svgenlargement .edge').filter( function(index) { - return $(this).children( 'title' ).text() == $('
').html(edge_id).text() ; - }); -} - function node_obj(ellipse) { this.ellipse = ellipse; var self = this; @@ -119,17 +166,19 @@ function node_obj(ellipse) { this.node_elements = node_elements_for(self.ellipse); this.get_id = function() { - return self.ellipse.siblings('title').text() + return $(self.ellipse).parent().attr('id') } this.set_draggable = function( draggable ) { if( draggable ) { - self.ellipse.attr( {stroke:'black', fill:'#fff'} ); - self.ellipse.mousedown( this.mousedown_listener ); - self.ellipse.hover( this.enter_node, this.leave_node ); + $(self.ellipse).attr( {stroke:'black', fill:'#fff'} ); + $(self.ellipse).parent().mousedown( this.mousedown_listener ); + $(self.ellipse).parent().hover( this.enter_node, this.leave_node ); + self.ellipse.siblings('text').attr('class', 'noselect draggable'); } else { - self.ellipse.unbind('mouseenter').unbind('mouseleave').unbind('mousedown'); - self.ellipse.attr( {stroke:'green', fill:'#b3f36d'} ); + self.ellipse.siblings('text').attr('class', ''); + $(self.ellipse).parent().unbind('mouseenter').unbind('mouseleave').unbind('mousedown'); + $(self.ellipse).attr( {stroke:'green', fill:'#b3f36d'} ); } } @@ -139,7 +188,7 @@ function node_obj(ellipse) { self.y = evt.clientY; $('body').mousemove( self.mousemove_listener ); $('body').mouseup( self.mouseup_listener ); - self.ellipse.unbind('mouseenter').unbind('mouseleave') + $(self.ellipse).parent().unbind('mouseenter').unbind('mouseleave') self.ellipse.attr( 'fill', '#ff66ff' ); first_node_g_element = $("#svgenlargement g .node" ).filter( ":first" ); if( first_node_g_element.attr('id') !== self.get_g().attr('id') ) { self.get_g().insertBefore( first_node_g_element ) }; @@ -149,20 +198,27 @@ function node_obj(ellipse) { self.dx = (evt.clientX - self.x) / mouse_scale; self.dy = (evt.clientY - self.y) / mouse_scale; self.move_elements(); + evt.returnValue = false; + evt.preventDefault(); + return false; } this.mouseup_listener = function(evt) { if( $('ellipse[fill="#ffccff"]').size() > 0 ) { - var source_node_id = self.ellipse.siblings('title').text(); - var target_node_id = $('ellipse[fill="#ffccff"]').siblings("title").text(); + var source_node_id = $(self.ellipse).parent().attr('id'); + var source_node_text = self.ellipse.siblings('text').text(); + var target_node_id = $('ellipse[fill="#ffccff"]').parent().attr('id'); + var target_node_text = $('ellipse[fill="#ffccff"]').siblings("text").text(); $('#source_node_id').val( source_node_id ); + $('#source_node_text').val( source_node_text ); $('#target_node_id').val( target_node_id ); + $('#target_node_text').val( target_node_text ); $('#dialog-form').dialog( 'open' ); }; $('body').unbind('mousemove'); $('body').unbind('mouseup'); self.ellipse.attr( 'fill', '#fff' ); - self.ellipse.hover( self.enter_node, self.leave_node ); + $(self.ellipse).parent().hover( self.enter_node, self.leave_node ); self.reset_elements(); } @@ -250,12 +306,14 @@ function svgpath( path_element, svg_element ) { if( this.svg_element.parent(filter).size() != 0 ) { this.svg_element.attr('stroke', '#e5e5e5'); this.svg_element.siblings('text').attr('fill', '#e5e5e5'); + this.svg_element.siblings('text').attr('class', 'noselect'); } } this.un_grey_out = function(filter) { if( this.svg_element.parent(filter).size() != 0 ) { this.svg_element.attr('stroke', '#000000'); this.svg_element.siblings('text').attr('fill', '#000000'); + this.svg_element.siblings('text').attr('class', ''); } } } @@ -269,7 +327,7 @@ function node_elements_for( ellipse ) { function get_edge_elements_for( ellipse ) { edge_elements = new Array(); - node_id = ellipse.siblings('title').text(); + node_id = ellipse.parent().attr('id'); edge_in_pattern = new RegExp( node_id + '$' ); edge_out_pattern = new RegExp( '^' + node_id ); $.each( $('#svgenlargement .edge,#svgenlargement .relation').children('title'), function(index) { @@ -298,12 +356,8 @@ function relation_factory() { this.relation_colors = [ "#5CCCCC", "#67E667", "#F9FE72", "#6B90D4", "#FF7673", "#E467B3", "#AA67D5", "#8370D8", "#FFC173" ]; this.create_temporary = function( source_node_id, target_node_id ) { - var relation = $('#svgenlargement .relation').filter( function(index) { - var relation_id = $(this).children('title').text(); - if( ( relation_id == ( source_node_id + '->' + target_node_id ) ) || ( relation_id == ( target_node_id + '->' + source_node_id ) ) ) { - return true; - } - } ); + var relation_id = get_relation_id( source_node_id, target_node_id ); + var relation = $( jq( relation_id ) ); if( relation.size() == 0 ) { draw_relation( source_node_id, target_node_id, self.temp_color ); } else { @@ -331,16 +385,16 @@ function relation_factory() { return relation; } this.toggle_active = function( relation_id ) { - var relation = $("#svgenlargement .relation:has(title:contains('" + relation_id + "'))"); + var relation = $( jq( relation_id ) ); var relation_path = relation.children('path'); if( !relation.data( 'active' ) ) { relation_path.css( {'cursor':'pointer'} ); relation_path.mouseenter( function(event) { outerTimer = setTimeout( function() { timer = setTimeout( function() { - var title = relation.children('title').text(); - var source_node_id = title.substring( 0, title.indexOf( "->" ) ); - var target_node_id = title.substring( (title.indexOf( "->" ) + 2) ); + var related_nodes = get_related_nodes( relation_id ); + var source_node_id = related_nodes[0]; + var target_node_id = related_nodes[1]; $('#delete_source_node_id').val( source_node_id ); $('#delete_target_node_id').val( target_node_id ); self.showinfo(relation); @@ -373,7 +427,7 @@ function relation_factory() { var p = svg_root.createSVGPoint(); p.x = xs + ((xe-xs)*1.1); p.y = ye - ((ye-ys)/2); - var ctm = svg_root.children[0].getScreenCTM(); + var ctm = svg_root_element.getScreenCTM(); var nx = p.matrixTransform(ctm).x; var ny = p.matrixTransform(ctm).y; var dialog_aria = $ ("div[aria-labelledby='ui-dialog-title-delete-form']"); @@ -381,14 +435,28 @@ function relation_factory() { dialog_aria.offset({ left: nx, top: ny }); } this.remove = function( relation_id ) { - var relation = $("#svgenlargement .relation:has(title:contains('" + relation_id + "'))"); + var relation = $( jq( relation_id ) ); relation.remove(); } } +// Utility function to create/return the ID of a relation link between +// a source and target. +function get_relation_id( source_id, target_id ) { + var idlist = [ source_id, target_id ]; + idlist.sort(); + return 'relation-' + idlist[0] + '-...-' + idlist[1]; +} + +function get_related_nodes( relation_id ) { + var srctotarg = relation_id.substr( 9 ); + return srctotarg.split('-...-'); +} + function draw_relation( source_id, target_id, relation_color ) { var source_ellipse = get_ellipse( source_id ); var target_ellipse = get_ellipse( target_id ); + var relation_id = get_relation_id( source_id, target_id ); var svg = $('#svgenlargement').children('svg').svg().svg('get'); var path = svg.createPath(); var sx = parseInt( source_ellipse.attr('cx') ); @@ -396,7 +464,8 @@ function draw_relation( source_id, target_id, relation_color ) { var sy = parseInt( source_ellipse.attr('cy') ); var ex = parseInt( target_ellipse.attr('cx') ); var ey = parseInt( target_ellipse.attr('cy') ); - var relation = svg.group( $("#svgenlargement svg g"), {'class':'relation'} ); + var relation = svg.group( $("#svgenlargement svg g"), + { 'class':'relation', 'id':relation_id } ); svg.title( relation, source_id + '->' + target_id ); svg.path( relation, path.move( sx, sy ).curveC( sx + (2*rx), sy, ex + (2*rx), ey, ex, ey ), {fill: 'none', stroke: relation_color, strokeWidth: 4}); var relation_element = $('#svgenlargement .relation').filter( ':last' ); @@ -417,11 +486,13 @@ $(document).ready(function () { .data('x', event.clientX) .data('y', event.clientY) .data('scrollLeft', this.scrollLeft) - stateTf = svg_root.children[0].getCTM().inverse(); + stateTf = svg_root_element.getCTM().inverse(); var p = svg_root.createSVGPoint(); p.x = event.clientX; p.y = event.clientY; stateOrigin = p.matrixTransform(stateTf); + event.returnValue = false; + event.preventDefault(); return false; }).mouseup(function (event) { $(this).data('down', false); @@ -434,22 +505,28 @@ $(document).ready(function () { p = p.matrixTransform(stateTf); var matrix = stateTf.inverse().translate(p.x - stateOrigin.x, p.y - stateOrigin.y); var s = "matrix(" + matrix.a + "," + matrix.b + "," + matrix.c + "," + matrix.d + "," + matrix.e + "," + matrix.f + ")"; - svg_root.children[0].setAttribute("transform", s); + svg_root_element.setAttribute("transform", s); } + event.returnValue = false; + event.preventDefault(); }).mousewheel(function (event, delta) { event.returnValue = false; event.preventDefault(); if ( $('#update_workspace_button').data('locked') == false ) { + if (!delta || delta == null || delta == 0) delta = event.originalEvent.wheelDelta; + if (!delta || delta == null || delta == 0) delta = -1 * event.originalEvent.detail; if( delta < -9 ) { delta = -9 }; var z = 1 + delta/10; - var g = svg_root.children[0]; - if( (z<1 && (g.getScreenCTM().a * start_element_height) > 4.0) || (z>1 && (g.getScreenCTM().a * start_element_height) < 100) ) { + z = delta > 0 ? 1 : -1; + var g = svg_root_element; + if (g && ((z<1 && (g.getScreenCTM().a * start_element_height) > 4.0) || (z>=1 && (g.getScreenCTM().a * start_element_height) < 100))) { var root = svg_root; var p = root.createSVGPoint(); - p.x = event.clientX; - p.y = event.clientY; + p.x = event.originalEvent.clientX; + p.y = event.originalEvent.clientY; p = p.matrixTransform(g.getCTM().inverse()); - var k = root.createSVGMatrix().translate(p.x, p.y).scale(z).translate(-p.x, -p.y); + var scaleLevel = 1+(z/20); + var k = root.createSVGMatrix().translate(p.x, p.y).scale(scaleLevel).translate(-p.x, -p.y); var matrix = g.getCTM().multiply(k); var s = "matrix(" + matrix.a + "," + matrix.b + "," + matrix.c + "," + matrix.d + "," + matrix.e + "," + matrix.f + ")"; g.setAttribute("transform", s); @@ -469,16 +546,20 @@ $(document).ready(function () { buttons: { "Ok": function() { $('#status').empty(); - form_values = $('#collapse_node_form').serialize() + form_values = $('#collapse_node_form').serialize(); ncpath = getRelationshipURL(); $(':button :contains("Ok")').attr("disabled", true); var jqjson = $.post( ncpath, form_values, function(data) { $.each( data, function(item, source_target) { - var relation = relation_manager.create( source_target[0], source_target[1], $('#rel_type').attr('selectedIndex') ); - relation.data( 'type', $('#rel_type :selected').text() ); - relation.data( 'scope', $('#scope :selected').text() ); - relation.data( 'note', $('#note').val() ); - relation_manager.toggle_active( relation.children('title').text() ); + var source_found = get_ellipse( source_target[0] ); + var target_found = get_ellipse( source_target[1] ); + if( source_found.size() && target_found.size() ) { + var relation = relation_manager.create( source_target[0], source_target[1], $('#rel_type')[0].selectedIndex-1 ); + relation.data( 'type', $('#rel_type :selected').text() ); + relation.data( 'scope', $('#scope :selected').text() ); + relation.data( 'note', $('#note').val() ); + relation_manager.toggle_active( relation.attr('id') ); + } }); $( "#dialog-form" ).dialog( "close" ); }, 'json' ); @@ -494,12 +575,12 @@ $(document).ready(function () { var jqjson = $.getJSON( basepath + '/definitions', function(data) { var types = data.types.sort(); $.each( types, function(index, value) { - $('#rel_type').append( $('