pull changes from lemmatizer
[scpubgit/stemmatology.git] / stemmaweb / root / js / relationship.js
1 function getTextPath() {
2     var currpath = window.location.pathname
3     if( currpath.lastIndexOf('/') == currpath.length - 1 ) { 
4         currpath = currpath.slice( 0, currpath.length - 1) 
5     };
6     var path_elements = currpath.split('/');
7     var textid = path_elements.pop();
8     var basepath = path_elements.join( '/' );
9     var path_parts = [ basepath, textid ];
10     return path_parts;
11 }
12
13 function svgLoaded() {
14   // some initial scaling
15   var svg_element = $('#svgbasics').children('svg');
16   var svg_graph = svg_element.svg().svg('get').root();
17   var svg_vbwidth = svg_graph.viewBox.baseVal.width;
18   var svg_vbheight = svg_graph.viewBox.baseVal.height;
19   var scroll_padding = $('#graph_container').width();
20   // (Use attr('width') to set width attr, otherwise style="width: npx;" is set.)
21   var svg_element_width = svg_vbwidth/svg_vbheight * parseInt(svg_element.attr('height'));
22   svg_element_width += scroll_padding;
23   svg_element.attr( 'width', svg_element_width );
24   $('ellipse').attr( {stroke:'black', fill:'#fff'} );
25 }
26
27 function svgEnlargementLoaded() {
28   // some initial scaling
29   var svg_element = $('#svgenlargement').children('svg');
30   var svg_graph = svg_element.svg().svg('get').root()
31   var svg_vbwidth = svg_graph.viewBox.baseVal.width;
32   var svg_vbheight = svg_graph.viewBox.baseVal.height;
33   var scroll_padding = $('#enlargement_container').width();
34   // (Use attr('width') to set width attr, otherwise style="width: npx;" is set.)
35   var svg_element_width = svg_vbwidth/svg_vbheight * parseInt(svg_element.attr('height'));
36   svg_element_width += scroll_padding;
37   svg_element.attr( 'width', svg_element_width );
38     $('ellipse').attr({
39         stroke: 'black',
40         fill: '#fff'
41     });
42   var svg_height = parseInt( $('#svgenlargement').height() );
43   scroll_enlargement_ratio = svg_height/svg_vbheight;
44     add_relations();
45 }
46
47 function add_relations() {
48         var pathparts = getTextPath();
49     $.getJSON( pathparts[0] + '/definitions', function(data) {
50         var rel_types = data.types.sort();
51         var pathparts = getTextPath();
52         $.getJSON( pathparts[0] + '/' + pathparts[1] + '/relationships',
53         function(data) {
54             $.each(data, function( index, rel_info ) {
55                 var type_index = $.inArray(rel_info.type, rel_types);
56                 if( type_index != -1 ) {
57                     relation_manager.create( rel_info.source, rel_info.target, type_index );
58                 }
59             })
60         });    
61     });
62 }
63
64 function get_ellipse( node_id ) {
65   return $('#svgenlargement .node').children('title').filter( function(index) {
66     return $(this).text() == node_id;
67   }).siblings('ellipse');
68 }
69
70 function get_node_obj( node_id ) {
71   return get_ellipse( node_id ).data( 'node_obj' );
72 }
73
74 function get_edge( edge_id ) {
75   return $('#svgenlargement .edge').filter( function(index) {
76     return $(this).children( 'title' ).text() == $('<div/>').html(edge_id).text() ;
77   });
78 }
79
80 function node_obj(ellipse) {
81   this.ellipse = ellipse;
82   var self = this;
83   
84   this.x = 0;
85   this.y = 0;
86   this.dx = 0;
87   this.dy = 0;
88   this.node_elements = node_elements_for(self.ellipse);
89
90   this.get_id = function() {
91     return self.ellipse.siblings('title').text()
92   }
93   
94   this.set_draggable = function( draggable ) {
95     if( draggable ) {
96       self.ellipse.attr( {stroke:'black', fill:'#fff'} );
97       self.ellipse.mousedown( this.mousedown_listener );
98       self.ellipse.hover( this.enter_node, this.leave_node );  
99     } else {
100       self.ellipse.unbind('mouseenter').unbind('mouseleave').unbind('mousedown');
101       self.ellipse.attr( {stroke:'green', fill:'#b3f36d'} );
102     }
103   }
104
105   this.mousedown_listener = function(evt) {
106     evt.stopPropagation();
107     self.x = evt.clientX;
108     self.y = evt.clientY;
109     $('body').mousemove( self.mousemove_listener );
110     $('body').mouseup( self.mouseup_listener );
111     self.ellipse.unbind('mouseenter').unbind('mouseleave')
112     self.ellipse.attr( 'fill', '#ff66ff' );
113     first_node_g_element = $("#svgenlargement g .node" ).filter( ":first" );
114     if( first_node_g_element.attr('id') !== self.get_g().attr('id') ) { self.get_g().insertBefore( first_node_g_element ) };
115   }
116
117   this.mousemove_listener = function(evt) {
118     // magnification on workspace lock temporarily disabled
119     // self.dx = (evt.clientX - self.x) / mousemove_enlargement_ratio;
120     // self.dy = (evt.clientY - self.y) / mousemove_enlargement_ratio;
121     self.dx = (evt.clientX - self.x) / scroll_enlargement_ratio;
122     self.dy = (evt.clientY - self.y) / scroll_enlargement_ratio;
123     self.move_elements();
124   }
125
126   this.mouseup_listener = function(evt) {    
127     if( $('ellipse[fill="#ffccff"]').size() > 0 ) {
128         var source_node_id = self.ellipse.siblings('title').text();
129         var target_node_id = $('ellipse[fill="#ffccff"]').siblings("title").text();
130         $('#source_node_id').val( source_node_id );
131         $('#target_node_id').val( target_node_id );
132         $('#dialog-form').dialog( 'open' );
133     };
134     $('body').unbind('mousemove');
135     $('body').unbind('mouseup');
136     self.ellipse.attr( 'fill', '#fff' );
137     self.ellipse.hover( self.enter_node, self.leave_node );
138         self.reset_elements();
139   }
140
141   this.cpos = function() {
142     return { x: self.ellipse.attr('cx'), y: self.ellipse.attr('cy') };
143   }
144
145   this.get_g = function() {
146     return self.ellipse.parent('g');
147   }
148
149   this.enter_node = function(evt) {
150     self.ellipse.attr( 'fill', '#ffccff' );
151   }
152
153   this.leave_node = function(evt) {
154     self.ellipse.attr( 'fill', '#fff' );
155   }
156
157   this.greyout_edges = function() {
158       $.each( self.node_elements, function(index, value) {
159         value.grey_out('.edge');
160       });
161   }
162
163   this.ungreyout_edges = function() {
164       $.each( self.node_elements, function(index, value) {
165         value.un_grey_out('.edge');
166       });
167   }
168
169   this.move_elements = function() {
170     $.each( self.node_elements, function(index, value) {
171       value.move(self.dx,self.dy);
172     });
173   }
174
175   this.reset_elements = function() {
176     $.each( self.node_elements, function(index, value) {
177       value.reset();
178     });
179   }
180
181   this.update_elements = function() {
182       self.node_elements = node_elements_for(self.ellipse);
183   }
184
185   self.set_draggable( true );
186 }
187
188 function svgshape( shape_element ) {
189   this.shape = shape_element;
190   this.move = function(dx,dy) {
191     this.shape.attr( "transform", "translate(" + dx + " " + dy + ")" );
192   }
193   this.reset = function() {
194     this.shape.attr( "transform", "translate( 0, 0 )" );
195   }
196   this.grey_out = function(filter) {
197       if( this.shape.parent(filter).size() != 0 ) {
198           this.shape.attr({'stroke':'#e5e5e5', 'fill':'#e5e5e5'});
199       }
200   }
201   this.un_grey_out = function(filter) {
202       if( this.shape.parent(filter).size() != 0 ) {
203         this.shape.attr({'stroke':'#000000', 'fill':'#000000'});
204       }
205   }
206 }
207
208 function svgpath( path_element, svg_element ) {
209   this.svg_element = svg_element;
210   this.path = path_element;
211   this.x = this.path.x;
212   this.y = this.path.y;
213   this.move = function(dx,dy) {
214     this.path.x = this.x + dx;
215     this.path.y = this.y + dy;
216   }
217   this.reset = function() {
218     this.path.x = this.x;
219     this.path.y = this.y;
220   }
221   this.grey_out = function(filter) {
222       if( this.svg_element.parent(filter).size() != 0 ) {
223           this.svg_element.attr('stroke', '#e5e5e5');
224           this.svg_element.siblings('text').attr('fill', '#e5e5e5');
225       }
226   }
227   this.un_grey_out = function(filter) {
228       if( this.svg_element.parent(filter).size() != 0 ) {
229           this.svg_element.attr('stroke', '#000000');
230           this.svg_element.siblings('text').attr('fill', '#000000');
231       }
232   }
233 }
234
235 function node_elements_for( ellipse ) {
236   node_elements = get_edge_elements_for( ellipse );
237   node_elements.push( new svgshape( ellipse.siblings('text') ) );
238   node_elements.push( new svgshape( ellipse ) );
239   return node_elements;
240 }
241
242 function get_edge_elements_for( ellipse ) {
243   edge_elements = new Array();
244   node_id = ellipse.siblings('title').text();
245   edge_in_pattern = new RegExp( node_id + '$' );
246   edge_out_pattern = new RegExp( '^' + node_id );
247   $.each( $('#svgenlargement .edge,#svgenlargement .relation').children('title'), function(index) {
248     title = $(this).text();
249     if( edge_in_pattern.test(title) ) {
250         polygon = $(this).siblings('polygon');
251         if( polygon.size() > 0 ) {
252             edge_elements.push( new svgshape( polygon ) );
253         }
254         path_segments = $(this).siblings('path')[0].pathSegList;
255         edge_elements.push( new svgpath( path_segments.getItem(path_segments.numberOfItems - 1), $(this).siblings('path') ) );
256     }
257     if( edge_out_pattern.test(title) ) {
258       path_segments = $(this).siblings('path')[0].pathSegList;
259       edge_elements.push( new svgpath( path_segments.getItem(0), $(this).siblings('path') ) );
260     }
261   });
262   return edge_elements;
263
264
265 function relation_factory() {
266     var self = this;
267     this.color_memo = null;
268     //TODO: colors hard coded for now
269     this.temp_color = '#FFA14F';
270     this.relation_colors = [ "#5CCCCC", "#67E667", "#F9FE72", "#6B90D4", "#FF7673", "#E467B3", "#AA67D5", "#8370D8", "#FFC173" ];
271
272     this.create_temporary = function( source_node_id, target_node_id ) {
273     var relation = $('#svgenlargement .relation').filter( function(index) {
274         var relation_id = $(this).children('title').text();
275             if( ( relation_id == ( source_node_id + '->' + target_node_id ) ) || ( relation_id == ( target_node_id + '->' + source_node_id ) ) ) {
276             return true;
277         } 
278     } );
279     if( relation.size() == 0 ) {
280             draw_relation( source_node_id, target_node_id, self.temp_color );
281         } else {
282             self.color_memo = relation.children('path').attr( 'stroke' );
283             relation.children('path').attr( 'stroke', self.temp_color );
284         }
285     }
286     this.remove_temporary = function() {
287         var path_element = $('#svgenlargement .relation').children('path[stroke="' + self.temp_color + '"]');
288         if( self.color_memo != null ) {
289             path_element.attr( 'stroke', self.color_memo );
290             self.color_memo = null;
291         } else {
292             path_element.parent('g').remove();
293         }
294     }
295     this.create = function( source_node_id, target_node_id, color_index ) {
296         //TODO: Protect from (color_)index out of bound..
297         var relation_color = self.relation_colors[ color_index ];
298         draw_relation( source_node_id, target_node_id, relation_color );
299         var source_node = get_node_obj( source_node_id );
300         var target_node = get_node_obj( target_node_id );
301         if( source_node != null ) { source_node.update_elements() };
302         if( target_node != null ) { target_node.update_elements() };
303     }
304     this.remove = function( source_node_id, target_id ) {
305         //TODO (When needed)
306         console.log( "Unsupported function node_obj.remove()." );
307     }
308 }
309
310 function draw_relation( source_id, target_id, relation_color ) {
311         var source_ellipse = get_ellipse( source_id );
312         var target_ellipse = get_ellipse( target_id );
313         var svg = $('#svgenlargement').children('svg').svg().svg('get');
314         var path = svg.createPath(); 
315         var sx = parseInt( source_ellipse.attr('cx') );
316         var rx = parseInt( source_ellipse.attr('rx') );
317         var sy = parseInt( source_ellipse.attr('cy') );
318         var ex = parseInt( target_ellipse.attr('cx') );
319         var ey = parseInt( target_ellipse.attr('cy') );
320         var relation = svg.group( $("#svgenlargement svg g"), {'class':'relation'} );
321         svg.title( relation, source_id + '->' + target_id );
322         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});
323     var relation_element = $('#svgenlargement .relation').filter( ':last' );
324     relation_element.insertBefore( $('#svgenlargement g g').filter(':first') );
325 }
326
327 $(document).ready(function () {
328   
329   relation_manager = new relation_factory();
330   
331   scroll_ratio =  $('#enlargement').height() / $('#graph').height();
332   
333   $('#graph').mousedown(function (event) {
334     $(this)
335       .data('down', true)
336       .data('x', event.clientX)
337       .data('scrollLeft', this.scrollLeft);
338       return false;
339   }).mouseup(function (event) {
340     $(this).data('down', false);
341   }).mousemove(function (event) {
342     if ($(this).data('down') == true ) {
343       if ( $('#update_workspace_button').data('locked') != true ) {
344           var scroll_left = $(this).data('scrollLeft') + $(this).data('x') - event.clientX;
345           this.scrollLeft = scroll_left;
346           var enlarged_scroll_left = scroll_left * scroll_ratio; 
347           $('#enlargement').scrollLeft( enlarged_scroll_left );
348           color_enlarged();
349       }
350     }
351   }).mousewheel(function (event, delta) {
352       if ( $('#update_workspace_button').data('locked') != true ) {
353           var scroll_left = delta * 30;
354           this.scrollLeft -= scroll_left;
355           var enlarged_scroll_left = $('#enlargement').scrollLeft();
356           enlarged_scroll_left -= (scroll_left * scroll_ratio);
357           $('#enlargement').scrollLeft( enlarged_scroll_left );
358           color_enlarged();
359       }
360   }).css({
361     'overflow' : 'hidden',
362     'cursor' : '-moz-grab'
363   });
364   
365
366   $( "#dialog-form" ).dialog({
367     autoOpen: false,
368     height: 270,
369     width: 290,
370     modal: true,
371     buttons: {
372       "Ok": function() {
373         $('#status').empty();
374         form_values = $('#collapse_node_form').serialize()
375         pathparts = getTextPath();
376         ncpath = pathparts[0] + '/' + pathparts[1] + '/relationship';
377         var jqjson = $.post( ncpath, form_values, function(data) {
378             $.each( data, function(item, source_target) { 
379                 relation_manager.create( source_target[0], source_target[1], $('#rel_type').attr('selectedIndex') );
380             });
381             relation_manager.remove_temporary();
382             $( "#dialog-form" ).dialog( "close" );
383         }, 'json');
384       },
385       Cancel: function() {
386           relation_manager.remove_temporary();
387           $( this ).dialog( "close" );
388       }
389     },
390     create: function(event, ui) { 
391         $(this).data( 'relation_drawn', false );
392         //TODO? Err handling?
393         var pathparts = getTextPath();
394         var jqjson = $.getJSON( pathparts[0] + '/definitions', function(data) {
395             var types = data.types.sort();
396             $.each( types, function(index, value) {   
397                  $('#rel_type').append( $('<option>').attr( "value", value ).text(value) ); 
398                  $('#keymaplist').append( $('<li>').css( "border-color", relation_manager.relation_colors[index] ).text(value) ); 
399             });
400             var scopes = data.scopes;
401             $.each( scopes, function(index, value) {   
402                  $('#scope').append( $('<option>').attr( "value", value ).text(value) ); 
403             });
404         });        
405     },
406     open: function() {
407         relation_manager.create_temporary( $('#source_node_id').val(), $('#target_node_id').val() );
408       $(".ui-widget-overlay").css("background", "none");
409       $("#dialog_overlay").show();
410       $("#dialog_overlay").height( $("#enlargement_container").height() );
411       $("#dialog_overlay").width( $("#enlargement_container").width() );
412       $("#dialog_overlay").offset( $("#enlargement_container").offset() );
413     },
414     close: function() {
415         $( '#status' ).empty();
416         $("#dialog_overlay").hide();
417     }
418   }).ajaxError( function(event, jqXHR, ajaxSettings, thrownError) {
419       if( ( ajaxSettings.url.split("?")[0] == 'set_relationship' ) && jqXHR.status == 403 ) {
420           $('#status').append( '<p class="error">The relationship can not be made in this way between these nodes.</p>' );
421       }
422   } );
423
424   $('#update_workspace_button').click( function() {
425      var svg_enlargement = $('#svgenlargement').svg().svg('get').root();
426      if( $(this).data('locked')==true) {
427          $.each( ellipses_in_magnifier, function( index, ellipse ) {
428              ellipse.data( 'node_obj' ).ungreyout_edges();
429              ellipse.data( 'node_obj' ).set_draggable( false );
430              ellipse.data( 'node_obj', null );
431          })
432          // magnification on workspace lock temporarily disabled
433          // svg_enlargement.children[0].setAttribute( 'transform', $(this).data('transform_memo') );
434          // $('#enlargement').scrollLeft( $(this).data('scrollleft_memo') );
435          $(this).data('locked', false);
436          $(this).css('background-position', '0px 0px');
437      } else {
438          $(this).css('background-position', '0px 17px');
439          var y_min = parseInt( ellipses_in_magnifier[0].attr('cy') ) - parseInt( ellipses_in_magnifier[0].attr('ry') ); 
440          var y_max = parseInt( ellipses_in_magnifier[0].attr('cy') ) + parseInt( ellipses_in_magnifier[0].attr('ry') ); 
441          $.each( ellipses_in_magnifier, function( index, ellipse ) {
442              var ny_min = parseInt( ellipse.attr('cy') ) - parseInt( ellipse.attr('ry') ); 
443              var ny_max = parseInt( ellipse.attr('cy') ) + parseInt( ellipse.attr('ry') ); 
444              if( ny_min < y_min ) { y_min = ny_min }; 
445              if( ny_max > y_max ) { y_max = ny_max };
446              if( ellipse.data( 'node_obj' ) == null ) {
447                  ellipse.data( 'node_obj', new node_obj( ellipse ) );
448              } else {
449                  ellipse.data( 'node_obj' ).set_draggable( true );
450              }
451              ellipse.data( 'node_obj' ).greyout_edges();
452          })
453          // magnification on workspace lock temporarily disabled
454          // var graph_frag_height = y_max - y_min ;
455          // var svg_enlargement_vbheight = svg_enlargement.viewBox.baseVal.height;
456          // var svg_enlargement_vbwidth = svg_enlargement.viewBox.baseVal.width;
457          // var scale = svg_enlargement_vbheight / graph_frag_height;
458          // mousemove_enlargement_ratio = scroll_enlargement_ratio * scale;
459          // var scroll_padding = $('#enlargement_container').width();
460          // var scroll_scale =  svg_enlargement_vbwidth / ( parseFloat( $('#svgenlargement svg').attr('width') ) - scroll_padding );
461          // var vbx_of_scroll = ( $('#enlargement').scrollLeft() ) * scroll_scale;
462          // var translate_x = vbx_of_scroll;
463          // var transform = svg_enlargement.children[0].getAttribute('transform');
464          // $(this).data('transform_memo', transform );
465          // $(this).data('scrollleft_memo', $('#enlargement').scrollLeft() ); 
466          $(this).data('locked', true );
467          // $('#enlargement').scrollLeft(0);
468          // transform = 'scale(' + scale + ') translate(' + (-1 * translate_x) + ',' + (-1 * y_min) + ')';
469          // svg_enlargement.children[0].setAttribute( 'transform', transform );
470      }
471   });
472   
473 });
474
475 $(window).mouseout(function (event) {
476   if ($('#graph').data('down')) {
477     try {
478       if (event.originalTarget.nodeName == 'BODY' || event.originalTarget.nodeName == 'HTML') {
479         $('#graph').data('down', false);
480       }                
481     } catch (e) {}
482   }
483 });
484
485 function color_enlarged() {
486     ellipses_in_magnifier = [];
487     var scroll_offset = parseInt( $('#enlargement').scrollLeft() );
488     var scroll_padding = $('#enlargement_container').width()/2;
489     $('#svgenlargement ellipse,#svgbasics ellipse' ).each( function( index ) {
490         var cpos_inscrollcoor = parseInt( $(this).attr('cx') ) * scroll_enlargement_ratio;
491         if ( ( cpos_inscrollcoor > (scroll_offset - scroll_padding) ) && ( cpos_inscrollcoor < ( scroll_offset + scroll_padding ) ) ) {
492            $(this).attr( {stroke:'green', fill:'#b3f36d'} );
493            if( $(this).parents('#svgenlargement').size() == 1 ) { ellipses_in_magnifier.push( $(this) ) };
494         } else {
495            $(this).attr( {stroke:'black', fill:'#fff'} );
496         }
497     });   
498 }
499
500
501
502