var PATHNUM = 0; var MAXDEPTH = 0; var TREESPACE = 0; var NODEHEIGHT = 50; var DRAWHEIGHT = 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){ event.stopPropagation(); }); loadEndPointList(); $(".SBEndPointSelect").change(function() { changeEndPoint(); }); $(".SBStartClassSelect").change(function() { startClass = $(".SBStartClassSelect").val(); loadEndClassList(); }); $(".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(); resizeModalView(); $(".SBEndPointSelect").select2(); $(".SBStartClassSelect").select2(); $(".SBEndClassSelect").select2(); if(defendpoint != "" && defstartclass != "" && defendclass != ""){ $('.SBStartClassSelect').on('lsccomplete', function(){ $('.SBStartClassSelect').val(defstartclass); defstartclass = ""; $(".SBEndPointSelect").select2(); $(".SBStartClassSelect").select2(); $(".SBEndClassSelect").select2(); $('.SBStartClassSelect').unbind('lsccomplete'); loadEndClassList(); }); $('.SBEndClassSelect').on('leccomplete', function(){ $('.SBEndClassSelect').val(defendclass); defendclass = ""; $(".SBEndPointSelect").select2(); $(".SBStartClassSelect").select2(); $(".SBEndClassSelect").select2(); $('.SBEndClassSelect').unbind('leccomplete'); }); loadPathList(); var eplist = $('.SBEndPointSelect option'); if(eplist.length == 0){ $('.SBEndPointSelect').on('epcomplete', function(){ $('.SBEndPointSelect').val(defendpoint); defendpoint = ""; loadStartClassList(); $('.SBEndPointSelect').unbind('epcomplete'); }); }else{ $('.SBEndPointSelect').val(defendpoint); defendpoint = ""; loadStartClassList(); } } } function resizeModalView(){ if($('.SBModalView').css('display') == 'block'){ var mvw = $('.SBModalContents').width(); var mvh = $('.SBModalContents').height(); $('.SBModalContents .SBForms').css('width', (mvw - 201) + 'px').css('height', 56 + 'px'); $('.SBModalContents .SBMessage').css('width', 200 + 'px').css('height', 56 + 'px'); $('.SBModalContents .SBGraph').css('width', (mvw - 201) + 'px').css('height', (mvh - 57) + 'px'); $('.SBModalContents .SBPath').css('width', 180 + 'px').css('height', (mvh - 77 - 26) + 'px'); $('.SBModalContents .SBModalButtons').css('width', 200 + 'px').css('height', '26px'); var formw = $('.SBModalContents .SBForms').width(); var selw = Math.floor(formw - 120); if(selw % 2 == 1){ selw--; } $('.SBModalContents .SBSelects').css('width', selw); $('.SBModalContents .SBPermaLink').css('width', Math.floor(formw - selw)); } } 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(){ $('.SBPermaLinkButton').attr('disabled', true); var url = 'http://localhost:8080/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"){ loadStartClassList(); } }; loadStartClassList = function() { $('.SBPermaLinkButton').attr('disabled', true); var url = "http://localhost:8080/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(''); for (var i = 0; i < list.length; ++i) { $(".SBStartClassSelect").append(''); } $(".SBStartClassSelect").select2(); $(".SBEndClassSelect").select2(); $(".SBStartClassSelect").trigger(new $.Event('lsccomplete')); } }); }; loadEndClassList = function() { $('.SBPermaLinkButton').attr('disabled', true); var url = "http://localhost:8080/api/clist?ep=" + encodeURIComponent(endpoint) + '&class=' + encodeURIComponent($(".SBStartClassSelect").val()); $.ajax({ type : "GET", url : url, async : false, success : function(data) { var list = eval(data); $(".SBEndClassSelect").empty(); $(".SBEndClassSelect").append(''); for (var i = 0; i < list.length; ++i) { $(".SBEndClassSelect").append(''); } $(".SBStartClassSelect").select2(); $(".SBEndClassSelect").select2(); $(".SBEndClassSelect").trigger(new $.Event('leccomplete')); } }); }; 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 click to generate a SPARQL

'); var url = "http://localhost:8080/api/plist?ep=" + encodeURIComponent(endpoint) + "&startclass=" + encodeURIComponent(startclass) + "&endclass=" + encodeURIComponent(endclass); switchLoadIcon("view"); setTimeout(function(){ $.ajax({ type : "GET", url : url, //async : false, timeout : 1000000, success : function(data) { jsontext = data; view_map(); switchLoadIcon("hide"); $('.SBPermaLinkButton').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://localhost:8080/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(); // SVG内のグラフ部分高さ(パス数に応じる)をセット var graphheight = ((NODEHEIGHT * 1.5) * PATHNUM) + (NODEHEIGHT / 2); // スコア表示のマージン var scoreleftmargin = NODEHEIGHT * 1.5; var scrollsvg = function(delta){ // 現在のビューボックスの状態を取得 var vb = svg.attr("viewBox"); // スペースで区切り各値に分解 var spvb = vb.split(" "); // ビューボックスのyの値から今回のホイールイベントの差分を引く var vby = (parseInt(spvb[1]) - parseInt(delta)); // 0を割っていたら0に if(vby < 0){ vby = 0; // スクロール上限(グラフサイズ引く表示領域サイズ)を超えていたら補正 }else if(vby > (graphheight - height)){ vby = (graphheight - height); // 補正した結果0を割っていたら0に if(vby < 0){ vby = 0; } } // ここまででできたyをセットしビューボックスを更新 svg.attr("viewBox", "0 " + vby + " " + width + " " + height); } // 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); // SVGがスクロールされた時のイベントをブラウザに応じてセット var mousewheelevent = 'onwheel' in document ? 'wheel' : 'onmousewheel' in document ? 'mousewheel' : 'DOMMouseScroll'; $(".SBGraph svg").on(mousewheelevent,function(e){ // ブラウザに応じてスクロールの値を取得 var delta = e.originalEvent.deltaY ? -(e.originalEvent.deltaY) : e.originalEvent.wheelDelta ? e.originalEvent.wheelDelta : -(e.originalEvent.detail); // FireFoxだとスクロール速度が非常に遅い場合があるので補正 if(delta < 0 && delta > -100){ delta = -100; }else if(0 < delta && delta < 100){ delta = 100; } // スクロールのデフォルトの動作とバブリングをキャンセル e.preventDefault(); e.stopPropagation(); // 値を渡してスクロール scrollsvg(delta); }); // 背景の追加(高さ以外は描画領域そのまま) 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", 2); // 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("fill", function(d) { return d.nodecolor; }) .style("stroke", '#fafafa') .style("stroke-width", '1.5px') // 末端ノードのみマウスアイコンをポインターに .style("cursor", function(d){ if(d.path == "notend"){ return 'normal'; }else{ 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", '#000000') .style("text-anchor", '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", '#000000') .style("text-anchor", 'middle'); // スコアテキストの作成 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 + 4; }) .text(function(d) { return d.score; }) .style("fill", 'hsl(0, 50%, 75%)') .style("text-anchor", 'middle') .style("pointer-events", "none"); // ノードへのオンマウスでパス探索、パス中のリンク文字を表示 node.on("mouseover", function(d){ // 表示するパス保存用配列 var path = []; // パス表示情報保存用配列 var pathname = []; // まずオンマウスされたノードの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.nodeid == 0 || d.path == "notend"){ // パス表示領域をデフォルトに $('.SBSelectedPath').html('

Please select a leaf node and click 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] + "
"; } } // パス表示領域の内容を書き換え $('.SBSelectedPath').html(resultText); // サーブレットに送り返すパスオブジェクトを保存 pathobj = d.path; // パス表示領域の表示設定をvisivleに $('.SBPath').css('overflow-y', 'visible'); // パス内容の高さがパス表示領域を超えていたら if($('.SBPath').height() < $('.SBSelectedPath').innerHeight()){ // パス表示領域の表示設定をスクロールに $('.SBPath').css('overflow-y', 'scroll'); } } // オンマウスされたノードの高さに親を合わせるために合わせる高さを保存 var movey = d.y; // 各ノードに対し node // 輪郭線の色を設定 .style("stroke", function(d){ // まずは背景色(デフォルト)を指定 var strokecolor = "#fafafa"; // パスのノード数だけ繰り返しながら for(var n = 0; n < path.length; n++){ // パス内に含まれるノードだったら if(path[n] == d.nodeid){ // 輪郭線を赤に strokecolor = "#ffaaaa"; } } // ここまでで得られた輪郭線の色を返す return strokecolor; }) // 高さの値 .attr("cy", function(d){ // 現在の高さを取得 var currenty = d.y // パスのノード数だけ繰り返しながら for(var n = 0; n < path.length; n++){ // パス内に含まれるノードだったら if(path[n] == d.nodeid){ // 内部で持つ高さをオンマウスされたノードと同じに(再描画時に反映) d.y = movey; } } // 今は現時点の高さを返す return currenty; }); // 各リンクテキストに対し tlink // テキスト表示判定 .text(function(d) { // デフォルトで空をセット var linktext = ""; // パスのノード数だけ繰り返しながら for(var t = 0; t < path.length; t++){ // 自身がそのノードへ接続しているリンクならば if(path[t] == d.target){ // リンクテキストにプロパティの値をセット linktext = d.property } } // ここまででできたリンクテキストを返す return linktext; }); // 各リンクに対し link // 線の色判定 .style("stroke", function(d){ var strokecolor = "#999"; // パスのノード数だけ繰り返しながら for(var t = 0; t < path.length; t++){ // 自身がそのノードへ接続しているリンクならば if(path[t] == d.target){ // リンクの色に赤をセット strokecolor = "#ffaaaa" } } // 線の色を返す return strokecolor; }); // ここまでの設定を元に再描画 redraw(); // クリックされたとき }).on("click", function(d){ // 末端ノードならスパークル発行 if(d.path != "notend"){ generateSPARQL(); } }); // 再描画関数 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 = (NODEHEIGHT * 0.4); // 奇数番目の深さなら少し上げる if(d.group % 2 == 1){ updown = -(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) + 4;}); }; // 初回のみdurationを0と指定し再描画(アニメーションなし) redraw(0); }else{ // SVGの削除 d3.select(".SBGraph svg").remove(); } }; // データ作成メソッド make_data = function(){ // 結果用オブジェクトを初期化 ret = new Object(); ret['nodes'] = new Array(); ret['links'] = new Array(); // 各種変数の初期化 PATHNUM = 0; MAXDEPTH = 0; TREESPACE = 0; DRAWHEIGHT = NODEHEIGHT; // 表示するパス数 var viewnum; // jsontextを取得 var obj = jsontext; // 結果パス数のスタイルをリセット $('.SBResult').css('color', 'black').css('font-weight', 'normal').css('margin-top', '4px'); // 複数形のsをつける $('.SBPlural').text('s'); // パスの数が0だったら if(obj.length == 0){ // 結果パス数のスタイルを赤の太字にし領域内上下中央に $('.SBResult').css('color', 'red').css('font-weight', 'bold').css('margin-top', '20px'); // 複数形のsを削除 $('.SBPlural').text(''); // パス数が1なら }else if(obj.length == 1){ // 複数形のsを削除 $('.SBPlural').text(''); } // パスの数が十以下なら if(obj.length <= 10){ // 表示数をパス数に viewnum = obj.length; // 結果パス数のスタイルを領域内上下中央に $('.SBResult').css('margin-top', '20px'); // 全表示ボタンを隠す $('.SBViewAll').hide(); // リミットが10ならば }else if(pathlimit == 10){ // 表示パス数を10に viewnum = 10; // 全表示ボタンを出す $('.SBViewAll').show(); // リミットがなければ }else{ // 表示パス数を全パス数に viewnum = obj.length; // 結果パス数のスタイルを領域内上下中央に $('.SBResult').css('margin-top', '20px'); // 全表示ボタンを隠す $('.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, '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