X-Git-Url: http://git.shadowcat.co.uk/gitweb/gitweb.cgi?a=blobdiff_plain;f=root%2Fjs%2Frelationship.js;h=b2a6f2df75f1f6e4164230cf1379f12b3bb78bee;hb=cfa6ff766d71f99550b215cf31b9d5f82bdfcde4;hp=807b5d4dc44b82ef5dab13bf2139b5799c50e0ee;hpb=0a41fb05de94f7087e8ede5f1451b6d679c6b1a7;p=scpubgit%2Fstemmaweb.git diff --git a/root/js/relationship.js b/root/js/relationship.js index 807b5d4..b2a6f2d 100644 --- a/root/js/relationship.js +++ b/root/js/relationship.js @@ -5,36 +5,29 @@ var start_element_height = 0; var reltypes = {}; var readingdata = {}; -function getTextPath() { - var currpath = window.location.pathname; - // Get rid of trailing slash - if( currpath.lastIndexOf('/') == currpath.length - 1 ) { - currpath = currpath.slice( 0, currpath.length - 1) - }; - // Get rid of query parameters - if( currpath.lastIndexOf('?') != -1 ) { - currpath = currpath.slice( 0, currpath.lastIndexOf('?') ); - }; - var path_elements = currpath.split('/'); - var textid = path_elements.pop(); - var basepath = path_elements.join( '/' ); - var path_parts = [ basepath, textid ]; - return path_parts; -} +jQuery.removeFromArray = function(value, arr) { + return jQuery.grep(arr, function(elem, index) { + return elem !== value; + }); +}; -function getRelativePath() { - var path_parts = getTextPath(); - return path_parts[0]; -} +function arrayUnique(array) { + var a = array.concat(); + for(var i=0; i').css( "border-color", + relation_manager.relation_colors[index] ).text(typedef.name); + li_elm.append( $('
').attr('class', 'key_tip_container').append( + $('
').attr('class', 'key_tip').text(typedef.description) ) ); + $('#keymaplist').append( li_elm ); + }); + // Now fetch the relationships themselves and add them to the graph + var rel_types = $.map( relationship_types, function(t) { return t.name }); + // Save this list of names to the outer element data so that the relationship + // factory can access it + $('#keymap').data('relations', rel_types); var textrelpath = getTextURL( 'relationships' ); - $.getJSON( basepath + '/definitions', function(data) { - var rel_types = data.types.sort(); - $.getJSON( textrelpath, - function(data) { - $.each(data, function( index, rel_info ) { - var type_index = $.inArray(rel_info.type, rel_types); - var source_found = get_ellipse( rel_info.source ); - var target_found = get_ellipse( rel_info.target ); - if( type_index != -1 && source_found.size() && target_found.size() ) { - var relation = relation_manager.create( rel_info.source, rel_info.target, type_index ); - relation.data( 'type', rel_info.type ); - relation.data( 'scope', rel_info.scope ); - relation.data( 'note', rel_info.note ); - var node_obj = get_node_obj(rel_info.source); - node_obj.set_draggable( false ); - node_obj.ellipse.data( 'node_obj', null ); - node_obj = get_node_obj(rel_info.target); - node_obj.set_draggable( false ); - node_obj.ellipse.data( 'node_obj', null ); - } - }); - callback_fn.call(); - }); - }); + $.getJSON( textrelpath, function(data) { + $.each(data, function( index, rel_info ) { + var type_index = $.inArray(rel_info.type, rel_types); + var source_found = get_ellipse( rel_info.source_id ); + var target_found = get_ellipse( rel_info.target_id ); + var emphasis = rel_info.is_significant; + if( type_index != -1 && source_found.size() && target_found.size() ) { + var relation = relation_manager.create( rel_info.source_id, rel_info.target_id, type_index, emphasis ); + // Save the relationship data too. + $.each( rel_info, function( k, v ) { + relation.data( k, v ); + }); + if( editable ) { + var node_obj = get_node_obj(rel_info.source_id); + node_obj.set_selectable( false ); + node_obj.ellipse.data( 'node_obj', null ); + node_obj = get_node_obj(rel_info.target_id); + node_obj.set_selectable( false ); + node_obj.ellipse.data( 'node_obj', null ); + } + } + }); + callback_fn.call(); + }); } function get_ellipse( node_id ) { @@ -267,21 +289,46 @@ function node_obj(ellipse) { this.dx = 0; this.dy = 0; this.node_elements = node_elements_for(self.ellipse); - + this.get_id = function() { return $(self.ellipse).parent().attr('id') } + this.set_selectable = function( clickable ) { + if( clickable && editable ) { + $(self.ellipse).attr( {stroke:'black', fill:'#fff'} ); + $(self.ellipse).parent().hover( this.enter_node, this.leave_node ); + $(self.ellipse).parent().mousedown( function(evt) { evt.stopPropagation() } ); + $(self.ellipse).parent().click( function(evt) { + evt.stopPropagation(); + if( $('ellipse[fill="#9999ff"]').size() > 0 ) { + $('ellipse[fill="#9999ff"]').each( function() { + $(this).data( 'node_obj' ).set_draggable( false ); + } ); + } + self.set_draggable( true ) + }); + } else { + $(self.ellipse).attr( {stroke:'black', fill:'#fff'} ); + self.ellipse.siblings('text').attr('class', ''); + $(self.ellipse).parent().unbind(); + $('body').unbind('mousemove'); + $('body').unbind('mouseup'); + } + } + this.set_draggable = function( draggable ) { - if( draggable ) { - $(self.ellipse).attr( {stroke:'black', fill:'#fff'} ); + if( draggable && editable ) { + $(self.ellipse).attr( {stroke:'black', fill:'#9999ff'} ); $(self.ellipse).parent().mousedown( this.mousedown_listener ); - $(self.ellipse).parent().hover( this.enter_node, this.leave_node ); + $(self.ellipse).parent().unbind( 'mouseenter' ).unbind( 'mouseleave' ); self.ellipse.siblings('text').attr('class', 'noselect draggable'); } else { + $(self.ellipse).attr( {stroke:'black', fill:'#fff'} ); self.ellipse.siblings('text').attr('class', ''); - $(self.ellipse).parent().unbind( 'mouseenter' ).unbind( 'mouseleave' ).unbind( 'mousedown' ); - color_inactive( self.ellipse ); + $(self.ellipse).parent().unbind( 'mousedown '); + $(self.ellipse).parent().mousedown( function(evt) { evt.stopPropagation() } ); + $(self.ellipse).parent().hover( this.enter_node, this.leave_node ); } } @@ -292,7 +339,7 @@ function node_obj(ellipse) { $('body').mousemove( self.mousemove_listener ); $('body').mouseup( self.mouseup_listener ); $(self.ellipse).parent().unbind('mouseenter').unbind('mouseleave') - self.ellipse.attr( 'fill', '#ff66ff' ); + self.ellipse.attr( 'fill', '#6b6bb2' ); 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 ) }; } @@ -314,14 +361,15 @@ function node_obj(ellipse) { var target_node_text = $('ellipse[fill="#ffccff"]').siblings("text").text(); $('#source_node_id').val( source_node_id ); $('#source_node_text').val( source_node_text ); + $('.rel_rdg_a').text( "'" + source_node_text + "'" ); $('#target_node_id').val( target_node_id ); $('#target_node_text').val( target_node_text ); + $('.rel_rdg_b').text( "'" + target_node_text + "'" ); $('#dialog-form').dialog( 'open' ); }; $('body').unbind('mousemove'); $('body').unbind('mouseup'); - self.ellipse.attr( 'fill', '#fff' ); - $(self.ellipse).parent().hover( self.enter_node, self.leave_node ); + self.ellipse.attr( 'fill', '#9999ff' ); self.reset_elements(); } @@ -353,32 +401,55 @@ function node_obj(ellipse) { }); } + this.reposition = function( dx, dy ) { + $.each( self.node_elements, function(index, value) { + value.reposition( dx, dy ); + } ); + } + this.move_elements = function() { $.each( self.node_elements, function(index, value) { - value.move(self.dx,self.dy); - }); + value.move( self.dx, self.dy ); + } ); } this.reset_elements = function() { $.each( self.node_elements, function(index, value) { value.reset(); - }); + } ); } this.update_elements = function() { self.node_elements = node_elements_for(self.ellipse); } - self.set_draggable( true ); + this.get_witnesses = function() { + return readingdata[self.get_id()].witnesses + } + + self.set_selectable( true ); } function svgshape( shape_element ) { this.shape = shape_element; + this.reposx = 0; + this.reposy = 0; + this.repositioned = this.shape.parent().data( 'repositioned' ); + if( this.repositioned != null ) { + this.reposx = this.repositioned[0]; + this.reposy = this.repositioned[1]; + } + this.reposition = function (dx, dy) { + this.move( dx, dy ); + this.reposx = this.reposx + dx; + this.reposy = this.reposy + dy; + this.shape.parent().data( 'repositioned', [this.reposx,this.reposy] ); + } this.move = function(dx,dy) { - this.shape.attr( "transform", "translate(" + dx + " " + dy + ")" ); + this.shape.attr( "transform", "translate( " + (this.reposx + dx) + " " + (this.reposy + dy) + " )" ); } this.reset = function() { - this.shape.attr( "transform", "translate( 0, 0 )" ); + this.shape.attr( "transform", "translate( " + this.reposx + " " + this.reposy + " )" ); } this.grey_out = function(filter) { if( this.shape.parent(filter).size() != 0 ) { @@ -397,14 +468,24 @@ function svgpath( path_element, svg_element ) { this.path = path_element; this.x = this.path.x; this.y = this.path.y; + + this.reposition = function (dx, dy) { + this.x = this.x + dx; + this.y = this.y + dy; + this.path.x = this.x; + this.path.y = this.y; + } + this.move = function(dx,dy) { this.path.x = this.x + dx; this.path.y = this.y + dy; } + this.reset = function() { this.path.x = this.x; this.path.y = this.y; } + this.grey_out = function(filter) { if( this.svg_element.parent(filter).size() != 0 ) { this.svg_element.attr('stroke', '#e5e5e5'); @@ -432,7 +513,7 @@ function get_edge_elements_for( ellipse ) { edge_elements = new Array(); node_id = ellipse.parent().attr('id'); edge_in_pattern = new RegExp( node_id + '$' ); - edge_out_pattern = new RegExp( '^' + node_id ); + edge_out_pattern = new RegExp( '^' + node_id + '-' ); $.each( $('#svgenlargement .edge,#svgenlargement .relation').children('title'), function(index) { title = $(this).text(); if( edge_in_pattern.test(title) ) { @@ -479,10 +560,10 @@ function relation_factory() { temporary = null; } } - this.create = function( source_node_id, target_node_id, color_index ) { + this.create = function( source_node_id, target_node_id, color_index, emphasis ) { //TODO: Protect from (color_)index out of bound.. var relation_color = self.relation_colors[ color_index ]; - var relation = draw_relation( source_node_id, target_node_id, relation_color ); + var relation = draw_relation( source_node_id, target_node_id, relation_color, emphasis ); get_node_obj( source_node_id ).update_elements(); get_node_obj( target_node_id ).update_elements(); return relation; @@ -517,11 +598,32 @@ function relation_factory() { } } this.showinfo = function(relation) { - var htmlstr = 'type: ' + relation.data( 'type' ) + '
scope: ' + relation.data( 'scope' ); + $('#delete_relation_type').text( relation.data('type') ); + $('#delete_relation_scope').text( relation.data('scope') ); + $('#delete_relation_attributes').empty(); + var significance = ' is not '; + if( relation.data( 'is_significant' ) === 'yes') { + significance = ' is '; + } else if ( relation.data( 'is_significant' ) === 'maybe' ) { + significance = ' might be '; + } + $('#delete_relation_attributes').append( + "This relationship" + significance + "stemmatically significant
"); + if( relation.data( 'a_derivable_from_b' ) ) { + $('#delete_relation_attributes').append( + "'" + relation.data('source_text') + "' derivable from '" + relation.data('target_text') + "'
"); + } + if( relation.data( 'b_derivable_from_a' ) ) { + $('#delete_relation_attributes').append( + "'" + relation.data('target_text') + "' derivable from '" + relation.data('source_text') + "'
"); + } + if( relation.data( 'non_independent' ) ) { + $('#delete_relation_attributes').append( + "Variance unlikely to arise coincidentally
"); + } if( relation.data( 'note' ) ) { - htmlstr = htmlstr + '
note: ' + relation.data( 'note' ); + $('#delete_relation_note').text('note: ' + relation.data( 'note' ) ); } - $('#delete-form-text').html( htmlstr ); var points = relation.children('path').attr('d').slice(1).replace('C',' ').split(' '); var xs = parseFloat( points[0].split(',')[0] ); var xe = parseFloat( points[1].split(',')[0] ); @@ -538,6 +640,9 @@ function relation_factory() { dialog_aria.offset({ left: nx, top: ny }); } this.remove = function( relation_id ) { + if( !editable ) { + return; + } var relation = $( jq( relation_id ) ); relation.remove(); } @@ -556,7 +661,7 @@ function get_related_nodes( relation_id ) { return srctotarg.split('-...-'); } -function draw_relation( source_id, target_id, relation_color ) { +function draw_relation( source_id, target_id, relation_color, emphasis ) { var source_ellipse = get_ellipse( source_id ); var target_ellipse = get_ellipse( target_id ); var relation_id = get_relation_id( source_id, target_id ); @@ -567,38 +672,345 @@ 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', 'id':relation_id } ); + 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 stroke_width = emphasis === "yes" ? 6 : emphasis === "maybe" ? 4 : 2; + svg.path( relation, path.move( sx, sy ).curveC( sx + (2*rx), sy, ex + (2*rx), ey, ex, ey ), {fill: 'none', stroke: relation_color, strokeWidth: stroke_width }); var relation_element = $('#svgenlargement .relation').filter( ':last' ); relation_element.insertBefore( $('#svgenlargement g g').filter(':first') ); return relation_element; } +function detach_node( readings ) { + // separate out the deleted relationships, discard for now + if( 'DELETED' in readings ) { + // Remove each of the deleted relationship links. + $.each( readings['DELETED'], function( idx, pair ) { + var relation_id = get_relation_id( pair[0], pair[1] ); + var relation = $( jq( relation_id ) ); + if( relation.size() == 0 ) { + relation_id = get_relation_id( pair[1], pair[0] ); + } + relation_manager.remove( relation_id ); + }); + delete readings['DELETED']; + } + // add new node(s) + $.extend( readingdata, readings ); + // remove from existing readings the witnesses for the new nodes/readings + $.each( readings, function( node_id, reading ) { + $.each( reading.witnesses, function( index, witness ) { + var witnesses = readingdata[ reading.orig_rdg ].witnesses; + readingdata[ reading.orig_rdg ].witnesses = $.removeFromArray( witness, witnesses ); + } ); + } ); + + detached_edges = []; + + // here we detach witnesses from the existing edges accoring to what's being relayed by readings + $.each( readings, function( node_id, reading ) { + var edges = edges_of( get_ellipse( reading.orig_rdg ) ); + incoming_remaining = []; + outgoing_remaining = []; + $.each( reading.witnesses, function( index, witness ) { + incoming_remaining.push( witness ); + outgoing_remaining.push( witness ); + } ); + $.each( edges, function( index, edge ) { + detached_edge = edge.detach_witnesses( reading.witnesses ); + if( detached_edge != null ) { + detached_edges.push( detached_edge ); + $.each( detached_edge.witnesses, function( index, witness ) { + if( detached_edge.is_incoming == true ) { + incoming_remaining = $.removeFromArray( witness, incoming_remaining ); + } else { + outgoing_remaining = $.removeFromArray( witness, outgoing_remaining ); + } + } ); + } + } ); + + // After detaching we still need to check if for *all* readings + // an edge was detached. It may be that a witness was not + // explicitly named on an edge but was part of a 'majority' edge + // in which case we need to duplicate and name that edge after those + // remaining witnesses. + if( outgoing_remaining.length > 0 ) { + $.each( edges, function( index, edge ) { + if( edge.get_label() == 'majority' && !edge.is_incoming ) { + detached_edges.push( edge.clone_for( outgoing_remaining ) ); + } + } ); + } + if( incoming_remaining.length > 0 ) { + $.each( edges, function( index, edge ) { + if( edge.get_label() == 'majority' && edge.is_incoming ) { + detached_edges.push( edge.clone_for( incoming_remaining ) ); + } + } ); + } + + // Finally multiple selected nodes may share edges + var copy_array = []; + $.each( detached_edges, function( index, edge ) { + var do_copy = true; + $.each( copy_array, function( index, copy_edge ) { + if( copy_edge.g_elem.attr( 'id' ) == edge.g_elem.attr( 'id' ) ) { do_copy = false } + } ); + if( do_copy == true ) { + copy_array.push( edge ); + } + } ); + detached_edges = copy_array; + + // Lots of unabstracted knowledge down here :/ + // Clone original node/reading, rename/id it.. + duplicate_node = get_ellipse( reading.orig_rdg ).parent().clone(); + duplicate_node.attr( 'id', node_id ); + duplicate_node.children( 'title' ).text( node_id ); + + // This needs somehow to move to node or even to shapes! #repositioned + duplicate_node_data = get_ellipse( reading.orig_rdg ).parent().data( 'repositioned' ); + if( duplicate_node_data != null ) { + duplicate_node.children( 'ellipse' ).parent().data( 'repositioned', duplicate_node_data ); + } + + // Add the node and all new edges into the graph + var graph_root = $('#svgenlargement svg g.graph'); + graph_root.append( duplicate_node ); + $.each( detached_edges, function( index, edge ) { + id_suffix = node_id.slice( node_id.indexOf( '_' ) ); + edge.g_elem.attr( 'id', ( edge.g_elem.attr( 'id' ) + id_suffix ) ); + edge_title = edge.g_elem.children( 'title' ).text(); + edge_weight = 0.8 + ( 0.2 * edge.witnesses.length ); + edge_title = edge_title.replace( reading.orig_rdg, node_id ); + edge.g_elem.children( 'title' ).text( edge_title ); + edge.g_elem.children( 'path').attr( 'stroke-width', edge_weight ); + // Reg unabstracted knowledge: isn't it more elegant to make + // it edge.append_to( graph_root )? + graph_root.append( edge.g_elem ); + } ); + + // Make the detached node a real node_obj + var ellipse_elem = get_ellipse( node_id ); + var new_node = new node_obj( ellipse_elem ); + ellipse_elem.data( 'node_obj', new_node ); + + // Move the node somewhat up for 'dramatic effect' :-p + new_node.reposition( 0, -70 ); + + } ); + +} + +function merge_nodes( source_node_id, target_node_id, consequences ) { + if( consequences.status != null && consequences.status == 'ok' ) { + merge_node( source_node_id, target_node_id ); + if( consequences.checkalign != null ) { + $.each( consequences.checkalign, function( index, node_ids ) { + var temp_relation = draw_relation( node_ids[0], node_ids[1], "#89a02c" ); + var sy = parseInt( temp_relation.children('path').attr('d').split('C')[0].split(',')[1] ); + var ey = parseInt( temp_relation.children('path').attr('d').split(' ')[2].split(',')[1] ); + var yC = ey + (( sy - ey )/2); + // TODO: compute xC to be always the same distance to the amplitude of the curve + var xC = parseInt( temp_relation.children('path').attr('d').split(' ')[1].split(',')[0] ); + var svg = $('#svgenlargement').children('svg').svg('get'); + parent_g = svg.group( $('#svgenlargement svg g') ); + var ids_text = node_ids[0] + '-' + node_ids[1]; + var merge_id = 'merge-' + ids_text; + svg.image( parent_g, xC, (yC-8), 16, 16, merge_button_yes, { id: merge_id } ); + svg.image( parent_g, (xC+20), (yC-8), 16, 16, merge_button_no, { id: 'no' + merge_id } ); + $( '#' + merge_id ).hover( function(){ $(this).addClass( 'draggable' ) }, function(){ $(this).removeClass( 'draggable' ) } ); + $( '#no' + merge_id ).hover( function(){ $(this).addClass( 'draggable' ) }, function(){ $(this).removeClass( 'draggable' ) } ); + $( '#' + merge_id ).click( function( evt ){ + merge_node( node_ids[0], node_ids[1] ); + temp_relation.remove(); + $( '#' + merge_id ).parent().remove(); + //notify backend + var ncpath = getTextURL( 'merge' ); + var form_values = "source_id=" + node_ids[0] + "&target_id=" + node_ids[1] + "&single=true"; + $.post( ncpath, form_values ); + } ); + $( '#no' + merge_id ).click( function( evt ) { + temp_relation.remove(); + $( '#' + merge_id ).parent().remove(); + } ); + } ); + } + } +} + +function merge_node( source_node_id, target_node_id ) { + $.each( edges_of( get_ellipse( source_node_id ) ), function( index, edge ) { + if( edge.is_incoming == true ) { + edge.attach_endpoint( target_node_id ); + } else { + edge.attach_startpoint( target_node_id ); + } + } ); + $( jq( source_node_id ) ).remove(); +} + +function Marquee() { + + var self = this; + + this.x = 0; + this.y = 0; + this.dx = 0; + this.dy = 0; + this.enlargementOffset = $('#svgenlargement').offset(); + this.svg_rect = $('#svgenlargement svg').svg('get'); + + this.show = function( event ) { + self.x = event.clientX; + self.y = event.clientY; + p = svg_root.createSVGPoint(); + p.x = event.clientX - self.enlargementOffset.left; + p.y = event.clientY - self.enlargementOffset.top; + self.svg_rect.rect( p.x, p.y, 0, 0, { fill: 'black', 'fill-opacity': '0.1', stroke: 'black', 'stroke-dasharray': '4,2', strokeWidth: '0.02em', id: 'marquee' } ); + }; + + this.expand = function( event ) { + self.dx = (event.clientX - self.x); + self.dy = (event.clientY - self.y); + var rect = $('#marquee'); + if( rect.length != 0 ) { + var rect_w = Math.abs( self.dx ); + var rect_h = Math.abs( self.dy ); + var rect_x = self.x - self.enlargementOffset.left; + var rect_y = self.y - self.enlargementOffset.top; + if( self.dx < 0 ) { rect_x = rect_x - rect_w } + if( self.dy < 0 ) { rect_y = rect_y - rect_h } + rect.attr("x", rect_x).attr("y", rect_y).attr("width", rect_w).attr("height", rect_h); + } + }; + + this.select = function() { + var rect = $('#marquee'); + if( rect.length != 0 ) { + //unselect any possible selected first + //TODO: unless SHIFT? + if( $('ellipse[fill="#9999ff"]').size() > 0 ) { + $('ellipse[fill="#9999ff"]').each( function() { + $(this).data( 'node_obj' ).set_draggable( false ); + } ); + } + //compute dimension of marquee + var left = $('#marquee').offset().left; + var top = $('#marquee').offset().top; + var right = left + parseInt( $('#marquee').attr( 'width' ) ); + var bottom = top + parseInt( $('#marquee').attr( 'height' ) ); + var tf = svg_root_element.getScreenCTM().inverse(); + var p = svg_root.createSVGPoint(); + p.x=left; + p.y=top; + var cx_min = p.matrixTransform(tf).x; + var cy_min = p.matrixTransform(tf).y; + p.x=right; + p.y=bottom; + var cx_max = p.matrixTransform(tf).x; + var cy_max = p.matrixTransform(tf).y; + //select any node with its center inside the marquee + var readings = []; + //also merge witness sets from nodes + var witnesses = []; + $('#svgenlargement ellipse').each( function( index ) { + var cx = parseInt( $(this).attr('cx') ); + var cy = parseInt( $(this).attr('cy') ); + + // This needs somehow to move to node or even to shapes! #repositioned + // We should ask something more aling the lines of: nodes.each { |item| node.selected? } + var org_translate = $(this).parent().data( 'repositioned' ); + if( org_translate != null ) { + cx = cx + org_translate[0]; + cy = cy + org_translate[1]; + } + + if( cx > cx_min && cx < cx_max) { + if( cy > cy_min && cy < cy_max) { + // we actually heve no real 'selected' state for nodes, except coloring + $(this).attr( 'fill', '#9999ff' ); + // Take note of the selected reading(s) and applicable witness(es) + // so we can populate the multipleselect-form + readings.push( $(this).parent().attr('id') ); + var this_witnesses = $(this).data( 'node_obj' ).get_witnesses(); + witnesses = arrayUnique( witnesses.concat( this_witnesses ) ); + } + } + }); + if( $('ellipse[fill="#9999ff"]').size() > 0 ) { + //add intersection of witnesses sets to the multi select form and open it + $('#detach_collated_form').empty(); + $.each( readings, function( index, value ) { + $('#detach_collated_form').append( $('').attr( + "type", "hidden").attr("name", "readings[]").attr( + "value", value ) ); + }); + $.each( witnesses, function( index, value ) { + $('#detach_collated_form').append( + '' + value + '
' ); + }); + $('#multiple_selected_readings').attr('value', readings.join(',') ); + $('#multipleselect-form').dialog( 'open' ); + } + self.svg_rect.remove( $('#marquee') ); + } + }; + + this.unselect = function() { + $('ellipse[fill="#9999ff"]').attr( 'fill', '#fff' ); + } + +} + +function readings_equivalent( source, target ) { + var sourcetext = readingdata[source].text; + var targettext = readingdata[target].text; + if( sourcetext === targettext ) { + return true; + } + // Lowercase and strip punctuation from both and compare again + var nonwc = XRegExp('[^\\p{L}\\s]|_'); + var stlc = XRegExp.replace( sourcetext.toLocaleLowerCase(), nonwc, "", 'all' ); + var ttlc = XRegExp.replace( targettext.toLocaleLowerCase(), nonwc, "", 'all' ); + if( stlc === ttlc ) { + return true; + } + return false; +} + $(document).ready(function () { timer = null; relation_manager = new relation_factory(); - $('#update_workspace_button').data('locked', false); + $('#update_workspace_button').data('locked', false); + + // Set up the mouse events on the SVG enlargement $('#enlargement').mousedown(function (event) { $(this) .data('down', true) .data('x', event.clientX) .data('y', event.clientY) .data('scrollLeft', this.scrollLeft) - 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; + stateTf = svg_root_element.getCTM().inverse(); + var p = svg_root.createSVGPoint(); + p.x = event.clientX; + p.y = event.clientY; + stateOrigin = p.matrixTransform(stateTf); + + // Activate marquee if in interaction mode + if( $('#update_workspace_button').data('locked') == true ) { marquee.show( event ) }; + + event.returnValue = false; + event.preventDefault(); + return false; }).mouseup(function (event) { - $(this).data('down', false); + marquee.select(); + $(this).data('down', false); }).mousemove(function (event) { if( timer != null ) { clearTimeout(timer); } if ( ($(this).data('down') == true) && ($('#update_workspace_button').data('locked') == false) ) { @@ -610,6 +1022,7 @@ $(document).ready(function () { var s = "matrix(" + matrix.a + "," + matrix.b + "," + matrix.c + "," + matrix.d + "," + matrix.e + "," + matrix.f + ")"; svg_root_element.setAttribute("transform", s); } + marquee.expand( event ); event.returnValue = false; event.preventDefault(); }).mousewheel(function (event, delta) { @@ -640,99 +1053,141 @@ $(document).ready(function () { 'cursor' : '-moz-grab' }); - - $( "#dialog-form" ).dialog({ - autoOpen: false, - height: 270, - width: 290, - modal: true, - buttons: { - "Ok": function( evt ) { - $(evt.target).button("disable"); - $('#status').empty(); - form_values = $('#collapse_node_form').serialize(); - ncpath = getTextURL( 'relationships' ); - var jqjson = $.post( ncpath, form_values, function(data) { - $.each( data, function(item, source_target) { - 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() ); + + // Set up the relationship creation dialog. This also functions as the reading + // merge dialog where appropriate. + + if( editable ) { + $( '#dialog-form' ).dialog( { + autoOpen: false, + height: 350, + width: 340, + modal: true, + buttons: { + 'Merge readings': function( evt ) { + var mybuttons = $(evt.target).closest('button').parent().find('button'); + mybuttons.button( 'disable' ); + $( '#status' ).empty(); + form_values = $( '#collapse_node_form' ).serialize(); + ncpath = getTextURL( 'merge' ); + var jqjson = $.post( ncpath, form_values, function( data ) { + merge_nodes( $( '#source_node_id' ).val(), $( '#target_node_id' ).val(), data ); + mybuttons.button( 'enable' ); + $( '#dialog-form' ).dialog( 'close' ); + } ); + }, + OK: function( evt ) { + var mybuttons = $(evt.target).closest('button').parent().find('button'); + mybuttons.button( 'disable' ); + $( '#status' ).empty(); + form_values = $( '#collapse_node_form' ).serialize(); + ncpath = getTextURL( 'relationships' ); + var jqjson = $.post( ncpath, form_values, function( data ) { + $.each( data, function( item, source_target ) { + var source_found = get_ellipse( source_target[0] ); + var target_found = get_ellipse( source_target[1] ); + var relation_found = $.inArray( source_target[2], $( '#keymap' ).data( 'relations' ) ); + if( source_found.size() && target_found.size() && relation_found > -1 ) { + var emphasis = $('#is_significant option:selected').attr('value'); + var relation = relation_manager.create( source_target[0], source_target[1], relation_found, emphasis ); relation_manager.toggle_active( relation.attr('id') ); + $.each( $('#collapse_node_form').serializeArray(), function( i, k ) { + relation.data( k.name, k.value ); + }); } - $(evt.target).button("enable"); - }); - $( "#dialog-form" ).dialog( "close" ); - }, 'json' ); - }, - Cancel: function() { - $( this ).dialog( "close" ); - } - }, - create: function(event, ui) { - $(this).data( 'relation_drawn', false ); - //TODO? Err handling? - var basepath = getRelativePath(); - var jqjson = $.getJSON( basepath + '/definitions', function(data) { - var types = data.types.sort(); - $.each( types, function(index, value) { - $('#rel_type').append( $('