X-Git-Url: http://git.shadowcat.co.uk/gitweb/gitweb.cgi?a=blobdiff_plain;f=root%2Fjs%2Frelationship.js;h=3c271c466060ad0bfd3fb2658381aba6be8db5af;hb=f2fb96fc32004e84c19df3cbf3d5eb00b99c11c7;hp=ed1b150f2998cfd7d16a7a5c45d7c05c3072f10c;hpb=05485bfd2130f51ff6625b247171891d085e7cc0;p=scpubgit%2Fstemmaweb.git diff --git a/root/js/relationship.js b/root/js/relationship.js index ed1b150..3c271c4 100644 --- a/root/js/relationship.js +++ b/root/js/relationship.js @@ -1,5 +1,12 @@ +var MARGIN=30; +var svg_root = null; +var svg_root_element = null; +var start_element_height = 0; +var reltypes = {}; +var readingdata = {}; + function getTextPath() { - var currpath = window.location.pathname + var currpath = window.location.pathname; // Get rid of trailing slash if( currpath.lastIndexOf('/') == currpath.length - 1 ) { currpath = currpath.slice( 0, currpath.length - 1) @@ -20,9 +27,19 @@ function getRelativePath() { return path_parts[0]; } -function getRelationshipURL() { +function getTextURL( which ) { + var path_parts = getTextPath(); + return path_parts[0] + '/' + path_parts[1] + '/' + which; +} + +function getReadingURL( reading_id ) { var path_parts = getTextPath(); - return path_parts[0] + '/' + path_parts[1] + '/relationships'; + return path_parts[0] + '/' + path_parts[1] + '/reading/' + reading_id; +} + +// Make an XML ID into a valid selector +function jq(myid) { + return '#' + myid.replace(/(:|\.)/g,'\\$1'); } function svgEnlargementLoaded() { @@ -37,11 +54,24 @@ function svgEnlargementLoaded() { { 'top': lo_height / 2 - $("#loading_message").height() / 2, 'left': lo_width / 2 - $("#loading_message").width() / 2 }); //Set viewbox widht and height to widht and height of $('#svgenlargement svg'). + $('#update_workspace_button').data('locked', false); + $('#update_workspace_button').css('background-position', '0px 44px'); //This is essential to make sure zooming and panning works properly. $('#svgenlargement ellipse').attr( {stroke:'green', fill:'#b3f36d'} ); + $('#svgenlargement ellipse').dblclick( node_dblclick_listener ); var graph_svg = $('#svgenlargement svg'); var svg_g = $('#svgenlargement svg g')[0]; + if (!svg_g) return; svg_root = graph_svg.svg().svg('get').root(); + + // Find the real root and ignore any text nodes + for (i = 0; i < svg_root.childNodes.length; ++i) { + if (svg_root.childNodes[i].nodeName != '#text') { + svg_root_element = svg_root.childNodes[i]; + break; + } + } + svg_root.viewBox.baseVal.width = graph_svg.attr( 'width' ); svg_root.viewBox.baseVal.height = graph_svg.attr( 'height' ); //Now set scale and translate so svg height is about 150px and vertically centered in viewbox. @@ -55,13 +85,13 @@ function svgEnlargementLoaded() { var transform = 'rotate(0) scale(' + scale + ') translate(4 ' + translate + ')'; svg_g.setAttribute('transform', transform); //used to calculate min and max zoom level: - start_element_height = $("#svgenlargement .node title:contains('START#')").siblings('ellipse')[0].getBBox().height; + start_element_height = $('#__START__ ellipse')[0].getBBox().height; add_relations( function() { $('#loading_overlay').hide(); }); } function add_relations( callback_fn ) { var basepath = getRelativePath(); - var textrelpath = getRelationshipURL(); + var textrelpath = getTextURL( 'relationships' ); $.getJSON( basepath + '/definitions', function(data) { var rel_types = data.types.sort(); $.getJSON( textrelpath, @@ -89,9 +119,7 @@ function add_relations( callback_fn ) { } function get_ellipse( node_id ) { - return $('#svgenlargement .node').children('title').filter( function(index) { - return $(this).text() == node_id; - }).siblings('ellipse'); + return $( jq( node_id ) + ' ellipse'); } function get_node_obj( node_id ) { @@ -102,12 +130,6 @@ function get_node_obj( node_id ) { return node_ellipse.data( 'node_obj' ); } -function get_edge( edge_id ) { - return $('#svgenlargement .edge').filter( function(index) { - return $(this).children( 'title' ).text() == $('
').html(edge_id).text() ; - }); -} - function node_obj(ellipse) { this.ellipse = ellipse; var self = this; @@ -119,7 +141,7 @@ function node_obj(ellipse) { this.node_elements = node_elements_for(self.ellipse); this.get_id = function() { - return self.ellipse.siblings('title').text() + return $(self.ellipse).parent().attr('id') } this.set_draggable = function( draggable ) { @@ -158,9 +180,9 @@ function node_obj(ellipse) { this.mouseup_listener = function(evt) { if( $('ellipse[fill="#ffccff"]').size() > 0 ) { - var source_node_id = self.ellipse.siblings('title').text(); + var source_node_id = $(self.ellipse).parent().attr('id'); var source_node_text = self.ellipse.siblings('text').text(); - var target_node_id = $('ellipse[fill="#ffccff"]').siblings("title").text(); + var target_node_id = $('ellipse[fill="#ffccff"]').parent().attr('id'); var target_node_text = $('ellipse[fill="#ffccff"]').siblings("text").text(); $('#source_node_id').val( source_node_id ); $('#source_node_text').val( source_node_text ); @@ -174,7 +196,7 @@ function node_obj(ellipse) { $(self.ellipse).parent().hover( self.enter_node, self.leave_node ); self.reset_elements(); } - + this.cpos = function() { return { x: self.ellipse.attr('cx'), y: self.ellipse.attr('cy') }; } @@ -280,7 +302,7 @@ function node_elements_for( ellipse ) { function get_edge_elements_for( ellipse ) { edge_elements = new Array(); - node_id = ellipse.siblings('title').text(); + node_id = ellipse.parent().attr('id'); edge_in_pattern = new RegExp( node_id + '$' ); edge_out_pattern = new RegExp( '^' + node_id ); $.each( $('#svgenlargement .edge,#svgenlargement .relation').children('title'), function(index) { @@ -301,6 +323,60 @@ function get_edge_elements_for( ellipse ) { return edge_elements; } +function node_dblclick_listener( evt ) { + // Open the reading dialogue for the given node. + // First get the reading info + var reading_id = $(this).attr('id'); + var reading_info = readingdata[reading_id]; + // and then populate the dialog box with it. + // Set the easy properties first + $('#reading-form').dialog( 'option', 'title', 'Reading information for "' + reading_info['text'] + '"' ); + $('#reading_id').val( reading_id ); + $('#reading_is_nonsense').val( reading_info['is_nonsense'] ); + $('#reading_grammar_invalid').val( reading_info['grammar_invalid'] ); + // Use .text as a backup for .normal_form + var normal_form = reading_info['normal_form']; + if( !normal_form ) { + normal_form = reading_info['text']; + } + $('#reading_normal_form').val( normal_form ); + // Now do the morphological properties. + // and then open the dialog. + var form_morph_elements = []; + $.each( reading_info['lexemes'], function( idx, lex ) { + var morphoptions = []; + $.each( lex['wordform_matchlist'], function( tdx, tag ) { + var tagstr = stringify_wordform( tag ); + morphoptions.push( tagstr ); + }); + var formtag = 'morphology_' + idx; + var formlabel = $('#label_morphology_0').clone(); + formlabel.attr('id', 'label_' + formtag ); + formlabel.text( lex['string'] + ': ' ); + var forminput = $('#reading_morphology_0').clone(); + forminput.attr('id', 'reading_' + formtag ); + forminput.attr('name', 'reading_' + formtag ); + forminput.autocomplete({ source: morphoptions, minLength: 0, disabled: false }); + //forminput.autocomplete('search', ''); + if( lex['form'] ) { + var formstr = stringify_wordform( lex['form'] ); + forminput.val( formstr ); + } else { + forminput.val( '' ); + } + form_morph_elements.push( formlabel, forminput, $('
') ); + }); + $('#morphology').empty(); + $.each( form_morph_elements, function( idx, el ) { + $('#morphology').append( el ); + }); + $('#reading-form').dialog("open"); +} + +function stringify_wordform ( tag ) { + return tag['lemma'] + ' // ' + tag['morphology']; +} + function relation_factory() { var self = this; this.color_memo = null; @@ -309,12 +385,8 @@ function relation_factory() { this.relation_colors = [ "#5CCCCC", "#67E667", "#F9FE72", "#6B90D4", "#FF7673", "#E467B3", "#AA67D5", "#8370D8", "#FFC173" ]; this.create_temporary = function( source_node_id, target_node_id ) { - var relation = $('#svgenlargement .relation').filter( function(index) { - var relation_id = $(this).children('title').text(); - if( ( relation_id == ( source_node_id + '->' + target_node_id ) ) || ( relation_id == ( target_node_id + '->' + source_node_id ) ) ) { - return true; - } - } ); + var relation_id = get_relation_id( source_node_id, target_node_id ); + var relation = $( jq( relation_id ) ); if( relation.size() == 0 ) { draw_relation( source_node_id, target_node_id, self.temp_color ); } else { @@ -342,16 +414,16 @@ function relation_factory() { return relation; } this.toggle_active = function( relation_id ) { - var relation = $("#svgenlargement .relation:has(title:contains('" + relation_id + "'))"); + var relation = $( jq( relation_id ) ); var relation_path = relation.children('path'); if( !relation.data( 'active' ) ) { relation_path.css( {'cursor':'pointer'} ); relation_path.mouseenter( function(event) { outerTimer = setTimeout( function() { timer = setTimeout( function() { - var title = relation.children('title').text(); - var source_node_id = title.substring( 0, title.indexOf( "->" ) ); - var target_node_id = title.substring( (title.indexOf( "->" ) + 2) ); + var related_nodes = get_related_nodes( relation_id ); + var source_node_id = related_nodes[0]; + var target_node_id = related_nodes[1]; $('#delete_source_node_id').val( source_node_id ); $('#delete_target_node_id').val( target_node_id ); self.showinfo(relation); @@ -384,7 +456,7 @@ function relation_factory() { var p = svg_root.createSVGPoint(); p.x = xs + ((xe-xs)*1.1); p.y = ye - ((ye-ys)/2); - var ctm = svg_root.children[0].getScreenCTM(); + var ctm = svg_root_element.getScreenCTM(); var nx = p.matrixTransform(ctm).x; var ny = p.matrixTransform(ctm).y; var dialog_aria = $ ("div[aria-labelledby='ui-dialog-title-delete-form']"); @@ -392,14 +464,28 @@ function relation_factory() { dialog_aria.offset({ left: nx, top: ny }); } this.remove = function( relation_id ) { - var relation = $("#svgenlargement .relation:has(title:contains('" + relation_id + "'))"); + var relation = $( jq( relation_id ) ); relation.remove(); } } +// Utility function to create/return the ID of a relation link between +// a source and target. +function get_relation_id( source_id, target_id ) { + var idlist = [ source_id, target_id ]; + idlist.sort(); + return 'relation-' + idlist[0] + '-...-' + idlist[1]; +} + +function get_related_nodes( relation_id ) { + var srctotarg = relation_id.substr( 9 ); + return srctotarg.split('-...-'); +} + function draw_relation( source_id, target_id, relation_color ) { var source_ellipse = get_ellipse( source_id ); var target_ellipse = get_ellipse( target_id ); + var relation_id = get_relation_id( source_id, target_id ); var svg = $('#svgenlargement').children('svg').svg().svg('get'); var path = svg.createPath(); var sx = parseInt( source_ellipse.attr('cx') ); @@ -407,7 +493,8 @@ 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'} ); + 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 relation_element = $('#svgenlargement .relation').filter( ':last' ); @@ -428,11 +515,13 @@ $(document).ready(function () { .data('x', event.clientX) .data('y', event.clientY) .data('scrollLeft', this.scrollLeft) - stateTf = svg_root.children[0].getCTM().inverse(); + 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; }).mouseup(function (event) { $(this).data('down', false); @@ -445,22 +534,28 @@ $(document).ready(function () { p = p.matrixTransform(stateTf); var matrix = stateTf.inverse().translate(p.x - stateOrigin.x, p.y - stateOrigin.y); var s = "matrix(" + matrix.a + "," + matrix.b + "," + matrix.c + "," + matrix.d + "," + matrix.e + "," + matrix.f + ")"; - svg_root.children[0].setAttribute("transform", s); + svg_root_element.setAttribute("transform", s); } + event.returnValue = false; + event.preventDefault(); }).mousewheel(function (event, delta) { event.returnValue = false; event.preventDefault(); if ( $('#update_workspace_button').data('locked') == false ) { + if (!delta || delta == null || delta == 0) delta = event.originalEvent.wheelDelta; + if (!delta || delta == null || delta == 0) delta = -1 * event.originalEvent.detail; if( delta < -9 ) { delta = -9 }; var z = 1 + delta/10; - var g = svg_root.children[0]; - if( (z<1 && (g.getScreenCTM().a * start_element_height) > 4.0) || (z>1 && (g.getScreenCTM().a * start_element_height) < 100) ) { + z = delta > 0 ? 1 : -1; + var g = svg_root_element; + if (g && ((z<1 && (g.getScreenCTM().a * start_element_height) > 4.0) || (z>=1 && (g.getScreenCTM().a * start_element_height) < 100))) { var root = svg_root; var p = root.createSVGPoint(); - p.x = event.clientX; - p.y = event.clientY; + p.x = event.originalEvent.clientX; + p.y = event.originalEvent.clientY; p = p.matrixTransform(g.getCTM().inverse()); - var k = root.createSVGMatrix().translate(p.x, p.y).scale(z).translate(-p.x, -p.y); + var scaleLevel = 1+(z/20); + var k = root.createSVGMatrix().translate(p.x, p.y).scale(scaleLevel).translate(-p.x, -p.y); var matrix = g.getCTM().multiply(k); var s = "matrix(" + matrix.a + "," + matrix.b + "," + matrix.c + "," + matrix.d + "," + matrix.e + "," + matrix.f + ")"; g.setAttribute("transform", s); @@ -471,6 +566,10 @@ $(document).ready(function () { 'cursor' : '-moz-grab' }); + var rdgpath = getTextURL( 'readings' ); + $.getJSON( rdgpath, function( data ) { + readingdata = data; + }); $( "#dialog-form" ).dialog({ autoOpen: false, @@ -480,16 +579,20 @@ $(document).ready(function () { buttons: { "Ok": function() { $('#status').empty(); - form_values = $('#collapse_node_form').serialize() - ncpath = getRelationshipURL(); + form_values = $('#collapse_node_form').serialize(); + ncpath = getTextURL( 'relationships' ); $(':button :contains("Ok")').attr("disabled", true); var jqjson = $.post( ncpath, form_values, function(data) { $.each( data, function(item, source_target) { - var relation = relation_manager.create( source_target[0], source_target[1], $('#rel_type').attr('selectedIndex') ); - relation.data( 'type', $('#rel_type :selected').text() ); - relation.data( 'scope', $('#scope :selected').text() ); - relation.data( 'note', $('#note').val() ); - relation_manager.toggle_active( relation.children('title').text() ); + 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() ); + relation_manager.toggle_active( relation.attr('id') ); + } }); $( "#dialog-form" ).dialog( "close" ); }, 'json' ); @@ -505,12 +608,12 @@ $(document).ready(function () { var jqjson = $.getJSON( basepath + '/definitions', function(data) { var types = data.types.sort(); $.each( types, function(index, value) { - $('#rel_type').append( $('