var PATHNUM = 0; var MAXDEPTH = 0; var TREESPACE = 0; var NODEHEIGHT = 0; var DRAWHEIGHT = 0; var MOUSEMOVED = 0; var endpoint = ""; var startclass= ""; var endclass = ""; var defendpoint = ""; var defstartclass= ""; var defendclass = ""; var jsontext = ""; var pathobj = ""; var pathlimit = 10; // ページ読み込みが完了次第実行 $(function(){ // ページのパーツ部分を追加 initparts(); getParameter(); if(defendpoint != "" && defstartclass != "" && defendclass != ""){ openSPARQLBuilder(); } }); function initparts(){ var sbdiv = $('div#SPARQLBUILDER'); if(sbdiv.find('div').length == 0){ var sbtop = '


'; var sbmodal = '
Path found.
'; sbdiv.html(sbtop); $('body').append(sbmodal); } $('.SBModalView').click(function(){ $(this).fadeOut(700); }); $('.SBModalContents').click(function(){ event.stopPropagation(); }); loadEndPointList(); $(".SBEndPointSelect").change(function() { changeEndPoint(); }); $(".SBStartClassSelect").change(function() { startClass = $(".SBStartClassSelect").val(); loadPathList(); }); $(".SBEndClassSelect").change(function() { endClass = $(".SBEndClassSelect").val(); loadPathList(); }); } function getParameter() { if( 1 < window.location.search.length ) { var query = window.location.search.substring( 1 ); var parameters = query.split( '&' ); for( var i = 0; i < parameters.length; i++ ) { var element = parameters[ i ].split( '=' ); if(decodeURIComponent( element[ 0 ] ) == "ep"){ defendpoint = decodeURIComponent( element[ 1 ] ) }else if(decodeURIComponent( element[ 0 ] ) == "st"){ defstartclass = decodeURIComponent( element[ 1 ] ) }else if(decodeURIComponent( element[ 0 ] ) == "en"){ defendclass = decodeURIComponent( element[ 1 ] ) } } } } function openSPARQLBuilder(){ $('.SBModalView').css('top', $(window).scrollTop()).css('height', window.innerHeight).fadeIn(); var mvw = $('.SBModalContents').width(); var mvh = $('.SBModalContents').height(); $('.SBModalContents .SBSelects').css('width', (mvw - 201) + 'px').css('height', 30 + 'px'); $('.SBModalContents .SBMessage').css('width', 200 + 'px').css('height', 30 + 'px'); $('.SBModalContents .SBGraph').css('width', (mvw - 201) + 'px').css('height', (mvh - 31) + 'px'); $('.SBModalContents .SBSelectedPath').css('width', 180 + 'px').css('height', (mvh - 51 - 26) + 'px'); $('.SBModalContents .SBModalButtons').css('width', 200 + 'px').css('height', '26px'); $(".SBEndPointSelect").select2(); $(".SBStartClassSelect").select2(); $(".SBEndClassSelect").select2(); if(defendpoint != "" && defstartclass != "" && defendclass != ""){ $('.SBStartClassSelect').on('lccomplete', function(){ $('.SBStartClassSelect').val(defstartclass); defstartclass = ""; $('.SBEndClassSelect').val(defendclass); defendclass = ""; $(".SBEndPointSelect").select2(); $(".SBStartClassSelect").select2(); $(".SBEndClassSelect").select2(); $('.SBStartClassSelect').unbind('lccomplete'); }); loadPathList(); var eplist = $('.SBEndPointSelect option'); if(eplist.length == 0){ $('.SBEndPointSelect').on('epcomplete', function(){ $('.SBEndPointSelect').val(defendpoint); defendpoint = ""; loadClassList(); $('.SBEndPointSelect').unbind('epcomplete'); }); }else{ $('.SBEndPointSelect').val(defendpoint); defendpoint = ""; loadClassList(); } } d3.select('.SBModalView').on("mousewheel", function(){ event.preventDefault(); }); } function openSample(ep, st, en){ defendpoint = ep; defstartclass = st; defendclass = en; openSPARQLBuilder(); } function openPermalink(){ var baseurl = location.href; var spliturl = baseurl.split('?'); var url = spliturl[0] + "?ep=" + encodeURIComponent(endpoint) + "&st=" + encodeURIComponent(startclass) + "&en=" + encodeURIComponent(endclass); window.open(url); } function closeSPARQLBuilder(){ $('.SBModalView').fadeOut(); } function switchLoadIcon(mode) { if(mode == "view"){ $('.SBAjaxLoad').show(); }else{ $('.SBAjaxLoad').hide(); } }; function loadEndPointList(){ $('.SBSaveESE').attr('disabled', true); var url = 'http://www.sparqlbuilder.org/api/eplist'; $.ajax({ url: url, success: function(data) { var list = eval(data); $(".SBEndPointSelect").empty(); $(".SBEndPointSelect").append(''); for (var i = 0; i < list.length; ++i) { $(".SBEndPointSelect").append(''); switchLoadIcon("hide"); if($('.SBModalView').attr('display') == 'block'){ $(".SBEndPointSelect").select2(); $(".SBStartClassSelect").select2(); $(".SBEndClassSelect").select2(); } } $(".SBEndPointSelect").trigger(new $.Event('epcomplete')); }, }); } changeEndPoint = function() { endpoint = $(".SBEndPointSelect").val(); if(endpoint != "SBDefault"){ loadClassList(); } }; loadClassList = function() { $('.SBSaveESE').attr('disabled', true); var url = "http://www.sparqlbuilder.org/api/clist?ep=" + encodeURIComponent(endpoint); $.ajax({ type : "GET", url : url, async : false, success : function(data) { var list = eval(data); $(".SBStartClassSelect").empty(); $(".SBEndClassSelect").empty(); $(".SBStartClassSelect").append(''); $(".SBEndClassSelect").append(''); for (var i = 0; i < list.length; ++i) { $(".SBStartClassSelect").append(''); $(".SBEndClassSelect").append(''); } $(".SBStartClassSelect").select2(); $(".SBEndClassSelect").select2(); $(".SBStartClassSelect").trigger(new $.Event('lccomplete')); } }); }; loadPathList = function() { startclass = $(".SBStartClassSelect").val(); endclass = $(".SBEndClassSelect").val(); if(defendpoint != "" && defstartclass != "" && defendclass != ""){ endpoint = defendpoint; startclass = defstartclass; endclass = defendclass; } $('.SBSaveESE').attr('disabled', true); if (startclass == null || endclass == null || startclass == "SBDefault" || endclass == "SBDefault"){ return; } pathlimit = 10; $('.SBResult').hide(); $('.SBViewAll').hide(); $('.SBSelectedPath').html('

Please select a leaf node and push button to generate a SPARQL

'); var url = "http://www.sparqlbuilder.org/api/plist?ep=" + encodeURIComponent(endpoint) + "&startclass=" + encodeURIComponent(startclass) + "&endclass=" + encodeURIComponent(endclass); //var self = this; switchLoadIcon("view"); setTimeout(function(){ $.ajax({ type : "GET", url : url, //async : false, timeout : 1000000, success : function(data) { jsontext = data; view_map(); switchLoadIcon("hide"); $('.SBSaveESE').attr('disabled', false); }, error: function(data){ switchLoadIcon("hide"); alert("error: ", data); } }); }, 100); }; function viewAll(){ pathlimit = 0; view_map(); } generateSPARQL = function() { var path = JSON.stringify(pathobj); var url = 'http://www.sparqlbuilder.org/api/sparql?path=' + encodeURIComponent(path); $.ajax({ type: "GET", url : url, dataType: 'text', async: false, success : function(data) { $(".SBSparqlArea").val(data); closeSPARQLBuilder(); } }); }; function sendSPARQL(){ var sendep = $(".SBEndPointSelect").val(); var query = $(".SBSparqlArea").val(); if(sendep == "SBDefault" || query == ""){ return; } query = encodeURIComponent(query); openpage = sendep + "?format=text%2Fhtml&query=" + query; window.open(openpage); } function downloadResult(){ var sendep = $(".SBEndPointSelect").val(); var query = $(".SBSparqlArea").val(); if(sendep == "SBDefault" || query == ""){ return; } qr = sendQuery(sendep,query); qr.fail( function (xhr, textStatus, thrownError) { alert("Error: A '" + textStatus+ "' occurred."); } ); qr.done( function (d) { downloadCSV(d.results.bindings); } ); } function downloadCSV(data){ if (data instanceof Array) { var result_txt =""; var i=0; for ( var key in data[0]) { if(i>0){result_txt +=",";} result_txt += key; i++; } result_txt += "\n"; for (var d = 0; d < data.length; d++) { var i = 0; for ( var key in data[d]) { if(i>0){result_txt +=",";} result_txt += data[d][key].value; i++; } result_txt += '\n'; } var blob = new Blob( [result_txt], {type: 'text/plain'} ) var link = document.createElement('a') link.href = URL.createObjectURL(blob) link.download = 'result' + '.csv' document.body.appendChild(link) // for Firefox link.click() document.body.removeChild(link) // for Firefox } }; view_map = function(){ // make_dataメソッドの結果を取得 var json = make_data(0); if(json['nodes'].length != 0){ // 出来上がった結果を渡してマップ上のロケーションをセット set_map_location(0, json['nodes'], json['links']); // SVGの幅と高さを設定(幅:画面いっぱい 高さ:パスの数に応じ設定) var width = $('.SBGraph').width(); var height = $('.SBGraph').height(); var graphheight = ((NODEHEIGHT * 1.5) * PATHNUM) + (NODEHEIGHT / 2); var scoreleftmargin = NODEHEIGHT * 1.5; // SVGの削除 d3.select(".SBGraph svg").remove(); // 画面サイズに合わせSVGの追加 var svg = d3.select(".SBGraph").append("svg") .attr("width", width) .attr("height", height) .attr("viewBox", "0 0 " + width + " " + height) .on("mousewheel", function(){ var vb = svg.attr("viewBox"); var spvb = vb.split(" "); var vby = (parseInt(spvb[1]) - event.wheelDelta); if(vby < 0){ vby = 0; }else if(vby > (graphheight - height)){ vby = (graphheight - height); if(vby < 0){ vby = 0; } }else{ event.preventDefault(); } svg.attr("viewBox", "0 " + vby + " " + width + " " + height); }); // 背景の追加 var bg = svg .append("rect") .attr("x", 0) .attr("y", 0) .attr("width", width) .attr("height", function(){ if(graphheight < height){ return height; }else{ return graphheight; } }) .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", (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'; }); // nodes配列を渡しノードテキストの作成 var tscore = svg.selectAll("text.score") .data(json.nodes) .enter().append("svg:text") .attr("class", "tscore") .attr("x", function(d) { return (d.x + scoreleftmargin); }) .attr("y", function(d) { return d.y; }) .text(function(d) { return d.score; }) .style("fill", function(d) { return '#FF0000'; }) .style("text-anchor", function(d) { return 'middle'; }) .style("pointer-events", "none"); // マウスの動きカウントをルートに持たせる MOUSEMOVED = 0; // ノードへのオンマウスでパス探索、パス中のリンク文字を表示 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"){ // ツールチップを非表示に $('.SBSelectedPath').html('

Please select a leaf node and push button to generate a SPARQL

'); }else{ // 末端ノードならツールチップの情報を更新 var resultText = ""; // パスの名前配列分後ろから繰り返しながら for (var i = pathname.length;i > 0; i--){ // 奇数番目(ノードの名前)は太字に if(i % 2 == 1){ if(i == 1){ resultText = resultText + "
" + pathname[i - 1] + "
"; }else if(i == pathname.length){ resultText = resultText + "
" + pathname[i - 1] + "
"; }else{ resultText = resultText + "
" + pathname[i - 1] + "
"; } // 偶数番目(リンクの名前)はそのままで表示 }else{ resultText = resultText + "
" + pathname[i - 1] + "
"; } } resultText = resultText + ''; // ツールチップの内容を書き換え $('.SBSelectedPath').html(resultText); if(svg.attr("width") == width){ $('.SBTooltip').show(); } // サーブレットに送り返すパスオブジェクトを保存 pathobj = d.path; } } // オンマウスされたノードの高さに親を合わせるために合わせる高さを保存 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; }); // 各リンクに対し 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;}); // 各ノードについて設定された位置に再描画(かつ折りたたまれ中の場合の描画分岐処理) node .transition() .duration(duration) .attr("cx", function(d) {return d.x;}) .attr("cy", function(d) {return d.y;}); // 各ノードテキストについて設定された位置に再描画、テキスト描画位置を上下に振る(かつ折りたたまれ中の場合の描画分岐処理) 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; }); // 各リンクテキストについて設定された位置に再描画 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;}); }; // 背景部分がクリックされたら表示の固定化を解除 bg.on("click", function() { // ツールチップを非表示 $('.SBTooltip').hide(); 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"; }); /* if(svg.attr("width") == width){ svg.attr("width", (width / 5)) .attr("height", (width * 9 / 16 / 5)) .attr("viewBox", "0 0 " + width + " " + (width * 9 / 16)); }else{ svg.attr("width", width) .attr("height", height) .attr("viewBox", "0 0 " + width + " " + height); } */ }); // 背景上でマウスが動くごとに bg.on("mousemove", function(){ // MOUSEMOVEDを追加(ノードにオンマウスされる度にカウントリセット) MOUSEMOVED++; // 30を超えたら if(MOUSEMOVED > 30){ // ツールチップを非表示にしてカウントリセット $('.SBTooltip').hide(); MOUSEMOVED = 0; } }); // 初回のみdurationを0と指定し再描画(アニメーションなし) redraw(0); }else{ // SVGの削除 d3.select(".SBGraph svg").remove(); } }; make_data = function(tdepth, ret, parent, depth){ // retが未定義ならば定義して代入 if (ret == undefined){ ret = new Object(); ret['nodes'] = new Array(); ret['links'] = new Array(); } PATHNUM = 0; MAXDEPTH = 0; TREESPACE = 0; NODEHEIGHT = 50; DRAWHEIGHT = NODEHEIGHT; var viewnum; var obj = jsontext; $('.SBResult').css('color', 'black').css('font-weight', 'normal'); $('.SBPlural').text('s'); if(obj.length == 0){ $('.SBResult').css('color', 'red').css('font-weight', 'bold'); $('.SBPlural').text(''); }else if(obj.length == 1){ $('.SBPlural').text(''); } if(obj.length <= 10){ viewnum = obj.length; $('.SBViewall').hide(); }else if(pathlimit == 10){ viewnum = 10; $('.SBViewall').show(); }else{ viewnum = obj.length; $('.SBViewall').hide(); } $('.SBPathnum').text(obj.length); $('.SBResult').show(); // 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': 'hsl(40, 50%, 75%)'}); } // 先にsourceに0(ルート)を代入 var source = 0; // 共通ルート判定をtrueに var isCommon = true; var score = obj[i]['score']; // 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(MAXDEPTH < j+1){ 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