/templates/root/tool_menu.mako

https://bitbucket.org/cistrome/cistrome-harvard/ · Mako · 425 lines · 378 code · 33 blank · 14 comment · 4 complexity · c3ba02dd6027cbc195087ac8072f0433 MD5 · raw file

  1. <%!
  2. import re
  3. %>
  4. <%namespace file="/tagging_common.mako" import="render_tool_tagging_elements" />
  5. ## Render a tool
  6. <%def name="render_tool( tool, section )">
  7. %if not tool.hidden:
  8. %if section:
  9. <div class="toolTitle">
  10. %else:
  11. <div class="toolTitleNoSection">
  12. %endif
  13. <%
  14. if tool.input_required:
  15. link = h.url_for( controller='tool_runner', tool_id=tool.id )
  16. else:
  17. link = h.url_for( tool.action, ** tool.get_static_param_values( t ) )
  18. %>
  19. ## FIXME: This doesn't look right
  20. ## %if "[[" in tool.description and "]]" in tool.description:
  21. ## ${tool.description.replace( '[[', '<a href="link" target="galaxy_main">' % $tool.id ).replace( "]]", "</a>" )
  22. <% tool_id = re.sub( '[^a-z0-9_]', '_', tool.id.lower() ) %>
  23. %if tool.name:
  24. <a class="link-${tool_id} tool-link" href="${link}" target=${tool.target} minsizehint="${tool.uihints.get( 'minwidth', -1 )}">${_(tool.name)}</a> ${tool.description}
  25. %else:
  26. <a class="link-${tool_id} tool-link" href="${link}" target=${tool.target} minsizehint="${tool.uihints.get( 'minwidth', -1 )}">${tool.description}</a>
  27. %endif
  28. </div>
  29. %endif
  30. </%def>
  31. ## Render a workflow
  32. <%def name="render_workflow( key, workflow, section )">
  33. %if section:
  34. <div class="toolTitle">
  35. %else:
  36. <div class="toolTitleNoSection">
  37. %endif
  38. <% encoded_id = key.lstrip( 'workflow_' ) %>
  39. <a id="link-${workflow.id}" href="${ h.url_for( controller='workflow', action='run', id=encoded_id )}" target="_parent">${_(workflow.name)}</a>
  40. </div>
  41. </%def>
  42. ## Render a label
  43. <%def name="render_label( label )">
  44. <div class="toolPanelLabel" id="title_${label.id}">
  45. <span>${label.text}</span>
  46. </div>
  47. </%def>
  48. <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
  49. <html>
  50. <head>
  51. <title>${_('Galaxy Tools')}</title>
  52. <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
  53. <link href="${h.url_for('/static/style/base.css')}" rel="stylesheet" type="text/css" />
  54. <link href="${h.url_for('/static/style/tool_menu.css')}" rel="stylesheet" type="text/css" />
  55. <link href="${h.url_for('/static/style/autocomplete_tagging.css')}" rel="stylesheet" type="text/css" />
  56. ##<script type="text/javascript" src="${h.url_for('/static/scripts/jquery.js')}"></script>
  57. ${h.js( "jquery", "galaxy.base", "json2", "autocomplete_tagging" )}
  58. <script type="text/javascript">
  59. // Set up GalaxyAsync object.
  60. var galaxy_async = new GalaxyAsync(${str(trans.app.config.log_actions).lower()});
  61. galaxy_async.set_func_url(galaxy_async.log_user_action, "${h.url_for( controller='user', action='log_user_action_async' )}");
  62. $(document).ready(function() {
  63. // Init showing/hiding of tool sections.
  64. $( "div.toolSectionBody" ).hide();
  65. $( "div.toolSectionTitle > span" ).wrap( "<a href='#'></a>" )
  66. var last_expanded = null;
  67. $( "div.toolSectionTitle" ).each( function() {
  68. var body = $(this).next( "div.toolSectionBody" );
  69. $(this).click( function() {
  70. if ( body.is( ":hidden" ) ) {
  71. if ( last_expanded ) {
  72. last_expanded.slideUp( "fast" );
  73. }
  74. last_expanded = body;
  75. body.slideDown( "fast" );
  76. } else {
  77. body.slideUp( "fast" );
  78. last_expanded = null;
  79. }
  80. return false;
  81. });
  82. });
  83. // Log clicks on tools.
  84. $("div.toolTitle > a").click( function() {
  85. var tool_title = $(this).attr('id').split("-")[1];
  86. var section_title = $.trim( $(this).parents("div.toolSectionWrapper").find("div.toolSectionTitle").text() );
  87. var search_active = $(this).parents("div.toolTitle").hasClass("search_match");
  88. // Log action.
  89. galaxy_async.log_user_action("tool_menu_click." + tool_title, section_title,
  90. JSON.stringify({"search_active" : search_active}));
  91. });
  92. $( "a[minsizehint]" ).click( function() {
  93. if ( parent.handle_minwidth_hint ) {
  94. parent.handle_minwidth_hint( $(this).attr( "minsizehint" ) );
  95. }
  96. });
  97. // Init searching.
  98. $("#tool-search-query").click( function () {
  99. $(this).focus();
  100. $(this).select();
  101. }).keyup( function () {
  102. // Remove italics.
  103. $(this).css("font-style", "normal");
  104. // Don't search if the search value is < 3 chars, but clear the search if there was a previous query
  105. if ( this.value.length < 3 && this.lastValue && this.lastValue.length >= 3 ) {
  106. reset_tool_search(false);
  107. // Re-apply tags
  108. if ( current_tags.length > 0 ) {
  109. $.get("${h.url_for( controller='root', action='tool_search' )}", { query: "", tags: current_tags }, function (data) {
  110. apply_search_results(data);
  111. }, "json" );
  112. }
  113. // Don't update if same value as last time
  114. } else if ( this.value !== this.lastValue ) {
  115. // Add class to denote that searching is active.
  116. $(this).addClass("search_active");
  117. // input.addClass(config.loadingClass);
  118. // Add '*' to facilitate partial matching.
  119. var q = this.value + '*';
  120. // Stop previous ajax-request
  121. if (this.timer) {
  122. clearTimeout(this.timer);
  123. }
  124. // Start a new ajax-request in X ms
  125. $("#search-spinner").show();
  126. this.timer = setTimeout(function () {
  127. $.get("${h.url_for( controller='root', action='tool_search' )}", { query: q, tags: current_tags }, function (data) {
  128. apply_search_results(data);
  129. $("#search-spinner").hide();
  130. }, "json" );
  131. }, 200 );
  132. }
  133. this.lastValue = this.value;
  134. });
  135. // Apply stored tags
  136. %if trans.user and trans.user.preferences.get( 'selected_tool_tags', '' ):
  137. current_tags = "${trans.user.preferences['selected_tool_tags']}".split(",")
  138. $.get("${h.url_for( controller='root', action='tool_search' )}", { query: "", tags: current_tags }, function (data) {
  139. apply_search_results(data);
  140. }, "json" );
  141. $("span.tag-name").each( function() {
  142. for ( var i in current_tags ) {
  143. if ( $(this).text() == current_tags[i] ) {
  144. $(this).addClass("active-tag-name");
  145. $(this).append("<img class='delete-tag-img' src='${h.url_for('/static/images/delete_tag_icon_gray.png')}'/>")
  146. }
  147. }
  148. });
  149. %endif
  150. });
  151. var apply_search_results = function (data) {
  152. // input.removeClass(config.loadingClass);
  153. // Show live-search if results and search-term aren't empty
  154. $("#search-no-results").hide();
  155. // Hide all tool sections.
  156. $(".toolSectionWrapper").hide();
  157. // This hides all tools but not workflows link (which is in a .toolTitle div).
  158. $(".toolSectionWrapper").find(".toolTitle").hide();
  159. if ( data.length !== 0 ) {
  160. // Map tool ids to element ids and join them.
  161. var s = $.map( data, function( n, i ) { return ".link-" + n.toLowerCase().replace(/[^a-z0-9_]/g,'_'); } ).join( ", " );
  162. // First pass to show matching tools and their parents.
  163. $(s).each( function() {
  164. // Add class to denote match.
  165. $(this).parent().addClass("search_match");
  166. if ($(this).parents("#recently_used_wrapper").length === 0) {
  167. // Default behavior.
  168. $(this).parent().show().parent().parent().show().parent().show();
  169. } else if ($(this).parents(".user_pref_visible").length !== 0) {
  170. // RU menu is visible, so filter it as normal.
  171. $(this).parent().show().parent().parent().show().parent().show();
  172. } else {
  173. // RU menu is not visible, so set up classes and visibility so that if menu shown matching is
  174. // aleady in place.
  175. $(this).parent().show();
  176. }
  177. });
  178. // Hide labels that have no visible children.
  179. $(".toolPanelLabel").each( function() {
  180. var this_label = $(this);
  181. var next = this_label.next();
  182. var no_visible_tools = true;
  183. // Look through tools following label and, if none are visible, hide label.
  184. while (next.length !== 0 && next.hasClass("toolTitle")) {
  185. if (next.is(":visible")) {
  186. no_visible_tools = false;
  187. break;
  188. } else {
  189. next = next.next();
  190. }
  191. }
  192. if (no_visible_tools) {
  193. this_label.hide();
  194. }
  195. });
  196. } else {
  197. $("#search-no-results").show();
  198. }
  199. }
  200. // Update recently used tools menu. Function inserts a new item and removes the last item.
  201. function update_recently_used() {
  202. $.ajax({
  203. url: "${h.url_for( controller='/user', action='get_most_recently_used_tool_async' )}",
  204. dataType: 'json',
  205. error: function() {
  206. // console.log( "Failed to update recently used list." );
  207. },
  208. success: function(new_tool_info) {
  209. var recently_used_elts = $("#recently_used").find(".toolTitle");
  210. var first_elt = $(recently_used_elts.first());
  211. var found_in_list = false;
  212. // Look for new tool in current list. If found, rearrange list to move tool to top.
  213. var new_tool_info_id = new_tool_info.id.toLowerCase().replace(/[^a-z0-9_]/, "_")
  214. recently_used_elts.each( function(index) {
  215. var anchor = $(this).find("a");
  216. if (anchor.hasClass("link-" + new_tool_info_id)) {
  217. found_in_list = true;
  218. // If tool is first, do nothing.
  219. if (index === 0) {
  220. return;
  221. } else {
  222. // Tool not first; reorder.
  223. $(this).remove();
  224. first_elt.before($(this));
  225. }
  226. }
  227. });
  228. // If tool not in list, create new element, remove last element, and put new element first in list.
  229. if (!found_in_list) {
  230. new_tool_elt = $("<div class='toolTitle'> \
  231. <a class='link-" + new_tool_info.id + "' href='" + new_tool_info.link + "' target='" +
  232. new_tool_info.target + "' minsizehint='" + new_tool_info.minsizehint + "'>" +
  233. new_tool_info.name + "</a> " + new_tool_info.description + " \
  234. </div>");
  235. recently_used_elts.last().remove();
  236. recently_used_elts.first().before(new_tool_elt);
  237. }
  238. }
  239. });
  240. }
  241. var current_tags = new Array();
  242. function tool_tag_click(tag_name, tag_value) {
  243. var add = true;
  244. for ( var i = 0 ; i < current_tags.length ; i++ ) {
  245. if ( current_tags[i] == tag_name ) {
  246. current_tags.splice( i, 1 );
  247. add = false;
  248. }
  249. }
  250. if ( add ) {
  251. current_tags.push( tag_name );
  252. $("span.tag-name").each( function() {
  253. if ( $(this).text() == tag_name ) {
  254. $(this).addClass("active-tag-name");
  255. $(this).append("<img class='delete-tag-img' src='${h.url_for('/static/images/delete_tag_icon_gray.png')}'/>")
  256. }
  257. });
  258. } else {
  259. $("span.tag-name").each( function() {
  260. if ( $(this).text() == tag_name ) {
  261. $(this).removeClass("active-tag-name");
  262. $(this).text(tag_name);
  263. }
  264. });
  265. }
  266. if ( current_tags.length == 0 ) {
  267. $("#search-no-results").hide();
  268. $(".tool-link").each( function() {
  269. reset_tool_search(false);
  270. });
  271. return;
  272. }
  273. var q = $("input#tool-search-query").val();
  274. if ( q == "search tools" ) {
  275. q = "";
  276. } else if ( q.length > 0 ) {
  277. q = q + '*';
  278. }
  279. $.get("${h.url_for( controller='root', action='tool_search' )}", { query: q, tags: current_tags }, function (data) {
  280. apply_search_results(data);
  281. }, "json" );
  282. }
  283. </script>
  284. </head>
  285. <body class="toolMenuPage">
  286. <div class="toolMenu">
  287. ## Tool search.
  288. <%
  289. show_tool_search = True
  290. if trans.user:
  291. show_tool_search = trans.user.preferences.get( "show_tool_search", "False" ) == "True"
  292. if show_tool_search:
  293. display = "block"
  294. else:
  295. display = "none"
  296. %>
  297. <div id="tool-search" style="padding-bottom: 5px; position: relative; display: ${display}; width: 100%">
  298. %if trans.app.config.get_bool( 'enable_tool_tags', False ):
  299. <b>Tags:</b>
  300. ${render_tool_tagging_elements()}
  301. %endif
  302. <input type="text" name="query" value="search tools" id="tool-search-query" autocomplete="off" style="width: 100%; font-style:italic; font-size: inherit"/>
  303. <img src="${h.url_for('/static/images/loading_small_white_bg.gif')}" id="search-spinner" style="display: none; position: absolute; right: 0; top: 5px;"/>
  304. </div>
  305. ## Recently used tools.
  306. %if trans.user:
  307. <%
  308. if trans.user.preferences.get( 'show_recently_used_menu', 'False' ) == 'True':
  309. display = "block"
  310. pref_class = "user_pref_visible"
  311. else:
  312. display = "none"
  313. pref_class = "user_pref_hidden"
  314. %>
  315. <div class="toolSectionWrapper ${pref_class}" id="recently_used_wrapper"
  316. style="display: ${display}; padding-bottom: 5px">
  317. <div class="toolSectionTitle">
  318. <span>Recently Used</span>
  319. </div>
  320. <div id="recently_used" class="toolSectionBody">
  321. <div class="toolSectionBg">
  322. %for tool in recent_tools:
  323. ${render_tool( tool, True )}
  324. %endfor
  325. </div>
  326. </div>
  327. <div class="toolSectionPad"></div>
  328. </div>
  329. %endif
  330. ## Tools.
  331. %for key, val in toolbox.tool_panel.items():
  332. <div class="toolSectionWrapper">
  333. %if key.startswith( 'tool' ):
  334. ${render_tool( val, False )}
  335. %elif key.startswith( 'workflow' ):
  336. ${render_workflow( key, val, False )}
  337. %elif key.startswith( 'section' ):
  338. <% section = val %>
  339. <div class="toolSectionTitle" id="title_${section.id}">
  340. <span>${section.name}</span>
  341. </div>
  342. <div id="${section.id}" class="toolSectionBody">
  343. <div class="toolSectionBg">
  344. %for section_key, section_val in section.elems.items():
  345. %if section_key.startswith( 'tool' ):
  346. ${render_tool( section_val, True )}
  347. %elif section_key.startswith( 'workflow' ):
  348. ${render_workflow( section_key, section_val, True )}
  349. %elif section_key.startswith( 'label' ):
  350. ${render_label( section_val )}
  351. %endif
  352. %endfor
  353. </div>
  354. </div>
  355. %elif key.startswith( 'label' ):
  356. ${render_label( val )}
  357. %endif
  358. <div class="toolSectionPad"></div>
  359. </div>
  360. %endfor
  361. ## Feedback when search returns no results.
  362. <div id="search-no-results" style="display: none; padding-top: 5px">
  363. <em><strong>Search did not match any tools.</strong></em>
  364. </div>
  365. ## Link to workflow management. The location of this may change, but eventually
  366. ## at least some workflows will appear here (the user should be able to
  367. ## configure which of their stored workflows appear in the tools menu).
  368. %if t.user:
  369. <div class="toolSectionPad"></div>
  370. <div class="toolSectionPad"></div>
  371. <div class="toolSectionTitle" id="title_XXinternalXXworkflow">
  372. <span>Workflows</span>
  373. </div>
  374. <div id="XXinternalXXworkflow" class="toolSectionBody">
  375. <div class="toolSectionBg">
  376. %if t.user.stored_workflow_menu_entries:
  377. %for m in t.user.stored_workflow_menu_entries:
  378. <div class="toolTitle">
  379. <a href="${h.url_for( controller='workflow', action='run', id=trans.security.encode_id(m.stored_workflow_id) )}" target="galaxy_main">${m.stored_workflow.name}</a>
  380. </div>
  381. %endfor
  382. %endif
  383. <div class="toolTitle">
  384. <a href="${h.url_for( controller='workflow', action='list_for_run')}" target="galaxy_main">All workflows</a>
  385. </div>
  386. </div>
  387. </div>
  388. %endif
  389. </div>
  390. </div>
  391. </body>
  392. </html>