Marquee now selects in any direction. Deselecting of nodes on multpiple nodes selecte...
[scpubgit/stemmaweb.git] / root / js / relationship.js
index 873945b..e822d70 100644 (file)
@@ -5,6 +5,17 @@ var start_element_height = 0;
 var reltypes = {};
 var readingdata = {};
 
+function arrayUnique(array) {
+    var a = array.concat();
+    for(var i=0; i<a.length; ++i) {
+        for(var j=i+1; j<a.length; ++j) {
+            if(a[i] === a[j])
+                a.splice(j--, 1);
+        }
+    }
+    return a;
+};
+
 function getTextURL( which ) {
        return basepath + textid + '/' + which;
 }
@@ -209,6 +220,10 @@ function svgEnlargementLoaded() {
     //used to calculate min and max zoom level:
     start_element_height = $('#__START__').children('ellipse')[0].getBBox().height;
     add_relations( function() { $('#loading_overlay').hide(); });
+    
+    //initialize marquee
+    marquee = new Marquee();
+    
 }
 
 function add_relations( callback_fn ) {
@@ -373,6 +388,10 @@ function node_obj(ellipse) {
       self.node_elements = node_elements_for(self.ellipse);
   }
 
+  this.get_witnesses = function() {
+      return readingdata[self.get_id()].witnesses
+  }
+  
   self.set_draggable( true );
 }
 
@@ -521,11 +540,11 @@ function relation_factory() {
         }
     }
     this.showinfo = function(relation) {
-       var htmlstr = 'type: ' + relation.data( 'type' ) + '<br/>scope: ' + relation.data( 'scope' );
+       $('#delete_relation_type').text( relation.data('type') );
+       $('#delete_relation_scope').text( relation.data('scope') );
        if( relation.data( 'note' ) ) {
-               htmlstr = htmlstr + '<br/>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] );
@@ -583,29 +602,118 @@ function draw_relation( source_id, target_id, relation_color ) {
     return relation_element;
 }
 
+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 ) {
+        // TODO: uncolor possible selected
+        // TODO: unless SHIFT?
+        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 ) {
+            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;
+            var witnesses = [];
+            $('#svgenlargement ellipse').each( function( index ) {
+                var cx = parseInt( $(this).attr('cx') );
+                var cy = parseInt( $(this).attr('cy') );
+                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', '#ffccff' );
+                        var this_witnesses = $(this).data( 'node_obj' ).get_witnesses();
+                        witnesses = arrayUnique( witnesses.concat( this_witnesses ) );
+                    }
+                }
+            });
+            if( $('ellipse[fill="#ffccff"]').size() > 0 ) {
+                $.each( witnesses, function( index, value ) {
+                    $('#multipleselect-form').append( '<input type="checkbox" name="witnesses" value="' + value + '">' + value + '<br>' );
+                });
+                $('#multipleselect-form').dialog( 'open' );
+            }
+            self.svg_rect.remove( $('#marquee') );
+        }
+    };
+    
+    this.unselect = function() {
+        $('ellipse[fill="#ffccff"]').attr( 'fill', '#fff' );
+    }
+     
+}
+
 
 $(document).ready(function () {
     
   timer = null;
   relation_manager = new relation_factory();
-  $('#update_workspace_button').data('locked', false);
   
+  $('#update_workspace_button').data('locked', false);
+                
   $('#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) ) {
@@ -617,6 +725,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) {
@@ -647,6 +756,7 @@ $(document).ready(function () {
     'cursor' : '-moz-grab'
   });
   
+  
   if( editable ) {
        $( "#dialog-form" ).dialog({
        autoOpen: false,
@@ -682,12 +792,21 @@ $(document).ready(function () {
        },
        create: function(event, ui) { 
                $(this).data( 'relation_drawn', false );
+               $('#rel_type').data( 'changed_after_open', false );
                $.each( relationship_types, function(index, typedef) {   
                         $('#rel_type').append( $('<option />').attr( "value", typedef.name ).text(typedef.name) ); 
                });
                $.each( relationship_scopes, function(index, value) {   
                         $('#scope').append( $('<option />').attr( "value", value ).text(value) ); 
-               });        
+               });
+               // Handler to clear the annotation field, the first time the relationship is
+               // changed after opening the form.
+               $('#rel_type').change( function () {
+                       if( !$(this).data( 'changed_after_open' ) ) {
+                               $('#note').val('');
+                       }
+                       $(this).data( 'changed_after_open', true );
+               });
        },
        open: function() {
                relation_manager.create_temporary( $('#source_node_id').val(), $('#target_node_id').val() );
@@ -696,6 +815,7 @@ $(document).ready(function () {
                $("#dialog_overlay").height( $("#enlargement_container").height() );
                $("#dialog_overlay").width( $("#enlargement_container").innerWidth() );
                $("#dialog_overlay").offset( $("#enlargement_container").offset() );
+               $('#rel_type').data( 'changed_after_open', false );
        },
        close: function() {
                relation_manager.remove_temporary();
@@ -722,36 +842,25 @@ $(document).ready(function () {
        } );
   }
 
+  var deletion_buttonset = {
+        cancel: function() { $( this ).dialog( "close" ); },
+        global: function () { delete_relation( true ); },
+        delete: function() { delete_relation( false ); }
+  };   
+  
   $( "#delete-form" ).dialog({
     autoOpen: false,
     height: 135,
-    width: 160,
+    width: 250,
     modal: false,
-    buttons: {
-        Cancel: function() {
-            $( this ).dialog( "close" );
-        },
-        Delete: function() {
-          form_values = $('#delete_relation_form').serialize();
-          ncpath = getTextURL( 'relationships' );
-          var jqjson = $.ajax({ url: ncpath, data: form_values, success: function(data) {
-              $.each( data, function(item, source_target) { 
-                  relation_manager.remove( get_relation_id( source_target[0], source_target[1] ) );
-              });
-              $( "#delete-form" ).dialog( "close" );
-          }, dataType: 'json', type: 'DELETE' });
-        }
-    },
     create: function(event, ui) {
-       // Swap out the buttons if we are in readonly mode
-       if( !editable ) {
-               $( this ).dialog( "option", "buttons", 
-                       [{ text: "OK", click: function() { $( this ).dialog( "close" ); }}] );
-       }
-       
        // TODO What is this logic doing?
+       // This scales the buttons in the dialog and makes it look proper
+       // Not sure how essential it is, does anything break if it's not here?
         var buttonset = $(this).parent().find( '.ui-dialog-buttonset' ).css( 'width', '100%' );
         buttonset.find( "button:contains('Cancel')" ).css( 'float', 'right' );
+       // A: This makes sure that the pop up delete relation dialogue for a hovered over
+       // relation auto closes if the user doesn't engage (mouseover) with it.
         var dialog_aria = $("div[aria-labelledby='ui-dialog-title-delete-form']");  
         dialog_aria.mouseenter( function() {
             if( mouseWait != null ) { clearTimeout(mouseWait) };
@@ -761,12 +870,78 @@ $(document).ready(function () {
         })
     },
     open: function() {
+       if( !editable ) {
+               $( this ).dialog( "option", "buttons", 
+                       [{ text: "OK", click: deletion_buttonset['cancel'] }] );
+       } else if( $('#delete_relation_scope').text() === 'local' ) {
+               $( this ).dialog( "option", "width", 160 );
+               $( this ).dialog( "option", "buttons",
+                       [{ text: "Delete", click: deletion_buttonset['delete'] },
+                        { text: "Cancel", click: deletion_buttonset['cancel'] }] );
+       } else {
+               $( this ).dialog( "option", "width", 200 );
+               $( this ).dialog( "option", "buttons",
+                       [{ text: "Delete", click: deletion_buttonset['delete'] },
+                        { text: "Delete all", click: deletion_buttonset['global'] },
+                        { text: "Cancel", click: deletion_buttonset['cancel'] }] );
+               }       
+                       
         mouseWait = setTimeout( function() { $("#delete-form").dialog( "close" ) }, 2000 );
     },
-    close: function() {
-    }
+    close: function() {}
+  });
+
+  var multipleselect_buttonset = {
+        cancel: function() { $( this ).dialog( "close" ); },
+        button1: function () {  },
+        button2: function() {  }
+  };   
+
+  $( "#multipleselect-form" ).dialog({
+    autoOpen: false,
+    height: 150,
+    width: 250,
+    modal: true,
+    create: function(event, ui) {
+        var buttonset = $(this).parent().find( '.ui-dialog-buttonset' ).css( 'width', '100%' );
+        buttonset.find( "button:contains('Cancel')" ).css( 'float', 'right' );
+    },
+    open: function() {
+        $( this ).dialog( "option", "width", 200 );
+        $( this ).dialog( "option", "buttons",
+            [{ text: "Button_1", click: multipleselect_buttonset['button1'] },
+             { text: "Button_2", click: multipleselect_buttonset['button2'] },
+             { text: "Cancel", click: multipleselect_buttonset['cancel'] }] );
+    },
+    close: function() { marquee.unselect(); }
   });
 
+  // Helpers for relationship deletion
+  
+  function delete_relation( scopewide ) {
+         form_values = $('#delete_relation_form').serialize();
+         if( scopewide ) {
+               form_values += "&scopewide=true";
+         }
+         ncpath = getTextURL( 'relationships' );
+         var jqjson = $.ajax({ url: ncpath, data: form_values, success: function(data) {
+                 $.each( data, function(item, source_target) { 
+                         relation_manager.remove( get_relation_id( source_target[0], source_target[1] ) );
+                 });
+                 $( "#delete-form" ).dialog( "close" );
+         }, dataType: 'json', type: 'DELETE' });
+  }
+  
+  function toggle_relation_active( node_id ) {
+      $('#svgenlargement .relation').find( "title:contains('" + node_id +  "')" ).each( function(index) {
+          matchid = new RegExp( "^" + node_id );
+          if( $(this).text().match( matchid ) != null ) {
+                 var relation_id = $(this).parent().attr('id');
+              relation_manager.toggle_active( relation_id );
+          };
+      });
+  }
+
   // function for reading form dialog should go here; 
   // just hide the element for now if we don't have morphology
   if( can_morphologize ) {
@@ -926,17 +1101,6 @@ $(document).ready(function () {
          scrollbars:1 
   }); 
 
-  
-  function toggle_relation_active( node_id ) {
-      $('#svgenlargement .relation').find( "title:contains('" + node_id +  "')" ).each( function(index) {
-          matchid = new RegExp( "^" + node_id );
-          if( $(this).text().match( matchid ) != null ) {
-                 var relation_id = $(this).parent().attr('id');
-              relation_manager.toggle_active( relation_id );
-          };
-      });
-  }
-
   expandFillPageClients();
   $(window).resize(function() {
     expandFillPageClients();
@@ -963,6 +1127,7 @@ function loadSVG(svgData) {
 }
 
 
+
 /*     OS Gadget stuff
 
 function svg_select_callback(topic, data, subscriberData) {