Daniel@0: /* Part of DML (Digital Music Laboratory) Daniel@0: Copyright 2014-2015 Samer Abdallah, University of London Daniel@0: Daniel@0: This program is free software; you can redistribute it and/or Daniel@0: modify it under the terms of the GNU General Public License Daniel@0: as published by the Free Software Foundation; either version 2 Daniel@0: of the License, or (at your option) any later version. Daniel@0: Daniel@0: This program is distributed in the hope that it will be useful, Daniel@0: but WITHOUT ANY WARRANTY; without even the implied warranty of Daniel@0: MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the Daniel@0: GNU General Public License for more details. Daniel@0: Daniel@0: You should have received a copy of the GNU General Public Daniel@0: License along with this library; if not, write to the Free Software Daniel@0: Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA Daniel@0: */ Daniel@0: Daniel@0: :- module(callgraph_ui, []). Daniel@0: Daniel@0: :- use_module(library(http/html_write)). Daniel@0: :- use_module(library(http/html_head)). Daniel@0: :- use_module(library(http/http_dispatch)). Daniel@0: :- use_module(library(http/http_parameters)). Daniel@0: :- use_module(library(http/http_host)). Daniel@0: :- use_module(library(http/js_write)). Daniel@0: :- use_module(library(semweb/rdfs)). Daniel@0: :- use_module(library(dcg_core)). Daniel@0: :- use_module(library(fileutils)). Daniel@0: :- use_module(library(callgraph)). Daniel@0: :- use_module(library(decoration)). Daniel@0: :- use_module(library(httpfiles)). Daniel@0: :- use_module(components(icons)). Daniel@0: Daniel@0: Daniel@0: :- http_handler(root(dml/callgraph/ui), callgraph_ui, []). Daniel@0: :- http_handler(root(dml/callgraph/view), callgraph_viewer, []). Daniel@0: :- http_handler(api(callgraph/render), callgraph_render, []). Daniel@0: Daniel@0: :- html_resource(js('callgraph.js'), [requires(jquery)]). Daniel@0: Daniel@0: cliopatria:menu_item(500=help/callgraph_ui,'Module callgraph'). Daniel@0: Daniel@0: % adds a cube icon to all module URIs to link to rendered callgraph Daniel@0: decoration:resource_decoration(URI,Link) --> Daniel@0: {rdfs_individual_of(URI,memo:'Module')}, !, Daniel@0: {uripattern:pattern_uri(dml:module/prolog/enc(Mod),URI)}, Daniel@0: {http_link_to_id(callgraph_viewer,[module(Mod)],URL)}, Daniel@0: html_requires("//maxcdn.bootstrapcdn.com/font-awesome/4.1.0/css/font-awesome.min.css"), Daniel@0: html(span([ a(href(URL),[' ',\icon(cube)]), \Link])). Daniel@0: Daniel@0: %% callgraph_viewer(+Request) is det. Daniel@0: % Web page displaying the callgraph for a given module. Daniel@0: % Uses the callgraph_view//1 component for the view area. Daniel@0: callgraph_viewer(Request) :- Daniel@0: http_parameters(Request, Daniel@0: [ module(Module, [ optional(false), description("Name of module to graph") ]) ]), Daniel@0: reply_html_page(cliopatria(wide), [title(["Module callgraph: ",Module])], Daniel@0: [ h1(["Module callgraph: ",Module]), \callgraph_view(Module) ], Daniel@0: [ stable ]). Daniel@0: Daniel@0: Daniel@0: %% callgraph_view(+Module:module)// is det. Daniel@0: % Daniel@0: % HTML component containing a pannable/zoomable SVG callgraph of the named module. Daniel@0: % The graph itself is rendered by callgraph_render/1 via an HTTP request made just Daniel@0: % by Javascript code inserted directly after the view element. Daniel@0: callgraph_view(Module) --> Daniel@0: html_requires(js('callgraph.js')), Daniel@0: html_requires(js('svg-pan-zoom.min.js')), Daniel@0: html_requires(jquery), Daniel@0: html_post(head,style("svg text {font-family:Times}")), Daniel@0: {http_link_to_id(callgraph_render,[module(Module),format(svg)],URL)}, Daniel@0: html( [ div([style="width:100%;height:25em;padding:0em",id=output,class="output-box"],[]) Daniel@0: , \js_script({|javascript(URL)|| load_svg('#output',URL);|}) Daniel@0: ]). Daniel@0: Daniel@0: %% callgraph_render(+Request) is det. Daniel@0: % Daniel@0: % Replies with a the predicate dependency graph for a given module, created using library(callgraph). Daniel@0: % Default reply format is SVG. In some formats, each predicate contains a link to the documentation Daniel@0: % for that predicate. Daniel@0: callgraph_render(Request) :- Daniel@0: http_parameters(Request, Daniel@0: [ module(Module, [ optional(false), description("Name of module to graph") ]), Daniel@0: chain(Chain, [ optional(true), default(4), nonneg, description("unflatten -c parameter") ]), Daniel@0: link(Link, [ optional(true), default(4), nonneg, description("unflatten -l parameter") ]), Daniel@0: format(Fmt, [ optional(true), default(svg), atom, description("Graphviz output format") ]) Daniel@0: ]), Daniel@0: debug(callgraph_ui,"Calling callgraph on ~w.",[Module]), Daniel@0: http_link_to_id(pldoc_object,[object=''],DocBase), Daniel@0: Method=unflatten([fl(Link),c(Chain)]), Daniel@0: with_mutex(callgraph, Daniel@0: with_temp_dir(Dir, ( Daniel@0: atomic_list_concat([Dir,'/',Module,'.',Fmt],File), Daniel@0: with_output_to(string(_), module_render(Module,[ filename(File), linkbase(DocBase), format(Fmt), method(Method)])), Daniel@0: reply_file(File,Fmt)))). Daniel@0: Daniel@0: Daniel@0: %% callgraph_ui(+Request) is det. Daniel@0: % Web page containing a form for a module name and an output area for Daniel@0: % rendered graphs. The form and view area are created using the callgraph_view//1 component. Daniel@0: callgraph_ui(_) :- Daniel@0: reply_html_page(cliopatria(wide), [title("Module callgraph")], Daniel@0: [ h1("Module callgraph") , \callgraph ], Daniel@0: [ stable ]). Daniel@0: Daniel@0: callgraph --> Daniel@0: {http_location_by_id(callgraph_render,Loc)}, Daniel@0: html_requires(js('callgraph.js')), Daniel@0: html_requires(js('svg-pan-zoom.min.js')), Daniel@0: html_requires(jquery), Daniel@0: html_post(head,style("svg text {font-family:Times}")), Daniel@0: % html_post(head,script(type("text/javascript"), Daniel@0: % "$(document).ready(function() { $('#output').on('load',activate_obj); });")), Daniel@0: html( div(class="callgraph-ui", Daniel@0: [ form([class=forms,target=dummy,method=post,action="about:blank",onsubmit="return false;"], Daniel@0: [ label(["Module" Daniel@0: , div(class="input-groups", Daniel@0: [ input([type=text,autocomplete=on,name=module,id=module],[]) Daniel@0: , \seqmap( append_control, Daniel@0: [ %number(chain,1-10,4) Daniel@0: button(graph,"update_svg('~w');"-[Loc], "graph") Daniel@0: , button(clear,"clear_output();", "clear") Daniel@0: ]) Daniel@0: ])]) Daniel@0: , label(["Output" Daniel@0: , div([style="width:100%;height:25em;padding:0em",id=output,class="output-box"],[])]) Daniel@0: ]) Daniel@0: , iframe([id=dummy,style="display:none",src="about:blank"],["Dummy"]) Daniel@0: ])). Daniel@0: Daniel@0: append_control(B1) --> html(span(class="btn-append",\B1)). Daniel@0: Daniel@0: number(Id,Min-Max,Val) --> Daniel@0: html(input([type=number,id=Id,name=Id,min=Min,max=Max,value=Val,style="min-width:7ex"],[])). Daniel@0: Daniel@0: button(Id,OnClick,Label) --> Daniel@0: html(button([class="btn", onclick=OnClick,id=Id], Label)).