X-Git-Url: http://git.shadowcat.co.uk/gitweb/gitweb.cgi?a=blobdiff_plain;f=stemmaweb%2Froot%2Fjs%2Frelationship.js;h=ab7c3ca8a060e73188ef6e3363da64929ac89c7e;hb=a8928d1d98b99bdb121debeddec0db89774ce4a6;hp=ed1b150f2998cfd7d16a7a5c45d7c05c3072f10c;hpb=0fa07e25e9bdb209065f4002a9eaf5185f42b31a;p=scpubgit%2Fstemmatology.git diff --git a/stemmaweb/root/js/relationship.js b/stemmaweb/root/js/relationship.js index ed1b150..ab7c3ca 100644 --- a/stemmaweb/root/js/relationship.js +++ b/stemmaweb/root/js/relationship.js @@ -1,5 +1,12 @@ +var MARGIN=30; +var svg_root = null; +var svg_root_element = null; +var start_element_height = 0; +var reltypes = {}; +var readingdata = {}; + 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) @@ -20,9 +27,19 @@ function getRelativePath() { return path_parts[0]; } -function getRelationshipURL() { +function getTextURL( which ) { var path_parts = getTextPath(); - return path_parts[0] + '/' + path_parts[1] + '/relationships'; + return path_parts[0] + '/' + path_parts[1] + '/' + which; +} + +function getReadingURL( reading_id ) { + var path_parts = getTextPath(); + return path_parts[0] + '/' + path_parts[1] + '/reading/' + reading_id; +} + +// Make an XML ID into a valid selector +function jq(myid) { + return '#' + myid.replace(/(:|\.)/g,'\\$1'); } function svgEnlargementLoaded() { @@ -37,11 +54,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,13 +116,13 @@ 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(); }); } function add_relations( callback_fn ) { var basepath = getRelativePath(); - var textrelpath = getRelationshipURL(); + var textrelpath = getTextURL( 'relationships' ); $.getJSON( basepath + '/definitions', function(data) { var rel_types = data.types.sort(); $.getJSON( textrelpath, @@ -89,9 +150,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 +161,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,7 +172,7 @@ 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 ) { @@ -158,9 +211,9 @@ function node_obj(ellipse) { this.mouseup_listener = function(evt) { if( $('ellipse[fill="#ffccff"]').size() > 0 ) { - var source_node_id = self.ellipse.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"]').siblings("title").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 ); @@ -280,7 +333,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) { @@ -309,12 +362,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 { @@ -342,16 +391,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); @@ -384,7 +433,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']"); @@ -392,14 +441,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') ); @@ -407,7 +470,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' ); @@ -428,11 +492,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); @@ -445,22 +511,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); @@ -471,6 +543,10 @@ $(document).ready(function () { 'cursor' : '-moz-grab' }); + var rdgpath = getTextURL( 'readings' ); + $.getJSON( rdgpath, function( data ) { + readingdata = data; + }); $( "#dialog-form" ).dialog({ autoOpen: false, @@ -480,16 +556,20 @@ $(document).ready(function () { buttons: { "Ok": function() { $('#status').empty(); - form_values = $('#collapse_node_form').serialize() - ncpath = getRelationshipURL(); + form_values = $('#collapse_node_form').serialize(); + ncpath = getTextURL( 'relationships' ); $(':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' ); @@ -505,12 +585,12 @@ $(document).ready(function () { var jqjson = $.getJSON( basepath + '/definitions', function(data) { var types = data.types.sort(); $.each( types, function(index, value) { - $('#rel_type').append( $('