grnsight 4.0.0 → 6.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (458) hide show
  1. package/.travis.yml +3 -1
  2. package/GRNsight - Beta.html +194 -0
  3. package/Gemfile.lock +259 -0
  4. package/README.md +2 -2
  5. package/_gh_pages/about.html +360 -45
  6. package/_gh_pages/assets/css/footer.css +3 -0
  7. package/_gh_pages/assets/css/main.css +28 -14
  8. package/_gh_pages/assets/images/21-genes_31-edges_Schade-data_estimation_output_binary-no-targetless-genes_sif.png +0 -0
  9. package/_gh_pages/assets/images/21-genes_31-edges_Schade-data_estimation_output_binary_sif.png +0 -0
  10. package/_gh_pages/assets/images/21-genes_31-edges_Schade-data_graphml_3-edges-and-footer.png +0 -0
  11. package/_gh_pages/assets/images/21-genes_31-edges_Schade-data_graphml_header-and-3-nodes.png +0 -0
  12. package/_gh_pages/assets/images/21-genes_31-edges_Schade-data_graphml_output_3-edges-and-footer.png +0 -0
  13. package/_gh_pages/assets/images/21-genes_31-edges_Schade-data_graphml_output_header-and-3-nodes.png +0 -0
  14. package/_gh_pages/assets/images/21-genes_31-edges_Schade-data_input_binary-no-targetless-genes_sif.png +0 -0
  15. package/_gh_pages/assets/images/21-genes_31-edges_Schade-data_input_binary_sif.png +0 -0
  16. package/_gh_pages/assets/images/21-genes_31-edges_Schade-data_input_concatenated-no-targetless-genes_sif.png +0 -0
  17. package/_gh_pages/assets/images/21-genes_31-edges_Schade-data_input_concatenated_sif.png +0 -0
  18. package/_gh_pages/assets/images/Choe-Shin_CMSI402-poster-session_20180430.jpg +0 -0
  19. package/_gh_pages/assets/images/Choe_SCCUR_2017.jpg +0 -0
  20. package/_gh_pages/assets/images/Dahlquist-Choe-Shin_CMSI402-poster-session_20180430.jpg +0 -0
  21. package/_gh_pages/assets/images/Dionisio-Dahlquist_GRNsight-shades_20170506.jpg +0 -0
  22. package/_gh_pages/assets/images/Klein_Samdarshi_TriBeta_2018_20180317.jpg +0 -0
  23. package/_gh_pages/assets/images/Shin_SCCUR_2017.jpg +0 -0
  24. package/{documents/manuscripts/peerj-computerscience-2016/figures/submitted-versions/Figure1_zoom145_900pix-wide.png → _gh_pages/assets/images/demo-3_network-sheet.png} +0 -0
  25. package/{documents/manuscripts/peerj-computerscience-2016/figures/submitted-versions/Figure2_zoom145_900pix-wide.png → _gh_pages/assets/images/demo-4_network-optimized-weights-sheet.png} +0 -0
  26. package/_gh_pages/assets/images/gene-pages-0.png +0 -0
  27. package/_gh_pages/assets/images/gene-pages-1.png +0 -0
  28. package/_gh_pages/assets/images/gene-pages-2.png +0 -0
  29. package/_gh_pages/assets/images/gene-pages-3.png +0 -0
  30. package/_gh_pages/assets/images/grnsight2020.png +0 -0
  31. package/_gh_pages/assets/images/v3demo2-grid+nodecoloring.png +0 -0
  32. package/_gh_pages/assets/images/v3demo2-nodecoloring.png +0 -0
  33. package/_gh_pages/assets/images/v3demo2.png +0 -0
  34. package/_gh_pages/assets/js/ga-report.js +11 -11
  35. package/_gh_pages/assets/js/iframeResizer.min.js +9 -0
  36. package/_gh_pages/assets/js/main.js +43 -43
  37. package/_gh_pages/beta.html +29 -24
  38. package/_gh_pages/contact.html +31 -31
  39. package/_gh_pages/coverage/coverage.json +1 -0
  40. package/_gh_pages/coverage/coverage.raw.json +1 -0
  41. package/_gh_pages/coverage/lcov-report/base.css +223 -0
  42. package/_gh_pages/coverage/lcov-report/block-navigation.js +63 -0
  43. package/_gh_pages/coverage/lcov-report/controllers/additional-sheet-parser.js.html +330 -0
  44. package/_gh_pages/coverage/lcov-report/controllers/constants.js.html +243 -0
  45. package/_gh_pages/coverage/lcov-report/controllers/export-controller.js.html +285 -0
  46. package/_gh_pages/coverage/lcov-report/controllers/exporters/graphml.js.html +405 -0
  47. package/_gh_pages/coverage/lcov-report/controllers/exporters/index.html +110 -0
  48. package/_gh_pages/coverage/lcov-report/controllers/exporters/sif.js.html +150 -0
  49. package/_gh_pages/coverage/lcov-report/controllers/helpers.js.html +114 -0
  50. package/_gh_pages/coverage/lcov-report/controllers/import-controller.js.html +233 -0
  51. package/_gh_pages/coverage/lcov-report/controllers/importers/graphml.js.html +716 -0
  52. package/_gh_pages/coverage/lcov-report/controllers/importers/index.html +106 -0
  53. package/_gh_pages/coverage/lcov-report/controllers/importers/sif.js.html +488 -0
  54. package/_gh_pages/coverage/lcov-report/controllers/index.html +162 -0
  55. package/_gh_pages/coverage/lcov-report/controllers/semantic-checker.js.html +810 -0
  56. package/_gh_pages/coverage/lcov-report/controllers/spreadsheet-controller.js.html +1779 -0
  57. package/_gh_pages/coverage/lcov-report/index.html +136 -0
  58. package/_gh_pages/coverage/lcov-report/prettify.css +1 -0
  59. package/_gh_pages/coverage/lcov-report/prettify.js +1 -0
  60. package/_gh_pages/coverage/lcov-report/server/controllers/additional-sheet-parser.js.html +330 -0
  61. package/_gh_pages/coverage/lcov-report/server/controllers/constants.js.html +243 -0
  62. package/_gh_pages/coverage/lcov-report/server/controllers/export-controller.js.html +285 -0
  63. package/_gh_pages/coverage/lcov-report/server/controllers/exporters/graphml.js.html +405 -0
  64. package/_gh_pages/coverage/lcov-report/server/controllers/exporters/index.html +110 -0
  65. package/_gh_pages/coverage/lcov-report/server/controllers/exporters/sif.js.html +150 -0
  66. package/_gh_pages/coverage/lcov-report/server/controllers/graphml-constants.js.html +585 -0
  67. package/_gh_pages/coverage/lcov-report/server/controllers/helpers.js.html +114 -0
  68. package/_gh_pages/coverage/lcov-report/server/controllers/import-controller.js.html +237 -0
  69. package/_gh_pages/coverage/lcov-report/server/controllers/importers/graphml.js.html +585 -0
  70. package/_gh_pages/coverage/lcov-report/server/controllers/importers/index.html +110 -0
  71. package/_gh_pages/coverage/lcov-report/server/controllers/importers/sif.js.html +492 -0
  72. package/_gh_pages/coverage/lcov-report/server/controllers/index.html +188 -0
  73. package/_gh_pages/coverage/lcov-report/server/controllers/semantic-checker.js.html +810 -0
  74. package/_gh_pages/coverage/lcov-report/server/controllers/spreadsheet-controller.js.html +1779 -0
  75. package/_gh_pages/coverage/lcov-report/sort-arrow-sprite.png +0 -0
  76. package/_gh_pages/coverage/lcov-report/sorter.js +158 -0
  77. package/_gh_pages/coverage/lcov-report/web-client/public/js/grnstate.js.html +225 -0
  78. package/_gh_pages/coverage/lcov-report/web-client/public/js/index.html +97 -0
  79. package/_gh_pages/coverage/lcov.info +49 -0
  80. package/_gh_pages/documentation.html +998 -320
  81. package/_gh_pages/documents/abstracts/SIGGRAPH 2017 Abstract/siggraph-abstract-review.aux +47 -0
  82. package/_gh_pages/documents/abstracts/SIGGRAPH 2017 Abstract/siggraph-abstract-review.bbl +73 -0
  83. package/_gh_pages/documents/abstracts/SIGGRAPH 2017 Abstract/siggraph-abstract-review.blg +52 -0
  84. package/_gh_pages/documents/abstracts/SIGGRAPH 2017 Abstract/siggraph-abstract-review.log +1056 -0
  85. package/_gh_pages/documents/abstracts/SIGGRAPH 2017 Abstract/siggraph-abstract-review.out +7 -0
  86. package/_gh_pages/documents/abstracts/SIGGRAPH 2017 Abstract/siggraph-abstract-review.synctex.gz +0 -0
  87. package/_gh_pages/documents/manuscripts/peerj-computerscience-2016/revisions/GRNsight_PeerJ-CS_manuscript_2016_text-only_revised-Dondi.docx +0 -0
  88. package/_gh_pages/encryption/server.cert +21 -0
  89. package/_gh_pages/encryption/server.key +28 -0
  90. package/_gh_pages/favicon.ico +0 -0
  91. package/_gh_pages/index.html +45 -22
  92. package/_gh_pages/links.html +47 -28
  93. package/_gh_pages/news.html +103 -21
  94. package/_gh_pages/onlyfooter.html +78 -0
  95. package/_gh_pages/onlyheader.html +64 -0
  96. package/_gh_pages/onlysidebar.html +73 -0
  97. package/_gh_pages/package-lock.json +14048 -0
  98. package/_gh_pages/people.html +129 -40
  99. package/_gh_pages/privacy.html +23 -17
  100. package/_gh_pages/publications.html +75 -33
  101. package/_gh_pages/robots.txt +1 -0
  102. package/_gh_pages/sitemap.xml +87 -74
  103. package/_gh_pages/test-files/import-samples/attributes.graphml +40 -0
  104. package/_gh_pages/test-files/import-samples/port.graphml +32 -0
  105. package/_gh_pages/test-files/import-samples/simple.graphml +31 -0
  106. package/_gh_pages/web-client/public/js/grnsight.min.js +2347 -0
  107. package/_gh_pages/web-client/public/stylesheets/grnsight.css +443 -0
  108. package/coverage/coverage.json +1 -1
  109. package/coverage/lcov.info +1847 -1142
  110. package/database/README.md +1 -0
  111. package/database/network-database/README.md +44 -0
  112. package/database/network-database/schema.sql +24 -0
  113. package/database/network-database/scripts/filter_genes.py +76 -0
  114. package/database/network-database/scripts/generate_network.py +199 -0
  115. package/database/network-database/scripts/generate_sgd_network_from_yeastract_network.py +120 -0
  116. package/database/network-database/scripts/loader.py +78 -0
  117. package/package.json +20 -11
  118. package/server/app.js +7 -2
  119. package/server/config/config.js +12 -3
  120. package/server/controllers/additional-sheet-parser.js +292 -55
  121. package/server/controllers/constants.js +4 -37
  122. package/server/controllers/custom-workbook-controller.js +66 -0
  123. package/server/controllers/demo-workbooks.js +5973 -0
  124. package/server/controllers/export-constants.js +78 -0
  125. package/server/controllers/export-controller.js +4 -3
  126. package/server/controllers/exporters/graphml.js +15 -15
  127. package/server/controllers/exporters/sif.js +7 -7
  128. package/server/controllers/exporters/xlsx.js +110 -34
  129. package/server/controllers/expression-database-controller.js +19 -0
  130. package/server/controllers/expression-sheet-parser.js +170 -0
  131. package/server/controllers/graphml-constants.js +0 -17
  132. package/server/controllers/helpers.js +25 -1
  133. package/server/controllers/import-controller.js +2 -2
  134. package/server/controllers/importers/graphml.js +17 -20
  135. package/server/controllers/importers/sif.js +22 -18
  136. package/server/controllers/network-database-controller.js +18 -0
  137. package/server/controllers/network-sheet-parser.js +307 -0
  138. package/server/controllers/semantic-checker.js +30 -162
  139. package/server/controllers/sif-constants.js +36 -0
  140. package/server/controllers/spreadsheet-controller.js +277 -425
  141. package/server/controllers/workbook-constants.js +521 -0
  142. package/server/dals/expression-dal.js +122 -0
  143. package/server/dals/network-dal.js +96 -0
  144. package/test/additional-sheet-parser-tests.js +147 -38
  145. package/test/api-tests.js +3 -6
  146. package/test/errors-adjacency-matrix-modifications.js +30 -29
  147. package/test/errors-graph-tests.js +4 -4
  148. package/test/errors-sheet-modifications.js +10 -2
  149. package/test/export-tests.js +187 -60
  150. package/test/expression-data-import-tests.js +113 -0
  151. package/test/grnstate-tests.js +8 -2
  152. package/test/import-graphml-tests.js +59 -40
  153. package/test/import-sif-tests.js +50 -37
  154. package/test/test.js +554 -91
  155. package/test/warnings-adjacency-matrix-modifications.js +8 -7
  156. package/test-files/additional-sheet-test-files/optimization-diagnostics-default.xlsx +0 -0
  157. package/test-files/additional-sheet-test-files/optimization-diagnostics-extraneous-data.xlsx +0 -0
  158. package/test-files/additional-sheet-test-files/optimization-diagnostics-incorrect-MSE-gene-header.xlsx +0 -0
  159. package/test-files/additional-sheet-test-files/optimization-diagnostics-incorrect-MSE-header.xlsx +0 -0
  160. package/test-files/additional-sheet-test-files/optimization-diagnostics-incorrect-column-headers.xlsx +0 -0
  161. package/test-files/additional-sheet-test-files/optimization-diagnostics-invalid-MSE-data.xlsx +0 -0
  162. package/test-files/additional-sheet-test-files/optimization-diagnostics-invalid-value.xlsx +0 -0
  163. package/test-files/additional-sheet-test-files/optimization-diagnostics-missing-MSE-data.xlsx +0 -0
  164. package/test-files/additional-sheet-test-files/optimization-diagnostics-missing-column-headers.xlsx +0 -0
  165. package/test-files/additional-sheet-test-files/optimization-diagnostics-missing-header.xlsx +0 -0
  166. package/test-files/additional-sheet-test-files/optimization-diagnostics-unknown-parameter.xlsx +0 -0
  167. package/test-files/additional-sheet-test-files/optimization-parameters-default.xlsx +0 -0
  168. package/test-files/additional-sheet-test-files/optimization-parameters-incorrect-headers.xlsx +0 -0
  169. package/test-files/additional-sheet-test-files/optimization-parameters-invalid-optimization-parameter.xlsx +0 -0
  170. package/test-files/additional-sheet-test-files/optimization-parameters-missing-headers.xlsx +0 -0
  171. package/test-files/additional-sheet-test-files/optimization-parameters-unknown-parameter.xlsx +0 -0
  172. package/test-files/additional-sheet-test-files/two-column-sheets-extraneous-data.xlsx +0 -0
  173. package/test-files/additional-sheet-test-files/two-column-sheets-incorrect-cell-A1.xlsx +0 -0
  174. package/test-files/additional-sheet-test-files/two-column-sheets-incorrect-column-header.xlsx +0 -0
  175. package/test-files/additional-sheet-test-files/two-column-sheets-invalid-gene-length.xlsx +0 -0
  176. package/test-files/additional-sheet-test-files/two-column-sheets-invalid-gene-type.xlsx +0 -0
  177. package/test-files/additional-sheet-test-files/two-column-sheets-invalid-value.xlsx +0 -0
  178. package/test-files/additional-sheet-test-files/two-column-sheets-missing-column-header.xlsx +0 -0
  179. package/test-files/additional-sheet-test-files/two-column-sheets-special-character.xlsx +0 -0
  180. package/test-files/adjacency-matrix-modifications/asymmetric-gene-order-input.xlsx +0 -0
  181. package/test-files/adjacency-matrix-modifications/asymmetric-gene-order-output.xlsx +0 -0
  182. package/test-files/adjacency-matrix-modifications/extra-column-adjacent-input.xlsx +0 -0
  183. package/test-files/adjacency-matrix-modifications/extra-column-adjacent-output.xlsx +0 -0
  184. package/test-files/adjacency-matrix-modifications/extra-column-end-of-sheet-input.xlsx +0 -0
  185. package/test-files/adjacency-matrix-modifications/extra-column-end-of-sheet-output.xlsx +0 -0
  186. package/test-files/adjacency-matrix-modifications/extra-data-random-cell-both-output.xlsx +0 -0
  187. package/test-files/adjacency-matrix-modifications/extra-data-random-cell-network-only-input.xlsx +0 -0
  188. package/test-files/adjacency-matrix-modifications/extra-data-random-cell-network-only-output.xlsx +0 -0
  189. package/test-files/adjacency-matrix-modifications/extra-data-random-cell-network-optimized-only-output.xlsx +0 -0
  190. package/test-files/adjacency-matrix-modifications/extra-row-end-of-sheet-input.xlsx +0 -0
  191. package/test-files/adjacency-matrix-modifications/extra-row-end-of-sheet-output.xlsx +0 -0
  192. package/test-files/adjacency-matrix-modifications/incorrect-network-cell-A1.xlsx +0 -0
  193. package/test-files/adjacency-matrix-modifications/missing-column-end-input.xlsx +0 -0
  194. package/test-files/adjacency-matrix-modifications/missing-column-end-output.xlsx +0 -0
  195. package/test-files/adjacency-matrix-modifications/missing-data-input.xlsx +0 -0
  196. package/test-files/adjacency-matrix-modifications/missing-data-output.xlsx +0 -0
  197. package/test-files/adjacency-matrix-modifications/missing-row-end-input.xlsx +0 -0
  198. package/test-files/adjacency-matrix-modifications/missing-row-end-output.xlsx +0 -0
  199. package/test-files/adjacency-matrix-modifications/missing-row-middle-input.xlsx +0 -0
  200. package/test-files/adjacency-matrix-modifications/missing-row-middle-output.xlsx +0 -0
  201. package/test-files/adjacency-matrix-modifications/missing-value-bottom-corner-input.xlsx +0 -0
  202. package/test-files/adjacency-matrix-modifications/missing-value-bottom-corner-output.xlsx +0 -0
  203. package/test-files/adjacency-matrix-modifications/value-replaced-w-spaces-both-output.xlsx +0 -0
  204. package/test-files/adjacency-matrix-modifications/value-replaced-w-spaces-net-only-input.xlsx +0 -0
  205. package/test-files/adjacency-matrix-modifications/value-replaced-w-spaces-net-op-only-output.xlsx +0 -0
  206. package/test-files/adjacency-matrix-modifications/value-replaced/342/200/223w-spaces-net-only-output.xlsx +0 -0
  207. package/test-files/demo-files/15-genes_28-edges_db5_Dahlquist-data_estimation_output.xlsx +0 -0
  208. package/test-files/demo-files/15-genes_28-edges_db5_Dahlquist-data_input.xlsx +0 -0
  209. package/test-files/expression-data-test-sheets/expression_sheet_correct_numbering.xlsx +0 -0
  210. package/test-files/expression-data-test-sheets/expression_sheet_different_number_of_columns.xlsx +0 -0
  211. package/test-files/expression-data-test-sheets/expression_sheet_empty_column.xlsx +0 -0
  212. package/test-files/expression-data-test-sheets/expression_sheet_empty_row.xlsx +0 -0
  213. package/test-files/expression-data-test-sheets/expression_sheet_erroneous_data.xlsx +0 -0
  214. package/test-files/expression-data-test-sheets/expression_sheet_extra_gene_name.xlsx +0 -0
  215. package/test-files/expression-data-test-sheets/expression_sheet_incorrectly_ordered_time_points.xlsx +0 -0
  216. package/test-files/expression-data-test-sheets/expression_sheet_mismatched_case_gene_names.xlsx +0 -0
  217. package/test-files/expression-data-test-sheets/expression_sheet_mismatched_gene_names.xlsx +0 -0
  218. package/test-files/expression-data-test-sheets/expression_sheet_missing_column_header.xlsx +0 -0
  219. package/test-files/expression-data-test-sheets/expression_sheet_missing_data_error.xlsx +0 -0
  220. package/test-files/expression-data-test-sheets/expression_sheet_missing_data_ok.xlsx +0 -0
  221. package/test-files/expression-data-test-sheets/expression_sheet_missing_data_ok_export_exact.xlsx +0 -0
  222. package/test-files/expression-data-test-sheets/expression_sheet_missing_gene_name.xlsx +0 -0
  223. package/test-files/expression-data-test-sheets/expression_sheet_name_not_in_optparams.xlsx +0 -0
  224. package/test-files/expression-data-test-sheets/expression_sheet_name_not_present.xlsx +0 -0
  225. package/test-files/expression-data-test-sheets/expression_sheet_negative_time_points.xlsx +0 -0
  226. package/test-files/expression-data-test-sheets/expression_sheet_non_numerical_time_points.xlsx +0 -0
  227. package/test-files/expression-data-test-sheets/expression_sheet_not_existing.xlsx +0 -0
  228. package/test-files/expression-data-test-sheets/expression_sheet_wrong_id_label.xlsx +0 -0
  229. package/test-files/expression-data-test-sheets/expression_sheet_wrong_order_gene_names.xlsx +0 -0
  230. package/test-files/expression-data-test-sheets/expression_sheet_wrong_sheet_name_case.xlsx +0 -0
  231. package/test-files/expression-data-test-sheets/expression_sheet_wrong_sheet_name_convention.xlsx +0 -0
  232. package/test-files/gene-name-modifications/NaN-as-gene-name-input.xlsx +0 -0
  233. package/test-files/gene-name-modifications/NaN-as-gene-name-output.xlsx +0 -0
  234. package/test-files/gene-name-modifications/mismatched-case-related-input.xlsx +0 -0
  235. package/test-files/gene-name-modifications/mismatched-case-related-output.xlsx +0 -0
  236. package/test-files/gene-name-modifications/mismatched-case-unrelated-input.xlsx +0 -0
  237. package/test-files/gene-name-modifications/mismatched-case-unrelated-output.xlsx +0 -0
  238. package/test-files/graph-tests/different-sized-networks/{80-genes-0-edges.xlsx → 134-genes-0-edges.xlsx} +0 -0
  239. package/test-files/graph-tests/different-sized-networks/{45-genes-max-edges.xlsx → 44-source-genes-45-target-genes-max-edges.xlsx} +0 -0
  240. package/test-files/graph-tests/different-sized-networks/{1-gene-0-edges.xlsx → unused-files/1-gene-0-edges.xlsx} +0 -0
  241. package/test-files/graph-tests/different-sized-networks/{1-gene-1-edges.xlsx → unused-files/1-gene-1-edges.xlsx} +0 -0
  242. package/test-files/graph-tests/different-sized-networks/{10-genes-50-edges.xlsx → unused-files/10-genes-50-edges.xlsx} +0 -0
  243. package/test-files/graph-tests/different-sized-networks/{10-genes-90-edges.xlsx → unused-files/10-genes-90-edges.xlsx} +0 -0
  244. package/test-files/graph-tests/different-sized-networks/{100-genes-0-edges.xlsx → unused-files/100-genes-0-edges.xlsx} +0 -0
  245. package/test-files/graph-tests/different-sized-networks/{110-genes-0-edges.xlsx → unused-files/110-genes-0-edges.xlsx} +0 -0
  246. package/test-files/graph-tests/different-sized-networks/{120-genes-0-edges.xlsx → unused-files/120-genes-0-edges.xlsx} +0 -0
  247. package/test-files/graph-tests/different-sized-networks/{130-genes-0-edges.xlsx → unused-files/130-genes-0-edges.xlsx} +0 -0
  248. package/test-files/graph-tests/different-sized-networks/{140-genes-0-edges.xlsx → unused-files/140-genes-0-edges.xlsx} +0 -0
  249. package/test-files/graph-tests/different-sized-networks/{150-genes-10000-edges.xlsx → unused-files/150-genes-10000-edges.xlsx} +0 -0
  250. package/test-files/graph-tests/different-sized-networks/{150-genes-20000-edges.xlsx → unused-files/150-genes-20000-edges.xlsx} +0 -0
  251. package/test-files/graph-tests/different-sized-networks/{150-genes-max-edges.xlsx → unused-files/150-genes-max-edges.xlsx} +0 -0
  252. package/test-files/graph-tests/different-sized-networks/{160-genes-max-edges.xlsx → unused-files/160-genes-max-edges.xlsx} +0 -0
  253. package/test-files/graph-tests/different-sized-networks/{20-genes-max-edges.xlsx → unused-files/20-genes-max-edges.xlsx} +0 -0
  254. package/test-files/graph-tests/different-sized-networks/{25-genes-max-edges.xlsx → unused-files/25-genes-max-edges.xlsx} +0 -0
  255. package/test-files/graph-tests/different-sized-networks/{30-genes-max-edges.xlsx → unused-files/30-genes-max-edges.xlsx} +0 -0
  256. package/test-files/graph-tests/different-sized-networks/{34-genes-0-edges.xlsx → unused-files/34-genes-0-edges.xlsx} +0 -0
  257. package/test-files/graph-tests/different-sized-networks/{34-genes-40-edges.xlsx → unused-files/34-genes-40-edges.xlsx} +0 -0
  258. package/test-files/graph-tests/different-sized-networks/{34-genes-65-edges.xlsx → unused-files/34-genes-65-edges.xlsx} +0 -0
  259. package/test-files/graph-tests/different-sized-networks/{34-genes-90-edges.xlsx → unused-files/34-genes-90-edges.xlsx} +0 -0
  260. package/test-files/graph-tests/different-sized-networks/{5-genes-max-edges.xlsx → unused-files/5-genes-max-edges.xlsx} +0 -0
  261. package/test-files/graph-tests/different-sized-networks/{51-genes-max-edges.xlsx → unused-files/51-genes-max-edges.xlsx} +0 -0
  262. package/test-files/graph-tests/different-sized-networks/{52-genes-max-edges.xlsx → unused-files/52-genes-max-edges.xlsx} +0 -0
  263. package/test-files/graph-tests/different-sized-networks/{55-genes-0-edges.xlsx → unused-files/55-genes-0-edges.xlsx} +0 -0
  264. package/test-files/graph-tests/different-sized-networks/{55-genes-max-edges.xlsx → unused-files/55-genes-max-edges.xlsx} +0 -0
  265. package/test-files/graph-tests/different-sized-networks/{65-genes-0-edges.xlsx → unused-files/65-genes-0-edges.xlsx} +0 -0
  266. package/test-files/graph-tests/different-sized-networks/{7-genes-max-edges.xlsx → unused-files/7-genes-max-edges.xlsx} +0 -0
  267. package/test-files/graph-tests/different-sized-networks/{70-genes-0-edges.xlsx → unused-files/70-genes-0-edges.xlsx} +0 -0
  268. package/test-files/graph-tests/different-sized-networks/{9-genes-max-edges.xlsx → unused-files/9-genes-max-edges.xlsx} +0 -0
  269. package/test-files/graph-tests/different-sized-networks/{90-genes-0-edges.xlsx → unused-files/90-genes-0-edges.xlsx} +0 -0
  270. package/test-files/graph-tests/different-sized-networks/{regulation-matrix-documented-20140709-AllTF-all-targets.xlsx → unused-files/regulation-matrix-documented-20140709-AllTF-all-targets.xlsx} +0 -0
  271. package/test-files/node-tests/long-gene-name-no-spaces-first.xlsx +0 -0
  272. package/test-files/node-tests/long-gene-name-no-spaces-second.xlsx +0 -0
  273. package/test-files/node-tests/long-gene-name-spaces.xlsx +0 -0
  274. package/test-files/species-test-data/15-genes_28-edges_db5_Dahlquist-data_input_no-species.xlsx +0 -0
  275. package/test-files/species-test-data/15-genes_28-edges_db5_Dahlquist-data_input_with-species.xlsx +0 -0
  276. package/test-files/species-test-data/3-gene_7-edge_elegans.xlsx +0 -0
  277. package/test-files/species-test-data/3-gene_7-edge_melanogaster.xlsx +0 -0
  278. package/test-files/species-test-data/3-gene_7-edge_musculus.xlsx +0 -0
  279. package/test-files/species-test-data/3-gene_7-edge_sapiens.xlsx +0 -0
  280. package/test-files/species-test-data/kev-fake-data-sapiens-no-exp-data.xlsx +0 -0
  281. package/test-files/spreadsheet-controller-test-files/sheet-name-capitalized-network-optimized-weights.xlsx +0 -0
  282. package/test-files/spreadsheet-controller-test-files/sheet-name-capitalized-network.xlsx +0 -0
  283. package/web-client/app.js +1 -1
  284. package/web-client/controllers/main.js +1 -1
  285. package/web-client/public/favicon.ico +0 -0
  286. package/web-client/public/gene/api.js +76 -14
  287. package/web-client/public/gene/info.js +19 -8
  288. package/web-client/public/js/api/grnsight-api.js +124 -0
  289. package/web-client/public/js/constants.js +90 -17
  290. package/web-client/public/js/createNetwork.js +195 -0
  291. package/web-client/public/js/graph-statistics.js +7 -7
  292. package/web-client/public/js/graph.js +369 -324
  293. package/web-client/public/js/grnsight.js +2 -2
  294. package/web-client/public/js/grnsight.min.js +128 -59
  295. package/web-client/public/js/grnstate.js +54 -30
  296. package/web-client/public/js/iframe-coordination.js +55 -0
  297. package/web-client/public/js/setup-handlers.js +358 -18
  298. package/web-client/public/js/setup-load-and-import-handlers.js +105 -66
  299. package/web-client/public/js/update-app.js +483 -93
  300. package/web-client/public/js/upload.js +349 -32
  301. package/web-client/public/js/warnings.js +13 -51
  302. package/web-client/public/lib/iframeSizer.contentWindow.min.js +10 -0
  303. package/web-client/public/stylesheets/grnsight.styl +323 -28
  304. package/web-client/views/components/demo.pug +10 -0
  305. package/web-client/views/{graph.jade → graph.pug} +0 -0
  306. package/web-client/views/{info.jade → info.pug} +1 -5
  307. package/web-client/views/upload.pug +580 -0
  308. package/_gh_pages/Gemfile +0 -7
  309. package/_gh_pages/Gemfile.lock +0 -129
  310. package/documents/SDF/CMSI_402/spring_2014/Southwick_CMSI402_Spring2014_software-development-plan.md +0 -71
  311. package/documents/SDF/CMSI_402/spring_2014/Southwick_CMSI402_Spring2014_software-requirements-specification.md +0 -71
  312. package/documents/SDF/CMSI_402/spring_2014/use-case-diagram-spring-2014.jpg +0 -0
  313. package/documents/SDF/CMSI_402/spring_2017/Anguiano_402_Final_Presentation.pptx +0 -0
  314. package/documents/SDF/CMSI_402/spring_2017/Anguiano_402_Presentation_Final_Poster.pdf +0 -0
  315. package/documents/SDF/CMSI_402/spring_2017/Anguiano_CMSI402_Spring2017_project-proposal-presentation.pptx +0 -0
  316. package/documents/SDF/CMSI_402/spring_2017/Anguiano_CMSI402_Spring2017_project-proposal.docx +0 -0
  317. package/documents/SDF/CMSI_402/spring_2017/Anguiano_CMSI402_Spring2017_software-development-plan.docx +0 -0
  318. package/documents/SDF/CMSI_402/spring_2017/Anguiano_CMSI402_Spring2017_software-requirements-specification.docx +0 -0
  319. package/documents/SDF/CMSI_402/spring_2017/Design Review Presentation.pptx +0 -0
  320. package/documents/SDF/CMSI_402/spring_2018/0402report.docx +0 -0
  321. package/documents/SDF/CMSI_402/spring_2018/0409report.docx +0 -0
  322. package/documents/SDF/CMSI_402/spring_2018/402-gantt.png +0 -0
  323. package/documents/SDF/CMSI_402/spring_2018/402SRS_GRNSightFeedback01.txt +0 -28
  324. package/documents/SDF/CMSI_402/spring_2018/402_SPD.md +0 -223
  325. package/documents/SDF/CMSI_402/spring_2018/ChoeShinCMSI402_Final.pptx +0 -0
  326. package/documents/SDF/CMSI_402/spring_2018/ProjectProposal.md +0 -22
  327. package/documents/SDF/CMSI_402/spring_2018/SoftwareRequirementsSpecification.md +0 -88
  328. package/documents/SDF/CMSI_402/spring_2018/homework/choe_eileen/Choe_Eileen_CMSI 402_HW1.pdf +0 -0
  329. package/documents/SDF/CMSI_402/spring_2018/homework/choe_eileen/Choe_Eileen_CMSI402_HW2.pdf +0 -0
  330. package/documents/SDF/CMSI_402/spring_2018/homework/shin_jen/402hw2.docx +0 -0
  331. package/documents/SDF/CMSI_402/spring_2018/homework/shin_jen/Jen - 402Hw1.docx +0 -0
  332. package/documents/SDF/CMSI_402/spring_2018/homework/shin_jen/Jen - hw3.docx +0 -0
  333. package/documents/SDF/CMSI_402/spring_2018/writtenStatusReport.docx +0 -0
  334. package/documents/SDF/CMSI_402/spring_2018/writtenStatusReport4 +0 -0
  335. package/documents/abstracts/Anguiano_Varshneya_Undergraduate-Research-Symposium_2017_abstract.pdf +0 -0
  336. package/documents/abstracts/SIGGRAPH 2017 Abstract/ACM-Reference-Format.bst +0 -3478
  337. package/documents/abstracts/SIGGRAPH 2017 Abstract/Figure1_zoom100.png +0 -0
  338. package/documents/abstracts/SIGGRAPH 2017 Abstract/acmart.cls +0 -2352
  339. package/documents/abstracts/SIGGRAPH 2017 Abstract/acmart.ins +0 -29
  340. package/documents/abstracts/SIGGRAPH 2017 Abstract/always-weights.png +0 -0
  341. package/documents/abstracts/SIGGRAPH 2017 Abstract/auto.png +0 -0
  342. package/documents/abstracts/SIGGRAPH 2017 Abstract/networkA.png +0 -0
  343. package/documents/abstracts/SIGGRAPH 2017 Abstract/networkB-normalized.png +0 -0
  344. package/documents/abstracts/SIGGRAPH 2017 Abstract/networkB.png +0 -0
  345. package/documents/abstracts/SIGGRAPH 2017 Abstract/never-weights.png +0 -0
  346. package/documents/abstracts/SIGGRAPH 2017 Abstract/representative-image/screenshot.jpg +0 -0
  347. package/documents/abstracts/SIGGRAPH 2017 Abstract/representative-image/screenshot3x2.png +0 -0
  348. package/documents/abstracts/SIGGRAPH 2017 Abstract/representative-image/withweights3x2.png +0 -0
  349. package/documents/abstracts/SIGGRAPH 2017 Abstract/screenshot-auto.png +0 -0
  350. package/documents/abstracts/SIGGRAPH 2017 Abstract/siggraph-abstract-review.bib +0 -85
  351. package/documents/abstracts/SIGGRAPH 2017 Abstract/siggraph-abstract-review.pdf +0 -0
  352. package/documents/abstracts/SIGGRAPH 2017 Abstract/siggraph-abstract-review.tex +0 -235
  353. package/documents/abstracts/SWE Collegiate Competition 2017.md +0 -9
  354. package/documents/abstracts/Samdarshi_GRNsight_SoCalSysBio_Abstract_2019.docx +0 -0
  355. package/documents/abstracts/Varshneya_Samdarshi_Southern-California-Systems-Biology_2017_abstract.docx +0 -0
  356. package/documents/abstracts/~$mdarshi_SoCalSysBio_Abstract_2019.docx +0 -0
  357. package/documents/developer_documents/State Diagram.graphml +0 -3525
  358. package/documents/developer_documents/graphml/State Diagram.graphml +0 -3115
  359. package/documents/developer_documents/older_versions/GRNsight State Diagram old.png +0 -0
  360. package/documents/developer_documents/older_versions/GRNsight State Diagram.png +0 -0
  361. package/documents/developer_documents/testing_script_generator/GRNsightTestingDocument.md +0 -998
  362. package/documents/developer_documents/testing_script_generator/featureList.json +0 -496
  363. package/documents/developer_documents/testing_script_generator/testing-script-generator.js +0 -149
  364. package/documents/manuscripts/peerj-computerscience-2016/GRNsight_PeerJ-CS_conference-presentations_2016.docx +0 -0
  365. package/documents/manuscripts/peerj-computerscience-2016/GRNsight_PeerJ-CS_manuscript_2016.docx +0 -0
  366. package/documents/manuscripts/peerj-computerscience-2016/GRNsight_PeerJ-CS_manuscript_2016_Table1.docx +0 -0
  367. package/documents/manuscripts/peerj-computerscience-2016/GRNsight_PeerJ-CS_manuscript_2016_references.rtf +0 -264
  368. package/documents/manuscripts/peerj-computerscience-2016/GRNsight_PeerJ-CS_manuscript_2016_text-only.docx +0 -0
  369. package/documents/manuscripts/peerj-computerscience-2016/figures/other-versions/21-genes_31-edges_Schade-data_for-screenshots.xlsx +0 -0
  370. package/documents/manuscripts/peerj-computerscience-2016/figures/other-versions/Figure1_zoom100.jpg +0 -0
  371. package/documents/manuscripts/peerj-computerscience-2016/figures/other-versions/Figure1_zoom100.png +0 -0
  372. package/documents/manuscripts/peerj-computerscience-2016/figures/other-versions/Figure1_zoom100.psd +0 -0
  373. package/documents/manuscripts/peerj-computerscience-2016/figures/other-versions/Figure1_zoom145.jpg +0 -0
  374. package/documents/manuscripts/peerj-computerscience-2016/figures/other-versions/Figure1_zoom145.png +0 -0
  375. package/documents/manuscripts/peerj-computerscience-2016/figures/other-versions/Figure1_zoom145.psd +0 -0
  376. package/documents/manuscripts/peerj-computerscience-2016/figures/other-versions/Figure1_zoom145_900pix-wide.psd +0 -0
  377. package/documents/manuscripts/peerj-computerscience-2016/figures/other-versions/Figure2_zoom100.jpg +0 -0
  378. package/documents/manuscripts/peerj-computerscience-2016/figures/other-versions/Figure2_zoom100.png +0 -0
  379. package/documents/manuscripts/peerj-computerscience-2016/figures/other-versions/Figure2_zoom100.psd +0 -0
  380. package/documents/manuscripts/peerj-computerscience-2016/figures/other-versions/Figure2_zoom145.jpg +0 -0
  381. package/documents/manuscripts/peerj-computerscience-2016/figures/other-versions/Figure2_zoom145.png +0 -0
  382. package/documents/manuscripts/peerj-computerscience-2016/figures/other-versions/Figure2_zoom145.psd +0 -0
  383. package/documents/manuscripts/peerj-computerscience-2016/figures/other-versions/Figure2_zoom145_900pix-wide.psd +0 -0
  384. package/documents/manuscripts/peerj-computerscience-2016/figures/other-versions/Figure5A.pdf +0 -0
  385. package/documents/manuscripts/peerj-computerscience-2016/figures/other-versions/Figure5B.pdf +0 -0
  386. package/documents/manuscripts/peerj-computerscience-2016/figures/other-versions/Figure5C.eps +0 -0
  387. package/documents/manuscripts/peerj-computerscience-2016/figures/other-versions/Figure5D.pdf +0 -0
  388. package/documents/manuscripts/peerj-computerscience-2016/figures/other-versions/Figure5E.pdf +0 -0
  389. package/documents/manuscripts/peerj-computerscience-2016/figures/other-versions/Figure5F.eps +0 -0
  390. package/documents/manuscripts/peerj-computerscience-2016/figures/other-versions/Figure5_compiled.png +0 -0
  391. package/documents/manuscripts/peerj-computerscience-2016/figures/submitted-versions/Figure3_GRNsight-Architecture.pdf +0 -0
  392. package/documents/manuscripts/peerj-computerscience-2016/figures/submitted-versions/Figure4_GRNsight-Screenshot.pdf +0 -0
  393. package/documents/manuscripts/peerj-computerscience-2016/figures/submitted-versions/Figure5_compiled.pdf +12 -5383
  394. package/documents/manuscripts/peerj-computerscience-2016/peerj-reviewing-10823-v0.pdf +0 -0
  395. package/documents/manuscripts/peerj-computerscience-2016/revisions/GRNsight_PeerJ-CS_conference-presentations_2016_revised.docx +0 -0
  396. package/documents/manuscripts/peerj-computerscience-2016/revisions/GRNsight_PeerJ-CS_cover-letter-and-response_2016.pdf +0 -0
  397. package/documents/manuscripts/peerj-computerscience-2016/revisions/GRNsight_PeerJ-CS_cover-letter_2016.docx +0 -0
  398. package/documents/manuscripts/peerj-computerscience-2016/revisions/GRNsight_PeerJ-CS_cover-letter_2016.pdf +0 -0
  399. package/documents/manuscripts/peerj-computerscience-2016/revisions/GRNsight_PeerJ-CS_manuscript_2016_Table1.docx +0 -0
  400. package/documents/manuscripts/peerj-computerscience-2016/revisions/GRNsight_PeerJ-CS_manuscript_2016_references_revised.rtf +0 -385
  401. package/documents/manuscripts/peerj-computerscience-2016/revisions/GRNsight_PeerJ-CS_manuscript_2016_text-only_revised.docx +0 -0
  402. package/documents/manuscripts/peerj-computerscience-2016/revisions/GRNsight_PeerJ-CS_manuscript_2016_text-only_revised_marked.docx +0 -0
  403. package/documents/manuscripts/peerj-computerscience-2016/revisions/GRNsight_PeerJ-CS_response-to-reviewers_2016.docx +0 -0
  404. package/documents/manuscripts/peerj-computerscience-2016/revisions/GRNsight_PeerJ-CS_response-to-reviewers_2016.pdf +0 -0
  405. package/documents/manuscripts/peerj-computerscience-2016/revisions/figures/Figure3_GRNsight-Architecture.pdf +0 -0
  406. package/documents/manuscripts/peerj-computerscience-2016/revisions/figures/Figure4_GRNsight-Screenshot-auto.pdf +0 -0
  407. package/documents/manuscripts/peerj-computerscience-2016/revisions/figures/Figure4_GRNsight-Screenshot.pdf +0 -0
  408. package/documents/manuscripts/peerj-computerscience-2016/revisions/peerj-reviewing-10823-v1.pdf +0 -0
  409. package/documents/posters/Anguiano_CMSI402_2017.pptx +0 -0
  410. package/documents/posters/Anguiano_Varshneya_LMU-Symposium_2015.pptx +0 -0
  411. package/documents/posters/Anguiano_Varshneya_SCCUR_2014.pptx +0 -0
  412. package/documents/posters/Choe_Shin_CMSI402_2018.pptx +0 -0
  413. package/documents/posters/Dahlquist-et-al_BOSC_ISMB_2016_poster.pptx +0 -0
  414. package/documents/posters/Lopez_Filler_LMU_Symposium_2019.pdf +0 -0
  415. package/documents/posters/Lopez_Filler_LMU_Symposium_2019.pptx +0 -0
  416. package/documents/posters/Samdarshi-et-al_LMU-Symposium_2017.pptx +0 -0
  417. package/documents/posters/Samdarshi-et-al_LMU-Symposium_2018.pptx +0 -0
  418. package/documents/posters/Samdarshi-et-al_SoCalSysBio_2019.pptx +0 -0
  419. package/documents/posters/Shin-et-al_SCCUR_2017.pptx +0 -0
  420. package/documents/posters/Southwick_CMSI402_2014.pptx +0 -0
  421. package/documents/posters/Varshneya_Samdarshi_LMU-Symposium_2016.pptx +0 -0
  422. package/documents/posters/Varshneya_Samdarshi_SoCalSysBio_2017.pptx +0 -0
  423. package/documents/presentations/Anguiano_402_Final_Presentation.pptx +0 -0
  424. package/documents/presentations/Choe_SWERapidFire2017_final.pptx +0 -0
  425. package/documents/presentations/Dahlquist_BOSC_20160709.pptx +0 -0
  426. package/documents/presentations/Dahlquist_ExperimentalBiology_20160404_talk.pptx +0 -0
  427. package/documents/presentations/Dahlquist_SoCalSysBio_20150131_talk.pptx +0 -0
  428. package/documents/presentations/Samdarshi_LMU-Symposium_20190323_talk.pdf +0 -0
  429. package/documents/presentations/Samdarshi_LMU-Symposium_20190323_talk.pptx +0 -0
  430. package/documents/presentations/Southwick_Anguiano_LMU-Symposium_20140329_talk.pptx +0 -0
  431. package/documents/presentations/Southwick_CMSI402_Presentation_20140508_talk.pptx +0 -0
  432. package/documents/presentations/Varshneya_Anguiano_LMU-Symposium_201703_talk.pptx +0 -0
  433. package/documents/reports/Samdarshi_Mihir_2019/Samdarshi_2019_Final.docx +0 -0
  434. package/documents/reports/Samdarshi_Mihir_2019/Samdarshi_Mihir_2019_AnnotatedBibliography.docx +0 -0
  435. package/documents/reports/Samdarshi_Mihir_2019/Samdarshi_Mihir_2019_Outline.docx +0 -0
  436. package/documents/reports/Samdarshi_Mihir_2019/~$mdarshi_2019_Draft.docx +0 -0
  437. package/documents/reports/Samdarshi_Mihir_2019/~$mdarshi_2019_Final_Paper_Draft.docx +0 -0
  438. package/documents/reports/Varshneya_Anu_2017/Varshneya-201701-AnnotatedBibliography.docx +0 -0
  439. package/documents/reports/Varshneya_Anu_2017/Varshneya-201702-Introduction.docx +0 -0
  440. package/documents/reports/Varshneya_Anu_2017/Varshneya-201702-Outline.docx +0 -0
  441. package/documents/reports/Varshneya_Anu_2017/Varshneya-201703-Discussion.docx +0 -0
  442. package/documents/reports/Varshneya_Anu_2017/Varshneya-201703-MMResults.docx +0 -0
  443. package/documents/reports/Varshneya_Anu_2017/Varshneya-201704-Draft-1.docx +0 -0
  444. package/documents/reports/Varshneya_Anu_2017/Varshneya-201704-Final.docx +0 -0
  445. package/onlysidebar.html +0 -8
  446. package/test/graph-library-tests.js +0 -165
  447. package/test-files/demo-files/21-genes_50-edges_Dahlquist-data_estimation_output.xlsx +0 -0
  448. package/test-files/demo-files/21-genes_50-edges_Dahlquist-data_input.xlsx +0 -0
  449. package/test-files/graph-tests/different-sized-networks/10-genes-max-edges.xlsx +0 -0
  450. package/test-files/graph-tests/different-sized-networks/12-genes-max-edges.xlsx +0 -0
  451. package/test-files/graph-tests/different-sized-networks/35-genes-max-edges.xlsx +0 -0
  452. package/test-files/graph-tests/different-sized-networks/40-genes-0-edges.xlsx +0 -0
  453. package/test-files/graph-tests/different-sized-networks/40-genes-max-edges.xlsx +0 -0
  454. package/test-files/graph-tests/different-sized-networks/42-genes-max-edges.xlsx +0 -0
  455. package/test-files/graph-tests/different-sized-networks/50-genes-max-edges.xlsx +0 -0
  456. package/test-files/graph-tests/different-sized-networks/75-genes-150-edges.xlsx +0 -0
  457. package/web-client/public/js/container.js +0 -121
  458. package/web-client/views/upload.jade +0 -464
@@ -3,10 +3,14 @@ import { grnState } from "./grnstate";
3
3
  import { modifyChargeParameter, modifyLinkDistanceParameter, valueValidator } from "./update-app";
4
4
  import {
5
5
  ENDS_IN_EXPRESSION_REGEXP,
6
- ZOOM_CONTROL,
6
+ VIEWPORT_FIT,
7
7
  ZOOM_INPUT,
8
8
  ZOOM_PERCENT,
9
- ZOOM_SLIDER
9
+ ZOOM_SLIDER,
10
+ ZOOM_DISPLAY_MINIMUM_VALUE,
11
+ ZOOM_DISPLAY_MAXIMUM_VALUE,
12
+ ZOOM_DISPLAY_MIDDLE,
13
+ ZOOM_ADAPTIVE_MAX_SCALE,
10
14
  } from "./constants";
11
15
 
12
16
  /* globals d3 */
@@ -25,6 +29,17 @@ import {
25
29
  /* eslint no-unused-vars: [2, {"varsIgnorePattern": "text|getMappedValue|manualZoom"}] */
26
30
  /* eslint-disable no-unused-vars */
27
31
 
32
+ /**
33
+ * Resize detection logic: to avoid "listener leaks," this is set up a single time here, with an assignable
34
+ * updateFunction being set when needed.
35
+ */
36
+ let mutationCallback = null;
37
+ const resizeObserver = new MutationObserver((mutationsList, observer) => {
38
+ if (typeof(mutationCallback) === "function") {
39
+ mutationCallback(mutationsList, observer);
40
+ }
41
+ });
42
+
28
43
  export var updaters = {
29
44
  setNodesToGrid: () => {},
30
45
  setNodesToForceGraph: () => {},
@@ -32,7 +47,7 @@ export var updaters = {
32
47
  removeNodeColoring: () => {},
33
48
  };
34
49
 
35
- export var drawGraph = function (network) {
50
+ export var drawGraph = function (workbook) {
36
51
  /* eslint-enable no-unused-vars */
37
52
  var $container = $(".grnsight-container");
38
53
  d3.selectAll("svg").remove();
@@ -48,9 +63,7 @@ export var drawGraph = function (network) {
48
63
 
49
64
  var CURSOR_CLASSES = "cursorGrab cursorGrabbing";
50
65
 
51
- var zoomSliderScale = 1;
52
-
53
- $("#warningMessage").html(network.warnings.length !== 0 ? "Click here in order to view warnings." : "");
66
+ $("#warningMessage").html(workbook.warnings.length !== 0 ? "Click here in order to view warnings." : "");
54
67
 
55
68
  var getNodeWidth = function (node) {
56
69
  return node.name.length * 12 + 5;
@@ -58,12 +71,31 @@ export var drawGraph = function (network) {
58
71
 
59
72
  var adaptive = !$("input[name='viewport']").prop("checked");
60
73
 
74
+ /**
75
+ * The *_SCALE values represent the actual zoom values used to transform the graph.
76
+ * The *_DISPLAY values represent the value that is shown in the user interface.
77
+ *
78
+ * Separating these values allows for flexible configuration of what the user sees vs. the actual scale factor
79
+ * used in transformations. This "distortion" is done so that "actual size" or 100% can be shown as the midpoint
80
+ * on the zoom slider, even if the numeric ranges to the left and right of the midpoint are asymmetric (as they
81
+ * are here).
82
+ */
83
+ const createZoomScale = (domainMin, domainMax, rangeMin, rangeMax) => d3.scaleLinear()
84
+ .domain([domainMin, domainMax])
85
+ .range([rangeMin, rangeMax])
86
+ .clamp(true);
87
+
88
+ const MIN_DISPLAY = ZOOM_DISPLAY_MINIMUM_VALUE;
89
+ const ADAPTIVE_MAX_DISPLAY = ZOOM_DISPLAY_MAXIMUM_VALUE;
61
90
  const MIN_SCALE = 0.25;
62
- const ADAPTIVE_MAX_SCALE = 4;
63
91
  const MIDDLE_SCALE = 1;
64
92
 
93
+ const zoomScaleLeft = createZoomScale(MIN_DISPLAY, ZOOM_DISPLAY_MIDDLE, MIN_SCALE, MIDDLE_SCALE);
94
+ const zoomScaleRight = createZoomScale(
95
+ ZOOM_DISPLAY_MIDDLE, ADAPTIVE_MAX_DISPLAY, MIDDLE_SCALE, ZOOM_ADAPTIVE_MAX_SCALE);
96
+
65
97
  // Create an array of all the network weights
66
- var allWeights = network.positiveWeights.concat(network.negativeWeights);
98
+ var allWeights = workbook.positiveWeights.concat(workbook.negativeWeights);
67
99
  // Assign the entire array weights of 1, if color edges turned off
68
100
  if (!grnState.colorOptimal) {
69
101
  for (var i = 0; i < allWeights.length; i++) {
@@ -77,12 +109,15 @@ export var drawGraph = function (network) {
77
109
  }
78
110
  }
79
111
 
112
+ const maxWeight = Math.max(Math.abs(d3.max(allWeights)), Math.abs(d3.min(allWeights)));
113
+
80
114
  // Get the largest magnitude weight and set that as the default normalization factor
81
- var maxWeight = Math.max(Math.abs(d3.max(allWeights)), Math.abs(d3.min(allWeights)));
82
- grnState.normalizationMax = maxWeight;
83
- grnState.resetNormalizationMax = maxWeight;
115
+ if (grnState.newWorkbook) {
116
+ grnState.normalizationMax = maxWeight;
117
+ grnState.resetNormalizationMax = maxWeight;
118
+ }
84
119
 
85
- // Normalize all weights b/w 2-14
120
+ // Normalize all weights b/w 2-14
86
121
  var normMax = +$("#normalization-max").val();
87
122
  var totalScale = d3.scaleLinear()
88
123
  .domain([0, normMax > 0 ? normMax : maxWeight])
@@ -91,8 +126,8 @@ export var drawGraph = function (network) {
91
126
 
92
127
  var unweighted = false;
93
128
 
94
- // if unweighted, all weights are 2
95
- if (network.sheetType === "unweighted") {
129
+ // if unweighted, all weights are 2
130
+ if (workbook.sheetType === "unweighted") {
96
131
  totalScale = d3.scaleQuantile()
97
132
  .domain([d3.extent(allWeights)])
98
133
  .range(["2"]);
@@ -162,7 +197,8 @@ export var drawGraph = function (network) {
162
197
 
163
198
  var svg = d3.select($container[0]).append("svg")
164
199
  .attr("width", width)
165
- .attr("height", height);
200
+ .attr("height", height)
201
+ .attr("id", "exportContainer");
166
202
 
167
203
  var zoomContainer = svg.append("g") // required for zoom to work
168
204
  .attr("class", "boundingBox")
@@ -172,10 +208,12 @@ export var drawGraph = function (network) {
172
208
  var boundingBoxContainer = zoomContainer.append("g"); // appended another g here...
173
209
 
174
210
  var zoom = d3.zoom()
175
- .scaleExtent([1 / 2, 4])
211
+ .scaleExtent([MIN_SCALE, ZOOM_ADAPTIVE_MAX_SCALE])
176
212
  .on("zoom", zoomed);
177
213
 
178
- svg.style("pointer-events", "all").call(zoomDrag);
214
+ svg.style("pointer-events", "all").call(zoomDrag)
215
+ .style("font-family", "sans-serif");
216
+
179
217
 
180
218
  function zoomed () {
181
219
  zoomContainer.attr("transform", d3.event.transform);
@@ -183,14 +221,14 @@ export var drawGraph = function (network) {
183
221
 
184
222
  d3.select("svg").on("dblclick.zoom", null); // disables double click zooming
185
223
 
186
- // This rectangle catches all of the mousewheel and pan events, without letting
187
- // them bubble up to the body.
224
+ // This rectangle catches all of the mousewheel and pan events, without letting
225
+ // them bubble up to the body.
188
226
  boundingBoxContainer.append("rect")
189
227
  .attr("width", width)
190
228
  .attr("height", height)
191
229
  .style("fill", "none")
192
230
  .style("pointer-events", "all")
193
- .attr("stroke", adaptive ? "none" : "#9A9A9A")
231
+ .attr("stroke", "none" )
194
232
  .append("g");
195
233
 
196
234
  d3.selectAll(".scrollBtn").on("click", null); // Remove event handlers, if there were any.
@@ -212,114 +250,77 @@ export var drawGraph = function (network) {
212
250
  zoom.scaleTo(container, zoomScale);
213
251
  };
214
252
 
253
+ // See setupZoomElements below to see how these are initialized. They are declared here because
254
+ // updateAppBasedOnZoomValue uses them (lexical positioning is chosen here for better context).
255
+ let sliderMidpoint;
256
+ let zoomScaleSliderLeft;
257
+ let zoomScaleSliderRight;
258
+
215
259
  const updateAppBasedOnZoomValue = () => {
216
- var currentPoint = grnState.zoomValue * 100;
217
- var equivalentScale;
218
- if (adaptive || (!adaptive && grnState.zoomValue <= ADAPTIVE_MAX_SCALE)) {
219
- if (currentPoint <= leftPoints) {
220
- equivalentScale = MIN_SCALE;
221
- equivalentScale += scaleIncreasePerLeftPoint * currentPoint;
222
- } else {
223
- currentPoint = currentPoint - leftPoints;
224
- equivalentScale = MIDDLE_SCALE;
225
- equivalentScale += scaleIncreasePerRightPoint * currentPoint;
226
- }
227
- zoomSliderScale = equivalentScale;
228
- setGraphZoom(equivalentScale);
229
- } else {
230
- // Prohibits zooming past 100% if (!adaptive && grnState.zoomValue >= ADAPTIVE_MAX_SCALE)
231
- grnState.zoomValue = ADAPTIVE_MAX_SCALE;
232
- setGraphZoom(MIDDLE_SCALE);
260
+
261
+ if (!adaptive) {
262
+ grnState.zoomValue = 100.0;
233
263
  }
234
264
 
235
- $(ZOOM_CONTROL).val(grnState.zoomValue);
265
+ const zoomDisplay = grnState.zoomValue;
266
+ setGraphZoom((zoomDisplay <= ZOOM_DISPLAY_MIDDLE ? zoomScaleLeft : zoomScaleRight)(zoomDisplay));
236
267
 
237
- let zoomAsPercent = Math.round(grnState.zoomValue / ZOOM_SLIDER_MAX_VAL * ZOOM_RANGE);
238
- if (zoomAsPercent === 0) {
239
- zoomAsPercent = MIDDLE_SCALE;
268
+ const finalDisplay = grnState.zoomValue;
269
+ $(ZOOM_PERCENT).text(`${finalDisplay}%`);
270
+
271
+ // Special handling for zoom input field: the user might be in the middle of typing a value that is
272
+ // _temporarily_ out of range (e.g., "1" while typing "100") and we don’t want to overwrite that.
273
+ // The special case can be detected if the input element currently has focus.
274
+ if (document.activeElement !== document.querySelector(ZOOM_INPUT)) {
275
+ $(ZOOM_INPUT).val(finalDisplay);
240
276
  }
241
277
 
242
- $(ZOOM_PERCENT).text(`${zoomAsPercent}%`);
243
- $(ZOOM_INPUT).val(zoomAsPercent);
278
+ $(ZOOM_SLIDER).val((finalDisplay <= ZOOM_DISPLAY_MIDDLE ? zoomScaleSliderLeft : zoomScaleSliderRight)
279
+ .invert(finalDisplay));
244
280
  };
245
281
 
246
- var leftPoints;
247
- var rightPoints;
248
- var scaleIncreasePerLeftPoint;
249
- var scaleIncreasePerRightPoint;
250
- /*
251
- We have to do some mapping so that the zoom slider appears as it should.
252
- Zooming out sets the scale to a value between 0 and 1. Zooming in sets it
253
- to a value between 1 and infinity. A scale of 0.25 to 5 on a zoom slider
254
- without transformations will have 1 at the very far left. However, that's
255
- an inaccurate way to represent what's actually happening. So this function
256
- maps the scale from 0 to some x, with that x being calculated based on the
257
- input scales.
258
- */
259
- var setupZoomSlider = function (minScale) {
260
- // If the maximumScale is 1, we won't need to calculate any values from 1 to maxScale.
261
- // So we'll just treat it as 0.
262
-
263
- var maxScale = ADAPTIVE_MAX_SCALE;
264
-
265
- // Each integer on the zoom is equivalent to 100 steps.
266
- var NUMBER_POINTS_PER_INT = 100;
267
-
268
- // Get the value that, if multiplied by the minScale value, would return 1. (ex: 0.25 * 4 = 1)
269
- // This gives us the equivalent value of this minimum scale, should it be treated
270
- // as a scale increase. This mostly allows us to treat this minimum scale as a non-decimal value,
271
- // but it also provides a way to compare the total effect this minimum scale would have
272
- // in a way that is easier to understand.
273
- var minScaleReversed = 100 / (minScale * 100);
274
-
275
- // Number of points required to display the minimum scale, now that's it's been transformed. These
276
- // are the points that represent everything on the scale less than one.
277
- leftPoints = minScaleReversed * NUMBER_POINTS_PER_INT;
278
-
279
- // We want to end up with a total increase that, once we've gone through all the
280
- // left points, produces 1 when added to minscale. So for scale 0.25 with 400
281
- // left points, we need to know what we could add to 0.25 400 times to produce 1.
282
- // We divide 0.75 by 400 to get that result.
283
- scaleIncreasePerLeftPoint = (1 - minScale) / leftPoints;
284
-
285
- // Points representing scales greater than 1.
286
- rightPoints = maxScale * NUMBER_POINTS_PER_INT;
287
-
288
- // For the same concept as above, we need to figure out what to add to 1 so
289
- // so that we can end up with maxScale. Note that we start at 1 and not 0 because
290
- // the scale is beginning at 1.
291
- scaleIncreasePerRightPoint = (maxScale - MIDDLE_SCALE) / rightPoints;
292
- var totalPoints = leftPoints + rightPoints;
293
-
294
- // Returns the x that we're mapping to. Now we can set up the range slider.
295
- var maxRangeValue = totalPoints / 100;
296
-
297
- $(ZOOM_SLIDER).attr("min", 0);
298
- $(ZOOM_SLIDER).attr("max", maxRangeValue);
299
- if (grnState.newNetwork) {
300
- grnState.zoomValue = 0.01 * leftPoints;
282
+ /**
283
+ * To eliminate coupling between how the zoom slider element is defined in markup and how zoom values are
284
+ * calculated and displayed, we define this function to read the zoom slider for its minimum, maximum, and
285
+ * midpoint. The slider’s minimum will be shown as MIN_DISPLAY, the slider’s maximum will be shown as
286
+ * ADAPTIVE_MAX_DISPLAY, and the slider’s midpoint will be shown as ZOOM_DISPLAY_MIDDLE.
287
+ *
288
+ * Elements showing minimum and maximum display values are also updated here so that they are consistent
289
+ * with these constants. This way, all zoom calculations are based on these constants, and changing these
290
+ * constants should be all that is needed to adjust displayed and actual zoom values.
291
+ */
292
+ var setupZoomSlider = () => {
293
+ const sliderMin = +$(ZOOM_SLIDER).attr("min");
294
+ const sliderMax = +$(ZOOM_SLIDER).attr("max");
295
+ sliderMidpoint = (sliderMin + sliderMax) / 2;
296
+
297
+ zoomScaleSliderLeft = createZoomScale(sliderMin, sliderMidpoint, MIN_DISPLAY, ZOOM_DISPLAY_MIDDLE);
298
+ zoomScaleSliderRight = createZoomScale(sliderMidpoint, sliderMax, ZOOM_DISPLAY_MIDDLE, ADAPTIVE_MAX_DISPLAY);
299
+
300
+ // Reset the zoom value to the midpoint whenever we load a new workbook.
301
+ if (grnState.newWorkbook) {
302
+ grnState.zoomValue = ZOOM_DISPLAY_MIDDLE;
301
303
  }
302
304
 
303
305
  updateAppBasedOnZoomValue();
304
306
  };
305
307
 
306
- setupZoomSlider(MIN_SCALE);
307
-
308
- var ZOOM_SLIDER_MAX_VAL = 8;
309
- var ZOOM_RANGE = 200;
308
+ setupZoomSlider();
310
309
 
311
310
  var zoomInputValidator = function (value) {
312
- return valueValidator(1, 200, value);
311
+ return valueValidator(MIN_DISPLAY, ADAPTIVE_MAX_DISPLAY, value);
313
312
  };
314
313
 
315
314
  $(ZOOM_INPUT).on("input", () => {
316
- const zoomAsPercent = zoomInputValidator(+$(ZOOM_INPUT).val());
317
- grnState.zoomValue = zoomAsPercent * (ZOOM_SLIDER_MAX_VAL / ZOOM_RANGE);
315
+ grnState.zoomValue = zoomInputValidator(+$(ZOOM_INPUT).val());
318
316
  updateAppBasedOnZoomValue();
319
- });
317
+ }).blur(() => $(ZOOM_INPUT).val(grnState.zoomValue));
320
318
 
321
319
  d3.select(ZOOM_SLIDER).on("input", function () {
322
- grnState.zoomValue = +$(this).val();
320
+ const sliderValue = +$(this).val();
321
+ grnState.zoomValue = Math.floor(
322
+ (sliderValue <= sliderMidpoint ? zoomScaleSliderLeft : zoomScaleSliderRight)(sliderValue)
323
+ );
323
324
  updateAppBasedOnZoomValue();
324
325
  }).on("mousedown", function () {
325
326
  manualZoom = true;
@@ -327,11 +328,11 @@ export var drawGraph = function (network) {
327
328
  manualZoom = false;
328
329
  });
329
330
 
330
- if (!grnState.newNetwork) {
331
+ if (!grnState.newWorkbook) {
331
332
  updateAppBasedOnZoomValue();
332
333
  }
333
334
 
334
- d3.selectAll(".boundBoxSize").on("click", function () {
335
+ const adjustGraphSize = () => {
335
336
  var newWidth = $container.width();
336
337
  var newHeight = $container.height();
337
338
 
@@ -343,29 +344,39 @@ export var drawGraph = function (network) {
343
344
  height = newHeight;
344
345
  }
345
346
 
346
- // Subtract 1 from SVG height if we are fitting to window so as to prevent scrollbars from showing up
347
- // Is inconsistent, but I'm tired of fighting with it...
347
+ // Subtract 1 from SVG height if we are fitting to window so as to prevent scrollbars from showing up
348
+ // Is inconsistent, but I'm tired of fighting with it...
348
349
  d3.select("svg").attr("width", newWidth)
349
- .attr("height", $(".grnsight-container").hasClass("containerFit") ? newHeight : newHeight);
350
+ .attr("height", $(".grnsight-container").hasClass(VIEWPORT_FIT) ? newHeight : newHeight);
350
351
  d3.select("rect").attr("width", width).attr("height", height);
351
352
  d3.select(".boundingBox").attr("width", width).attr("height", height);
352
- });
353
+ };
354
+
355
+ mutationCallback = adjustGraphSize;
356
+ resizeObserver.disconnect();
357
+ resizeObserver.observe($container.get(0), { attributes: true });
353
358
 
354
359
  var restrictGraphToViewport = function (fixed) {
355
360
  if (!fixed) {
356
361
  $("#restrict-graph-to-viewport span").removeClass("glyphicon-ok");
362
+ $(document).ready(function () {
363
+ $(".scale-and-scroll").show();
364
+ });
357
365
  $("input[name=viewport]").removeProp("checked");
358
- $container.addClass("cursorGrab");
366
+ $container.addClass("cursorGrabbing");
359
367
  adaptive = true;
360
368
  d3.select("rect").attr("stroke", "none");
361
369
  center();
362
370
  } else if (fixed) {
363
371
  $("#restrict-graph-to-viewport span").addClass("glyphicon-ok");
364
372
  $("input[name=viewport]").prop("checked", "checked");
373
+ $(document).ready(function () {
374
+ $(".scale-and-scroll").hide();
375
+ });
365
376
  adaptive = false;
366
377
  $container.removeClass(CURSOR_CLASSES);
367
- if (zoomSliderScale > 1) {
368
- grnState.zoomValue = ADAPTIVE_MAX_SCALE;
378
+ if (grnState.zoomValue > ZOOM_DISPLAY_MIDDLE) {
379
+ grnState.zoomValue = ZOOM_DISPLAY_MIDDLE;
369
380
  updateAppBasedOnZoomValue();
370
381
  $container.removeClass(CURSOR_CLASSES);
371
382
  }
@@ -377,6 +388,7 @@ export var drawGraph = function (network) {
377
388
  $(".boundingBox").attr("width", width).attr("height", height);
378
389
  center();
379
390
  }
391
+ updateAppBasedOnZoomValue(); // Update zoom value within bounds
380
392
  };
381
393
 
382
394
  d3.select("#restrict-graph-to-viewport").on("click", function () {
@@ -389,22 +401,15 @@ export var drawGraph = function (network) {
389
401
  restrictGraphToViewport(fixed);
390
402
  });
391
403
 
392
- $(window).on("resize", function () {
393
- if ($container.hasClass("containerFit")) {
394
- $(".boundBoxSize").trigger("click");
395
- }
396
- });
397
-
398
404
  function center () {
399
405
  var viewportWidth = $container.width();
400
406
  var viewportHeight = $container.height();
401
407
  zoom.translateTo(zoomContainer, viewportWidth / 2, viewportHeight / 2);
402
- simulation.alphaTarget(0.3).restart();
403
408
  }
404
409
 
405
410
  function move (direction) {
406
- var width = direction === "left" ? 50 : (direction === "right" ? -50 : 0);
407
- var height = direction === "up" ? 50 : (direction === "down" ? -50 : 0);
411
+ var width = direction === "left" ? -50 : (direction === "right" ? 50 : 0);
412
+ var height = direction === "up" ? -50 : (direction === "down" ? 50 : 0);
408
413
  zoom.translateBy(zoomContainer, width, height);
409
414
  }
410
415
 
@@ -415,18 +420,18 @@ export var drawGraph = function (network) {
415
420
  var weight = boundingBoxContainer.selectAll(".weight");
416
421
 
417
422
  simulation
418
- .nodes(network.genes)
423
+ .nodes(workbook.genes)
419
424
  .on("tick", tick);
420
425
 
421
426
  simulation.force("link")
422
- .links(network.links);
427
+ .links(workbook.links);
423
428
 
424
- link = link.data(network.links)
429
+ link = link.data(workbook.links)
425
430
  .enter().append("g")
426
431
  .attr("class", "link")
427
432
  .attr("strokeWidth", getEdgeThickness);
428
433
 
429
- node = node.data(network.genes)
434
+ node = node.data(workbook.genes)
430
435
  .enter().append("g")
431
436
  .attr("class", "node")
432
437
  .attr("id", function (d) {
@@ -437,13 +442,14 @@ export var drawGraph = function (network) {
437
442
  .call(drag)
438
443
  .on("dblclick", dblclick);
439
444
 
440
- if (network.sheetType === "weighted") {
445
+ if (workbook.sheetType === "weighted") {
441
446
  link.append("path")
442
447
  .attr("class", "mousezone")
443
448
  .style("stroke-width", function (d) {
444
449
  var baseThickness = getEdgeThickness(d);
445
450
  return Math.max(baseThickness, 7);
446
- });
451
+ })
452
+ .attr("stroke-opacity", "0");
447
453
  }
448
454
 
449
455
  link.append("path")
@@ -480,7 +486,7 @@ export var drawGraph = function (network) {
480
486
  var xOffsets;
481
487
  var color;
482
488
 
483
- if (Math.abs(d.value / maxWeight) <= grayThreshold) {
489
+ if (normalize(d) <= grayThreshold) {
484
490
  minimum = "gray";
485
491
  }
486
492
  if ( x1 === x2 && y1 === y2 ) {
@@ -493,9 +499,9 @@ export var drawGraph = function (network) {
493
499
  return "url(#" + d.type + selfRef + "_StrokeWidth" + d.strokeWidth + minimum + ")";
494
500
  } else {
495
501
 
496
- // If negative, you need one bar for horizontal and one for vertical.
497
- // If the user is not coloring the weighted
498
- // sheets, then we make all of the markers arrowheads.
502
+ // If negative, you need one bar for horizontal and one for vertical.
503
+ // If the user is not coloring the weighted
504
+ // sheets, then we make all of the markers arrowheads.
499
505
  if (d.value < 0 && grnState.colorOptimal) {
500
506
  defs.append("marker")
501
507
  .attr("id", "repressor" + selfRef + "_StrokeWidth" + d.strokeWidth + minimum)
@@ -522,22 +528,22 @@ export var drawGraph = function (network) {
522
528
  })
523
529
  .attr("orient", 180)
524
530
  .append("rect")
525
- .attr("width", function () {
526
- return d.strokeWidth;
527
- })
528
- .attr("height", function () {
529
- return 25 + d.strokeWidth;
530
- })
531
- .attr("rx", 10)
532
- .attr("ry", 10)
533
- .attr("style", function () {
534
- if ( normalize(d) <= grayThreshold) {
535
- color = "gray";
536
- } else {
537
- color = d.stroke;
538
- }
539
- return "stroke:" + color + "; fill: " + color + "; stroke-width: 0";
540
- });
531
+ .attr("width", function () {
532
+ return d.strokeWidth;
533
+ })
534
+ .attr("height", function () {
535
+ return 25 + d.strokeWidth;
536
+ })
537
+ .attr("rx", 10)
538
+ .attr("ry", 10)
539
+ .attr("style", function () {
540
+ if ( normalize(d) <= grayThreshold) {
541
+ color = "gray";
542
+ } else {
543
+ color = d.stroke;
544
+ }
545
+ return "stroke:" + color + "; fill: " + color + "; stroke-width: 0";
546
+ });
541
547
 
542
548
  defs.append("marker")
543
549
  .attr("id", "repressorHorizontal" + selfRef + "_StrokeWidth" + d.strokeWidth + minimum)
@@ -632,11 +638,11 @@ export var drawGraph = function (network) {
632
638
  })
633
639
  .attr("orient", function () {
634
640
  return (x1 === x2 && y1 === y2) ?
635
- {
636
- 2: 270, 3: 270, 4: 268, 5: 264, 6: 268, 7: 252,
637
- 8: 248, 9: 243, 10: 240, 11: 240, 12: 235, 13: 233,
638
- 14: 232
639
- }[d.strokeWidth] : "auto";
641
+ {
642
+ 2: 270, 3: 270, 4: 268, 5: 264, 6: 268, 7: 252,
643
+ 8: 248, 9: 243, 10: 240, 11: 240, 12: 235, 13: 233,
644
+ 14: 232
645
+ }[d.strokeWidth] : "auto";
640
646
  })
641
647
  .append("path")
642
648
  .attr("d", "M 0 0 L 14 5 L 0 10 Q 6 5 0 0")
@@ -655,25 +661,29 @@ export var drawGraph = function (network) {
655
661
  return "url(#" + d.type + selfRef + "_StrokeWidth" + d.strokeWidth + minimum + ")";
656
662
  });
657
663
 
658
- if (network.sheetType === "weighted") {
664
+ if (workbook.sheetType === "weighted") {
659
665
  link.append("text")
660
- .attr("class", "weight")
661
- .attr("text-anchor", "middle")
662
- .attr("text-anchor", "middle")
663
- .text(function (d) {
664
- return d.value.toPrecision(4);
665
- });
666
+ .attr("class", "weight")
667
+ .attr("text-anchor", "middle")
668
+ .attr("text-anchor", "middle")
669
+ .attr("fill", "rgb(0,0,0)")
670
+ .style("font-family", "sans-serif")
671
+ .text(function (d) {
672
+ return d.value.toPrecision(4);
673
+ });
666
674
 
667
- weight = weight.data(network.links)
668
- .enter().append("text")
669
- .attr("class", "weight")
670
- .attr("text-anchor", "middle")
671
- .text(function (d) {
672
- return d.value.toPrecision(4);
673
- })
674
- .each(function (d) {
675
- d.weightElement = d3.select(this);
676
- });
675
+ weight = weight.data(workbook.links)
676
+ .enter().append("text")
677
+ .attr("class", "weight")
678
+ .attr("text-anchor", "middle")
679
+ .attr("fill", "rgb(0,0,0)")
680
+ .style("font-family", "sans-serif")
681
+ .text(function (d) {
682
+ return d.value.toPrecision(4);
683
+ })
684
+ .each(function (d) {
685
+ d.weightElement = d3.select(this);
686
+ });
677
687
 
678
688
  }
679
689
 
@@ -693,6 +703,7 @@ export var drawGraph = function (network) {
693
703
  };
694
704
 
695
705
  var CURVE_THRESHOLD = 200;
706
+ var EDGE_OFFSET = 20;
696
707
  var lineTo = function (d) {
697
708
  var node = d3.select("#node" + d.target.index);
698
709
  var w = +node.attr("width");
@@ -705,14 +716,14 @@ export var drawGraph = function (network) {
705
716
  d.target.centerX = d.target.x + (w / 2);
706
717
  d.target.centerY = d.target.y + (h / 2);
707
718
 
708
- // This function calculates the newX and newY.
719
+ // This function calculates the newX and newY.
709
720
  smartPathEnd(d, w, h);
710
721
  x1 = d.source.newX;
711
722
  y1 = d.source.newY;
712
723
  x2 = d.target.newX;
713
724
  y2 = d.target.newY;
714
725
 
715
- // Unit vectors.
726
+ // Unit vectors.
716
727
  var ux = x2 - x1;
717
728
  var uy = y2 - y1;
718
729
  var umagnitude = Math.sqrt(ux * ux + uy * uy);
@@ -725,7 +736,7 @@ export var drawGraph = function (network) {
725
736
  vx /= vmagnitude;
726
737
  vy /= vmagnitude;
727
738
 
728
- // Check for vector direction.
739
+ // Check for vector direction.
729
740
  if (((d.target.newX > d.source.x) && (d.target.newY > d.source.y)) ||
730
741
  ((d.target.newX < d.source.x) && (d.target.newY < d.source.y))) {
731
742
  vx = -vx; vy = -vy;
@@ -739,15 +750,16 @@ export var drawGraph = function (network) {
739
750
  var cp2x = x2 - inlineOffset * ux + vx * orthoOffset;
740
751
  var cp2y = y2 - inlineOffset * uy + vy * orthoOffset;
741
752
 
742
- d.label = {
743
- x: (x1 + cp1x + cp2x + x2) / 4,
744
- y: (y1 + cp1y + cp2y + y2) / 4
745
- };
746
-
747
753
  cp1x = Math.min(Math.max(0, cp1x), width);
748
754
  cp1y = Math.min(Math.max(0, cp1y), height);
749
755
  cp2x = Math.min(Math.max(0, cp2x), width);
750
756
  cp2y = Math.min(Math.max(0, cp2y), height);
757
+
758
+ d.label = {
759
+ x: Math.min(Math.max((x1 + cp1x + cp2x + x2) / 4, EDGE_OFFSET), width - 2 * EDGE_OFFSET),
760
+ y: Math.min(Math.max((y1 + cp1y + cp2y + y2) / 4, EDGE_OFFSET), height - EDGE_OFFSET)
761
+ };
762
+
751
763
  return "C" + cp1x + " " + cp1y + ", " +
752
764
  cp2x + " " + cp2y + ", " +
753
765
  x2 + " " + y2;
@@ -768,7 +780,7 @@ export var drawGraph = function (network) {
768
780
  MINIMUM_DISTANCE = d.strokeWidth > 11 ? 16.5 : 15;
769
781
  }
770
782
 
771
- // Set an offset if the edge is a repressor to make room for the flat arrowhead
783
+ // Set an offset if the edge is a repressor to make room for the flat arrowhead
772
784
  var globalOffset = parseFloat(d.strokeWidth);
773
785
 
774
786
  if (d.value < 0 && grnState.colorOptimal) {
@@ -777,62 +789,62 @@ export var drawGraph = function (network) {
777
789
 
778
790
  var thicknessAdjustment = globalOffset > MINIMUM_DISTANCE ? 1 : 0;
779
791
 
780
- // We need to work out the (tan of the) angle between the
781
- // imaginary horizontal line running through the center of the
782
- // target node and the imaginary line connecting the center of
783
- // the target node with the top-left corner of the same
784
- // node. Of course, this angle is fixed.
792
+ // We need to work out the (tan of the) angle between the
793
+ // imaginary horizontal line running through the center of the
794
+ // target node and the imaginary line connecting the center of
795
+ // the target node with the top-left corner of the same
796
+ // node. Of course, this angle is fixed.
785
797
  d.tanRatioFixed = (d.target.centerY - d.target.y) / (d.target.centerX - d.target.x);
786
798
 
787
- // We also need to work out the (tan of the) angle between the
788
- // imaginary horizontal line running through the center of the
789
- // target node and the imaginary line connecting the center of
790
- // the target node with the center of the source node. This
791
- // angle changes as the nodes move around the screen.
799
+ // We also need to work out the (tan of the) angle between the
800
+ // imaginary horizontal line running through the center of the
801
+ // target node and the imaginary line connecting the center of
802
+ // the target node with the center of the source node. This
803
+ // angle changes as the nodes move around the screen.
792
804
  d.tanRatioMoveable = Math.abs(d.target.centerY - d.source.newY) / Math.abs(d.target.centerX - d.source.newX);
793
805
  // Note, JavaScript handles division-by-zero by returning
794
806
  // Infinity, which in this case is useful, especially
795
807
  // since it handles the subsequent Infinity arithmetic
796
808
  // correctly.
797
809
 
798
- // Now work out the intersection point
810
+ // Now work out the intersection point
799
811
  if (d.tanRatioMoveable === d.tanRatioFixed) {
800
- // Then path is intersecting at corner of textbox so draw
801
- // path to that point
812
+ // Then path is intersecting at corner of textbox so draw
813
+ // path to that point
802
814
 
803
- // By default assume path intersects a left-side corner
815
+ // By default assume path intersects a left-side corner
804
816
  d.target.newX = d.target.x - globalOffset;
805
817
 
806
- // But...
818
+ // But...
807
819
  if (d.target.centerX < d.source.newX) {
808
- // i.e. if target node is to left of the source node
809
- // then path intersects a right-side corner
820
+ // i.e. if target node is to left of the source node
821
+ // then path intersects a right-side corner
810
822
  d.target.newX = d.target.x + w + globalOffset;
811
823
  }
812
824
 
813
- // By default assume path intersects a top corner
825
+ // By default assume path intersects a top corner
814
826
  d.target.newY = d.target.y - globalOffset;
815
827
 
816
- // But...
828
+ // But...
817
829
  if (d.target.centerY < d.source.newY) {
818
- // i.e. if target node is above the source node
819
- // then path intersects a bottom corner
830
+ // i.e. if target node is above the source node
831
+ // then path intersects a bottom corner
820
832
  d.target.newY = d.target.y + h + globalOffset;
821
833
  }
822
834
  }
823
835
 
824
836
  if (d.tanRatioMoveable < d.tanRatioFixed) {
825
- // Then path is intersecting on a vertical side of the
826
- // textbox, which means we know the x-coordinate of the
827
- // path endpoint but we need to work out the y-coordinate
837
+ // Then path is intersecting on a vertical side of the
838
+ // textbox, which means we know the x-coordinate of the
839
+ // path endpoint but we need to work out the y-coordinate
828
840
 
829
- // By default assume path intersects left vertical side
841
+ // By default assume path intersects left vertical side
830
842
  d.target.newX = d.target.x - globalOffset;
831
843
 
832
- // But...
844
+ // But...
833
845
  if (d.target.centerX < d.source.newX) {
834
- // i.e. if target node is to left of the source node
835
- // then path intersects right vertical side
846
+ // i.e. if target node is to left of the source node
847
+ // then path intersects right vertical side
836
848
  if (d.type !== "arrowhead") {
837
849
  d.target.newX = d.target.x + w + globalOffset + 0.25 * d.strokeWidth - thicknessAdjustment;
838
850
  } else {
@@ -840,31 +852,31 @@ export var drawGraph = function (network) {
840
852
  }
841
853
  }
842
854
 
843
- // Now use a bit of trigonometry to work out the y-coord.
855
+ // Now use a bit of trigonometry to work out the y-coord.
844
856
 
845
- // By default assume path intersects towards top of node
857
+ // By default assume path intersects towards top of node
846
858
  d.target.newY = d.target.centerY - ((d.target.centerX - d.target.x) * d.tanRatioMoveable);
847
859
 
848
- // But...
860
+ // But...
849
861
  if (d.target.centerY < d.source.newY) {
850
- // i.e. if target node is above the source node
851
- // then path intersects towards bottom of the node
862
+ // i.e. if target node is above the source node
863
+ // then path intersects towards bottom of the node
852
864
  d.target.newY = (2 * d.target.y) - d.target.newY + h;
853
865
  }
854
866
  }
855
867
 
856
868
  if (d.tanRatioMoveable > d.tanRatioFixed) {
857
- // Then path is intersecting on a horizontal side of the
858
- // textbox, which means we know the y-coordinate of the
859
- // path endpoint but we need to work out the x-coordinate
869
+ // Then path is intersecting on a horizontal side of the
870
+ // textbox, which means we know the y-coordinate of the
871
+ // path endpoint but we need to work out the x-coordinate
860
872
 
861
- // By default assume path intersects top horizontal side
873
+ // By default assume path intersects top horizontal side
862
874
  d.target.newY = d.target.y - globalOffset;
863
875
 
864
- // But...
876
+ // But...
865
877
  if (d.target.centerY < d.source.newY) {
866
- // i.e. if target node is above the source node
867
- // then path intersects bottom horizontal side
878
+ // i.e. if target node is above the source node
879
+ // then path intersects bottom horizontal side
868
880
  if (d.type !== "arrowhead") {
869
881
  d.target.newY = d.target.y + h + globalOffset + 0.25 * d.strokeWidth - thicknessAdjustment;
870
882
  } else {
@@ -872,15 +884,15 @@ export var drawGraph = function (network) {
872
884
  }
873
885
  }
874
886
 
875
- // Now use a bit of trigonometry to work out the x-coord.
887
+ // Now use a bit of trigonometry to work out the x-coord.
876
888
 
877
- // By default assume path intersects towards lefthand side
889
+ // By default assume path intersects towards lefthand side
878
890
  d.target.newX = d.target.centerX - ((d.target.centerY - d.target.y) / d.tanRatioMoveable);
879
891
 
880
- // But...
892
+ // But...
881
893
  if (d.target.centerX < d.source.newX) {
882
- // i.e. if target node is to left of the source node
883
- // then path intersects towards the righthand side
894
+ // i.e. if target node is to left of the source node
895
+ // then path intersects towards the righthand side
884
896
  d.target.newX = (2 * d.target.x) - d.target.newX + w;
885
897
  }
886
898
  }
@@ -914,11 +926,12 @@ export var drawGraph = function (network) {
914
926
  node.selectAll(".nodeText").remove();
915
927
  var text = node.append("text")
916
928
  .attr("dy", NODE_HEIGHT)
917
- .attr("text-anchor", "middle")
918
929
  .attr("class", "nodeText")
930
+ .attr("fill", "rgb(0, 0, 0)")
931
+ .style("text-anchor", "middle")
919
932
  .style("font-size", "18px")
920
933
  .style("stroke-width", "0")
921
- .style("fill", "black")
934
+ .style("font-family", "sans-serif")
922
935
  .text(function (d) {
923
936
  return d.name;
924
937
  })
@@ -935,8 +948,12 @@ export var drawGraph = function (network) {
935
948
  .attr({
936
949
  href: "info?" + $.param({
937
950
  symbol: gene.name,
938
- species: grnState.species,
939
- taxon: grnState.taxon}),
951
+ species: grnState.genePageData.species,
952
+ jaspar: grnState.genePageData.taxonJaspar,
953
+ uniprot: grnState.genePageData.taxonUniprot,
954
+ ensembl: grnState.genePageData.ensembl,
955
+ mine: grnState.genePageData.mine
956
+ }),
940
957
  target: "_blank"
941
958
  });
942
959
  $("body").append(tempLink);
@@ -960,129 +977,152 @@ export var drawGraph = function (network) {
960
977
  return self.indexOf(value) === index;
961
978
  }
962
979
 
963
- var getExpressionData = function (gene, strain, average) {
964
- var strainData = grnState.network["expression"][strain];
980
+ const getExpressionData = (gene, strain, average) => {
981
+ const strainData = grnState.workbook.expression[strain];
965
982
  if (average) {
966
- var uniqueTimePoints = strainData.time_points.filter(onlyUnique);
967
- var avgMap = {};
983
+ const uniqueTimePoints = strainData.timePoints.filter(onlyUnique);
984
+ let avgMap = {};
968
985
  uniqueTimePoints.forEach(function (key) {
969
986
  avgMap[key] = [];
970
987
  });
971
- strainData.time_points.forEach(function (time, index) {
988
+ strainData.timePoints.forEach(function (time, index) {
972
989
  avgMap[time].push(strainData.data[gene][index]);
973
990
  });
974
- var avgs = [];
991
+ let avgs = [];
975
992
  Object.keys(avgMap).forEach(function (key) {
976
- var length = avgMap[key].length;
977
- var sum = avgMap[key].reduce(function (partialSum, currentValue) {
993
+ const length = avgMap[key].length;
994
+ const sum = avgMap[key].reduce(function (partialSum, currentValue) {
978
995
  return partialSum + currentValue;
979
996
  }, 0);
980
997
  avgs.push(sum / length);
981
998
  });
982
999
  return {data: avgs, timePoints: uniqueTimePoints};
983
1000
  }
984
- return {data: strainData.data[gene], timePoints: strainData.time_points};
1001
+ return {data: strainData.data[gene], timePoints: strainData.timePoints};
985
1002
  };
986
1003
 
987
1004
  var colorNodes = function (position, dataset, average, logFoldChangeMaxValue) {
988
1005
  var timePoints = [];
989
1006
  node.each(function (p) {
990
1007
  d3.select(this)
991
- .append("g")
992
- .selectAll(".coloring")
993
- .data(function () {
994
- var result = getExpressionData(p.name, dataset, average);
995
- timePoints = result.timePoints;
996
- return result.data;
997
- })
998
- .attr("class", "coloring")
999
- .enter().append("rect")
1000
- .attr("width", function () {
1001
- var width = rect.attr("width") / timePoints.length;
1002
- return width + "px";
1003
- })
1004
- .attr("class", "coloring")
1005
- .attr("height", rect.attr("height") / 2 + "px")
1006
- .attr("transform", function (d, i) {
1007
- var yOffset = position === "top" ? 0 : rect.attr("height") / 2;
1008
- var xOffset = i * (rect.attr("width") / timePoints.length);
1009
- return "translate(" + xOffset + "," + yOffset + ")";
1010
- })
1011
- .attr("stroke-width", "0px")
1012
- .style("fill", function (d) {
1013
- d = d || 0; // missing values are changed to 0
1014
- var scale = d3.scaleLinear()
1015
- .domain([-logFoldChangeMaxValue, logFoldChangeMaxValue])
1016
- .range([0, 1]);
1017
- return d3.interpolateRdBu(scale(-d));
1018
- })
1019
- .text(function (d) {
1020
- return "data " + JSON.stringify(d) + " of " + p.name;
1021
- });
1008
+ .append("g")
1009
+ .selectAll(".coloring")
1010
+ .data(function () {
1011
+ if (grnState.workbook.expression[dataset].data[p.name]) {
1012
+ const result = getExpressionData(p.name, dataset, average);
1013
+ timePoints = result.timePoints;
1014
+ return result.data;
1015
+ } else {
1016
+ return 0;
1017
+ }
1018
+ })
1019
+ .attr("class", "coloring")
1020
+ .enter().append("rect")
1021
+ .attr("width", function () {
1022
+ var width = (p.textWidth + (2 * NODE_MARGIN)) / timePoints.length;
1023
+ return width + "px";
1024
+ })
1025
+ .attr("class", "coloring")
1026
+ .attr("height", rect.attr("height") / 2 + "px")
1027
+ .attr("transform", function (d, i) {
1028
+ var yOffset = position === "top" ? 0 : rect.attr("height") / 2;
1029
+ var xOffset = i * ((p.textWidth + (2 * NODE_MARGIN)) / timePoints.length);
1030
+ return "translate(" + xOffset + "," + yOffset + ")";
1031
+ })
1032
+ .attr("stroke-width", "0px")
1033
+ .style("fill", function (d) {
1034
+ d = d || 0; // missing values are changed to 0
1035
+ var scale = d3.scaleLinear()
1036
+ .domain([-logFoldChangeMaxValue, logFoldChangeMaxValue])
1037
+ .range([0, 1]);
1038
+ return d3.interpolateRdBu(scale(-d));
1039
+ })
1040
+ .text(function (d) {
1041
+ return "data " + JSON.stringify(d) + " of " + p.name;
1042
+ });
1022
1043
  });
1023
1044
  };
1024
1045
 
1025
1046
  var renderNodeColoringLegend = function (logFoldChangeMaxValue) {
1026
1047
  var $nodeColoringLegend = $(".node-coloring-legend");
1027
1048
  d3.select($nodeColoringLegend[0]).selectAll("svg").remove();
1028
- var xMargin = 10;
1029
- var yMargin = 30;
1030
- var width = 200;
1049
+ var yMargin = 20;
1050
+ var width = 203;
1031
1051
  var height = 10;
1032
1052
  var textYOffset = 10;
1033
- var increment = 0.1;
1034
1053
 
1035
1054
  var svg = d3.select($nodeColoringLegend[0])
1036
1055
  .append("svg")
1037
- .attr("width", width + xMargin * 2)
1056
+ .attr("width", "100%")
1038
1057
  .attr("height", height + yMargin)
1039
1058
  .append("g")
1040
- .attr("transform", "translate(" + xMargin / 2 + "," + yMargin / 2 + ")");
1041
-
1059
+ .attr("transform", "translate(0, 5)")
1060
+ .attr("id", "nodeColoringLegendId");
1061
+
1062
+ // Thank you https://www.visualcinnamon.com/2016/05/smooth-color-legend-d3-svg-gradient.html
1063
+ const linearGradientId = "node-coloring-color-scale";
1064
+ var defs = svg.append("defs");
1065
+ var linearGradient = defs.append("linearGradient")
1066
+ .attr("id", linearGradientId)
1067
+ .attr("x1", "0%")
1068
+ .attr("y1", "0%")
1069
+ .attr("x2", "100%")
1070
+ .attr("y2", "0%");
1071
+
1072
+ const increment = Math.abs(logFoldChangeMaxValue) / 50; // Guarantee 50 steps regardless of the range.
1042
1073
  var gradientValues = d3.range(-logFoldChangeMaxValue, logFoldChangeMaxValue, increment);
1074
+ var scale = d3.scaleLinear()
1075
+ .domain([-logFoldChangeMaxValue, logFoldChangeMaxValue])
1076
+ .range([0, 1]);
1043
1077
 
1044
- var coloring = svg.selectAll(".node-coloring-legend")
1078
+ linearGradient.selectAll("stop")
1045
1079
  .data(gradientValues)
1046
- .attr("class", "node-coloring-legend");
1047
-
1048
- coloring.enter().append("rect")
1049
- .attr("width", width / gradientValues.length + "px")
1050
- .attr("height", height + "px")
1051
- .attr("transform", function (d, i) {
1052
- return "translate(" + (i * (width / gradientValues.length)) + "," + 0 + ")";
1080
+ .enter().append("stop")
1081
+ .attr("offset", function (d, i) {
1082
+ return i / (gradientValues.length - 1);
1053
1083
  })
1054
- .style("fill", function (d) {
1055
- var scale = d3.scaleLinear()
1056
- .domain([-logFoldChangeMaxValue, logFoldChangeMaxValue])
1057
- .range([0, 1]);
1058
-
1059
- // We negate d because we actually want red to be on the right.
1084
+ .attr("stop-color", function (d) {
1060
1085
  return d3.interpolateRdBu(scale(-d));
1061
1086
  });
1062
1087
 
1088
+ svg.append("rect")
1089
+ .attr("width", `${width}px`)
1090
+ .attr("height", `${height}px`)
1091
+ .style("fill", `url(#${linearGradientId})`);
1092
+
1063
1093
  var legendLabels = {
1064
- "left": {
1065
- "textContent": (-logFoldChangeMaxValue).toFixed(2),
1066
- "x": -xMargin / 2
1067
- },
1068
- "center": {
1069
- "textContent": "0",
1070
- "x": width / 2
1094
+ left: {
1095
+ textAnchor: "start",
1096
+ textContent: (-logFoldChangeMaxValue).toFixed(2),
1097
+ x: 0
1071
1098
  },
1072
- "right": {
1073
- "textContent": (logFoldChangeMaxValue).toFixed(2),
1074
- "x": width - xMargin / 2
1099
+ center: {
1100
+ textAnchor: "middle",
1101
+ textContent: "0",
1102
+ x: width / 2
1075
1103
  },
1104
+ right: {
1105
+ textAnchor: "end",
1106
+ textContent: (logFoldChangeMaxValue).toFixed(2),
1107
+ x: width
1108
+ }
1076
1109
  };
1077
- var g = document.querySelector("body > div.sidebar > div.node-coloring > div > svg > g");
1110
+ /* eslint-disable max-len */
1111
+ var g = document.getElementById("nodeColoringLegendId");
1112
+ /* eslint-enable max-len */
1113
+
1078
1114
  for (var key in legendLabels) {
1079
1115
  var label = document.createElementNS("http://www.w3.org/2000/svg", "text");
1080
1116
  label.textContent = legendLabels[key].textContent;
1081
1117
  label.setAttribute("font-size", "8px");
1118
+ label.setAttribute("text-anchor", legendLabels[key].textAnchor);
1082
1119
  label.setAttribute("x", legendLabels[key].x);
1083
1120
  label.setAttribute("y", height + textYOffset + "px");
1121
+ label.setAttribute("fill", "rgb(0,0,0)");
1122
+
1084
1123
  g.appendChild(label);
1085
1124
  }
1125
+
1086
1126
  };
1087
1127
 
1088
1128
  updaters.removeNodeColoring = function () {
@@ -1093,9 +1133,9 @@ export var drawGraph = function (network) {
1093
1133
  updaters.renderNodeColoring = function () {
1094
1134
  if (grnState.nodeColoring.nodeColoringEnabled) {
1095
1135
  colorNodes("top", grnState.nodeColoring.topDataset, grnState.nodeColoring.averageTopDataset,
1096
- grnState.nodeColoring.logFoldChangeMaxValue);
1136
+ grnState.nodeColoring.logFoldChangeMaxValue);
1097
1137
  colorNodes("bottom", grnState.nodeColoring.bottomDataset, grnState.nodeColoring.averageBottomDataset,
1098
- grnState.nodeColoring.logFoldChangeMaxValue);
1138
+ grnState.nodeColoring.logFoldChangeMaxValue);
1099
1139
  renderNodeLabels();
1100
1140
  renderNodeColoringLegend(grnState.nodeColoring.logFoldChangeMaxValue);
1101
1141
  }
@@ -1110,7 +1150,7 @@ export var drawGraph = function (network) {
1110
1150
  return false;
1111
1151
  };
1112
1152
 
1113
- if (!$.isEmptyObject(network.expression) && hasExpressionData(network.expression) &&
1153
+ if (!$.isEmptyObject(workbook.expression) && hasExpressionData(workbook.expression) &&
1114
1154
  grnState.nodeColoring.topDataset !== undefined) {
1115
1155
  updaters.renderNodeColoring();
1116
1156
  }
@@ -1131,7 +1171,7 @@ export var drawGraph = function (network) {
1131
1171
 
1132
1172
  var currentWeightVisibilitySetting = null;
1133
1173
 
1134
- if (network.sheetType === "weighted") {
1174
+ if (workbook.sheetType === "weighted") {
1135
1175
  if ($(".weightedGraphOptions").hasClass("hidden")) {
1136
1176
  $(".weightedGraphOptions").removeClass("hidden");
1137
1177
  }
@@ -1235,7 +1275,7 @@ export var drawGraph = function (network) {
1235
1275
  updaters.setNodesToGrid = () => { // eslint-disable-line no-unused-vars
1236
1276
  const margin = 10;
1237
1277
  const grid = Grid() // eslint-disable-line no-undef
1238
- .data(network.genes)
1278
+ .data(workbook.genes)
1239
1279
  .bands(true)
1240
1280
  .padding([0.2, 0])
1241
1281
  .size([$container.width() - margin, $container.height() - margin]); // set size of container
@@ -1257,8 +1297,8 @@ export var drawGraph = function (network) {
1257
1297
  }
1258
1298
  };
1259
1299
 
1260
- // Tick only runs while the graph physics are still running.
1261
- // (I.e. when the graph is completely relaxed, tick stops running.)
1300
+ // Tick only runs while the graph physics are still running.
1301
+ // (I.e. when the graph is completely relaxed, tick stops running.)
1262
1302
  function tick () {
1263
1303
  var getSelfReferringEdge = function (node) {
1264
1304
  return link.select("path")["_groups"][0].map(function (path) {
@@ -1282,7 +1322,7 @@ export var drawGraph = function (network) {
1282
1322
 
1283
1323
  var selfReferringEdgeWidth = (selfReferringEdge ? getSelfReferringRadius(selfReferringEdge) +
1284
1324
  selfReferringEdge.strokeWidth + 2 : 0);
1285
- var rightBoundary = width - d.textWidth - BOUNDARY_MARGIN - selfReferringEdgeWidth;
1325
+ var rightBoundary = width - (d.textWidth + OFFSET_VALUE) - BOUNDARY_MARGIN - selfReferringEdgeWidth;
1286
1326
  var currentXPos = Math.max(BOUNDARY_MARGIN, Math.min(rightBoundary, d.x));
1287
1327
  if (adaptive && width < MAX_WIDTH &&
1288
1328
  (currentXPos === BOUNDARY_MARGIN || currentXPos === rightBoundary)) {
@@ -1349,7 +1389,7 @@ export var drawGraph = function (network) {
1349
1389
  var dy = y2 - y1;
1350
1390
  var dr = Math.sqrt(dx * dx + dy * dy);
1351
1391
 
1352
- // Defaults for normal edge.
1392
+ // Defaults for normal edge.
1353
1393
  var drx = dr;
1354
1394
  var dry = dr;
1355
1395
  var xRotation = 0; // degrees
@@ -1357,33 +1397,34 @@ export var drawGraph = function (network) {
1357
1397
  var sweep = 1; // 1 or 0
1358
1398
  var offset = parseFloat(d.strokeWidth);
1359
1399
 
1360
- // Edge adjustment values when long self-node edges get hidden behind the node.
1400
+ // Edge adjustment values when long self-node edges get hidden behind the node.
1361
1401
  var DEFAULT_NODE_SHIFT = 1.033;
1362
1402
  var SHORT_NODE_LIMIT = 135;
1363
1403
  var ADDITIONAL_SHIFT = 0.07;
1364
1404
  var END_POINT_ADJUSTMENT = 1.2;
1365
1405
 
1366
1406
 
1367
- // Self edge.
1407
+ // Self edge.
1368
1408
  if (x1 === x2 && y1 === y2) {
1369
- // Move the position of the loop.
1409
+ // Move the position of the loop.
1370
1410
  x1 = d.source.x + (d.source.textWidth) * DEFAULT_NODE_SHIFT;
1371
1411
  y1 = d.source.y + (nodeHeight / 2) + SELF_REFERRING_Y_OFFSET;
1372
1412
 
1373
- // Fiddle with this angle to get loop oriented.
1413
+ // Fiddle with this angle to get loop oriented.
1414
+ // (Future: This doesn't appear to change anything?)
1374
1415
  xRotation = 45;
1375
1416
 
1376
- // Needs to be 1.
1417
+ // Needs to be 1.
1377
1418
  largeArc = 1;
1378
1419
 
1379
- // Change sweep to change orientation of loop.
1420
+ // Change sweep to change orientation of loop.
1380
1421
  sweep = 1;
1381
1422
 
1382
1423
  drx = getSelfReferringRadius(d);
1383
1424
  dry = getSelfReferringRadius(d);
1384
1425
 
1385
- // For whatever reason the arc collapses to a point if the beginning
1386
- // and ending points of the arc are the same, so kludge it.
1426
+ // For whatever reason the arc collapses to a point if the beginning
1427
+ // and ending points of the arc are the same, so kludge it.
1387
1428
  if (d.source.textWidth > SHORT_NODE_LIMIT) {
1388
1429
  DEFAULT_NODE_SHIFT += ADDITIONAL_SHIFT;
1389
1430
  }
@@ -1395,11 +1436,12 @@ export var drawGraph = function (network) {
1395
1436
  }
1396
1437
  }
1397
1438
 
1398
- d.label = { x: x1, y: y1 + dry * 3 };
1439
+ d.label = { x: Math.min(width - (13 * offset), x1), // For 4 decimal places
1440
+ y: Math.min(height - offset, y1 + dry * 3)};
1399
1441
 
1400
1442
  return "M" + x1 + "," + y1 +
1401
- "A" + drx + "," + dry + " " + xRotation + "," + largeArc + "," + sweep + " " +
1402
- x2 + "," + (y2 + offset);
1443
+ "A" + drx + "," + dry + " " + xRotation + "," + largeArc + "," + sweep + " " +
1444
+ x2 + "," + (y2 + offset);
1403
1445
  } else {
1404
1446
  return moveTo(d) + lineTo(d);
1405
1447
  }
@@ -1445,7 +1487,7 @@ export var drawGraph = function (network) {
1445
1487
  }
1446
1488
 
1447
1489
  function normalize (d) {
1448
- return Math.abs(d.value / maxWeight);
1490
+ return Math.abs(d.value / maxWeight).toPrecision(4);
1449
1491
  }
1450
1492
 
1451
1493
  function dragstart (d) {
@@ -1463,6 +1505,9 @@ export var drawGraph = function (network) {
1463
1505
 
1464
1506
  grnState.simulation = simulation;
1465
1507
 
1508
+ // The restrict graph state is sometimes carried over across reloads
1509
+ restrictGraphToViewport( $("input[name=viewport]").prop("checked"));
1510
+
1466
1511
  modifyChargeParameter(grnState.chargeSlider.currentVal);
1467
1512
  modifyLinkDistanceParameter(grnState.linkDistanceSlider.currentVal);
1468
1513