rbbt-views 1.0.1 → 2.0.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (177) hide show
  1. data/lib/rbbt/workflow/rest.rb +365 -95
  2. data/lib/rbbt/workflow/rest/client.rb +2 -0
  3. data/lib/rbbt/workflow/rest/entity.rb +82 -76
  4. data/lib/rbbt/workflow/rest/finder.rb +78 -0
  5. data/lib/rbbt/workflow/rest/helpers.rb +288 -47
  6. data/lib/rbbt/workflow/rest/notes.rb +125 -0
  7. data/lib/rbbt/workflow/rest/render.rb +280 -240
  8. data/lib/rbbt/workflow/rest/util.rb +47 -31
  9. data/share/views/compass/_base.sass +0 -8
  10. data/share/views/compass/colors.sass +10 -0
  11. data/share/views/compass/colors/ventana_azul.sass +5 -0
  12. data/share/views/compass/compass.config +2 -1
  13. data/share/views/compass/details.sass +50 -0
  14. data/share/views/compass/dom.sass +64 -0
  15. data/share/views/compass/embedded.sass +111 -0
  16. data/share/views/compass/entities.sass +8 -0
  17. data/share/views/compass/error.sass +13 -0
  18. data/share/views/compass/finder.sass +42 -0
  19. data/share/views/compass/fonts.sass +8 -0
  20. data/share/views/compass/form.sass +49 -1
  21. data/share/views/compass/header.sass +68 -0
  22. data/share/views/compass/{main.sass → helpers.sass} +53 -21
  23. data/share/views/compass/layout.sass +31 -199
  24. data/share/views/compass/lists.sass +128 -0
  25. data/share/views/compass/menu.sass +35 -0
  26. data/share/views/compass/notes.sass +52 -0
  27. data/share/views/compass/sizes.sass +8 -0
  28. data/share/views/compass/tables.sass +22 -0
  29. data/share/views/compass/wait.sass +9 -0
  30. data/share/views/edit_list.haml +41 -30
  31. data/share/views/entity/GOTerm.haml +4 -3
  32. data/share/views/entity/Gene.haml +8 -1
  33. data/share/views/entity/GenomicMutation.haml +4 -1
  34. data/share/views/entity/KeggPathway.haml +1 -1
  35. data/share/views/entity/MutatedIsoform.haml +68 -11
  36. data/share/views/entity/NCIBioCartaPathway.haml +2 -3
  37. data/share/views/entity/NCINaturePathway.haml +3 -3
  38. data/share/views/entity/NCIReactomePathway.haml +2 -3
  39. data/share/views/entity/PfamDomain.haml +2 -0
  40. data/share/views/entity/Transcript.haml +10 -0
  41. data/share/views/entity_list/Default.haml +4 -0
  42. data/share/views/entity_list/Default/compare_to_other_lists.haml +41 -0
  43. data/share/views/entity_list/Default/entity_links.haml +6 -0
  44. data/share/views/entity_list/Default/find_in_other_lists.haml +36 -0
  45. data/share/views/error.haml +6 -1
  46. data/share/views/find.haml +10 -0
  47. data/share/views/form.haml +1 -1
  48. data/share/views/info.haml +1 -1
  49. data/share/views/js/_action_setup.js +250 -0
  50. data/share/views/js/_basic.js +269 -0
  51. data/share/views/js/_cytoscape.js +44 -0
  52. data/share/views/js/_details.js +239 -0
  53. data/share/views/js/_dom_updates.js +384 -0
  54. data/share/views/js/_embedded.js +95 -0
  55. data/share/views/js/_images.js +20 -0
  56. data/share/views/js/_layout.js +21 -0
  57. data/share/views/js/_lists.js +224 -0
  58. data/share/views/js/_table.js +36 -0
  59. data/share/views/js/_workflow.js +42 -0
  60. data/share/views/js/app.js +0 -570
  61. data/share/views/js/fix_tablesorter_scientific.js +25 -0
  62. data/share/views/js/json2.js +487 -0
  63. data/share/views/layout.haml +8 -98
  64. data/share/views/notes.haml +15 -0
  65. data/share/views/partials/_form.haml +22 -7
  66. data/share/views/partials/_job_control.haml +8 -8
  67. data/share/views/partials/_list_control.haml +32 -0
  68. data/share/views/partials/_list_header.haml +6 -0
  69. data/share/views/partials/_note_list.haml +64 -0
  70. data/share/views/partials/_result_table.haml +12 -7
  71. data/share/views/partials/_result_table_controls.haml +11 -5
  72. data/share/views/partials/_table.haml +24 -6
  73. data/share/views/partials/layout/_coda.haml +10 -0
  74. data/share/views/partials/layout/_details.haml +25 -0
  75. data/share/views/partials/layout/_head.haml +22 -0
  76. data/share/views/partials/layout/_header.haml +29 -0
  77. data/share/views/partials/layout/_menu.haml +20 -0
  78. data/share/views/partials/layout/_resources.haml +41 -0
  79. data/share/views/public/background.png +0 -0
  80. data/share/views/public/plugins/cytoscape-web/LICENSE.txt +504 -0
  81. data/share/views/public/plugins/cytoscape-web/README.txt +213 -0
  82. data/share/views/public/plugins/cytoscape-web/js/AC_OETags.js +1 -0
  83. data/share/views/public/plugins/cytoscape-web/js/cytoscapeweb.js +1 -0
  84. data/share/views/public/plugins/cytoscape-web/js/json2.js +1 -0
  85. data/share/views/public/plugins/cytoscape-web/swf/CytoscapeWeb.swf +0 -0
  86. data/share/views/public/plugins/cytoscape-web/swf/playerProductInstall.swf +0 -0
  87. data/share/views/public/plugins/fancybox/CHANGELOG.md +68 -0
  88. data/share/views/public/plugins/fancybox/README.md +220 -0
  89. data/share/views/public/plugins/fancybox/css/blank.gif +0 -0
  90. data/share/views/public/plugins/fancybox/css/fancybox_loading.gif +0 -0
  91. data/share/views/public/plugins/fancybox/css/fancybox_sprite.png +0 -0
  92. data/share/views/public/plugins/fancybox/css/jquery.fancybox.css +234 -0
  93. data/share/views/public/plugins/fancybox/helpers/fancybox_buttons.png +0 -0
  94. data/share/views/public/plugins/fancybox/helpers/jquery.fancybox-buttons.css +85 -0
  95. data/share/views/public/plugins/fancybox/helpers/jquery.fancybox-buttons.js +115 -0
  96. data/share/views/public/plugins/fancybox/helpers/jquery.fancybox-media.js +85 -0
  97. data/share/views/public/plugins/fancybox/helpers/jquery.fancybox-thumbs.css +54 -0
  98. data/share/views/public/plugins/fancybox/helpers/jquery.fancybox-thumbs.js +157 -0
  99. data/share/views/public/plugins/fancybox/js/jquery.fancybox.js +1463 -0
  100. data/share/views/public/plugins/fancybox/js/jquery.mousewheel-3.0.6.pack.js +13 -0
  101. data/share/views/public/plugins/fancybox/lib/jquery-1.7.2.min.js +4 -0
  102. data/share/views/public/plugins/featuredimagezoomer/css/featuredimagezoomer.css +8 -0
  103. data/share/views/public/plugins/featuredimagezoomer/images/spinningred.gif +0 -0
  104. data/share/views/public/plugins/featuredimagezoomer/js/featuredimagezoomer.js +245 -0
  105. data/share/views/public/plugins/jquery-ui.hitarea.js +173 -0
  106. data/share/views/public/plugins/jquery/js/jquery-1.8.0.min.js +2 -0
  107. data/share/views/public/plugins/tablesorter/addons/pager/jquery.tablesorter.pager.css +25 -0
  108. data/share/views/public/plugins/tablesorter/addons/pager/jquery.tablesorter.pager.js +184 -0
  109. data/share/views/public/plugins/tablesorter/build.xml +26 -0
  110. data/share/views/public/plugins/tablesorter/build/ParseMaster.js +106 -0
  111. data/share/views/public/plugins/tablesorter/build/js.jar +0 -0
  112. data/share/views/public/plugins/tablesorter/build/jsmin.js +316 -0
  113. data/share/views/public/plugins/tablesorter/build/min.js +5 -0
  114. data/share/views/public/plugins/tablesorter/build/pack.js +5 -0
  115. data/share/views/public/plugins/tablesorter/build/packer.js +316 -0
  116. data/share/views/public/plugins/tablesorter/build/writeFile.js +19 -0
  117. data/share/views/public/plugins/tablesorter/changelog +41 -0
  118. data/share/views/public/plugins/tablesorter/docs/assets/ajax-content.html +43 -0
  119. data/share/views/public/plugins/tablesorter/docs/css/jq.css +29 -0
  120. data/share/views/public/plugins/tablesorter/docs/example-ajax.html +119 -0
  121. data/share/views/public/plugins/tablesorter/docs/example-empty-table.html +75 -0
  122. data/share/views/public/plugins/tablesorter/docs/example-extending-defaults.html +109 -0
  123. data/share/views/public/plugins/tablesorter/docs/example-meta-headers.html +108 -0
  124. data/share/views/public/plugins/tablesorter/docs/example-meta-parsers.html +105 -0
  125. data/share/views/public/plugins/tablesorter/docs/example-meta-sort-list.html +107 -0
  126. data/share/views/public/plugins/tablesorter/docs/example-option-debug.html +116 -0
  127. data/share/views/public/plugins/tablesorter/docs/example-option-digits.html +106 -0
  128. data/share/views/public/plugins/tablesorter/docs/example-option-sort-force.html +107 -0
  129. data/share/views/public/plugins/tablesorter/docs/example-option-sort-key.html +108 -0
  130. data/share/views/public/plugins/tablesorter/docs/example-option-sort-list.html +108 -0
  131. data/share/views/public/plugins/tablesorter/docs/example-option-sort-order.html +108 -0
  132. data/share/views/public/plugins/tablesorter/docs/example-option-text-extraction.html +85 -0
  133. data/share/views/public/plugins/tablesorter/docs/example-options-headers.html +118 -0
  134. data/share/views/public/plugins/tablesorter/docs/example-pager.html +329 -0
  135. data/share/views/public/plugins/tablesorter/docs/example-parsers.html +112 -0
  136. data/share/views/public/plugins/tablesorter/docs/example-trigger-sort.html +113 -0
  137. data/share/views/public/plugins/tablesorter/docs/example-triggers.html +336 -0
  138. data/share/views/public/plugins/tablesorter/docs/example-update-cell.html +118 -0
  139. data/share/views/public/plugins/tablesorter/docs/example-widgets.html +383 -0
  140. data/share/views/public/plugins/tablesorter/docs/img/external.png +0 -0
  141. data/share/views/public/plugins/tablesorter/docs/index.html +576 -0
  142. data/share/views/public/plugins/tablesorter/docs/js/docs.js +23 -0
  143. data/share/views/public/plugins/tablesorter/docs/js/examples.js +29 -0
  144. data/share/views/public/plugins/tablesorter/jquery-latest.js +154 -0
  145. data/share/views/public/plugins/tablesorter/jquery.metadata.js +122 -0
  146. data/share/views/public/plugins/tablesorter/jquery.tablesorter.js +1031 -0
  147. data/share/views/public/plugins/tablesorter/jquery.tablesorter.min.js +4 -0
  148. data/share/views/public/plugins/tablesorter/tests/assets/ajax-content.html +28 -0
  149. data/share/views/public/plugins/tablesorter/tests/cell.metadata.html +112 -0
  150. data/share/views/public/plugins/tablesorter/tests/checkbox.html +117 -0
  151. data/share/views/public/plugins/tablesorter/tests/colspan.html +149 -0
  152. data/share/views/public/plugins/tablesorter/tests/demo.html +353 -0
  153. data/share/views/public/plugins/tablesorter/tests/index.html +22 -0
  154. data/share/views/public/plugins/tablesorter/tests/large.html +30 -0
  155. data/share/views/public/plugins/tablesorter/tests/lockedOrder.html +355 -0
  156. data/share/views/public/plugins/tablesorter/tests/metadata.html +320 -0
  157. data/share/views/public/plugins/tablesorter/tests/multiple-headers.html +67 -0
  158. data/share/views/public/plugins/tablesorter/tests/pager.html +553 -0
  159. data/share/views/public/plugins/tablesorter/themes/blue/asc.gif +0 -0
  160. data/share/views/public/plugins/tablesorter/themes/blue/bg.gif +0 -0
  161. data/share/views/public/plugins/tablesorter/themes/blue/blue.zip +0 -0
  162. data/share/views/public/plugins/tablesorter/themes/blue/desc.gif +0 -0
  163. data/share/views/public/plugins/tablesorter/themes/blue/style.css +39 -0
  164. data/share/views/public/plugins/tablesorter/themes/green/asc.png +0 -0
  165. data/share/views/public/plugins/tablesorter/themes/green/bg.png +0 -0
  166. data/share/views/public/plugins/tablesorter/themes/green/desc.png +0 -0
  167. data/share/views/public/plugins/tablesorter/themes/green/green.zip +0 -0
  168. data/share/views/public/plugins/tablesorter/themes/green/style.css +39 -0
  169. data/share/views/public/plugins/wz_jsgraphics.js +1108 -0
  170. data/share/views/public/tmp/foo +0 -0
  171. data/share/views/result.haml +6 -1
  172. data/share/views/step_error.haml +5 -0
  173. data/share/views/user_lists.haml +8 -7
  174. data/share/views/wait.haml +40 -2
  175. metadata +302 -106
  176. data/share/views/compass/screen.sass +0 -8
  177. data/share/views/public/plugins/jquery/js/jquery-1.6.2.min.js +0 -18
@@ -52,6 +52,8 @@ class RbbtRestClient
52
52
  RestClient.get(File.join(task.url, task.name.to_s, @remote_name) + "?_format=raw")
53
53
  when :tsv
54
54
  TSV.open(StringIO.new(RestClient.get(File.join(task.url, task.name.to_s, @remote_name) + "?_format=raw")))
55
+ when :annotations
56
+ Annotated.load_tsv(TSV.open(StringIO.new(RestClient.get(File.join(task.url, task.name.to_s, @remote_name) + "?_format=raw"))))
55
57
  else
56
58
  res = JSON.parse(RestClient.get(File.join(task.url, task.name.to_s, @remote_name) + "?_format=json"))
57
59
  res = TSV.setup(res) if task.result_type.to_sym == :tsv
@@ -1,11 +1,14 @@
1
+ require 'rbbt/workflow/rest/render'
2
+ require 'rbbt/workflow/rest/helpers'
3
+
1
4
  module Entity
2
5
 
3
6
  module REST
4
7
  include RbbtHTMLHelpers
5
8
 
6
9
  def rbbt_id
7
- return "" unless self.annotations.include? :format and String === self.format
8
- ([self.format.downcase, self] * "=").gsub(/[^\w]/,'_')
10
+ return "entity_id='#{self.id}'" unless self.annotations.include? :format and String === self.format
11
+ ([self.format.downcase, '"' + self + '"'] * "=").gsub(/[^"\w=]/,'_')
9
12
  end
10
13
 
11
14
  class << self
@@ -14,17 +17,19 @@ module Entity
14
17
 
15
18
  def link(text = nil, url = nil, options = {})
16
19
  return nil if self.nil?
20
+
17
21
  if Array === self
18
22
  text ||= self.name if self.respond_to? :name
19
23
  if Array === text
20
24
  translations = Misc.zip2hash(self, text)
21
- self.collect{|e| e.link(translations[e], url, options)}
25
+ self.collect{|e| e.link(translations[e], url, options) unless e.nil?}.compact
22
26
  else
23
- self.collect{|e| e.link(text, url, options)}
27
+ self.collect{|e| e.link(text, url, options) unless e.nil?}.compact
24
28
  end
25
29
  else
30
+ text = nil if String === text and text.empty?
31
+ text ||= self.name if self.respond_to? :name
26
32
 
27
- text ||= self.name if self.respond_to? :name
28
33
  text = self if text.nil? or text == :literal or text.to_s.strip.empty?
29
34
 
30
35
  klass = options[:class]
@@ -33,98 +38,90 @@ module Entity
33
38
  klass += " " << options[:extra_classes] || "" if options.include? :extra_classes
34
39
  klass += " " << html_class if self.respond_to? :html_class
35
40
 
36
- type = self.respond_to?(:format)? annotation_types.last.to_s +
41
+ main_annotation = options[:entity_type] || annotation_types.select{|a| Entity === a}.last.to_s
42
+ entity_type = self.respond_to?(:format)? main_annotation +
37
43
  ":" +
38
- (self.format || "") : annotation_types.last.to_s
44
+ CGI.escape(self.format || "") : main_annotation
39
45
 
40
- params = options[:params] || self.info.reject{|k,v| [:annotation_types, :format].include?(k)}
46
+ params = options[:params] || self.info(true).reject{|k,v| [:annotation_types, :format].include?(k)}
41
47
  params.merge! options[:extra_params] if options.include? :extra_params
48
+ params.merge! :entity_id => self.id if annotation_types.select{|annotation_type| annotation_type.respond_to?(:keep_id) and annotation_type.keep_id}.any?
42
49
 
43
50
  url ||= if options.include? :action
44
- File.join("/entity_action", type, options[:action].to_s, self)
51
+ File.join("/entity_action", entity_type, options[:action].to_s, self)
45
52
  else
46
- File.join("/entity", type, self)
53
+ File.join("/entity", entity_type, self)
47
54
  end
48
55
 
49
- annotations_str = params.collect{|k,v| [k, CGI.escape(v.to_s)] * "="} * "&"
50
- "<a class='#{klass}' #{rbbt_id} href='#{url + (annotations_str.any? ? '?' + annotations_str : "")}'>#{text}</a>"
56
+ annotations_str = params.collect{|k,v| v = v * "|" if Array === v; [k, CGI.escape(v.to_s)] * "="} * "&amp;"
57
+ "<a class='#{klass}' #{rbbt_id} href=\"#{annotations_str.empty? ? url : url + '?' + annotations_str}\" #{options.include?(:html_link_extra_attrs)? options[:html_link_extra_attrs] : "" } title='#{text}'>#{text}</a>"
51
58
  end
52
59
  end
53
60
 
54
- def self.list_file(type, id, user = nil)
55
- type = type.split(":").first if not type.nil? and type.index ':'
56
- if production?
57
- if user.nil?
58
- File.join(Entity.entity_list_cache, type.to_s, id)
59
- else
60
- File.join(Entity.entity_list_cache, user.to_s, type.to_s, id)
61
- end
61
+ def self.list_file(entity_type, id, user = nil)
62
+ entity_type = entity_type.split(":").first if not entity_type.nil? and entity_type.index ':'
63
+
64
+ id = CGI.unescape(id)
65
+ id = Misc.sanitize_filename(id)
66
+ raise "Ilegal list id: #{ id }" unless Misc.path_relative_to ".", File.join('.', id)
67
+
68
+ if user.nil?
69
+ File.join(Entity.entity_list_cache, entity_type.to_s, id)
62
70
  else
63
- nil
71
+ File.join(Entity.entity_list_cache, user.to_s, entity_type.to_s, id)
64
72
  end
65
73
  end
66
74
 
67
75
  def self.list_files(user)
68
- if production?
69
- lists = {}
70
- Dir.glob(File.join(Entity.entity_list_cache, user, '*', '*')).each do |file|
71
- user, type, list = file.split("/")[-3..-1]
72
- lists[type] ||= []
73
- lists[type] << list
74
- end
75
- lists
76
- else
77
- {}
76
+ lists = {}
77
+ Dir.glob(File.join(Entity.entity_list_cache, user, '*', '*')).each do |file|
78
+ next if File.directory? file
79
+
80
+ file = File.expand_path(file)
81
+ raise "Ilegal path: #{ file }. Not relative to #{File.expand_path('.')}" unless Misc.path_relative_to(File.expand_path('.'), file)
82
+
83
+ user, entity_type, list = file.split("/")[-3..-1]
84
+ lists[entity_type] ||= []
85
+ lists[entity_type] << list
78
86
  end
87
+ lists
79
88
  end
80
89
 
81
- def self.load_list(type, id, user = nil)
82
- if production?
83
- path = list_file(type, id, user)
84
- path = list_file(type, id, :public) unless File.exists? path
85
- path = list_file(type, id) unless File.exists? path
90
+ def self.load_list(entity_type, id, user = nil)
91
+ path = list_file(entity_type, id, user)
92
+ path = list_file(entity_type, id, :public) unless path != nil and File.exists? path
93
+ path = list_file(entity_type, id) unless path != nil and File.exists? path
86
94
 
87
- Misc.lock path do
88
- Annotated.load_tsv TSV.open(path)
89
- end
90
- else
91
- @@list_cache ||= {}
92
- @@list_cache[id]
95
+ begin
96
+ Annotated.load_tsv TSV.open(path)
97
+ rescue
98
+ nil
93
99
  end
94
100
  end
95
101
 
96
- def self.save_list(type, id, list, user = nil)
97
- if production?
98
- path = list_file(type, id, user)
99
-
100
- Misc.lock path do
101
- begin
102
- Open.write(path, Annotated.tsv(list, :all).to_s)
103
- rescue
104
- FileUtils.rm path if File.exists? path
105
- raise $!
106
- end
102
+ def self.save_list(entity_type, id, list, user = nil)
103
+ path = list_file(entity_type, id, user)
104
+
105
+ Misc.lock path do
106
+ begin
107
+ Open.write(path, Annotated.tsv(list, :all).to_s)
108
+ rescue
109
+ FileUtils.rm path if File.exists? path
110
+ raise $!
107
111
  end
108
- else
109
- @@list_cache ||= {}
110
- @@list_cache[id] = list
111
112
  end
112
113
  end
113
114
 
114
115
 
115
- def self.delete_list(type, id, user)
116
- if production?
117
- path = list_file(type, id, user)
118
- "This list does not belong to #{ user }: #{[type, id] * ": "}" unless File.exists? path
119
- Misc.lock path do
120
- begin
121
- FileUtils.rm path if File.exists? path
122
- rescue
123
- raise $!
124
- end
116
+ def self.delete_list(entity_type, id, user)
117
+ path = list_file(entity_type, id, user)
118
+ "This list does not belong to #{ user }: #{[entity_type, id] * ": "}" unless File.exists? path
119
+ Misc.lock path do
120
+ begin
121
+ FileUtils.rm path if File.exists? path
122
+ rescue
123
+ raise $!
125
124
  end
126
- else
127
- @@list_cache.delete(id) if @@list_cache and @@list_cache.include? id
128
125
  end
129
126
  end
130
127
 
@@ -134,19 +131,24 @@ module Entity
134
131
 
135
132
  id ||= options[:id] || Misc.digest((self * "|").inspect)
136
133
 
134
+ id = clean_list_id(id)
137
135
 
138
136
  klass = (self.respond_to?(:format) and not self.format.nil?) ? self.format.gsub(/\s/,'_') : annotation_types.collect{|t| t.to_s.gsub(/\s/,'_')} * " "
139
137
 
140
138
  klass += " " << options[:extra_classes] if options.include? :extra_classes
141
139
  klass += " " << html_class if self.respond_to? :html_class
142
140
 
143
- type = (self.respond_to?(:format) and not self.format.nil?) ? annotation_types.last.to_s + ":" + self.format : annotation_types.last.to_s
141
+ main_annotation = options[:entity_type] || annotation_types.select{|a| Entity === a}.last.to_s
142
+ entity_type = self.respond_to?(:format)? main_annotation +
143
+ ":" +
144
+ CGI.escape(self.format || "") : main_annotation
145
+
144
146
 
145
147
  Entity::REST.save_list(annotation_types.last.to_s, id, self) unless reuse
146
148
 
147
- annotations_str = self.info.reject{|k,v| [:annotation_types, :format].include?(k)}.collect{|k,v| [k, CGI.escape(v.to_s)] * "="} * "&"
149
+ annotations_str = self.info(true).reject{|k,v| [:annotation_types, :format].include?(k)}.collect{|k,v| [k, CGI.escape(v.to_s)] * "="} * "&amp;"
148
150
 
149
- "<a class='entity_list #{klass}' href='#{File.join("/entity_list", type, id) + (annotations_str.any? ? '?' + annotations_str : "")}'>#{text}</a>"
151
+ "<a class='entity_list #{klass}' href='#{File.join("/entity_list", entity_type, CGI.escape(id)) + (! annotations_str.empty? ? '?' + annotations_str : "")}' title='#{text}'>#{text}</a>"
150
152
  end
151
153
 
152
154
  def action_link(action, text = nil, inputs = {})
@@ -157,7 +159,7 @@ module Entity
157
159
  clean_inputs[k] = Array === v ? v * "," : v
158
160
  end
159
161
 
160
- klass ||= "entity_action " + (self.respond_to?(:format)? self.format.gsub(/\s/,'_') : annotation_types.collect{|t| t.to_s.gsub(/\s/,'_')} * " ")
162
+ klass ||= "entity_action " + ((self.respond_to?(:format) and not self.format.nil?) ? self.format.gsub(/\s/,'_') : annotation_types.collect{|t| t.to_s.gsub(/\s/,'_')} * " ")
161
163
  klass += " " << options[:extra_classes] if options.include? :extra_classes
162
164
  klass += " " << html_class if self.respond_to? :html_class
163
165
 
@@ -172,19 +174,23 @@ module Entity
172
174
 
173
175
  text = id if text == :id
174
176
 
177
+ id = clean_list_id(id)
175
178
 
176
179
  klass = (self.respond_to?(:format) and not self.format.nil?) ? self.format.gsub(/\s/,'_') : annotation_types.collect{|t| t.to_s.gsub(/\s/,'_')} * " "
177
180
 
178
181
  klass += " " << options.delete(:extra_classes) if options.include? :extra_classes
179
182
  klass += " " << html_class if self.respond_to? :html_class
180
-
181
- type = (self.respond_to?(:format) and not self.format.nil?)? annotation_types.last.to_s + ":" + self.format : annotation_types.last.to_s
183
+
184
+ main_annotation = options[:entity_type] || annotation_types.select{|a| Entity === a}.last.to_s
185
+ entity_type = self.respond_to?(:format)? main_annotation +
186
+ ":" +
187
+ CGI.escape(self.format || "") : main_annotation
182
188
 
183
189
  Entity::REST.save_list(annotation_types.last.to_s, id, self) unless reuse
184
190
 
185
- annotations_str = self.info.merge(options).reject{|k,v| [:annotation_types, :format].include?(k)}.collect{|k,v| [k, CGI.escape(v.to_s)] * "="} * "&"
191
+ annotations_str = self.info.merge(options).reject{|k,v| [:annotation_types, :format].include?(k)}.collect{|k,v| [k, CGI.escape(v.to_s)] * "="} * "&amp;"
186
192
 
187
- "<a class='entity_list_action #{klass}' href='#{File.join("/entity_list_action", type, action, id) + (annotations_str.any? ? '?' + annotations_str : "")}'>#{text}</a>"
193
+ "<a class='entity_list_action #{klass}' href='#{File.join("/entity_list_action", entity_type, action, CGI.escape(id)) + (!annotations_str.empty? ? '?' + annotations_str : "")}' title='#{text}'>#{text}</a>"
188
194
  end
189
195
 
190
196
  def html_table(fields, workflow = nil, field_names = nil)
@@ -0,0 +1,78 @@
1
+ require 'sinatra/base'
2
+ require 'rbbt/ner/finder'
3
+
4
+ module Sinatra
5
+ module RbbtFinder
6
+
7
+ module Helpers
8
+ def finder_find(term)
9
+ results = settings.finder.find(term)
10
+
11
+ results.uniq.collect{|r|
12
+ info = r.info
13
+ format_string = [info[:namespace], info[:format]].compact * ":"
14
+
15
+ info[:code] = r
16
+
17
+ if not format_string.empty?
18
+ info[:value] = r + " [#{format_string}]"
19
+ else
20
+ info[:value] = r
21
+ end
22
+
23
+ score = info.delete :score
24
+
25
+ if Array === score
26
+ info[:score] = score[0]
27
+ info[:best] = score[1]
28
+ end
29
+
30
+ info
31
+ }.sort_by{|i| i[:score] || 0}.reverse
32
+ end
33
+ end
34
+
35
+ def self.registered(app)
36
+ app.helpers RbbtFinder::Helpers
37
+
38
+ app.get '/find' do
39
+ return workflow_render('find.haml') if params[:term].nil?
40
+
41
+ Log.debug "find: #{params.inspect}"
42
+ term = params[:term]
43
+ sorted_results = finder_find(term)
44
+
45
+ raise "No finder defined" if app.finder.nil?
46
+ if request.xhr?
47
+ content_type "application/json"
48
+ sorted_results.to_json
49
+ else
50
+ i = sorted_results.first
51
+ raise "Term not recognized: #{ term }" if i.nil?
52
+ redirect_to_entity(i[:code], i[:format], i[:namespace])
53
+ end
54
+ end
55
+
56
+ app.post '/find' do
57
+ term = params[:term]
58
+ if term =~ /(.*) \[(.*)\]$/
59
+ term = $1
60
+ namespace, format = $2.split(":")
61
+ format, namespace = namespace, nil if format.nil?
62
+
63
+ redirect_to_entity(term, format, namespace)
64
+ else
65
+ sorted_results = finder_find(term)
66
+ i = sorted_results.first
67
+ raise "Term not recognized: #{ term }" if i.nil?
68
+ redirect_to_entity(i[:code], i[:format], i[:namespace])
69
+ end
70
+ end
71
+
72
+ end
73
+ end
74
+
75
+ register RbbtFinder
76
+ end
77
+
78
+
@@ -1,87 +1,328 @@
1
+ require 'rbbt/workflow/annotate'
2
+ require 'yui/compressor'
1
3
 
2
4
  module RbbtHTMLHelpers
3
- def input(type, name, options = {})
4
- id, hide, value, klass, default, description = Misc.process_options options, :id, :hide, :value, :class, :default, :description
5
- str = "<div class='form_input' id='#{ id }'>"
5
+ def clean_list_id(list_id)
6
+ list_id.gsub('/', '_')
7
+ end
6
8
 
7
- if hide
8
- str << "<input #{"id='#{ id }'" if id} type='hidden' name='#{ name }' #{"class='#{ klass }'" if klass} #{"value='#{ value }'" if value}/>" << '</div>' << "\n"
9
- return str
9
+ def log(status, message)
10
+ if Step.log_relay_step
11
+ Step.log_relay_step.log status, message
12
+ else
13
+ Log.warn "[#{ status }] #{ message }"
10
14
  end
15
+ end
16
+
17
+ def included(base)
18
+ base.include AnnotatedModule
19
+ end
20
+
21
+ def format_float(float)
22
+ "%.3f" % float
23
+ end
24
+
25
+ def redirect_to_entity(entity, format, namespace)
26
+ entity_type = Entity.formats[format]
27
+
28
+ entity_options = []
29
+
30
+ if defined? Organism and
31
+ entity_type.annotations.include? :organism and
32
+ ((String === namespace or Symbol === namespace) and
33
+ Organism.organisms.include? namespace.to_s or Organism.organisms.include? namespace.to_s.split("/").first)
34
+ entity_options << ["organism", namespace] * "="
35
+ end
36
+
37
+ entity_options = entity_options.empty? ? "" : "?" + entity_options * "&"
38
+
39
+ if format
40
+ redirect "/entity/#{ entity_type.to_s }:#{ format }/#{ entity }#{entity_options}"
41
+ else
42
+ redirect "/entity/#{ entity_type.to_s }/#{ entity }#{entity_options}"
43
+ end
44
+ end
45
+
46
+ def user_action_defaults
47
+ @@user_action_defaults ||= Persist.open_tokyocabinet(settings.user_action_defaults, false, :marshal)
48
+ end
49
+
50
+ def add_user_defaults(user, entity, action, params)
51
+ return params if params.delete :no_defaults
52
+ key = [user, entity, action] * ":"
53
+
54
+ user_action_defaults.read_and_close do
55
+ if user_action_defaults.include?(key) and not user_action_defaults[key] == :clear and not user_action_defaults[key].nil?
56
+ Log.debug("Loading user defaults for #{key}")
57
+ new = (user_action_defaults[key] || {} ).dup.merge(params)
58
+ new.each do |key, value|
59
+ next if params.include? key
60
+ params[key] = value
61
+ end
62
+ end
63
+ end
64
+
65
+ params
66
+ end
11
67
 
68
+ def save_user_defaults(user, entity, action, params)
69
+ key = [user, entity, action] * ":"
70
+ clear = user_action_defaults.read_and_close do user_action_defaults[key] == :clear end
71
+
72
+ if clear
73
+ Log.debug("Effectively clearing user defaults for #{key}")
74
+ user_action_defaults.write_and_close do
75
+ user_action_defaults.delete key
76
+ end
77
+ else
78
+ Log.debug("Saving user defaults for #{key}")
79
+ user_action_defaults.write_and_close do
80
+ user_action_defaults[key] = Hash[*(params.collect{|k,v| [k,v]}.flatten(1))]
81
+ end
82
+ end
83
+ end
84
+
85
+ def clear_user_defaults(user, entity, action)
86
+ key = [user, entity, action] * ":"
87
+ Log.debug("Clearing user defaults for #{key}")
88
+ user_action_defaults.write_and_close do
89
+ user_action_defaults[key] = :clear
90
+ end
91
+ end
92
+
93
+ def parameter_controls(workflow = nil, values = {}, &block)
94
+ o = Object.new
95
+ o.extend AnnotatedModule
96
+ o.instance_eval &block
97
+
98
+ inputs = o.inputs
99
+ input_types = o.input_types
100
+ input_defaults = o.input_defaults
101
+ input_options = o.input_options
102
+
103
+ hidden_inputs = []
104
+ inputs.each do |input|
105
+ if values[input].nil? and input_defaults.include? input
106
+ values[input] = input_defaults[input]
107
+ end
108
+ hidden_inputs << input if input_options.include? input and input_options[input].delete :hide
109
+ end
110
+
111
+ workflow_partial('partials/_form', workflow, :hide_inputs => hidden_inputs, :inputs => inputs, :input_defaults => input_defaults, :input_options => input_options, :input_types => input_types, :values => values, :action => headers["AJAX-URL"], :klass => 'task', :method => 'get')
112
+ end
113
+
114
+ def link_to_resource(filename = nil, text = nil, type = nil, options = {})
115
+ case
116
+ when filename.nil?
117
+ filename = File.basename(TmpFile.tmp_file)
118
+ when filename[0] == "."[0]
119
+ extension = filename
120
+ filename = File.basename(TmpFile.tmp_file) + extension
121
+ end
122
+
123
+ text ||= filename
124
+
125
+ filename = Misc.sanitize_filename(filename)
126
+ f = File.join(settings.file_dir, filename)
127
+ FileUtils.mkdir_p(File.dirname(f))
128
+ yield(f)
129
+
130
+ type ||= :link
12
131
  case type
13
- when :boolean
14
- not_value = ! value
15
- str << "<label #{"for='#{ id }" if id}'><span class='input_name'>#{ name }</span>#{": <span class='input_description'>#{ description }</span>" if description}</label>" << "\n"
16
- str << "<input type='radio' id='input_#{id}_true' name='#{ name }' value='true' #{"checked='true'" if value}>" << "\n"
17
- str << "<label type='radio_option radio_true' for='input_#{id}_true' name='#{ name }'>true</label>" << "\n"
18
- str << "<input type='radio' id='input_#{id}_false' name='#{ name }' value='false' #{"checked='false'" if not value}>" << "\n"
19
- str << "<label type='radio_option radio_false' for='input_#{id}_false' name='#{ name }'>false</label>" << "\n"
20
- str << "</div>"
21
- str
22
- when :string, :float, :integer
23
- str << "<label #{"for='#{ id }" if id}'><span class='input_name'>#{ name }</span>#{": <span class='input_description'>#{ description }</span>" if description}#{": <span class='input_default'>(Default: #{ default })</span>" if default}</label>" << "\n"
24
- str << "<input #{"class='#{klass ? klass : "#{ name } #{ type }" }'"} #{"id='#{ id }'" if id} name='#{ name }' #{"value='#{ value }'" if value}/>" << "\n"
25
- str << "</div>"
26
- str
27
- when :tsv, :array, :text
28
- str << "<label #{"for='#{ id }" if id}'><span class='input_name'>#{ name }</span>#{": <span class='input_description'>#{ description }</span>" if description}#{": <span class='input_default'>(Default: #{ default })</span>" if default}</label>" << "\n"
29
- str << "<p>"
30
- str << "<input type='file' #{"class='#{klass ? klass : "#{ name } #{ type }" }'"} #{"id='input_#{ id }_tsv_file'" if id} name='#{ name }_param_file' #{"value='#{ value }'" if value}/>" << "\n"
31
- str << "<span class='tsv_textarea_note note'>(or use text area bellow)</span>"
32
- str << "</p>"
33
- str << "<textarea #{"class='#{klass ? klass : "#{ name } #{ type }" }'"} name='#{ name }' #{"id='input_#{ id }'" if id}></textarea>"
34
- str << "</div>"
35
- str
132
+ when :image
133
+ "<img src='/files/#{ filename }' class='file_resource'/>"
134
+ when :link
135
+ "<a href='/files/#{ filename }' class='file_resource'>#{ text }</a>"
136
+ when :linked_image
137
+ "<a href='/files/#{ filename }' class='file_resource' target='_blank'><img src='/files/#{ filename }' class='file_resource'/></a>"
138
+ when :zoomable_image
139
+ id = options[:id] || Misc.digest(filename)
140
+ width, height= [300, 300]
141
+ "<div class='zoomable_image'><img id='#{id}' style='width:#{width}px; height:#{height}px' rel='/files/#{ filename }' src='/files/#{ filename }' class='file_resource'/></div>"
142
+ when :mapped_image
143
+ mapid = options[:mapid] || options[:id] + '_map'
144
+ width, height= [300, 300]
145
+ mapfile = f.sub(/\.[^.]+$/, '.html')
146
+ "<div class='mapped_image'>#{Open.read(mapfile)}<img class='has_map' usemap='##{mapid}' rel='/files/#{ filename }' src='/files/#{ filename }' class='file_resource'/></div>"
147
+ else
148
+ raise "Type not understood: #{ type }"
36
149
  end
37
150
  end
38
151
 
39
- def input(type, name, options = {})
40
- id, hide, value, klass, default, description, select_options = Misc.process_options options, :id, :hide, :value, :class, :default, :description, :select_options
41
- str = "<div class='form_input' id='#{ id }'>"
152
+ def html_options_str(html_options = {})
153
+ return "" if html_options.nil? or html_options.empty?
154
+ html_options.collect{|k,v|
155
+ case
156
+ when (k.nil? or v.nil? or (String === v and v.empty?))
157
+ nil
158
+ when String === v
159
+ [k,"'" + v + "'"] * "="
160
+ when Symbol === v
161
+ [k,"'" + v.to_s + "'"] * "="
162
+ when TrueClass === v
163
+ [k,"'" + k.to_s + "'"] * "="
164
+ else
165
+ nil
166
+ end
167
+ }.compact * " "
168
+ end
169
+
170
+ def form_input(type, name, options = {})
171
+ id, label, hide, value, klass, default, description, input_options, array_separator = Misc.process_options options,
172
+ :id, :label, :hide, :value, :class, :default, :description, :input_options, :array_separator
173
+
174
+ html_options ||= (input_options.nil? ? nil : input_options[:html_options]) || {}
175
+
176
+ if id and not id.empty?
177
+ str = "<div class='form_input' id='#{ id }'>"
178
+ else
179
+ str = "<div class='form_input'>"
180
+ end
181
+
182
+ array_separator ||= '|'
183
+ label ||= name
184
+
185
+ hide ||= html_options.delete :hidden
42
186
 
43
187
  if hide
44
- str << "<input #{"id='#{ id }'" if id} type='hidden' name='#{ name }' #{"class='#{ klass }'" if klass} #{"value='#{ value }'" if value}/>" << '</div>' << "\n"
188
+ value = value * array_separator if Array === value
189
+
190
+ html_options.merge! :id => id,
191
+ :type => 'hidden',
192
+ :name => name,
193
+ :value => value.to_s,
194
+ :class => [html_options[:class], klass].compact * " "
195
+
196
+ str = "<input #{html_options_str(html_options)}/>" << "\n"
45
197
  return str
46
198
  end
47
199
 
48
200
  case type
49
201
  when :select
50
- str << "<label #{"for='#{ id }" if id}'><span class='input_name'>#{ name }</span>#{": <span class='input_description'>#{ description }</span>" if description}</label>" << "\n"
51
- str << "<select id='input_#{id}_true' name='#{ name }'>" << "\n"
202
+ str << "<label #{html_options_str({:for => id})}>"
203
+ str << "<span class='input_name'>#{ label }</span>"
204
+ str << ": <span class='input_description'>#{ description }</span>" if description
205
+ str << "</label>" << "\n"
206
+
207
+ str << "<select #{html_options_str(html_options.merge :name => name, :id => ((id and not id.empty?) ? "input_#{id}_select" : nil ))}>" << "\n"
208
+
209
+ select_options = input_options.nil? ? nil : input_options[:select_options]
52
210
  if not select_options.nil?
53
211
  select_options.each do |option|
54
212
  option, option_name = option if Array === option
55
213
  option_name ||= option
56
- str << "<option id='input_#{id}_option_#{option.to_s}' value='#{option.to_s}' #{option.to_s == value.to_s ? "selected=selected" : "" }>" << option_name.to_s << "</option>" << "\n"
214
+ str << "<option #{html_options_str(html_options.merge :value => option, :selected => option.to_s == value.to_s)}>" << option_name.to_s << "</option>" << "\n"
57
215
  end
58
216
  end
217
+
218
+ if html_options["attr-allow-empty"]
219
+ str << "<option #{html_options_str(html_options.merge :value => "none", :selected => "none" == value.to_s)}>none</option>" << "\n"
220
+ end
221
+
59
222
  str << "</select>" << "\n"
60
223
  str << "</div>"
61
224
  str
62
225
  when :boolean
63
- not_value = ! value
64
- str << "<label #{"for='#{ id }" if id}'><span class='input_name'>#{ name }</span>#{": <span class='input_description'>#{ description }</span>" if description}</label>" << "\n"
65
- str << "<input type='radio' id='input_#{id}_true' name='#{ name }' value='true' #{"checked='true'" if value}>" << "\n"
66
- str << "<label type='radio_option radio_true' for='input_#{id}_true' name='#{ name }'>true</label>" << "\n"
67
- str << "<input type='radio' id='input_#{id}_false' name='#{ name }' value='false' #{"checked='false'" if not value}>" << "\n"
68
- str << "<label type='radio_option radio_false' for='input_#{id}_false' name='#{ name }'>false</label>" << "\n"
226
+ value = false if value == "false"
227
+ default = value unless value.nil?
228
+ default = false if default == "false"
229
+ default = true if default == "true"
230
+
231
+ check_true = default and %w(true yes y).include? default.to_s.downcase
232
+ check_false = (default != nil and %w(false no n).include?(default.to_s.downcase))
233
+
234
+ str << "<label #{"for='#{ id }'" if id}>"
235
+ str << "<span class='input_name'>#{ label }</span>"
236
+ str << "#{": <span class='input_description'>#{ description }</span>" if description}</label>" << "\n"
237
+
238
+ str << "<input type='radio' #{check_true ? "checked=checked " : ""}#{html_options_str(html_options.merge :name => name, :value => 'true', :id => ((id and not id.empty?) ? "input_#{id}_true" : nil))}>" << "\n"
239
+ str << "<label #{html_options_str(html_options.merge :for => ((id and not id.empty?) ? "input_#{id}_true" : nil), :class => [html_options[:class], 'radio_option radio_true'].compact * " ")}>true</label>" << "\n"
240
+
241
+ str << "<input type='radio' #{check_false ? "checked=checked " : ""}#{html_options_str(html_options.merge :name => name, :value => 'false', :id => ((id and not id.empty?) ? "input_#{id}_false" : nil))}>" << "\n"
242
+ str << "<label #{html_options_str(html_options.merge :for => ((id and not id.empty?) ? "input_#{id}_false" : nil), :class => [html_options[:class], 'radio_option radio_false'].compact * " ")}>false</label>" << "\n"
69
243
  str << "</div>"
70
244
  str
71
245
  when :string, :float, :integer
72
- str << "<label #{"for='#{ id }" if id}'><span class='input_name'>#{ name }</span>#{": <span class='input_description'>#{ description }</span>" if description}#{": <span class='input_default'>(Default: #{ default })</span>" if default}</label>" << "\n"
73
- str << "<input #{"class='#{klass ? klass : "#{ name } #{ type }" }'"} #{"id='#{ id }'" if id} name='#{ name }' #{"value='#{ value }'" if value}/>" << "\n"
246
+ value = value * array_separator if Array === value
247
+ str << "<label #{"for='#{ id }'" if id}><span class='input_name'>#{ label }</span>#{": <span class='input_description'>#{ description }</span>" if description}#{": <span class='input_default'>(Default: #{ default })</span>" if default}</label>" << "\n"
248
+ str << "<input #{html_options_str(html_options)} #{"class='#{klass ? klass : "#{ name } #{ type }" }'"} #{"id='#{ id }'" if id} name='#{ name }' #{"value='#{ value }'" if value}/>" << "\n"
74
249
  str << "</div>"
75
250
  str
76
251
  when :tsv, :array, :text
77
- str << "<label #{"for='#{ id }" if id}'><span class='input_name'>#{ name }</span>#{": <span class='input_description'>#{ description }</span>" if description}#{": <span class='input_default'>(Default: #{ default })</span>" if default}</label>" << "\n"
252
+ str << "<label #{"for='#{ id }'" if id}><span class='input_name'>#{ label }</span>#{": <span class='input_description'>#{ description }</span>" if description}#{": <span class='input_default'>(Default: #{ default })</span>" if default}</label>" << "\n"
78
253
  str << "<p>"
79
- str << "<input type='file' #{"class='#{klass ? klass : "#{ name } #{ type }" }'"} #{"id='input_#{ id }_tsv_file'" if id} name='#{ name }_param_file' #{"value='#{ value }'" if value}/>" << "\n"
254
+ str << "<input #{html_options_str(html_options)} type='file' #{"class='#{klass ? klass : "#{ name } #{ type }" }'"} #{"id='input_#{ id }_tsv_file'" if id} name='#{ name }_param_file' #{"value='#{ value }'" if value}/>" << "\n"
80
255
  str << "<span class='tsv_textarea_note note'>(or use text area bellow)</span>"
81
256
  str << "</p>"
82
- str << "<textarea #{"class='#{klass ? klass : "#{ name } #{ type }" }'"} name='#{ name }' #{"id='input_#{ id }'" if id}></textarea>"
257
+ str << "<textarea #{"class='#{klass ? klass : "#{ name } #{ type }" }'"} name='#{ name }' #{"id='input_#{ id }'" if id}>#{Array === value ? value * "\n" : value}</textarea>"
83
258
  str << "</div>"
84
259
  str
85
260
  end
86
261
  end
262
+
263
+ def table(rows, header = nil, options = {})
264
+ workflow_partial('partials/_table', StudyExplorer, nil, options.merge(:rows => rows, :header => header))
265
+ end
266
+
267
+ def followed_users(user)
268
+ settings.followed_users[user] || []
269
+ end
270
+
271
+ def record_js(file)
272
+ @js_files << file
273
+ end
274
+
275
+ def serve_js(compress = true)
276
+ res = if production? and compress
277
+
278
+ md5 = Misc.digest(@js_files * ",")
279
+ filename = File.join(settings.file_dir, "all_js-#{md5}.js")
280
+
281
+ if not File.exists?(filename)
282
+ text = @js_files.collect{|file|
283
+ file += '.js' unless file =~ /.js$/
284
+ if file =~ /^\/js\/entity\/(.*?)\/(.*?)\/(.*)/
285
+ entity_type, format = $1.split ":"
286
+ entity = setup_entity(entity_type, format, $2)
287
+ path = locate_file(WorkflowREST.workflows.last, nil, File.join("js", $3), entity)
288
+ else
289
+ path = begin
290
+ locate_file(WorkflowREST.workflows.last, nil, file.sub(/^\//,'')).find
291
+ rescue
292
+ locate_file(WorkflowREST.workflows.last, nil, File.join('public', file)).find
293
+ end
294
+ end
295
+ Open.read(path)
296
+ } * "\n"
297
+
298
+
299
+ FileUtils.mkdir_p File.dirname(filename) unless File.exists? File.dirname(filename)
300
+ Open.write(filename, YUI::JavaScriptCompressor.new(:munge => false).compress(text))
301
+ end
302
+
303
+ "<script src='/files/#{File.basename(filename)}' type='text/javascript'></script>"
304
+ else
305
+ @js_files.collect{|file|
306
+ file += '.js' unless file =~ /.js$/
307
+ "<script src='#{ file }' type='text/javascript'></script>"
308
+ } * "\n"
309
+ end
310
+ @js_files.clear
311
+ res
312
+ end
313
+
314
+ def record_css(file)
315
+ @css_files << file
316
+ end
317
+
318
+ def serve_css
319
+ res = @css_files.collect{|file|
320
+ file += '.css' unless file =~ /.css$/
321
+ "<link href='#{file}' rel='stylesheet' type='text/css' />"
322
+ } * "\n"
323
+ @css_files.clear
324
+ res
325
+ end
326
+
327
+
87
328
  end