var SPARQLBuilder = function(result,endpoint) { this.result = result; this.builder = this.createBuilder('http://www.sparqlbuilder.org/forms/guiform.php'); this.showBuilder(); this.drawGraph = null; this.startClass = null; this.endClass = null; this.endPoint = endpoint; }; SPARQLBuilder.prototype.createBuilder = function(form) { var builder = $('
'); var content = $('
'); builder.prepend(content); var self = this; $.ajax({ type : "GET", url : form, async : false, success : function(data) { content.prepend($(data)); } }); $("body").prepend(builder); return builder; }; SPARQLBuilder.prototype.showBuilder = function() { var width = $(document).width(); var height = $(document).height(); this.builder.css("width", width); this.builder.css("height", height); this.builder.css("display", "block"); document.getElementById("sparqlBuilderAjaxload").style.width = width; document.getElementById("sparqlBuilderAjaxload").style.height = height; this.loadIcon("hide"); var self = this; this.loadEndPointList(); $("#EndPointSelect").change(function() { self.changeEndPoint(); }); //$("#SetClassButton").click(function() { // self.startClass = $("#StartClassSelect").val(); // self.endClass = $("#EndClassSelect").val(); // self.loadPathList(); //}); $("#StartClassSelect").change(function() { self.startClass = $("#StartClassSelect").val(); self.loadPathList(); }); $("#EndClassSelect").change(function() { self.endClass = $("#EndClassSelect").val(); self.loadPathList(); }); $("#sparqlBuilderViewall").click(function() { self.drawGraph.setPathLimit(0); self.drawGraph.view_map(); }); $("#GenerateSPARQLButton").click(function() { var sparql = self.generateSPARQL(self.drawGraph.pathobj); }); $("#SparqlBuilderCancel").click(function() { self.hideBuilder(); }); }; SPARQLBuilder.prototype.loadIcon = function(mode) { if(mode == "view"){ document.getElementById("sparqlBuilderAjaxload").style.display = "block"; }else{ document.getElementById("sparqlBuilderAjaxload").style.display = "none"; } }; SPARQLBuilder.prototype.loadEndPointList = function() { var url = 'http://www.sparqlbuilder.org/api/eplist'; $.ajax({ url: url, success: function(data) { var list = eval(data); var event = new $.Event('complete'); $("#EndPointSelect").empty(); $("#EndPointSelect").append(''); for (var i = 0; i < list.length; ++i) { $("#EndPointSelect").append(''); } $("#seclass").trigger(new $.Event('epcomplete')); }, }); }; SPARQLBuilder.prototype.loadClassList = function() { var url = "http://www.sparqlbuilder.org/api/clist?ep=" + encodeURIComponent(this.endpoint); //var url = "http://localhost:8080/api/clist?ep=" + encodeURIComponent(this.endpoint); $.ajax({ type : "GET", url : url, async : false, success : function(data) { var list = eval(data); var event = new $.Event('complete'); $("#StartClassSelect").empty(); $("#EndClassSelect").empty(); for (var i = 0; i < list.length; ++i) { //$("#StartClassSelect").append(''); //$("#EndClassSelect").append(''); $("#StartClassSelect").append(''); $("#EndClassSelect").append(''); } $("#seclass").trigger(new $.Event('secomplete')); } }); }; SPARQLBuilder.prototype.loadSamplePathList = function() { var url = "http://www.sparqlbuilder.org/api/plist?ep=" + encodeURIComponent('http://www.ebi.ac.uk/rdf/services/reactome/sparql') + "&startclass=" + encodeURIComponent('http://www.biopax.org/release/biopax-level3.owl#Protein') + "&endclass=" + encodeURIComponent('http://www.biopax.org/release/biopax-level3.owl#Pathway'); //var url = "http://localhost:8080/api/plist?ep=" + encodeURIComponent(this.endpoint) // + "&startclass=" + encodeURIComponent(startclass) // + "&endclass=" + encodeURIComponent(endclass); var self = this; self.loadIcon("view"); setTimeout(function(){ $.ajax({ type : "GET", url : url, async : false, timeout : 1000000, success : function(data) { var width = $(".SparqlBuilderContent").width(); self.drawGraph = new SPARQLBuilderDrawGraph(data, width, 10); self.loadIcon("hide"); self.drawGraph.view_map(); $('select[name="selectendpoint"]').val('http://www.ebi.ac.uk/rdf/services/reactome/sparql'); }, error: function(data){ self.loadIcon("hide"); alert("error: ", data); } }); }, 100) }; SPARQLBuilder.prototype.loadPathList = function() { if (this.startClass == null || this.endClass == null ){ return; } var startclass = $("#StartClassSelect").val(); var endclass = $("#EndClassSelect").val(); var url = "http://www.sparqlbuilder.org/api/plist?ep=" + encodeURIComponent(this.endpoint) + "&startclass=" + encodeURIComponent(startclass) + "&endclass=" + encodeURIComponent(endclass); //var url = "http://localhost:8080/api/plist?ep=" + encodeURIComponent(this.endpoint) // + "&startclass=" + encodeURIComponent(startclass) // + "&endclass=" + encodeURIComponent(endclass); var self = this; self.loadIcon("view"); setTimeout(function(){ $.ajax({ type : "GET", url : url, async : false, timeout : 1000000, success : function(data) { var width = $(".SparqlBuilderContent").width(); self.drawGraph = new SPARQLBuilderDrawGraph(data, width, 10); self.loadIcon("hide"); self.drawGraph.view_map(); }, error: function(data){ self.loadIcon("hide"); alert("error: ", data); } }); }, 100) }; SPARQLBuilder.prototype.changeEndPoint = function() { this.endpoint = $("#EndPointSelect").val(); this.loadClassList(); }; SPARQLBuilder.prototype.hideBuilder = function() { this.builder.hide(); }; SPARQLBuilder.prototype.generateSPARQL = function(pathobj) { var path = JSON.stringify(pathobj); var url = 'http://www.sparqlbuilder.org/api/sparql?path=' + encodeURIComponent(path); var sparql = ''; var self = this; $.ajax({ type: "GET", url : url, dataType: 'text', async: false, success : function(data) { var event = new $.Event('write'); $("#" + self.result).val(data); $("#" + self.endPoint).val(self.endpoint); self.hideBuilder(); $("#" + self.result).trigger(event, [data, pathobj]); } }); }; var SPARQLBuilderDrawGraph = function(jsontext, width, pathlimit) { this.jsontext = jsontext; this.width = width; this.pathlimit = pathlimit; this.pathobj = null; }; SPARQLBuilderDrawGraph.prototype.setPathLimit = function(pathlimit) { this.pathlimit = pathlimit; }; SPARQLBuilderDrawGraph.prototype.view_map = function(){ // make_dataメソッドの結果を取得 var json = this.make_data(0); if(json['nodes'].length != 0){ // 出来上がった結果を渡してマップ上のロケーションをセット this.set_map_location(0, json['nodes'], json['links']); // SVGの幅と高さを設定(幅:画面いっぱい 高さ:パスの数に応じ設定) var width = this.width; var height = ((this.NODEHEIGHT * 1.5) * this.PATHNUM) + (this.NODEHEIGHT / 2); // カラーを取得 var color = d3.scale.category20(); // SVGの削除 d3.select("#sparqlBuilderGraph").html(""); // 画面サイズに合わせSVGの追加 var svg = d3.select("#sparqlBuilderGraph").append("svg") .attr("width", width) .attr("height", height); // 背景の追加 var bg = svg .append("rect") .attr("x", 0) .attr("y", 0) .attr("width", width) .attr("height", height) .attr("fill", "#fafafa"); // links配列を渡しリンクの作成 var link = svg.selectAll(".link") .data(json.links) .enter().append("line") .attr("class", "link") .style("stroke", "#999") .style("stroke-opacity", 0.6) .style("stroke-width", function(d) { return Math.sqrt(d.value);}); // nodes配列を渡しノードの作成 var node = svg.selectAll(".node") .data(json.nodes) .enter().append("circle") .attr("class", "node") .attr("r", (this.NODEHEIGHT / 2)) .attr("cx", function(d) { return d.x;} ) .attr("cy", function(d) { return d.y; }) .style("stroke", function(d) { return '#fafafa'; }) .style("stroke-width", function(d) { return '1.5px'; }) .style("fill", function(d) { return d.nodecolor; }) .style("cursor", function(d) { return 'pointer'; }); // nodes配列を渡しノードテキストの作成 var tnode = svg.selectAll("text.node") .data(json.nodes) .enter().append("svg:text") .attr("class", "tnode") .attr("x", function(d) { return d.x; }) .attr("y", function(d) { return d.y; }) .text(function(d) { return d.name; }) .style("fill", function(d) { return '#000000'; }) .style("text-anchor", function(d) { return 'middle'; }) .style("pointer-events", "none"); // リンクテキストの作成 var tlink = svg.selectAll("text.link") .data(json.links) .enter().append("svg:text") .attr("class", "tlink") .attr("x", function(d) { return (json.nodes[d.source].x + json.nodes[d.target].x) / 2; }) .attr("y", function(d) { return (json.nodes[d.source].y + json.nodes[d.target].y) / 2; }) .style("fill", function(d) { return '#000000'; }) .style("text-anchor", function(d) { return 'middle'; }); // マウスの動きカウントをルートに持たせる node.data()[0].mousemoved = 0; // ノードへのオンマウスでパス探索、パス中のリンク文字を表示 var self = this; node.on("mouseover", function(d){ // マウスの動きカウントをリセット node.data()[0].mousemoved = 0; // 表示するパス保存用配列 var path = []; // ツールチップへの名前表示用配列 var pathname = []; // ルートノード以外なら if(d.nodeid != 0){ // まずオンマウスされたノードのidと名前をそれぞれ追加 path.push(d.nodeid); pathname.push(d.name); // パス探索 do{ // リンクの数だけ繰り返し for(var i = 0; i < link.data().length; i++){ // 現在の最後尾に繋がるリンクがあれば if(path[(path.length-1)] == link.data()[i].target){ // そのリンクのソース側ノードのidを追加 path.push(link.data()[i].source); // そのリンクの名前とソース側ノードの名前を追加 pathname.push(link.data()[i].property); pathname.push(node.data()[link.data()[i].source].name); } } // ルートノードに辿り着くまで繰り返す }while(path[(path.length-1)] != 0); // 末端ノードでないなら if(d.path == "notend"){ // ツールチップを非表示に document.getElementById("sparqlBuilderShowpath").style.display = "none"; }else{ // 末端ノードならツールチップの情報を更新 var resultText = "

Selected Path

"; // パスの名前配列分後ろから繰り返しながら for (var i = pathname.length;i > 0; i--){ // 奇数番目(ノードの名前)は太字に if(i % 2 == 1){ resultText = resultText + "" + pathname[i - 1] + "

"; // 偶数番目(リンクの名前)はそのままで表示 }else{ resultText = resultText + pathname[i - 1] + "

"; } } // ツールチップの内容を書き換え document.getElementById("sparqlBuilderSelectpath").innerHTML=(resultText); document.getElementById("sparqlBuilderShowpath").style.display = "block"; // サーブレットに送り返すパスオブジェクトを保存 self.pathobj = d.path; // ツールチップ表示時の座標(オンマウスされたノードの横にボタンが来るよう配置) var xPosition = parseFloat(d3.select(this).attr("cx")) + parseFloat(d3.select(this).style("stroke-width")) + (self.NODEHEIGHT * 0.5); var yPosition = parseFloat(d3.select(this).attr("cy") - document.getElementById("sparqlBuilderShowpath").offsetHeight + 50 + document.getElementById("sparqlBuilderSetting").offsetHeight) + (self.NODEHEIGHT * 0.5); // ツールチップが画面外に出ないよう補正 if(xPosition < 0){ xPosition = 0; } if(yPosition < 0){ yPosition = 0; } // 生成した座標にツールチップを表示 document.getElementById("sparqlBuilderShowpath").style.left = xPosition + "px" document.getElementById("sparqlBuilderShowpath").style.top = yPosition + "px" } // ルートノードだったなら }else{ // ツールチップを非表示 document.getElementById("sparqlBuilderShowpath").style.display = "none"; } // オンマウスされたノードの高さに親を合わせるために合わせる高さを保存 var movey = d.y; // 各ノードに対し node // 輪郭線の色を設定 .style("stroke", function(d){ // まずは背景色(デフォルト)を指定 var strokecolor = "#fafafa"; // パス判定の前処理 // 表示フラグがnow(前回オンマウスで動いていたノード)なら if(d.view == "now"){ // ノードの表示フラグをnoに d.view = "no"; } // 表示フラグがmoved(クリックされ固定済みだが前回動いていたノード)なら if(d.view == "moved"){ // 表示フラグをclickedに戻す d.view = "clicked"; } // 表示フラグがnoのものから確認 if(d.view == "no"){ // パスのノード数だけ繰り返しながら for(var n = 0; n < path.length; n++){ // パス内に含まれるノードだったら if(path[n] == d.nodeid){ // 輪郭線を赤に strokecolor = "#ffaaaa"; // 表示フラグをnow(今回動いたノード)に d.view = "now"; } } // 固定済みノードだったら }else if(d.view == "clicked"){ // まずは輪郭線を赤に strokecolor = "#ffaaaa"; // パス内に含まれるノードかチェック for(var n = 0; n < path.length; n++){ if(path[n] == d.nodeid){ // 含まれていたなら今回動かすためフラグをmovedに d.view = "moved"; } } } // ここまでで得られた輪郭線の色を返す return strokecolor; }) // 高さの値 .attr("cy", function(d){ // ノードが今回オンマウスされた・または固定済みだが移動フラグをつけられていれば if(d.view == "now" || d.view == "moved"){ // 現在の高さを取得 var curty = d.y; // d.yに子ノードの高さをセット d.y = movey; // 現在の高さを返す(この時点では現在位置に描画され、redraw関数でd.yにアニメーションされる) return curty; // 移動対象でないなら }else{ // 現在位置をそのまま返す return d.y; } }); // 各リンクテキストに対し tlink // テキスト表示判定 .text(function(d) { // デフォルトで空をセット var linktext = ""; // 表示フラグがnow(前回表示されていたリンク)なら if(d.view == "now"){ // 表示フラグを解除 d.view = "no"; } // 表示フラグがnoなら if(d.view == "no"){ // パスのノード数分繰り返し for(var t = 0; t < path.length; t++){ // 自身がそのノードへ接続しているリンク(かつそのノードが折りたたまれていなければ) if(path[t] == d.target && node.data()[d.target].view != "hide"){ // リンクテキストにプロパティの値をセット linktext = d.property // 表示フラグにnowをセット d.view = "now"; } } // 表示フラグがfix(クリックされたパスのリンク)ならば(かつ繋がる先のノードが折りたたまれていなければ) }else if(d.view == "fix" && node.data()[d.target].view != "hide"){ // リンクテキストにプロパティの値をセット linktext = d.property } // ここまででできたリンクテキストを返す return linktext; }); // 各リンクに対し link // 線の色判定 .style("stroke", function(d){ // 表示フラグがnoならば if(d.view == "no"){ // 色をデフォルトに return "#999"; // それ以外(固定やオンマウスされたパスに含まれる)なら }else{ // 色を赤に return "#ffaaaa"; } }); // ここまでの設定を元に再描画 redraw(); // ノードへのクリックで選択固定化(及び折り畳み処理) }).on("click", function(d){ // 各ノードに対し node // 輪郭線の判定 .style("stroke", function(d) { // デフォルトの色をセット var strokecolor = "#fafafa" // 表示フラグがオンマウス中・固定中・移動中(選択されているノード)ならば if(d.view == "now" || d.view == "clicked" || d.view == "moved"){ // 色を赤に strokecolor = "#ffaaaa" // 表示フラグを固定中に d.view = "clicked"; } // ここまででできた色を返す return strokecolor; }); // ここから子ノードの畳み込み処理(現在は廃止、念のためコードは残しておく) /* var childs = []; var prevchilds = []; prevchilds.push(d.nodeid); // do{ var tmpchilds = []; var curchilds = []; for(var p = 0; p < prevchilds.length; p++){ tmpchilds = this.get_children(prevchilds[p], json['links']); curchilds = curchilds.concat(tmpchilds); } prevchilds = curchilds; childs = childs.concat(curchilds); }while(curchilds.length != 0); var childy = d.y; for(var c = 0; c < childs.length; c++){ if(node.data()[childs[c]].y < childy){ childy = node.data()[childs[c]].y; } } d.y = childy; var maxdy = 0; var mindy = 0; for(var c = 0; c < childs.length; c++){ if(node.data()[childs[c]].view != "hide"){ var dy = node.data()[childs[c]].y - d.y; if(dy > maxdy){ maxdy = dy; } node.data()[childs[c]].x = d.x; node.data()[childs[c]].y = d.y; node.data()[childs[c]].dy = dy; node.data()[childs[c]].view = "hide"; node.data()[childs[c]].hideparent = d.nodeid; }else{ if(node.data()[childs[c]].hideparent == d.nodeid){ node.data()[childs[c]].x = d.x + (this.TREESPACE * (node.data()[childs[c]].group - d.group)); node.data()[childs[c]].y = node.data()[childs[c]].y + node.data()[childs[c]].dy; if(-node.data()[childs[c]].dy < mindy){ mindy = -node.data()[childs[c]].dy; } node.data()[childs[c]].view = "appear"; node.data()[childs[c]].hideparent = -1; } } } node .attr("r", function(d){ if(d.view == "appear"){ d.view = "no"; }else if(d.y > childy){ d.y = d.y - maxdy - mindy; } if(d.view == "hide"){ d.x = node.data()[d.hideparent].x; d.y = node.data()[d.hideparent].y; } if(d.nodeid == 0){ d.y = childy; } return (this.NODEHEIGHT / 2); }); */ // 各リンクに対し tlink // テキスト表示判定 .text(function(d) { // デフォルトで空に var linktext = ""; // 表示フラグが現在表示中または固定化済みならば(かつ折り畳み中でなければ) if((d.view == "now" || d.view == "fix") && (node.data()[d.target].view != "hide")){ // リンクテキストにプロパティの値をセット linktext = d.property // 表示フラグを固定中に d.view = "fix"; } // テキストを返す return linktext; }); // ここまでの処理結果を元に再描画 redraw(); }); // 再描画関数 var redraw = function (duration){ // かける時間が未指定ならば if(duration == undefined){ // 0.5秒かけてアニメーション duration = 500; } // 各リンクについて設定された位置に再描画 link .transition() .duration(duration) .attr("x1", function(d) {return node.data()[d.source].x;}) .attr("y1", function(d) {return node.data()[d.source].y;}) .attr("x2", function(d) {return node.data()[d.target].x;}) .attr("y2", function(d) {return node.data()[d.target].y;}); // 各リンクテキストについて設定された位置に再描画 tlink .transition() .duration(duration) .attr("x", function(d) {return (node.data()[d.source].x + node.data()[d.target].x) / 2;}) .attr("y", function(d) {return ((node.data()[d.source].y + node.data()[d.target].y) / 2) + 5;}); // 各ノードについて設定された位置に再描画(かつ折りたたまれ中の場合の描画分岐処理) node .transition() .duration(duration) .attr("cx", function(d) {return d.x;}) .attr("cy", function(d) {return d.y;}) .style("opacity", function(d){ var opa = 1.0; if(d.view == "hide"){ opa = 0.0; } return opa; }) .style("fill", function(d) { var fcolor = d.nodecolor; for(var n = 0; n < node.data().length; n++){ if(d.nodeid == node.data()[n].hideparent){ fcolor = "ffaaaa"; } } return fcolor; }) .style("pointer-events", function(d){ var pe = "auto"; if(d.view == "hide"){ pe = "none"; } return pe; }); // 各ノードテキストについて設定された位置に再描画、テキスト描画位置を上下に振る(かつ折りたたまれ中の場合の描画分岐処理) tnode .transition() .duration(duration) .attr("x", function(d) {return d.x;}) .attr("y", function(d) { // デフォルトで少し下げる var updown = (self.NODEHEIGHT * 0.4); // 奇数番目の深さなら少し上げる if(d.group % 2 == 1){ updown = -(self.NODEHEIGHT * 0.2); } // その値を高さに返すことでテキスト描画位置が互い違いになる return d.y + updown; }) // 折り畳み状態ならテキスト表示を消す .text(function(d){ var nodetext = d.name if(d.view == "hide"){ nodetext = ""; } return nodetext; }); }; // 背景部分がクリックされたら表示の固定化を解除 bg.on("click", function() { // ツールチップを非表示 document.getElementById("sparqlBuilderShowpath").style.display = "none"; d3.selectAll(".node").style("stroke-width", function(d) { return '1.5px'; }); d3.selectAll(".node").style("stroke", function(d) { return '#ffffff'; }); // 各ノードの輪郭線の色をデフォルトに node .style("stroke", function(d){ if(d.view != "hide"){ d.view = "no"; } return "#fafafa"; }); // リンクテキストを全て空に tlink .text(function(d) { d.view = "no"; return ""; }); // リンクの色を全てデフォルトに link .style("stroke", function(d){ return "#999"; }); }); // 背景上でマウスが動くごとに bg.on("mousemove", function(){ // MOUSEMOVEDを追加(ノードにオンマウスされる度にカウントリセット) node.data()[0].mousemoved++; // 30を超えたら if(node.data()[0].mousemoved > 30){ // ツールチップを非表示にしてカウントリセット document.getElementById("sparqlBuilderShowpath").style.display = "none"; node.data()[0].mousemoved = 0; } }); // 初回のみdurationを0と指定し再描画(アニメーションなし) redraw(0); } }; // データの作成メソッド SPARQLBuilderDrawGraph.prototype.make_data = function(tdepth, ret, parent, depth){ // retが未定義ならば定義して代入 if (ret == undefined){ ret = new Object(); ret['nodes'] = new Array(); ret['links'] = new Array(); } this.PATHNUM = 0; this.MAXDEPTH = 0; this.TREESPACE = 0; this.NODEHEIGHT = 50; this.DRAWHEIGHT = this.NODEHEIGHT; var viewnum; var obj = this.jsontext; document.getElementById("sparqlBuilderResultmessage").style.color = "black"; document.getElementById("sparqlBuilderResultmessage").style.fontWeight = "normal"; document.getElementById("sparqlBuilderPlural").innerHTML = "s"; if(obj.length == 0){ document.getElementById("sparqlBuilderResultmessage").style.color = "red"; document.getElementById("sparqlBuilderResultmessage").style.fontWeight = "bold"; document.getElementById("sparqlBuilderPlural").innerHTML = ""; }else if(obj.length == 1){ document.getElementById("sparqlBuilderPlural").innerHTML = ""; } if(obj.length <= 10){ viewnum = obj.length; document.getElementById("sparqlBuilderViewall").style.display = "none"; }else if(this.pathlimit == 10){ viewnum = 10; document.getElementById("sparqlBuilderViewall").style.display = "block"; }else{ viewnum = obj.length; document.getElementById("sparqlBuilderViewall").style.display = "none"; } document.getElementById("sparqlBuilderPathnum").innerHTML = obj.length; document.getElementById("sparqlBuilderResultmessage").style.display = "block"; // objトップ階層の数だけ繰り返しながら for(var i = 0; i < viewnum; i++){ if(i == 0){ // 初回だけルートノードをプッシュ ret['nodes'].push({'name': obj[0]['label'], 'uri': obj[0]['startClass'], 'group': 0, 'x':50, 'y':50, 'nodeid':ret['nodes'].length, 'view' : 'no', 'path': 'notend', 'nodecolor': '#d0a36a'}); } // 先にsourceに0(ルート)を代入 var source = 0; // 共通ルート判定をtrueに var isCommon = true; // classLinksの数だけ繰り返しながら for(var j = 0;j < obj[i]['classLinks'].length; j++){ // リンクの名前をURL末尾から取得 var propertytext = obj[i]['classLinks'][j]['predicate']; var propertysplit1 = propertytext.split("/"); var propertysplit2 = propertysplit1[propertysplit1.length - 1]; var propertysplit3 = propertysplit2.split("#"); propertytext = propertysplit3[propertysplit3.length - 1]; if(this.MAXDEPTH < j+1){ this.MAXDEPTH = j+1; } // ここまで共通ルートなら if(isCommon){ // 今回も共通か確認するためのフラグ var isCommonNow = false; // nodes配列に同じlinkedClassが既にあるか確認 var targets = []; for(var k = 0; k < ret['nodes'].length; k++){ // 同階層かつ同じ名前のものがあったらtargets配列に番号を追加 if(ret['nodes'][k]['group'] == (j+1) && obj[i]['classLinks'][j]['linkedClass'] == ret['nodes'][k]['uri']){ targets.push(k); } } // 既にあった場合はlinks配列に同じlinkが存在するか確認 if(targets.length != 0){ // 先ほど見つけたtargetsの数だけ繰り返しながら for(var l = 0; l