Merge branch 'master' of github.com:tla/stemmatology
[scpubgit/stemmatology.git] / stemmaweb / root / js / relationship.js
CommitLineData
7fd1d97a 1function 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;
581aee24 11}
12
13function 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
27function 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 );
7fd1d97a 38 $('ellipse').attr({
39 stroke: 'black',
40 fill: '#fff'
41 });
581aee24 42 var svg_height = parseInt( $('#svgenlargement').height() );
43 scroll_enlargement_ratio = svg_height/svg_vbheight;
7fd1d97a 44 add_relations();
45}
46
47function 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 ) {
7fd1d97a 57 relation_manager.create( rel_info.source, rel_info.target, type_index );
7fd1d97a 58 }
59 })
60 });
61 });
581aee24 62}
63
64function get_ellipse( node_id ) {
65 return $('#svgenlargement .node').children('title').filter( function(index) {
66 return $(this).text() == node_id;
67 }).siblings('ellipse');
68}
69
70function get_node_obj( node_id ) {
71 return get_ellipse( node_id ).data( 'node_obj' );
72}
73
74function 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
80function 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) {
af9c16ef 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;
581aee24 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
188function 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
208function 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
235function 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
242function 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
265function 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 );
7fd1d97a 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() };
581aee24 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
310function 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()
7fd1d97a 375 pathparts = getTextPath();
376 ncpath = pathparts[0] + '/' + pathparts[1] + '/relationship';
581aee24 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?
7fd1d97a 393 var pathparts = getTextPath();
394 var jqjson = $.getJSON( pathparts[0] + '/definitions', function(data) {
581aee24 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 })
af9c16ef 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') );
581aee24 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 })
af9c16ef 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() );
581aee24 466 $(this).data('locked', true );
af9c16ef 467 // $('#enlargement').scrollLeft(0);
468 // transform = 'scale(' + scale + ') translate(' + (-1 * translate_x) + ',' + (-1 * y_min) + ')';
469 // svg_enlargement.children[0].setAttribute( 'transform', transform );
581aee24 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
485function 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