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