grnsight 3.0.0 → 5.1.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 (489) hide show
  1. package/.eslintignore +1 -0
  2. package/.travis.yml +1 -1
  3. package/GRNsight - Beta.html +194 -0
  4. package/Gemfile.lock +259 -0
  5. package/README.md +1 -1
  6. package/_gh_pages/about.html +360 -45
  7. package/_gh_pages/assets/css/footer.css +3 -0
  8. package/_gh_pages/assets/css/main.css +28 -14
  9. package/_gh_pages/assets/images/21-genes_31-edges_Schade-data_estimation_output_binary-no-targetless-genes_sif.png +0 -0
  10. package/_gh_pages/assets/images/21-genes_31-edges_Schade-data_estimation_output_binary_sif.png +0 -0
  11. package/_gh_pages/assets/images/21-genes_31-edges_Schade-data_graphml_3-edges-and-footer.png +0 -0
  12. package/_gh_pages/assets/images/21-genes_31-edges_Schade-data_graphml_header-and-3-nodes.png +0 -0
  13. package/_gh_pages/assets/images/21-genes_31-edges_Schade-data_graphml_output_3-edges-and-footer.png +0 -0
  14. package/_gh_pages/assets/images/21-genes_31-edges_Schade-data_graphml_output_header-and-3-nodes.png +0 -0
  15. package/_gh_pages/assets/images/21-genes_31-edges_Schade-data_input_binary-no-targetless-genes_sif.png +0 -0
  16. package/_gh_pages/assets/images/21-genes_31-edges_Schade-data_input_binary_sif.png +0 -0
  17. package/_gh_pages/assets/images/21-genes_31-edges_Schade-data_input_concatenated-no-targetless-genes_sif.png +0 -0
  18. package/_gh_pages/assets/images/21-genes_31-edges_Schade-data_input_concatenated_sif.png +0 -0
  19. package/_gh_pages/assets/images/Choe-Shin_CMSI402-poster-session_20180430.jpg +0 -0
  20. package/_gh_pages/assets/images/Choe_SCCUR_2017.jpg +0 -0
  21. package/_gh_pages/assets/images/Dahlquist-Choe-Shin_CMSI402-poster-session_20180430.jpg +0 -0
  22. package/_gh_pages/assets/images/Dionisio-Dahlquist_GRNsight-shades_20170506.jpg +0 -0
  23. package/_gh_pages/assets/images/Klein_Samdarshi_TriBeta_2018_20180317.jpg +0 -0
  24. package/_gh_pages/assets/images/Shin_SCCUR_2017.jpg +0 -0
  25. 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
  26. 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
  27. package/_gh_pages/assets/images/gene-pages-0.png +0 -0
  28. package/_gh_pages/assets/images/gene-pages-1.png +0 -0
  29. package/_gh_pages/assets/images/gene-pages-2.png +0 -0
  30. package/_gh_pages/assets/images/gene-pages-3.png +0 -0
  31. package/_gh_pages/assets/images/grnsight2020.png +0 -0
  32. package/_gh_pages/assets/images/v3demo2-grid+nodecoloring.png +0 -0
  33. package/_gh_pages/assets/images/v3demo2-nodecoloring.png +0 -0
  34. package/_gh_pages/assets/images/v3demo2.png +0 -0
  35. package/_gh_pages/assets/js/ga-report.js +11 -11
  36. package/_gh_pages/assets/js/iframeResizer.min.js +9 -0
  37. package/_gh_pages/assets/js/main.js +43 -43
  38. package/_gh_pages/beta.html +29 -24
  39. package/_gh_pages/contact.html +31 -31
  40. package/_gh_pages/coverage/coverage.json +1 -0
  41. package/_gh_pages/coverage/coverage.raw.json +1 -0
  42. package/_gh_pages/coverage/lcov-report/base.css +223 -0
  43. package/_gh_pages/coverage/lcov-report/block-navigation.js +63 -0
  44. package/_gh_pages/coverage/lcov-report/controllers/additional-sheet-parser.js.html +330 -0
  45. package/_gh_pages/coverage/lcov-report/controllers/constants.js.html +243 -0
  46. package/_gh_pages/coverage/lcov-report/controllers/export-controller.js.html +285 -0
  47. package/_gh_pages/coverage/lcov-report/controllers/exporters/graphml.js.html +405 -0
  48. package/_gh_pages/coverage/lcov-report/controllers/exporters/index.html +110 -0
  49. package/_gh_pages/coverage/lcov-report/controllers/exporters/sif.js.html +150 -0
  50. package/_gh_pages/coverage/lcov-report/controllers/helpers.js.html +114 -0
  51. package/_gh_pages/coverage/lcov-report/controllers/import-controller.js.html +233 -0
  52. package/_gh_pages/coverage/lcov-report/controllers/importers/graphml.js.html +716 -0
  53. package/_gh_pages/coverage/lcov-report/controllers/importers/index.html +106 -0
  54. package/_gh_pages/coverage/lcov-report/controllers/importers/sif.js.html +488 -0
  55. package/_gh_pages/coverage/lcov-report/controllers/index.html +162 -0
  56. package/_gh_pages/coverage/lcov-report/controllers/semantic-checker.js.html +810 -0
  57. package/_gh_pages/coverage/lcov-report/controllers/spreadsheet-controller.js.html +1779 -0
  58. package/_gh_pages/coverage/lcov-report/index.html +136 -0
  59. package/_gh_pages/coverage/lcov-report/prettify.css +1 -0
  60. package/_gh_pages/coverage/lcov-report/prettify.js +1 -0
  61. package/_gh_pages/coverage/lcov-report/server/controllers/additional-sheet-parser.js.html +330 -0
  62. package/_gh_pages/coverage/lcov-report/server/controllers/constants.js.html +243 -0
  63. package/_gh_pages/coverage/lcov-report/server/controllers/export-controller.js.html +285 -0
  64. package/_gh_pages/coverage/lcov-report/server/controllers/exporters/graphml.js.html +405 -0
  65. package/_gh_pages/coverage/lcov-report/server/controllers/exporters/index.html +110 -0
  66. package/_gh_pages/coverage/lcov-report/server/controllers/exporters/sif.js.html +150 -0
  67. package/_gh_pages/coverage/lcov-report/server/controllers/graphml-constants.js.html +585 -0
  68. package/_gh_pages/coverage/lcov-report/server/controllers/helpers.js.html +114 -0
  69. package/_gh_pages/coverage/lcov-report/server/controllers/import-controller.js.html +237 -0
  70. package/_gh_pages/coverage/lcov-report/server/controllers/importers/graphml.js.html +585 -0
  71. package/_gh_pages/coverage/lcov-report/server/controllers/importers/index.html +110 -0
  72. package/_gh_pages/coverage/lcov-report/server/controllers/importers/sif.js.html +492 -0
  73. package/_gh_pages/coverage/lcov-report/server/controllers/index.html +188 -0
  74. package/_gh_pages/coverage/lcov-report/server/controllers/semantic-checker.js.html +810 -0
  75. package/_gh_pages/coverage/lcov-report/server/controllers/spreadsheet-controller.js.html +1779 -0
  76. package/_gh_pages/coverage/lcov-report/sort-arrow-sprite.png +0 -0
  77. package/_gh_pages/coverage/lcov-report/sorter.js +158 -0
  78. package/_gh_pages/coverage/lcov-report/web-client/public/js/grnstate.js.html +225 -0
  79. package/_gh_pages/coverage/lcov-report/web-client/public/js/index.html +97 -0
  80. package/_gh_pages/coverage/lcov.info +49 -0
  81. package/_gh_pages/documentation.html +998 -320
  82. package/_gh_pages/documents/abstracts/SIGGRAPH 2017 Abstract/siggraph-abstract-review.aux +47 -0
  83. package/_gh_pages/documents/abstracts/SIGGRAPH 2017 Abstract/siggraph-abstract-review.bbl +73 -0
  84. package/_gh_pages/documents/abstracts/SIGGRAPH 2017 Abstract/siggraph-abstract-review.blg +52 -0
  85. package/_gh_pages/documents/abstracts/SIGGRAPH 2017 Abstract/siggraph-abstract-review.log +1056 -0
  86. package/_gh_pages/documents/abstracts/SIGGRAPH 2017 Abstract/siggraph-abstract-review.out +7 -0
  87. package/_gh_pages/documents/abstracts/SIGGRAPH 2017 Abstract/siggraph-abstract-review.synctex.gz +0 -0
  88. package/_gh_pages/documents/manuscripts/peerj-computerscience-2016/revisions/GRNsight_PeerJ-CS_manuscript_2016_text-only_revised-Dondi.docx +0 -0
  89. package/_gh_pages/encryption/server.cert +21 -0
  90. package/_gh_pages/encryption/server.key +28 -0
  91. package/_gh_pages/favicon.ico +0 -0
  92. package/_gh_pages/index.html +45 -22
  93. package/_gh_pages/links.html +47 -28
  94. package/_gh_pages/news.html +103 -21
  95. package/_gh_pages/onlyfooter.html +78 -0
  96. package/_gh_pages/onlyheader.html +64 -0
  97. package/_gh_pages/onlysidebar.html +73 -0
  98. package/_gh_pages/package-lock.json +14048 -0
  99. package/_gh_pages/people.html +129 -40
  100. package/_gh_pages/privacy.html +23 -17
  101. package/_gh_pages/publications.html +75 -33
  102. package/_gh_pages/robots.txt +1 -0
  103. package/_gh_pages/sitemap.xml +87 -74
  104. package/_gh_pages/test-files/import-samples/attributes.graphml +40 -0
  105. package/_gh_pages/test-files/import-samples/port.graphml +32 -0
  106. package/_gh_pages/test-files/import-samples/simple.graphml +31 -0
  107. package/_gh_pages/web-client/public/js/grnsight.min.js +2347 -0
  108. package/_gh_pages/web-client/public/stylesheets/grnsight.css +443 -0
  109. package/coverage/coverage.json +1 -1
  110. package/coverage/coverage.raw.json +1 -0
  111. package/coverage/lcov-report/base.css +18 -8
  112. package/coverage/lcov-report/block-navigation.js +63 -0
  113. package/coverage/lcov-report/controllers/additional-sheet-parser.js.html +330 -0
  114. package/coverage/lcov-report/controllers/constants.js.html +65 -61
  115. package/coverage/lcov-report/controllers/export-controller.js.html +96 -92
  116. package/coverage/lcov-report/controllers/exporters/graphml.js.html +168 -164
  117. package/coverage/lcov-report/controllers/exporters/index.html +36 -32
  118. package/coverage/lcov-report/controllers/exporters/sif.js.html +65 -61
  119. package/coverage/lcov-report/controllers/helpers.js.html +25 -21
  120. package/coverage/lcov-report/controllers/index.html +49 -45
  121. package/coverage/lcov-report/controllers/semantic-checker.js.html +403 -396
  122. package/coverage/lcov-report/controllers/spreadsheet-controller.js.html +973 -879
  123. package/coverage/lcov-report/index.html +45 -28
  124. package/coverage/lcov-report/server/controllers/additional-sheet-parser.js.html +330 -0
  125. package/coverage/lcov-report/server/controllers/constants.js.html +243 -0
  126. package/coverage/lcov-report/server/controllers/export-controller.js.html +285 -0
  127. package/coverage/lcov-report/server/controllers/exporters/graphml.js.html +405 -0
  128. package/coverage/lcov-report/server/controllers/exporters/index.html +110 -0
  129. package/coverage/lcov-report/server/controllers/exporters/sif.js.html +150 -0
  130. package/coverage/lcov-report/server/controllers/graphml-constants.js.html +585 -0
  131. package/coverage/lcov-report/server/controllers/helpers.js.html +114 -0
  132. package/coverage/lcov-report/server/controllers/import-controller.js.html +237 -0
  133. package/coverage/lcov-report/server/controllers/importers/graphml.js.html +585 -0
  134. package/coverage/lcov-report/server/controllers/importers/index.html +110 -0
  135. package/coverage/lcov-report/server/controllers/importers/sif.js.html +492 -0
  136. package/coverage/lcov-report/server/controllers/index.html +188 -0
  137. package/coverage/lcov-report/server/controllers/semantic-checker.js.html +810 -0
  138. package/coverage/lcov-report/server/controllers/spreadsheet-controller.js.html +1779 -0
  139. package/coverage/lcov-report/web-client/public/js/grnstate.js.html +225 -0
  140. package/coverage/lcov-report/web-client/public/js/index.html +97 -0
  141. package/coverage/lcov.info +1758 -876
  142. package/encryption/server.cert +21 -0
  143. package/encryption/server.key +28 -0
  144. package/package.json +46 -22
  145. package/server/app.js +6 -2
  146. package/server/config/config.js +16 -7
  147. package/server/controllers/additional-sheet-parser.js +292 -55
  148. package/server/controllers/api-controllers.js +36 -0
  149. package/server/controllers/constants.js +4 -37
  150. package/server/controllers/database-controller.js +129 -0
  151. package/server/controllers/demo-workbooks.js +5973 -0
  152. package/server/controllers/export-constants.js +78 -0
  153. package/server/controllers/export-controller.js +25 -3
  154. package/server/controllers/exporters/graphml.js +15 -15
  155. package/server/controllers/exporters/sif.js +7 -7
  156. package/server/controllers/exporters/xlsx.js +183 -0
  157. package/server/controllers/expression-sheet-parser.js +170 -0
  158. package/server/controllers/ga-controller.js +1 -1
  159. package/server/controllers/graphml-constants.js +0 -17
  160. package/server/controllers/helpers.js +25 -1
  161. package/server/controllers/import-controller.js +2 -2
  162. package/server/controllers/importers/graphml.js +17 -20
  163. package/server/controllers/importers/sif.js +22 -18
  164. package/server/controllers/network-sheet-parser.js +307 -0
  165. package/server/controllers/semantic-checker.js +30 -162
  166. package/server/controllers/sif-constants.js +36 -0
  167. package/server/controllers/spreadsheet-controller.js +277 -424
  168. package/server/controllers/workbook-constants.js +521 -0
  169. package/test/additional-sheet-parser-tests.js +147 -38
  170. package/test/api-tests.js +245 -0
  171. package/test/errors-adjacency-matrix-modifications.js +30 -29
  172. package/test/errors-gene-name-modifications.js +9 -0
  173. package/test/errors-graph-tests.js +4 -4
  174. package/test/errors-sheet-modifications.js +10 -2
  175. package/test/export-tests.js +431 -24
  176. package/test/expression-data-import-tests.js +113 -0
  177. package/test/grnstate-tests.js +29 -0
  178. package/test/import-graphml-tests.js +59 -40
  179. package/test/import-sif-tests.js +50 -37
  180. package/test/test.js +557 -93
  181. package/test/warnings-adjacency-matrix-modifications.js +8 -7
  182. package/test-files/additional-sheet-test-files/optimization-diagnostics-default.xlsx +0 -0
  183. package/test-files/additional-sheet-test-files/optimization-diagnostics-extraneous-data.xlsx +0 -0
  184. package/test-files/additional-sheet-test-files/optimization-diagnostics-incorrect-MSE-gene-header.xlsx +0 -0
  185. package/test-files/additional-sheet-test-files/optimization-diagnostics-incorrect-MSE-header.xlsx +0 -0
  186. package/test-files/additional-sheet-test-files/optimization-diagnostics-incorrect-column-headers.xlsx +0 -0
  187. package/test-files/additional-sheet-test-files/optimization-diagnostics-invalid-MSE-data.xlsx +0 -0
  188. package/test-files/additional-sheet-test-files/optimization-diagnostics-invalid-value.xlsx +0 -0
  189. package/test-files/additional-sheet-test-files/optimization-diagnostics-missing-MSE-data.xlsx +0 -0
  190. package/test-files/additional-sheet-test-files/optimization-diagnostics-missing-column-headers.xlsx +0 -0
  191. package/test-files/additional-sheet-test-files/optimization-diagnostics-missing-header.xlsx +0 -0
  192. package/test-files/additional-sheet-test-files/optimization-diagnostics-unknown-parameter.xlsx +0 -0
  193. package/test-files/additional-sheet-test-files/optimization-parameters-default.xlsx +0 -0
  194. package/test-files/additional-sheet-test-files/optimization-parameters-incorrect-headers.xlsx +0 -0
  195. package/test-files/additional-sheet-test-files/optimization-parameters-invalid-optimization-parameter.xlsx +0 -0
  196. package/test-files/additional-sheet-test-files/optimization-parameters-missing-headers.xlsx +0 -0
  197. package/test-files/additional-sheet-test-files/optimization-parameters-unknown-parameter.xlsx +0 -0
  198. package/test-files/additional-sheet-test-files/two-column-sheets-extraneous-data.xlsx +0 -0
  199. package/test-files/additional-sheet-test-files/two-column-sheets-incorrect-cell-A1.xlsx +0 -0
  200. package/test-files/additional-sheet-test-files/two-column-sheets-incorrect-column-header.xlsx +0 -0
  201. package/test-files/additional-sheet-test-files/two-column-sheets-invalid-gene-length.xlsx +0 -0
  202. package/test-files/additional-sheet-test-files/two-column-sheets-invalid-gene-type.xlsx +0 -0
  203. package/test-files/additional-sheet-test-files/two-column-sheets-invalid-value.xlsx +0 -0
  204. package/test-files/additional-sheet-test-files/two-column-sheets-missing-column-header.xlsx +0 -0
  205. package/test-files/additional-sheet-test-files/two-column-sheets-special-character.xlsx +0 -0
  206. package/test-files/adjacency-matrix-modifications/asymmetric-gene-order-input.xlsx +0 -0
  207. package/test-files/adjacency-matrix-modifications/asymmetric-gene-order-output.xlsx +0 -0
  208. package/test-files/adjacency-matrix-modifications/extra-column-adjacent-input.xlsx +0 -0
  209. package/test-files/adjacency-matrix-modifications/extra-column-adjacent-output.xlsx +0 -0
  210. package/test-files/adjacency-matrix-modifications/extra-column-end-of-sheet-input.xlsx +0 -0
  211. package/test-files/adjacency-matrix-modifications/extra-column-end-of-sheet-output.xlsx +0 -0
  212. package/test-files/adjacency-matrix-modifications/extra-data-random-cell-both-output.xlsx +0 -0
  213. package/test-files/adjacency-matrix-modifications/extra-data-random-cell-network-only-input.xlsx +0 -0
  214. package/test-files/adjacency-matrix-modifications/extra-data-random-cell-network-only-output.xlsx +0 -0
  215. package/test-files/adjacency-matrix-modifications/extra-data-random-cell-network-optimized-only-output.xlsx +0 -0
  216. package/test-files/adjacency-matrix-modifications/extra-row-end-of-sheet-input.xlsx +0 -0
  217. package/test-files/adjacency-matrix-modifications/extra-row-end-of-sheet-output.xlsx +0 -0
  218. package/test-files/adjacency-matrix-modifications/incorrect-network-cell-A1.xlsx +0 -0
  219. package/test-files/adjacency-matrix-modifications/missing-column-end-input.xlsx +0 -0
  220. package/test-files/adjacency-matrix-modifications/missing-column-end-output.xlsx +0 -0
  221. package/test-files/adjacency-matrix-modifications/missing-data-input.xlsx +0 -0
  222. package/test-files/adjacency-matrix-modifications/missing-data-output.xlsx +0 -0
  223. package/test-files/adjacency-matrix-modifications/missing-row-end-input.xlsx +0 -0
  224. package/test-files/adjacency-matrix-modifications/missing-row-end-output.xlsx +0 -0
  225. package/test-files/adjacency-matrix-modifications/missing-row-middle-input.xlsx +0 -0
  226. package/test-files/adjacency-matrix-modifications/missing-row-middle-output.xlsx +0 -0
  227. package/test-files/adjacency-matrix-modifications/missing-value-bottom-corner-input.xlsx +0 -0
  228. package/test-files/adjacency-matrix-modifications/missing-value-bottom-corner-output.xlsx +0 -0
  229. package/test-files/adjacency-matrix-modifications/value-replaced-w-spaces-both-output.xlsx +0 -0
  230. package/test-files/adjacency-matrix-modifications/value-replaced-w-spaces-net-only-input.xlsx +0 -0
  231. package/test-files/adjacency-matrix-modifications/value-replaced-w-spaces-net-op-only-output.xlsx +0 -0
  232. package/test-files/adjacency-matrix-modifications/value-replaced/342/200/223w-spaces-net-only-output.xlsx +0 -0
  233. package/test-files/demo-files/15-genes_28-edges_db5_Dahlquist-data_estimation_output.xlsx +0 -0
  234. package/test-files/demo-files/15-genes_28-edges_db5_Dahlquist-data_input.xlsx +0 -0
  235. package/test-files/expression-data-test-sheets/expression_sheet_correct_numbering.xlsx +0 -0
  236. package/test-files/expression-data-test-sheets/expression_sheet_different_number_of_columns.xlsx +0 -0
  237. package/test-files/expression-data-test-sheets/expression_sheet_empty_column.xlsx +0 -0
  238. package/test-files/expression-data-test-sheets/expression_sheet_empty_row.xlsx +0 -0
  239. package/test-files/expression-data-test-sheets/expression_sheet_erroneous_data.xlsx +0 -0
  240. package/test-files/expression-data-test-sheets/expression_sheet_extra_gene_name.xlsx +0 -0
  241. package/test-files/expression-data-test-sheets/expression_sheet_incorrectly_ordered_time_points.xlsx +0 -0
  242. package/test-files/expression-data-test-sheets/expression_sheet_mismatched_case_gene_names.xlsx +0 -0
  243. package/test-files/expression-data-test-sheets/expression_sheet_mismatched_gene_names.xlsx +0 -0
  244. package/test-files/expression-data-test-sheets/expression_sheet_missing_column_header.xlsx +0 -0
  245. package/test-files/expression-data-test-sheets/expression_sheet_missing_data_error.xlsx +0 -0
  246. package/test-files/expression-data-test-sheets/expression_sheet_missing_data_ok.xlsx +0 -0
  247. package/test-files/expression-data-test-sheets/expression_sheet_missing_data_ok_export_exact.xlsx +0 -0
  248. package/test-files/expression-data-test-sheets/expression_sheet_missing_gene_name.xlsx +0 -0
  249. package/test-files/expression-data-test-sheets/expression_sheet_name_not_in_optparams.xlsx +0 -0
  250. package/test-files/expression-data-test-sheets/expression_sheet_name_not_present.xlsx +0 -0
  251. package/test-files/expression-data-test-sheets/expression_sheet_negative_time_points.xlsx +0 -0
  252. package/test-files/expression-data-test-sheets/expression_sheet_non_numerical_time_points.xlsx +0 -0
  253. package/test-files/expression-data-test-sheets/expression_sheet_not_existing.xlsx +0 -0
  254. package/test-files/expression-data-test-sheets/expression_sheet_wrong_id_label.xlsx +0 -0
  255. package/test-files/expression-data-test-sheets/expression_sheet_wrong_order_gene_names.xlsx +0 -0
  256. package/test-files/expression-data-test-sheets/expression_sheet_wrong_sheet_name_case.xlsx +0 -0
  257. package/test-files/expression-data-test-sheets/expression_sheet_wrong_sheet_name_convention.xlsx +0 -0
  258. package/test-files/gene-name-modifications/NaN-as-gene-name-input.xlsx +0 -0
  259. package/test-files/gene-name-modifications/NaN-as-gene-name-output.xlsx +0 -0
  260. package/test-files/gene-name-modifications/mismatched-case-related-input.xlsx +0 -0
  261. package/test-files/gene-name-modifications/mismatched-case-related-output.xlsx +0 -0
  262. package/test-files/gene-name-modifications/mismatched-case-unrelated-input.xlsx +0 -0
  263. package/test-files/gene-name-modifications/mismatched-case-unrelated-output.xlsx +0 -0
  264. package/test-files/gene-name-modifications/numbers-as-gene-name-related-input.xlsx +0 -0
  265. package/test-files/gene-name-modifications/numbers-as-gene-name-related-output.xlsx +0 -0
  266. package/test-files/gene-name-modifications/numbers-as-gene-name-unrelated-input.xlsx +0 -0
  267. package/test-files/gene-name-modifications/numbers-as-gene-name-unrelated-output.xlsx +0 -0
  268. package/test-files/graph-tests/different-sized-networks/{80-genes-0-edges.xlsx → 134-genes-0-edges.xlsx} +0 -0
  269. package/test-files/graph-tests/different-sized-networks/{45-genes-max-edges.xlsx → 44-source-genes-45-target-genes-max-edges.xlsx} +0 -0
  270. package/test-files/graph-tests/different-sized-networks/{1-gene-0-edges.xlsx → unused-files/1-gene-0-edges.xlsx} +0 -0
  271. package/test-files/graph-tests/different-sized-networks/{1-gene-1-edges.xlsx → unused-files/1-gene-1-edges.xlsx} +0 -0
  272. package/test-files/graph-tests/different-sized-networks/{10-genes-50-edges.xlsx → unused-files/10-genes-50-edges.xlsx} +0 -0
  273. package/test-files/graph-tests/different-sized-networks/{10-genes-90-edges.xlsx → unused-files/10-genes-90-edges.xlsx} +0 -0
  274. package/test-files/graph-tests/different-sized-networks/{100-genes-0-edges.xlsx → unused-files/100-genes-0-edges.xlsx} +0 -0
  275. package/test-files/graph-tests/different-sized-networks/{110-genes-0-edges.xlsx → unused-files/110-genes-0-edges.xlsx} +0 -0
  276. package/test-files/graph-tests/different-sized-networks/{120-genes-0-edges.xlsx → unused-files/120-genes-0-edges.xlsx} +0 -0
  277. package/test-files/graph-tests/different-sized-networks/{130-genes-0-edges.xlsx → unused-files/130-genes-0-edges.xlsx} +0 -0
  278. package/test-files/graph-tests/different-sized-networks/{140-genes-0-edges.xlsx → unused-files/140-genes-0-edges.xlsx} +0 -0
  279. package/test-files/graph-tests/different-sized-networks/{150-genes-10000-edges.xlsx → unused-files/150-genes-10000-edges.xlsx} +0 -0
  280. package/test-files/graph-tests/different-sized-networks/{150-genes-20000-edges.xlsx → unused-files/150-genes-20000-edges.xlsx} +0 -0
  281. package/test-files/graph-tests/different-sized-networks/{150-genes-max-edges.xlsx → unused-files/150-genes-max-edges.xlsx} +0 -0
  282. package/test-files/graph-tests/different-sized-networks/{160-genes-max-edges.xlsx → unused-files/160-genes-max-edges.xlsx} +0 -0
  283. package/test-files/graph-tests/different-sized-networks/{20-genes-max-edges.xlsx → unused-files/20-genes-max-edges.xlsx} +0 -0
  284. package/test-files/graph-tests/different-sized-networks/{25-genes-max-edges.xlsx → unused-files/25-genes-max-edges.xlsx} +0 -0
  285. package/test-files/graph-tests/different-sized-networks/{30-genes-max-edges.xlsx → unused-files/30-genes-max-edges.xlsx} +0 -0
  286. package/test-files/graph-tests/different-sized-networks/{34-genes-0-edges.xlsx → unused-files/34-genes-0-edges.xlsx} +0 -0
  287. package/test-files/graph-tests/different-sized-networks/{34-genes-40-edges.xlsx → unused-files/34-genes-40-edges.xlsx} +0 -0
  288. package/test-files/graph-tests/different-sized-networks/{34-genes-65-edges.xlsx → unused-files/34-genes-65-edges.xlsx} +0 -0
  289. package/test-files/graph-tests/different-sized-networks/{34-genes-90-edges.xlsx → unused-files/34-genes-90-edges.xlsx} +0 -0
  290. package/test-files/graph-tests/different-sized-networks/{5-genes-max-edges.xlsx → unused-files/5-genes-max-edges.xlsx} +0 -0
  291. package/test-files/graph-tests/different-sized-networks/{51-genes-max-edges.xlsx → unused-files/51-genes-max-edges.xlsx} +0 -0
  292. package/test-files/graph-tests/different-sized-networks/{52-genes-max-edges.xlsx → unused-files/52-genes-max-edges.xlsx} +0 -0
  293. package/test-files/graph-tests/different-sized-networks/{55-genes-0-edges.xlsx → unused-files/55-genes-0-edges.xlsx} +0 -0
  294. package/test-files/graph-tests/different-sized-networks/{55-genes-max-edges.xlsx → unused-files/55-genes-max-edges.xlsx} +0 -0
  295. package/test-files/graph-tests/different-sized-networks/{65-genes-0-edges.xlsx → unused-files/65-genes-0-edges.xlsx} +0 -0
  296. package/test-files/graph-tests/different-sized-networks/{7-genes-max-edges.xlsx → unused-files/7-genes-max-edges.xlsx} +0 -0
  297. package/test-files/graph-tests/different-sized-networks/{70-genes-0-edges.xlsx → unused-files/70-genes-0-edges.xlsx} +0 -0
  298. package/test-files/graph-tests/different-sized-networks/{9-genes-max-edges.xlsx → unused-files/9-genes-max-edges.xlsx} +0 -0
  299. package/test-files/graph-tests/different-sized-networks/{90-genes-0-edges.xlsx → unused-files/90-genes-0-edges.xlsx} +0 -0
  300. 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
  301. package/test-files/node-tests/long-gene-name-no-spaces-first.xlsx +0 -0
  302. package/test-files/node-tests/long-gene-name-no-spaces-second.xlsx +0 -0
  303. package/test-files/node-tests/long-gene-name-spaces.xlsx +0 -0
  304. package/test-files/species-test-data/15-genes_28-edges_db5_Dahlquist-data_input_no-species.xlsx +0 -0
  305. package/test-files/species-test-data/15-genes_28-edges_db5_Dahlquist-data_input_with-species.xlsx +0 -0
  306. package/test-files/species-test-data/3-gene_7-edge_elegans.xlsx +0 -0
  307. package/test-files/species-test-data/3-gene_7-edge_melanogaster.xlsx +0 -0
  308. package/test-files/species-test-data/3-gene_7-edge_musculus.xlsx +0 -0
  309. package/test-files/species-test-data/3-gene_7-edge_sapiens.xlsx +0 -0
  310. package/test-files/species-test-data/kev-fake-data-sapiens-no-exp-data.xlsx +0 -0
  311. package/test-files/spreadsheet-controller-test-files/sheet-name-capitalized-network-optimized-weights.xlsx +0 -0
  312. package/test-files/spreadsheet-controller-test-files/sheet-name-capitalized-network.xlsx +0 -0
  313. package/web-client/app.js +1 -1
  314. package/web-client/config/config.js +5 -5
  315. package/web-client/controllers/main.js +5 -1
  316. package/web-client/public/favicon.ico +0 -0
  317. package/web-client/public/gene/GRNSight.svg +689 -0
  318. package/web-client/public/gene/PageDesignREADME.md +1 -0
  319. package/web-client/public/gene/api.js +442 -0
  320. package/web-client/public/gene/info.css +181 -0
  321. package/web-client/public/gene/info.js +334 -0
  322. package/web-client/public/gene/integrationREADME.md +52 -0
  323. package/web-client/public/js/constants.js +182 -0
  324. package/web-client/public/js/getGeneInformationREADME.md +4 -0
  325. package/web-client/public/js/graph-statistics.js +7 -7
  326. package/web-client/public/js/graph.js +480 -476
  327. package/web-client/public/js/grnsight.js +10 -9
  328. package/web-client/public/js/grnsight.min.js +2335 -0
  329. package/web-client/public/js/grnstate.js +147 -0
  330. package/web-client/public/js/iframe-coordination.js +55 -0
  331. package/web-client/public/js/setup-handlers.js +617 -0
  332. package/web-client/public/js/setup-load-and-import-handlers.js +180 -0
  333. package/web-client/public/js/update-app.js +980 -0
  334. package/web-client/public/js/upload.js +352 -578
  335. package/web-client/public/js/warnings.js +60 -0
  336. package/web-client/public/lib/iframeSizer.contentWindow.min.js +10 -0
  337. package/{documents/SDF/CMSI_402/spring_2018/writtenStatusReport4 → web-client/public/lib/jaspar-front.html} +0 -0
  338. package/web-client/public/stylesheets/grnsight.styl +176 -26
  339. package/web-client/public/stylesheets/print.styl +10 -4
  340. package/web-client/views/{graph.jade → graph.pug} +1 -3
  341. package/web-client/views/info.pug +215 -0
  342. package/web-client/views/upload.pug +587 -0
  343. package/_gh_pages/Gemfile +0 -7
  344. package/_gh_pages/Gemfile.lock +0 -129
  345. package/documents/SDF/CMSI_402/spring_2014/Southwick_CMSI402_Spring2014_software-development-plan.md +0 -71
  346. package/documents/SDF/CMSI_402/spring_2014/Southwick_CMSI402_Spring2014_software-requirements-specification.md +0 -71
  347. package/documents/SDF/CMSI_402/spring_2014/use-case-diagram-spring-2014.jpg +0 -0
  348. package/documents/SDF/CMSI_402/spring_2017/Anguiano_402_Final_Presentation.pptx +0 -0
  349. package/documents/SDF/CMSI_402/spring_2017/Anguiano_402_Presentation_Final_Poster.pdf +0 -0
  350. package/documents/SDF/CMSI_402/spring_2017/Anguiano_CMSI402_Spring2017_project-proposal-presentation.pptx +0 -0
  351. package/documents/SDF/CMSI_402/spring_2017/Anguiano_CMSI402_Spring2017_project-proposal.docx +0 -0
  352. package/documents/SDF/CMSI_402/spring_2017/Anguiano_CMSI402_Spring2017_software-development-plan.docx +0 -0
  353. package/documents/SDF/CMSI_402/spring_2017/Anguiano_CMSI402_Spring2017_software-requirements-specification.docx +0 -0
  354. package/documents/SDF/CMSI_402/spring_2017/Design Review Presentation.pptx +0 -0
  355. package/documents/SDF/CMSI_402/spring_2018/0402report.docx +0 -0
  356. package/documents/SDF/CMSI_402/spring_2018/0409report.docx +0 -0
  357. package/documents/SDF/CMSI_402/spring_2018/402-gantt.png +0 -0
  358. package/documents/SDF/CMSI_402/spring_2018/402SRS_GRNSightFeedback01.txt +0 -28
  359. package/documents/SDF/CMSI_402/spring_2018/402_SPD.md +0 -223
  360. package/documents/SDF/CMSI_402/spring_2018/ChoeShinCMSI402_Final.pptx +0 -0
  361. package/documents/SDF/CMSI_402/spring_2018/ProjectProposal.md +0 -22
  362. package/documents/SDF/CMSI_402/spring_2018/SoftwareRequirementsSpecification.md +0 -88
  363. package/documents/SDF/CMSI_402/spring_2018/homework/choe_eileen/Choe_Eileen_CMSI 402_HW1.pdf +0 -0
  364. package/documents/SDF/CMSI_402/spring_2018/homework/choe_eileen/Choe_Eileen_CMSI402_HW2.pdf +0 -0
  365. package/documents/SDF/CMSI_402/spring_2018/homework/shin_jen/402hw2.docx +0 -0
  366. package/documents/SDF/CMSI_402/spring_2018/homework/shin_jen/Jen - 402Hw1.docx +0 -0
  367. package/documents/SDF/CMSI_402/spring_2018/homework/shin_jen/Jen - hw3.docx +0 -0
  368. package/documents/SDF/CMSI_402/spring_2018/writtenStatusReport.docx +0 -0
  369. package/documents/Varshneya_Samdarshi_LMU_Symposium_2016.pptx +0 -0
  370. package/documents/abstracts/Anguiano_Varshneya_Undergraduate-Research-Symposium_2017_abstract.pdf +0 -0
  371. package/documents/abstracts/SIGGRAPH 2017 Abstract/ACM-Reference-Format.bst +0 -3478
  372. package/documents/abstracts/SIGGRAPH 2017 Abstract/Figure1_zoom100.png +0 -0
  373. package/documents/abstracts/SIGGRAPH 2017 Abstract/acmart.cls +0 -2352
  374. package/documents/abstracts/SIGGRAPH 2017 Abstract/acmart.ins +0 -29
  375. package/documents/abstracts/SIGGRAPH 2017 Abstract/always-weights.png +0 -0
  376. package/documents/abstracts/SIGGRAPH 2017 Abstract/auto.png +0 -0
  377. package/documents/abstracts/SIGGRAPH 2017 Abstract/networkA.png +0 -0
  378. package/documents/abstracts/SIGGRAPH 2017 Abstract/networkB-normalized.png +0 -0
  379. package/documents/abstracts/SIGGRAPH 2017 Abstract/networkB.png +0 -0
  380. package/documents/abstracts/SIGGRAPH 2017 Abstract/never-weights.png +0 -0
  381. package/documents/abstracts/SIGGRAPH 2017 Abstract/representative-image/screenshot.jpg +0 -0
  382. package/documents/abstracts/SIGGRAPH 2017 Abstract/representative-image/screenshot3x2.png +0 -0
  383. package/documents/abstracts/SIGGRAPH 2017 Abstract/representative-image/withweights3x2.png +0 -0
  384. package/documents/abstracts/SIGGRAPH 2017 Abstract/screenshot-auto.png +0 -0
  385. package/documents/abstracts/SIGGRAPH 2017 Abstract/siggraph-abstract-review.bib +0 -85
  386. package/documents/abstracts/SIGGRAPH 2017 Abstract/siggraph-abstract-review.pdf +0 -0
  387. package/documents/abstracts/SIGGRAPH 2017 Abstract/siggraph-abstract-review.tex +0 -235
  388. package/documents/abstracts/SWE Collegiate Competition 2017.md +0 -9
  389. package/documents/abstracts/Varshneya_Samdarshi_Southern-California-Systems-Biology_2017_abstract.docx +0 -0
  390. package/documents/developer_documents/State Diagram.graphml +0 -3525
  391. package/documents/developer_documents/graphml/State Diagram.graphml +0 -3115
  392. package/documents/developer_documents/older_versions/GRNsight State Diagram old.png +0 -0
  393. package/documents/developer_documents/older_versions/GRNsight State Diagram.png +0 -0
  394. package/documents/developer_documents/testing_script_generator/GRNsightTestingDocument.md +0 -998
  395. package/documents/developer_documents/testing_script_generator/featureList.json +0 -486
  396. package/documents/developer_documents/testing_script_generator/testing-script-generator.js +0 -149
  397. package/documents/manuscripts/peerj-computerscience-2016/GRNsight_PeerJ-CS_conference-presentations_2016.docx +0 -0
  398. package/documents/manuscripts/peerj-computerscience-2016/GRNsight_PeerJ-CS_manuscript_2016.docx +0 -0
  399. package/documents/manuscripts/peerj-computerscience-2016/GRNsight_PeerJ-CS_manuscript_2016_Table1.docx +0 -0
  400. package/documents/manuscripts/peerj-computerscience-2016/GRNsight_PeerJ-CS_manuscript_2016_references.rtf +0 -264
  401. package/documents/manuscripts/peerj-computerscience-2016/GRNsight_PeerJ-CS_manuscript_2016_text-only.docx +0 -0
  402. package/documents/manuscripts/peerj-computerscience-2016/figures/other-versions/21-genes_31-edges_Schade-data_for-screenshots.xlsx +0 -0
  403. package/documents/manuscripts/peerj-computerscience-2016/figures/other-versions/Figure1_zoom100.jpg +0 -0
  404. package/documents/manuscripts/peerj-computerscience-2016/figures/other-versions/Figure1_zoom100.png +0 -0
  405. package/documents/manuscripts/peerj-computerscience-2016/figures/other-versions/Figure1_zoom100.psd +0 -0
  406. package/documents/manuscripts/peerj-computerscience-2016/figures/other-versions/Figure1_zoom145.jpg +0 -0
  407. package/documents/manuscripts/peerj-computerscience-2016/figures/other-versions/Figure1_zoom145.png +0 -0
  408. package/documents/manuscripts/peerj-computerscience-2016/figures/other-versions/Figure1_zoom145.psd +0 -0
  409. package/documents/manuscripts/peerj-computerscience-2016/figures/other-versions/Figure1_zoom145_900pix-wide.psd +0 -0
  410. package/documents/manuscripts/peerj-computerscience-2016/figures/other-versions/Figure2_zoom100.jpg +0 -0
  411. package/documents/manuscripts/peerj-computerscience-2016/figures/other-versions/Figure2_zoom100.png +0 -0
  412. package/documents/manuscripts/peerj-computerscience-2016/figures/other-versions/Figure2_zoom100.psd +0 -0
  413. package/documents/manuscripts/peerj-computerscience-2016/figures/other-versions/Figure2_zoom145.jpg +0 -0
  414. package/documents/manuscripts/peerj-computerscience-2016/figures/other-versions/Figure2_zoom145.png +0 -0
  415. package/documents/manuscripts/peerj-computerscience-2016/figures/other-versions/Figure2_zoom145.psd +0 -0
  416. package/documents/manuscripts/peerj-computerscience-2016/figures/other-versions/Figure2_zoom145_900pix-wide.psd +0 -0
  417. package/documents/manuscripts/peerj-computerscience-2016/figures/other-versions/Figure5A.pdf +0 -0
  418. package/documents/manuscripts/peerj-computerscience-2016/figures/other-versions/Figure5B.pdf +0 -0
  419. package/documents/manuscripts/peerj-computerscience-2016/figures/other-versions/Figure5C.eps +0 -0
  420. package/documents/manuscripts/peerj-computerscience-2016/figures/other-versions/Figure5D.pdf +0 -0
  421. package/documents/manuscripts/peerj-computerscience-2016/figures/other-versions/Figure5E.pdf +0 -0
  422. package/documents/manuscripts/peerj-computerscience-2016/figures/other-versions/Figure5F.eps +0 -0
  423. package/documents/manuscripts/peerj-computerscience-2016/figures/other-versions/Figure5_compiled.png +0 -0
  424. package/documents/manuscripts/peerj-computerscience-2016/figures/submitted-versions/Figure3_GRNsight-Architecture.pdf +0 -0
  425. package/documents/manuscripts/peerj-computerscience-2016/figures/submitted-versions/Figure4_GRNsight-Screenshot.pdf +0 -0
  426. package/documents/manuscripts/peerj-computerscience-2016/figures/submitted-versions/Figure5_compiled.pdf +12 -5383
  427. package/documents/manuscripts/peerj-computerscience-2016/peerj-reviewing-10823-v0.pdf +0 -0
  428. package/documents/manuscripts/peerj-computerscience-2016/revisions/GRNsight_PeerJ-CS_conference-presentations_2016_revised.docx +0 -0
  429. package/documents/manuscripts/peerj-computerscience-2016/revisions/GRNsight_PeerJ-CS_cover-letter-and-response_2016.pdf +0 -0
  430. package/documents/manuscripts/peerj-computerscience-2016/revisions/GRNsight_PeerJ-CS_cover-letter_2016.docx +0 -0
  431. package/documents/manuscripts/peerj-computerscience-2016/revisions/GRNsight_PeerJ-CS_cover-letter_2016.pdf +0 -0
  432. package/documents/manuscripts/peerj-computerscience-2016/revisions/GRNsight_PeerJ-CS_manuscript_2016_Table1.docx +0 -0
  433. package/documents/manuscripts/peerj-computerscience-2016/revisions/GRNsight_PeerJ-CS_manuscript_2016_references_revised.rtf +0 -385
  434. package/documents/manuscripts/peerj-computerscience-2016/revisions/GRNsight_PeerJ-CS_manuscript_2016_text-only_revised.docx +0 -0
  435. package/documents/manuscripts/peerj-computerscience-2016/revisions/GRNsight_PeerJ-CS_manuscript_2016_text-only_revised_marked.docx +0 -0
  436. package/documents/manuscripts/peerj-computerscience-2016/revisions/GRNsight_PeerJ-CS_response-to-reviewers_2016.docx +0 -0
  437. package/documents/manuscripts/peerj-computerscience-2016/revisions/GRNsight_PeerJ-CS_response-to-reviewers_2016.pdf +0 -0
  438. package/documents/manuscripts/peerj-computerscience-2016/revisions/figures/Figure3_GRNsight-Architecture.pdf +0 -0
  439. package/documents/manuscripts/peerj-computerscience-2016/revisions/figures/Figure4_GRNsight-Screenshot-auto.pdf +0 -0
  440. package/documents/manuscripts/peerj-computerscience-2016/revisions/figures/Figure4_GRNsight-Screenshot.pdf +0 -0
  441. package/documents/manuscripts/peerj-computerscience-2016/revisions/peerj-reviewing-10823-v1.pdf +0 -0
  442. package/documents/posters/Anguiano_402_Presentation_Draft_Poster.pdf +0 -0
  443. package/documents/posters/Anguiano_402_Presentation_Draft_Poster.pptx +0 -0
  444. package/documents/posters/Anguiano_Varshneya_LMU-Symposium_2015_poster.pptx +0 -0
  445. package/documents/posters/Anguiano_Varshneya_SCCUR-Poster_20141122_poster.pptx +0 -0
  446. package/documents/posters/ChoeShinCMSI402-2.pptx +0 -0
  447. package/documents/posters/ChoeShinCMSI402.pptx +0 -0
  448. package/documents/posters/ChoeShinCMSI402Final.pptx +0 -0
  449. package/documents/posters/Dahlquist-et-al_BOSC_ISMB_2016_poster.pptx +0 -0
  450. package/documents/posters/Samdarshi et al. LMU Symposium 2017-finalDraft.pptx +0 -0
  451. package/documents/posters/Samdarshi et al. LMU Symposium 2018-firstDraft.pptx +0 -0
  452. package/documents/posters/Shin et al. SCCUR 2017 FinalDraft.pptx +0 -0
  453. package/documents/posters/Southwick_CMSI402_2014_poster.pptx +0 -0
  454. package/documents/posters/Varshneya_Samdarshi_LMU-Symposium_2016_poster.pptx +0 -0
  455. package/documents/posters/Varshneya_Samdarshi_Southern-California-Systems-Biology-Conference_2017_poster.pptx +0 -0
  456. package/documents/posters/~$Samdarshi et al. LMU Symposium 2018-firstDraft.pptx +0 -0
  457. package/documents/presentations/Anguiano_402_Final_Presentation.pptx +0 -0
  458. package/documents/presentations/Choe_SCCUR2017.pptx +0 -0
  459. package/documents/presentations/Choe_SWERapidFire2017_final.pptx +0 -0
  460. package/documents/presentations/Dahlquist_BOSC_20160709.pptx +0 -0
  461. package/documents/presentations/Dahlquist_ExperimentalBiology_20160404_talk.pptx +0 -0
  462. package/documents/presentations/Dahlquist_SoCalSysBio_20150131_talk.pptx +0 -0
  463. package/documents/presentations/Samdarshi et al. LMU Symposium 2017-draft.pptx +0 -0
  464. package/documents/presentations/Southwick_Anguiano_LMU-Symposium_20140329_talk.pptx +0 -0
  465. package/documents/presentations/Southwick_CMSI402_Presentation_20140508_talk.pptx +0 -0
  466. package/documents/presentations/Varshneya-Anguiano-LMU Symposium-201703.pptx +0 -0
  467. package/documents/presentations/~$Choe_SWERapidFire2017_final.pptx +0 -0
  468. package/documents/reports/Varshneya-201701-AnnotatedBibliography.docx +0 -0
  469. package/documents/reports/Varshneya-201702-Introduction.docx +0 -0
  470. package/documents/reports/Varshneya-201702-Outline.docx +0 -0
  471. package/documents/reports/Varshneya-201703-Discussion.docx +0 -0
  472. package/documents/reports/Varshneya-201703-MMResults.docx +0 -0
  473. package/documents/reports/Varshneya-201704-Draft-1.docx +0 -0
  474. package/documents/reports/Varshneya-201704-Final.docx +0 -0
  475. package/test/graph-library-tests.js +0 -165
  476. package/test-files/demo-files/21-genes_50-edges_Dahlquist-data_estimation_output.xlsx +0 -0
  477. package/test-files/demo-files/21-genes_50-edges_Dahlquist-data_input.xlsx +0 -0
  478. package/test-files/graph-tests/different-sized-networks/10-genes-max-edges.xlsx +0 -0
  479. package/test-files/graph-tests/different-sized-networks/12-genes-max-edges.xlsx +0 -0
  480. package/test-files/graph-tests/different-sized-networks/35-genes-max-edges.xlsx +0 -0
  481. package/test-files/graph-tests/different-sized-networks/40-genes-0-edges.xlsx +0 -0
  482. package/test-files/graph-tests/different-sized-networks/40-genes-max-edges.xlsx +0 -0
  483. package/test-files/graph-tests/different-sized-networks/42-genes-max-edges.xlsx +0 -0
  484. package/test-files/graph-tests/different-sized-networks/50-genes-max-edges.xlsx +0 -0
  485. package/test-files/graph-tests/different-sized-networks/75-genes-150-edges.xlsx +0 -0
  486. package/web-client/public/js/container.js +0 -121
  487. package/web-client/public/js/node-coloring.js +0 -306
  488. package/web-client/public/js/sliders.js +0 -197
  489. package/web-client/views/upload.jade +0 -458
@@ -1,5 +1,17 @@
1
1
  import Grid from "d3-v4-grid";
2
- const hasExpressionData = require("./node-coloring").hasExpressionData;
2
+ import { grnState } from "./grnstate";
3
+ import { modifyChargeParameter, modifyLinkDistanceParameter, valueValidator } from "./update-app";
4
+ import {
5
+ ENDS_IN_EXPRESSION_REGEXP,
6
+ VIEWPORT_FIT,
7
+ ZOOM_INPUT,
8
+ ZOOM_PERCENT,
9
+ ZOOM_SLIDER,
10
+ ZOOM_DISPLAY_MINIMUM_VALUE,
11
+ ZOOM_DISPLAY_MAXIMUM_VALUE,
12
+ ZOOM_DISPLAY_MIDDLE,
13
+ ZOOM_ADAPTIVE_MAX_SCALE,
14
+ } from "./constants";
3
15
 
4
16
  /* globals d3 */
5
17
  /* eslint-disable no-use-before-define, func-style */
@@ -17,7 +29,25 @@ const hasExpressionData = require("./node-coloring").hasExpressionData;
17
29
  /* eslint no-unused-vars: [2, {"varsIgnorePattern": "text|getMappedValue|manualZoom"}] */
18
30
  /* eslint-disable no-unused-vars */
19
31
 
20
- export var drawGraph = function (network, sliderController, nodeColoring) {
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
+
43
+ export var updaters = {
44
+ setNodesToGrid: () => {},
45
+ setNodesToForceGraph: () => {},
46
+ renderNodeColoring: () => {},
47
+ removeNodeColoring: () => {},
48
+ };
49
+
50
+ export var drawGraph = function (workbook) {
21
51
  /* eslint-enable no-unused-vars */
22
52
  var $container = $(".grnsight-container");
23
53
  d3.selectAll("svg").remove();
@@ -27,39 +57,47 @@ export var drawGraph = function (network, sliderController, nodeColoring) {
27
57
  var width = $container.width();
28
58
  var height = $container.height();
29
59
  var nodeHeight = 30;
30
- var colorOptimal = true;
31
60
  var grayThreshold = +$("#grayThresholdInput").val();
32
61
 
33
62
  var dashedLine = $("#dashedGrayLineButton").prop("checked");
34
63
 
35
64
  var CURSOR_CLASSES = "cursorGrab cursorGrabbing";
36
65
 
37
- var zoomSliderScale = 1; // Tracks the value of the zoom slider, initally at 100%
38
- $("#zoomPercent").html(100 + "%"); // initalize zoom percentage value
39
-
40
- $("#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." : "");
41
67
 
42
68
  var getNodeWidth = function (node) {
43
69
  return node.name.length * 12 + 5;
44
70
  };
45
71
 
46
- // If colorOptimal is false, then weighting is ignored, and the lines are all drawn as if it was an unweighted sheet
47
- if (!$("#colorEdges").hasClass("active")) {
48
- colorOptimal = false;
49
- }
50
-
51
72
  var adaptive = !$("input[name='viewport']").prop("checked");
52
73
 
53
- var MIN_SCALE = 0.25;
54
- var ADAPTIVE_MAX_SCALE = 4;
55
- var MIDDLE_SCALE = 1;
56
- // regardless of whether the viewport is fixed or adaptive, the zoom slider now operates on the same scale
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);
57
87
 
58
- var minimumScale = MIN_SCALE;
88
+ const MIN_DISPLAY = ZOOM_DISPLAY_MINIMUM_VALUE;
89
+ const ADAPTIVE_MAX_DISPLAY = ZOOM_DISPLAY_MAXIMUM_VALUE;
90
+ const MIN_SCALE = 0.25;
91
+ const MIDDLE_SCALE = 1;
59
92
 
60
- var allWeights = network.positiveWeights.concat(network.negativeWeights);
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);
61
96
 
62
- if (!colorOptimal) {
97
+ // Create an array of all the network weights
98
+ var allWeights = workbook.positiveWeights.concat(workbook.negativeWeights);
99
+ // Assign the entire array weights of 1, if color edges turned off
100
+ if (!grnState.colorOptimal) {
63
101
  for (var i = 0; i < allWeights.length; i++) {
64
102
  if ( allWeights[i] !== 0 ) {
65
103
  allWeights[i] = 1;
@@ -71,18 +109,25 @@ export var drawGraph = function (network, sliderController, nodeColoring) {
71
109
  }
72
110
  }
73
111
 
74
- // normalization all weights b/w 2-14
112
+ const maxWeight = Math.max(Math.abs(d3.max(allWeights)), Math.abs(d3.min(allWeights)));
113
+
114
+ // Get the largest magnitude weight and set that as the default normalization factor
115
+ if (grnState.newWorkbook) {
116
+ grnState.normalizationMax = maxWeight;
117
+ grnState.resetNormalizationMax = maxWeight;
118
+ }
119
+
120
+ // Normalize all weights b/w 2-14
75
121
  var normMax = +$("#normalization-max").val();
76
122
  var totalScale = d3.scaleLinear()
77
- .domain([0, normMax > 0 ? normMax : d3.max(allWeights)])
123
+ .domain([0, normMax > 0 ? normMax : maxWeight])
78
124
  .range([2, 14])
79
125
  .clamp(true);
80
126
 
81
127
  var unweighted = false;
82
128
 
83
- // normalization all weights b/w size 2 and size 14
84
- // if unweighted, weight is 2
85
- if (network.sheetType === "unweighted") {
129
+ // if unweighted, all weights are 2
130
+ if (workbook.sheetType === "unweighted") {
86
131
  totalScale = d3.scaleQuantile()
87
132
  .domain([d3.extent(allWeights)])
88
133
  .range(["2"]);
@@ -90,13 +135,12 @@ export var drawGraph = function (network, sliderController, nodeColoring) {
90
135
  $(".normalization-form").append("placeholder='unweighted'");
91
136
  document.getElementById("edge-weight-normalization-factor-menu").setAttribute("placeholder", "");
92
137
  } else {
93
- var maxWeight = d3.max(allWeights);
94
138
  document.getElementById("normalization-max").setAttribute("placeholder", maxWeight);
95
139
  document.getElementById("edge-weight-normalization-factor-menu").setAttribute("placeholder", maxWeight);
96
140
  }
97
141
 
98
142
  var getEdgeThickness = function (edge) {
99
- return Math.round(totalScale(Math.abs(edge.value)));
143
+ return Math.floor(totalScale(Math.abs(edge.value)));
100
144
  };
101
145
 
102
146
  var simulation = d3.forceSimulation()
@@ -153,7 +197,8 @@ export var drawGraph = function (network, sliderController, nodeColoring) {
153
197
 
154
198
  var svg = d3.select($container[0]).append("svg")
155
199
  .attr("width", width)
156
- .attr("height", height);
200
+ .attr("height", height)
201
+ .attr("id", "exportContainer");
157
202
 
158
203
  var zoomContainer = svg.append("g") // required for zoom to work
159
204
  .attr("class", "boundingBox")
@@ -163,10 +208,12 @@ export var drawGraph = function (network, sliderController, nodeColoring) {
163
208
  var boundingBoxContainer = zoomContainer.append("g"); // appended another g here...
164
209
 
165
210
  var zoom = d3.zoom()
166
- .scaleExtent([1 / 2, 4])
211
+ .scaleExtent([MIN_SCALE, ZOOM_ADAPTIVE_MAX_SCALE])
167
212
  .on("zoom", zoomed);
168
213
 
169
- svg.style("pointer-events", "all").call(zoomDrag);
214
+ svg.style("pointer-events", "all").call(zoomDrag)
215
+ .style("font-family", "sans-serif");
216
+
170
217
 
171
218
  function zoomed () {
172
219
  zoomContainer.attr("transform", d3.event.transform);
@@ -174,14 +221,14 @@ export var drawGraph = function (network, sliderController, nodeColoring) {
174
221
 
175
222
  d3.select("svg").on("dblclick.zoom", null); // disables double click zooming
176
223
 
177
- // This rectangle catches all of the mousewheel and pan events, without letting
178
- // 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.
179
226
  boundingBoxContainer.append("rect")
180
227
  .attr("width", width)
181
228
  .attr("height", height)
182
229
  .style("fill", "none")
183
230
  .style("pointer-events", "all")
184
- .attr("stroke", adaptive ? "none" : "#9A9A9A")
231
+ .attr("stroke", "none" )
185
232
  .append("g");
186
233
 
187
234
  d3.selectAll(".scrollBtn").on("click", null); // Remove event handlers, if there were any.
@@ -193,146 +240,99 @@ export var drawGraph = function (network, sliderController, nodeColoring) {
193
240
  });
194
241
  d3.select(".center").on("click", center);
195
242
 
196
- var leftPoints;
197
- var rightPoints;
198
- var scaleIncreasePerLeftPoint;
199
- var scaleIncreasePerRightPoint;
200
- /*
201
- We have to do some mapping so that the zoom slider appears as it should.
202
- Zooming out sets the scale to a value between 0 and 1. Zooming in sets it
203
- to a value between 1 and infinity. A scale of 0.25 to 5 on a zoom slider
204
- without transformations will have 1 at the very far left. However, that's
205
- an inaccurate way to represent what's actually happening. So this function
206
- maps the scale from 0 to some x, with that x being calculated based on the
207
- input scales.
208
- */
209
- var setupZoomSlider = function (minScale) {
210
- // If the maximumScale is 1, we won't need to calculate any values from 1 to maxScale.
211
- // So we'll just treat it as 0.
212
-
213
- var maxScale = ADAPTIVE_MAX_SCALE;
214
-
215
- // Each integer on the zoom is equivalent to 100 steps.
216
- var NUMBER_POINTS_PER_INT = 100;
217
-
218
- // Get the value that, if multiplied by the minScale value, would return 1. (ex: 0.25 * 4 = 1)
219
- // This gives us the equivalent value of this minimum scale, should it be treated
220
- // as a scale increase. This mostly allows us to treat this minimum scale as a non-decimal value,
221
- // but it also provides a way to compare the total effect this minimum scale would have
222
- // in a way that is easier to understand.
223
- var minScaleReversed = 100 / (minScale * 100);
224
-
225
- // Number of points required to display the minimum scale, now that's it's been transformed. These
226
- // are the points that represent everything on the scale less than one.
227
- leftPoints = minScaleReversed * NUMBER_POINTS_PER_INT;
228
-
229
- // We want to end up with a total increase that, once we've gone through all the
230
- // left points, produces 1 when added to minscale. So for scale 0.25 with 400
231
- // left points, we need to know what we could add to 0.25 400 times to produce 1.
232
- // We divide 0.75 by 400 to get that result.
233
- scaleIncreasePerLeftPoint = (1 - minScale) / leftPoints;
234
-
235
- // Points representing scales greater than 1.
236
- rightPoints = maxScale * NUMBER_POINTS_PER_INT;
237
-
238
- // For the same concept as above, we need to figure out what to add to 1 so
239
- // so that we can end up with maxScale. Note that we start at 1 and not 0 because
240
- // the scale is beginning at 1.
241
- scaleIncreasePerRightPoint = (maxScale - MIDDLE_SCALE) / rightPoints;
242
- var totalPoints = leftPoints + rightPoints;
243
-
244
- // Returns the x that we're mapping to. Now we can set up the range slider.
245
- var maxRangeValue = totalPoints / 100;
246
-
247
- $(".zoomSlider").attr("min", 0);
248
- $(".zoomSlider").attr("max", maxRangeValue);
249
- $(".zoomSlider").val(0.01 * leftPoints);
243
+ const setGraphZoom = zoomScale => {
244
+ if (zoomScale < MIDDLE_SCALE) {
245
+ $container.removeClass(CURSOR_CLASSES).addClass("cursorGrab");
246
+ } else if (!adaptive && zoomScale >= MIDDLE_SCALE) {
247
+ $container.removeClass(CURSOR_CLASSES);
248
+ }
249
+ var container = zoomContainer;
250
+ zoom.scaleTo(container, zoomScale);
250
251
  };
251
252
 
252
- setupZoomSlider(minimumScale);
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;
253
258
 
254
- var ZOOM_SLIDER_MAX_VAL = 8;
255
- var ZOOM_RANGE = 200;
259
+ const updateAppBasedOnZoomValue = () => {
256
260
 
257
- function updateZoomValue (input) {
258
- var value = input || Math.round(($(".zoomSlider").val() / ZOOM_SLIDER_MAX_VAL * ZOOM_RANGE));
259
- value = value === 0 ? MIDDLE_SCALE : value;
260
- $("#zoomPercent").html(value + "%");
261
- $("#zoomInput").val(value);
262
- }
263
-
264
- function getMappedValue (scale) {
265
- // Reverse the calculations from setupZoomSlider to get value from equivalentScale
266
- var equivalentPoint;
267
- if (scale <= MIDDLE_SCALE) {
268
- equivalentPoint = (scale - minimumScale) / (scaleIncreasePerLeftPoint * 100);
269
- } else {
270
- equivalentPoint = (scale - 1) / scaleIncreasePerRightPoint + leftPoints;
271
- equivalentPoint /= 100;
261
+ if (!adaptive) {
262
+ grnState.zoomValue = 100.0;
272
263
  }
273
- $(".zoomSlider").val(equivalentPoint.toFixed(2));
274
- }
275
264
 
276
- var updateViewportZoom = function (value) {
277
- var currentPoint = value * 100;
278
- var equivalentScale;
279
- if (adaptive || (!adaptive && value <= ADAPTIVE_MAX_SCALE)) {
280
- if (currentPoint <= leftPoints) {
281
- equivalentScale = minimumScale;
282
- equivalentScale += scaleIncreasePerLeftPoint * currentPoint;
283
- } else {
284
- currentPoint = currentPoint - leftPoints;
285
- equivalentScale = MIDDLE_SCALE;
286
- equivalentScale += scaleIncreasePerRightPoint * currentPoint;
287
- }
288
- zoomSliderScale = equivalentScale;
289
- manualZoomFunction(equivalentScale);
290
- } else {
291
- // Prohibits zooming past 100% if (!adaptive && value >= ADAPTIVE_MAX_SCALE)
292
- $(".zoomSlider").val(ADAPTIVE_MAX_SCALE);
293
- manualZoomFunction(MIDDLE_SCALE);
294
- updateZoomValue();
265
+ const zoomDisplay = grnState.zoomValue;
266
+ setGraphZoom((zoomDisplay <= ZOOM_DISPLAY_MIDDLE ? zoomScaleLeft : zoomScaleRight)(zoomDisplay));
267
+
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);
295
276
  }
277
+
278
+ $(ZOOM_SLIDER).val((finalDisplay <= ZOOM_DISPLAY_MIDDLE ? zoomScaleSliderLeft : zoomScaleSliderRight)
279
+ .invert(finalDisplay));
296
280
  };
297
281
 
298
- var valueValidator = function (min, max, value) {
299
- return Math.min(max, Math.max(min, value));
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;
303
+ }
304
+
305
+ updateAppBasedOnZoomValue();
300
306
  };
301
307
 
308
+ setupZoomSlider();
309
+
302
310
  var zoomInputValidator = function (value) {
303
- return valueValidator(1, 200, value);
311
+ return valueValidator(MIN_DISPLAY, ADAPTIVE_MAX_DISPLAY, value);
304
312
  };
305
313
 
306
- $("#zoomInput").on("change", function () {
307
- var value = zoomInputValidator(+$("#zoomInput").val());
308
- var scaledValue = value * (ZOOM_SLIDER_MAX_VAL / ZOOM_RANGE);
309
- $(".zoomSlider").val(scaledValue);
310
- updateViewportZoom(scaledValue);
311
- updateZoomValue(value);
312
- });
313
-
314
- d3.select(".zoomSlider").on("input", function () {
315
- var value = $(this).val();
316
- updateViewportZoom(value);
314
+ $(ZOOM_INPUT).on("input", () => {
315
+ grnState.zoomValue = zoomInputValidator(+$(ZOOM_INPUT).val());
316
+ updateAppBasedOnZoomValue();
317
+ }).blur(() => $(ZOOM_INPUT).val(grnState.zoomValue));
318
+
319
+ d3.select(ZOOM_SLIDER).on("input", function () {
320
+ const sliderValue = +$(this).val();
321
+ grnState.zoomValue = Math.floor(
322
+ (sliderValue <= sliderMidpoint ? zoomScaleSliderLeft : zoomScaleSliderRight)(sliderValue)
323
+ );
324
+ updateAppBasedOnZoomValue();
317
325
  }).on("mousedown", function () {
318
326
  manualZoom = true;
319
327
  }).on("mouseup", function () {
320
328
  manualZoom = false;
321
329
  });
322
330
 
331
+ if (!grnState.newWorkbook) {
332
+ updateAppBasedOnZoomValue();
333
+ }
323
334
 
324
- var manualZoomFunction = function (zoomScale) {
325
- if (zoomScale < MIDDLE_SCALE) {
326
- $container.removeClass(CURSOR_CLASSES).addClass("cursorGrab");
327
- } else if (!adaptive && zoomScale >= MIDDLE_SCALE) {
328
- $container.removeClass(CURSOR_CLASSES);
329
- }
330
- updateZoomValue();
331
- var container = zoomContainer;
332
- zoom.scaleTo(container, zoomScale);
333
- };
334
-
335
- d3.selectAll(".boundBoxSize").on("click", function () {
335
+ const adjustGraphSize = () => {
336
336
  var newWidth = $container.width();
337
337
  var newHeight = $container.height();
338
338
 
@@ -344,30 +344,40 @@ export var drawGraph = function (network, sliderController, nodeColoring) {
344
344
  height = newHeight;
345
345
  }
346
346
 
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...
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...
349
349
  d3.select("svg").attr("width", newWidth)
350
- .attr("height", $(".grnsight-container").hasClass("containerFit") ? newHeight : newHeight);
350
+ .attr("height", $(".grnsight-container").hasClass(VIEWPORT_FIT) ? newHeight : newHeight);
351
351
  d3.select("rect").attr("width", width).attr("height", height);
352
352
  d3.select(".boundingBox").attr("width", width).attr("height", height);
353
- });
353
+ };
354
+
355
+ mutationCallback = adjustGraphSize;
356
+ resizeObserver.disconnect();
357
+ resizeObserver.observe($container.get(0), { attributes: true });
354
358
 
355
359
  var restrictGraphToViewport = function (fixed) {
356
360
  if (!fixed) {
357
361
  $("#restrict-graph-to-viewport span").removeClass("glyphicon-ok");
362
+ $(document).ready(function () {
363
+ $(".scale-and-scroll").show();
364
+ });
358
365
  $("input[name=viewport]").removeProp("checked");
359
- $container.addClass("cursorGrab");
366
+ $container.addClass("cursorGrabbing");
360
367
  adaptive = true;
361
368
  d3.select("rect").attr("stroke", "none");
362
369
  center();
363
370
  } else if (fixed) {
364
371
  $("#restrict-graph-to-viewport span").addClass("glyphicon-ok");
365
372
  $("input[name=viewport]").prop("checked", "checked");
373
+ $(document).ready(function () {
374
+ $(".scale-and-scroll").hide();
375
+ });
366
376
  adaptive = false;
367
377
  $container.removeClass(CURSOR_CLASSES);
368
- if (zoomSliderScale > 1) {
369
- $(".zoomSlider").val(ADAPTIVE_MAX_SCALE);
370
- manualZoomFunction(1);
378
+ if (grnState.zoomValue > ZOOM_DISPLAY_MIDDLE) {
379
+ grnState.zoomValue = ZOOM_DISPLAY_MIDDLE;
380
+ updateAppBasedOnZoomValue();
371
381
  $container.removeClass(CURSOR_CLASSES);
372
382
  }
373
383
  width = $container.width();
@@ -378,6 +388,7 @@ export var drawGraph = function (network, sliderController, nodeColoring) {
378
388
  $(".boundingBox").attr("width", width).attr("height", height);
379
389
  center();
380
390
  }
391
+ updateAppBasedOnZoomValue(); // Update zoom value within bounds
381
392
  };
382
393
 
383
394
  d3.select("#restrict-graph-to-viewport").on("click", function () {
@@ -390,22 +401,15 @@ export var drawGraph = function (network, sliderController, nodeColoring) {
390
401
  restrictGraphToViewport(fixed);
391
402
  });
392
403
 
393
- $(window).on("resize", function () {
394
- if ($container.hasClass("containerFit")) {
395
- $(".boundBoxSize").trigger("click");
396
- }
397
- });
398
-
399
404
  function center () {
400
405
  var viewportWidth = $container.width();
401
406
  var viewportHeight = $container.height();
402
407
  zoom.translateTo(zoomContainer, viewportWidth / 2, viewportHeight / 2);
403
- simulation.alphaTarget(0.3).restart();
404
408
  }
405
409
 
406
410
  function move (direction) {
407
- var width = direction === "left" ? 50 : (direction === "right" ? -50 : 0);
408
- 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);
409
413
  zoom.translateBy(zoomContainer, width, height);
410
414
  }
411
415
 
@@ -416,18 +420,18 @@ export var drawGraph = function (network, sliderController, nodeColoring) {
416
420
  var weight = boundingBoxContainer.selectAll(".weight");
417
421
 
418
422
  simulation
419
- .nodes(network.genes)
423
+ .nodes(workbook.genes)
420
424
  .on("tick", tick);
421
425
 
422
426
  simulation.force("link")
423
- .links(network.links);
427
+ .links(workbook.links);
424
428
 
425
- link = link.data(network.links)
429
+ link = link.data(workbook.links)
426
430
  .enter().append("g")
427
431
  .attr("class", "link")
428
432
  .attr("strokeWidth", getEdgeThickness);
429
433
 
430
- node = node.data(network.genes)
434
+ node = node.data(workbook.genes)
431
435
  .enter().append("g")
432
436
  .attr("class", "node")
433
437
  .attr("id", function (d) {
@@ -438,13 +442,14 @@ export var drawGraph = function (network, sliderController, nodeColoring) {
438
442
  .call(drag)
439
443
  .on("dblclick", dblclick);
440
444
 
441
- if (network.sheetType === "weighted") {
445
+ if (workbook.sheetType === "weighted") {
442
446
  link.append("path")
443
447
  .attr("class", "mousezone")
444
448
  .style("stroke-width", function (d) {
445
449
  var baseThickness = getEdgeThickness(d);
446
450
  return Math.max(baseThickness, 7);
447
- });
451
+ })
452
+ .attr("stroke-opacity", "0");
448
453
  }
449
454
 
450
455
  link.append("path")
@@ -452,9 +457,10 @@ export var drawGraph = function (network, sliderController, nodeColoring) {
452
457
  .attr("id", function (d) {
453
458
  return "path" + d.source.index + "_" + d.target.index;
454
459
  }).style("stroke-width", function (d) {
455
- return d.strokeWidth = getEdgeThickness(d);
460
+ d.strokeWidth = grnState.colorOptimal ? getEdgeThickness(d) : 2;
461
+ return d.strokeWidth;
456
462
  }).style("stroke-dasharray", function (d) {
457
- if (unweighted || !colorOptimal) {
463
+ if (unweighted || !grnState.colorOptimal) {
458
464
  return "0";
459
465
  } else if (normalize(d) <= grayThreshold && dashedLine === true) {
460
466
  return "6, 9";
@@ -462,7 +468,7 @@ export var drawGraph = function (network, sliderController, nodeColoring) {
462
468
  return "0";
463
469
  }
464
470
  }).style("stroke", function (d) {
465
- if (unweighted || !colorOptimal) {
471
+ if (unweighted || !grnState.colorOptimal) {
466
472
  return "black";
467
473
  } else if (normalize(d) <= grayThreshold) {
468
474
  return "gray";
@@ -480,7 +486,7 @@ export var drawGraph = function (network, sliderController, nodeColoring) {
480
486
  var xOffsets;
481
487
  var color;
482
488
 
483
- if (Math.abs(d.value / (d3.max(allWeights))) <= grayThreshold) {
489
+ if (normalize(d) <= grayThreshold) {
484
490
  minimum = "gray";
485
491
  }
486
492
  if ( x1 === x2 && y1 === y2 ) {
@@ -493,10 +499,10 @@ export var drawGraph = function (network, sliderController, nodeColoring) {
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.
499
- if (d.value < 0 && colorOptimal) {
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.
505
+ if (d.value < 0 && grnState.colorOptimal) {
500
506
  defs.append("marker")
501
507
  .attr("id", "repressor" + selfRef + "_StrokeWidth" + d.strokeWidth + minimum)
502
508
  .attr("refX", function () {
@@ -522,22 +528,22 @@ export var drawGraph = function (network, sliderController, nodeColoring) {
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,16 +638,16 @@ export var drawGraph = function (network, sliderController, nodeColoring) {
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")
643
649
  .attr("style", function () {
644
- if (unweighted || !colorOptimal) {
650
+ if (unweighted || !grnState.colorOptimal) {
645
651
  color = "black";
646
652
  } else if ( normalize(d) <= grayThreshold) {
647
653
  color = "gray";
@@ -655,25 +661,29 @@ export var drawGraph = function (network, sliderController, nodeColoring) {
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, sliderController, nodeColoring) {
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, sliderController, nodeColoring) {
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, sliderController, nodeColoring) {
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, sliderController, nodeColoring) {
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,71 +780,71 @@ export var drawGraph = function (network, sliderController, nodeColoring) {
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
- if (d.value < 0 && colorOptimal) {
786
+ if (d.value < 0 && grnState.colorOptimal) {
775
787
  globalOffset = Math.max(globalOffset, MINIMUM_DISTANCE);
776
788
  }
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, sliderController, nodeColoring) {
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, sliderController, nodeColoring) {
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, sliderController, nodeColoring) {
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
  })
@@ -927,7 +940,27 @@ export var drawGraph = function (network, sliderController, nodeColoring) {
927
940
  d.textWidth = textWidth < MINIMUM_NODE_WIDTH ? MINIMUM_NODE_WIDTH : textWidth;
928
941
  return d.textWidth / 2 + NODE_MARGIN;
929
942
  })
930
- .on("dblclick", nodeTextDblclick);
943
+ .on("dblclick", nodeTextDblclick)
944
+
945
+ // this function triggers the gene page
946
+ .on("contextmenu", function (gene) {
947
+ const tempLink = $("<a></a>")
948
+ .attr({
949
+ href: "info?" + $.param({
950
+ symbol: gene.name,
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
+ }),
957
+ target: "_blank"
958
+ });
959
+ $("body").append(tempLink);
960
+ tempLink.get(0).click();
961
+ tempLink.remove();
962
+ d3.event.preventDefault();
963
+ });
931
964
 
932
965
  rect
933
966
  .attr("width", function (d) {
@@ -944,149 +977,182 @@ export var drawGraph = function (network, sliderController, nodeColoring) {
944
977
  return self.indexOf(value) === index;
945
978
  }
946
979
 
947
- var getExpressionData = function (gene, strain, average) {
948
- var strainData = network["expression"][strain];
980
+ const getExpressionData = (gene, strain, average) => {
981
+ const strainData = grnState.workbook.expression[strain];
949
982
  if (average) {
950
- var uniqueTimePoints = strainData.time_points.filter(onlyUnique);
951
- var avgMap = {};
983
+ const uniqueTimePoints = strainData.timePoints.filter(onlyUnique);
984
+ let avgMap = {};
952
985
  uniqueTimePoints.forEach(function (key) {
953
986
  avgMap[key] = [];
954
987
  });
955
- strainData.time_points.forEach(function (time, index) {
988
+ strainData.timePoints.forEach(function (time, index) {
956
989
  avgMap[time].push(strainData.data[gene][index]);
957
990
  });
958
- var avgs = [];
991
+ let avgs = [];
959
992
  Object.keys(avgMap).forEach(function (key) {
960
- var length = avgMap[key].length;
961
- var sum = avgMap[key].reduce(function (partialSum, currentValue) {
993
+ const length = avgMap[key].length;
994
+ const sum = avgMap[key].reduce(function (partialSum, currentValue) {
962
995
  return partialSum + currentValue;
963
996
  }, 0);
964
997
  avgs.push(sum / length);
965
998
  });
966
999
  return {data: avgs, timePoints: uniqueTimePoints};
967
1000
  }
968
- return {data: strainData.data[gene], timePoints: strainData.time_points};
1001
+ return {data: strainData.data[gene], timePoints: strainData.timePoints};
969
1002
  };
970
1003
 
971
1004
  var colorNodes = function (position, dataset, average, logFoldChangeMaxValue) {
972
1005
  var timePoints = [];
973
1006
  node.each(function (p) {
974
1007
  d3.select(this)
975
- .append("g")
976
- .selectAll(".coloring")
977
- .data(function () {
978
- var result = getExpressionData(p.name, dataset, average);
979
- timePoints = result.timePoints;
980
- return result.data;
981
- })
982
- .attr("class", "coloring")
983
- .enter().append("rect")
984
- .attr("width", function () {
985
- var width = rect.attr("width") / timePoints.length;
986
- return width + "px";
987
- })
988
- .attr("class", "coloring")
989
- .attr("height", rect.attr("height") / 2 + "px")
990
- .attr("transform", function (d, i) {
991
- var yOffset = position === "top" ? 0 : rect.attr("height") / 2;
992
- var xOffset = i * (rect.attr("width") / timePoints.length);
993
- return "translate(" + xOffset + "," + yOffset + ")";
994
- })
995
- .attr("stroke-width", "0px")
996
- .style("fill", function (d) {
997
- d = d || 0; // missing values are changed to 0
998
- var scale = d3.scaleLinear()
999
- .domain([-logFoldChangeMaxValue, logFoldChangeMaxValue])
1000
- .range([0, 1]);
1001
- return d3.interpolateRdBu(scale(-d));
1002
- })
1003
- .text(function (d) {
1004
- return "data " + JSON.stringify(d) + " of " + p.name;
1005
- });
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
+ });
1006
1043
  });
1007
1044
  };
1008
1045
 
1009
1046
  var renderNodeColoringLegend = function (logFoldChangeMaxValue) {
1010
1047
  var $nodeColoringLegend = $(".node-coloring-legend");
1011
1048
  d3.select($nodeColoringLegend[0]).selectAll("svg").remove();
1012
- var xMargin = 10;
1013
- var yMargin = 30;
1014
- var width = 200;
1049
+ var yMargin = 20;
1050
+ var width = 203;
1015
1051
  var height = 10;
1016
1052
  var textYOffset = 10;
1017
- var increment = 0.1;
1018
1053
 
1019
1054
  var svg = d3.select($nodeColoringLegend[0])
1020
1055
  .append("svg")
1021
- .attr("width", width + xMargin * 2)
1056
+ .attr("width", "100%")
1022
1057
  .attr("height", height + yMargin)
1023
1058
  .append("g")
1024
- .attr("transform", "translate(" + xMargin / 2 + "," + yMargin / 2 + ")");
1025
-
1026
- var logFoldChangeMaxValueMagnitude = Math.abs(logFoldChangeMaxValue);
1027
- var gradientValues = d3.range(-logFoldChangeMaxValueMagnitude, logFoldChangeMaxValueMagnitude, increment);
1028
- gradientValues = logFoldChangeMaxValue < 0 ? gradientValues.reverse() : gradientValues;
1029
-
1030
- var flippedScale = logFoldChangeMaxValue < 0 ? true : false;
1031
-
1032
- var coloring = svg.selectAll(".node-coloring-legend")
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.
1073
+ var gradientValues = d3.range(-logFoldChangeMaxValue, logFoldChangeMaxValue, increment);
1074
+ var scale = d3.scaleLinear()
1075
+ .domain([-logFoldChangeMaxValue, logFoldChangeMaxValue])
1076
+ .range([0, 1]);
1077
+
1078
+ linearGradient.selectAll("stop")
1033
1079
  .data(gradientValues)
1034
- .attr("class", "node-coloring-legend");
1035
-
1036
- coloring.enter().append("rect")
1037
- .attr("width", width / gradientValues.length + "px")
1038
- .attr("height", height + "px")
1039
- .attr("transform", function (d, i) {
1040
- return "translate(" + (i * (width / gradientValues.length)) + "," + 0 + ")";
1080
+ .enter().append("stop")
1081
+ .attr("offset", function (d, i) {
1082
+ return i / (gradientValues.length - 1);
1041
1083
  })
1042
- .style("fill", function (d) {
1043
- var scale = d3.scaleLinear()
1044
- .domain([-logFoldChangeMaxValue, logFoldChangeMaxValue])
1045
- .range([0, 1]);
1046
- return d3.interpolateRdBu(scale(flippedScale ? d : -d));
1084
+ .attr("stop-color", function (d) {
1085
+ return d3.interpolateRdBu(scale(-d));
1047
1086
  });
1048
1087
 
1088
+ svg.append("rect")
1089
+ .attr("width", `${width}px`)
1090
+ .attr("height", `${height}px`)
1091
+ .style("fill", `url(#${linearGradientId})`);
1092
+
1049
1093
  var legendLabels = {
1050
- "left": {
1051
- "textContent": (flippedScale ? +logFoldChangeMaxValue : -logFoldChangeMaxValue).toFixed(0),
1052
- "x": -xMargin / 2
1094
+ left: {
1095
+ textAnchor: "start",
1096
+ textContent: (-logFoldChangeMaxValue).toFixed(2),
1097
+ x: 0
1053
1098
  },
1054
- "center": {
1055
- "textContent": "0",
1056
- "x": width / 2
1057
- },
1058
- "right": {
1059
- "textContent": (flippedScale ? -logFoldChangeMaxValue : +logFoldChangeMaxValue).toFixed(0),
1060
- "x": width - xMargin / 2
1099
+ center: {
1100
+ textAnchor: "middle",
1101
+ textContent: "0",
1102
+ x: width / 2
1061
1103
  },
1104
+ right: {
1105
+ textAnchor: "end",
1106
+ textContent: (logFoldChangeMaxValue).toFixed(2),
1107
+ x: width
1108
+ }
1062
1109
  };
1063
- 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
+
1064
1114
  for (var key in legendLabels) {
1065
1115
  var label = document.createElementNS("http://www.w3.org/2000/svg", "text");
1066
1116
  label.textContent = legendLabels[key].textContent;
1067
1117
  label.setAttribute("font-size", "8px");
1118
+ label.setAttribute("text-anchor", legendLabels[key].textAnchor);
1068
1119
  label.setAttribute("x", legendLabels[key].x);
1069
1120
  label.setAttribute("y", height + textYOffset + "px");
1121
+ label.setAttribute("fill", "rgb(0,0,0)");
1122
+
1070
1123
  g.appendChild(label);
1071
1124
  }
1125
+
1072
1126
  };
1073
1127
 
1074
- nodeColoring.removeNodeColoring = function () {
1075
- this.nodeColoringEnabled = false;
1128
+ updaters.removeNodeColoring = function () {
1129
+ grnState.nodeColoring.nodeColoringEnabled = false;
1076
1130
  node.selectAll(".coloring").remove();
1077
1131
  };
1078
1132
 
1079
- nodeColoring.renderNodeColoring = function () {
1080
- if (this.nodeColoringEnabled) {
1081
- colorNodes("top", this.topDataset, this.avgTopDataset, this.logFoldChangeMaxValue);
1082
- colorNodes("bottom", this.bottomDataset, this.avgBottomDataset, this.logFoldChangeMaxValue);
1133
+ updaters.renderNodeColoring = function () {
1134
+ if (grnState.nodeColoring.nodeColoringEnabled) {
1135
+ colorNodes("top", grnState.nodeColoring.topDataset, grnState.nodeColoring.averageTopDataset,
1136
+ grnState.nodeColoring.logFoldChangeMaxValue);
1137
+ colorNodes("bottom", grnState.nodeColoring.bottomDataset, grnState.nodeColoring.averageBottomDataset,
1138
+ grnState.nodeColoring.logFoldChangeMaxValue);
1083
1139
  renderNodeLabels();
1084
- renderNodeColoringLegend(this.logFoldChangeMaxValue);
1140
+ renderNodeColoringLegend(grnState.nodeColoring.logFoldChangeMaxValue);
1141
+ }
1142
+ };
1143
+
1144
+ const hasExpressionData = sheets => {
1145
+ for (var property in sheets) {
1146
+ if (property.match(ENDS_IN_EXPRESSION_REGEXP)) {
1147
+ return true;
1148
+ }
1085
1149
  }
1150
+ return false;
1086
1151
  };
1087
1152
 
1088
- if (!$.isEmptyObject(network.expression) && hasExpressionData(network.expression)) {
1089
- nodeColoring.renderNodeColoring();
1153
+ if (!$.isEmptyObject(workbook.expression) && hasExpressionData(workbook.expression) &&
1154
+ grnState.nodeColoring.topDataset !== undefined) {
1155
+ updaters.renderNodeColoring();
1090
1156
  }
1091
1157
 
1092
1158
  $(".node").css({
@@ -1105,7 +1171,7 @@ export var drawGraph = function (network, sliderController, nodeColoring) {
1105
1171
 
1106
1172
  var currentWeightVisibilitySetting = null;
1107
1173
 
1108
- if (network.sheetType === "weighted") {
1174
+ if (workbook.sheetType === "weighted") {
1109
1175
  if ($(".weightedGraphOptions").hasClass("hidden")) {
1110
1176
  $(".weightedGraphOptions").removeClass("hidden");
1111
1177
  }
@@ -1178,7 +1244,6 @@ export var drawGraph = function (network, sliderController, nodeColoring) {
1178
1244
  }
1179
1245
 
1180
1246
  // resets graph options so when new graph is loaded, initial layout is always force graph
1181
- $("#forceGraph").trigger("click");
1182
1247
 
1183
1248
  const getMarginWidth = function (gridNodes, row) {
1184
1249
  const containerWidth = $container.width();
@@ -1206,52 +1271,34 @@ export var drawGraph = function (network, sliderController, nodeColoring) {
1206
1271
  return name1 > name2 ? 1 : -1;
1207
1272
  };
1208
1273
 
1209
- let layout = false;
1210
-
1211
- var GRID_LAYOUT_BUTTON = "#gridLayoutButton";
1212
- $(GRID_LAYOUT_BUTTON)[0].value = "Grid Layout";
1213
- $(GRID_LAYOUT_BUTTON).on("click", {handler: this}, function (event) { // eslint-disable-line no-unused-vars
1214
- let nodeGroup = node._groups[0].sort(sortNode);
1215
- if (!layout) {
1216
- $("#gridLayout")
1217
- .addClass("called")
1218
- .trigger("click")
1219
- .removeClass("called");
1220
- this.value = "Force Graph";
1221
- layout = true;
1222
- const margin = 10;
1223
- const grid = Grid() // create new grid layout
1224
- .data(network.genes)
1274
+ let nodeGroup = node._groups[0].sort(sortNode);
1275
+ updaters.setNodesToGrid = () => { // eslint-disable-line no-unused-vars
1276
+ const margin = 10;
1277
+ const grid = Grid() // eslint-disable-line no-undef
1278
+ .data(workbook.genes)
1225
1279
  .bands(true)
1226
1280
  .padding([0.2, 0])
1227
1281
  .size([$container.width() - margin, $container.height() - margin]); // set size of container
1228
- grid.layout();
1229
- let gridNodes = grid.nodes();
1230
- let gridNumRow = grid.cols();
1231
- let marginWidth = getMarginWidth(gridNodes, gridNumRow);
1232
- let marginHeight = getMarginHeight(gridNodes);
1233
- /* eslint-disable block-scoped-var */
1234
- for (i in nodeGroup) {
1235
- nodeGroup[i].__data__.fx = marginWidth + gridNodes[i].x;
1236
- nodeGroup[i].__data__.fy = marginHeight + gridNodes[i].y;
1237
- }
1238
- } else {
1239
- $("#forceGraph")
1240
- .addClass("called")
1241
- .trigger("click")
1242
- .removeClass("called");
1243
- this.value = "Grid Layout";
1244
- layout = false;
1245
- for (i in nodeGroup) {
1246
- nodeGroup[i].__data__.fx = null;
1247
- nodeGroup[i].__data__.fy = null;
1248
- }
1282
+ grid.layout();
1283
+ let gridNodes = grid.nodes();
1284
+ let gridNumRow = grid.cols();
1285
+ let marginWidth = getMarginWidth(gridNodes, gridNumRow);
1286
+ let marginHeight = getMarginHeight(gridNodes);
1287
+ for (var i in nodeGroup) {
1288
+ nodeGroup[i].__data__.fx = marginWidth + gridNodes[i].x;
1289
+ nodeGroup[i].__data__.fy = marginHeight + gridNodes[i].y;
1249
1290
  }
1250
- /* eslint-enable block-scoped-var */
1251
- });
1291
+ };
1292
+
1293
+ updaters.setNodesToForceGraph = () => { // eslint-disable-line no-unused-vars
1294
+ for (var i in nodeGroup) {
1295
+ nodeGroup[i].__data__.fx = null;
1296
+ nodeGroup[i].__data__.fy = null;
1297
+ }
1298
+ };
1252
1299
 
1253
- // Tick only runs while the graph physics are still running.
1254
- // (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.)
1255
1302
  function tick () {
1256
1303
  var getSelfReferringEdge = function (node) {
1257
1304
  return link.select("path")["_groups"][0].map(function (path) {
@@ -1275,7 +1322,7 @@ export var drawGraph = function (network, sliderController, nodeColoring) {
1275
1322
 
1276
1323
  var selfReferringEdgeWidth = (selfReferringEdge ? getSelfReferringRadius(selfReferringEdge) +
1277
1324
  selfReferringEdge.strokeWidth + 2 : 0);
1278
- var rightBoundary = width - d.textWidth - BOUNDARY_MARGIN - selfReferringEdgeWidth;
1325
+ var rightBoundary = width - (d.textWidth + OFFSET_VALUE) - BOUNDARY_MARGIN - selfReferringEdgeWidth;
1279
1326
  var currentXPos = Math.max(BOUNDARY_MARGIN, Math.min(rightBoundary, d.x));
1280
1327
  if (adaptive && width < MAX_WIDTH &&
1281
1328
  (currentXPos === BOUNDARY_MARGIN || currentXPos === rightBoundary)) {
@@ -1301,15 +1348,14 @@ export var drawGraph = function (network, sliderController, nodeColoring) {
1301
1348
  }).attr("y", function (d) {
1302
1349
  var selfReferringEdge = getSelfReferringEdge(d);
1303
1350
  var selfReferringEdgeHeight = (selfReferringEdge ? getSelfReferringRadius(selfReferringEdge) +
1304
- selfReferringEdge.strokeWidth + SELF_REFERRING_Y_OFFSET + 0.5 : 0);
1351
+ selfReferringEdge.strokeWidth + SELF_REFERRING_Y_OFFSET + 0.5 : 0);
1305
1352
  var bottomBoundary = height - nodeHeight - BOUNDARY_MARGIN - selfReferringEdgeHeight;
1306
1353
  var currentYPos = Math.max(BOUNDARY_MARGIN, Math.min(bottomBoundary, d.y));
1307
1354
  if (adaptive && height < MAX_HEIGHT &&
1308
- (currentYPos === BOUNDARY_MARGIN || currentYPos === bottomBoundary)) {
1355
+ (currentYPos === BOUNDARY_MARGIN || currentYPos === bottomBoundary)) {
1309
1356
  if (!d3.select(this).classed("fixed")) {
1310
1357
  height += OFFSET_VALUE;
1311
1358
  boundingBoxContainer.attr("height", height);
1312
-
1313
1359
  link
1314
1360
  .attr("y1", function (d) {
1315
1361
  return d.source.y;
@@ -1343,7 +1389,7 @@ export var drawGraph = function (network, sliderController, nodeColoring) {
1343
1389
  var dy = y2 - y1;
1344
1390
  var dr = Math.sqrt(dx * dx + dy * dy);
1345
1391
 
1346
- // Defaults for normal edge.
1392
+ // Defaults for normal edge.
1347
1393
  var drx = dr;
1348
1394
  var dry = dr;
1349
1395
  var xRotation = 0; // degrees
@@ -1351,49 +1397,51 @@ export var drawGraph = function (network, sliderController, nodeColoring) {
1351
1397
  var sweep = 1; // 1 or 0
1352
1398
  var offset = parseFloat(d.strokeWidth);
1353
1399
 
1354
- // 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.
1355
1401
  var DEFAULT_NODE_SHIFT = 1.033;
1356
1402
  var SHORT_NODE_LIMIT = 135;
1357
1403
  var ADDITIONAL_SHIFT = 0.07;
1358
1404
  var END_POINT_ADJUSTMENT = 1.2;
1359
1405
 
1360
1406
 
1361
- // Self edge.
1407
+ // Self edge.
1362
1408
  if (x1 === x2 && y1 === y2) {
1363
- // Move the position of the loop.
1409
+ // Move the position of the loop.
1364
1410
  x1 = d.source.x + (d.source.textWidth) * DEFAULT_NODE_SHIFT;
1365
1411
  y1 = d.source.y + (nodeHeight / 2) + SELF_REFERRING_Y_OFFSET;
1366
1412
 
1367
- // 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?)
1368
1415
  xRotation = 45;
1369
1416
 
1370
- // Needs to be 1.
1417
+ // Needs to be 1.
1371
1418
  largeArc = 1;
1372
1419
 
1373
- // Change sweep to change orientation of loop.
1420
+ // Change sweep to change orientation of loop.
1374
1421
  sweep = 1;
1375
1422
 
1376
1423
  drx = getSelfReferringRadius(d);
1377
1424
  dry = getSelfReferringRadius(d);
1378
1425
 
1379
- // For whatever reason the arc collapses to a point if the beginning
1380
- // 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.
1381
1428
  if (d.source.textWidth > SHORT_NODE_LIMIT) {
1382
1429
  DEFAULT_NODE_SHIFT += ADDITIONAL_SHIFT;
1383
1430
  }
1384
1431
  x2 = d.source.x + d.source.textWidth / END_POINT_ADJUSTMENT * DEFAULT_NODE_SHIFT;
1385
1432
  y2 = d.source.y + nodeHeight;
1386
1433
 
1387
- if (d.value < 0 && colorOptimal) {
1434
+ if (d.value < 0 && grnState.colorOptimal) {
1388
1435
  offset = Math.max(10, parseFloat(d.strokeWidth));
1389
1436
  }
1390
1437
  }
1391
1438
 
1392
- 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)};
1393
1441
 
1394
1442
  return "M" + x1 + "," + y1 +
1395
- "A" + drx + "," + dry + " " + xRotation + "," + largeArc + "," + sweep + " " +
1396
- x2 + "," + (y2 + offset);
1443
+ "A" + drx + "," + dry + " " + xRotation + "," + largeArc + "," + sweep + " " +
1444
+ x2 + "," + (y2 + offset);
1397
1445
  } else {
1398
1446
  return moveTo(d) + lineTo(d);
1399
1447
  }
@@ -1415,7 +1463,7 @@ export var drawGraph = function (network, sliderController, nodeColoring) {
1415
1463
  selfRef = "_SelfReferential";
1416
1464
  }
1417
1465
 
1418
- if (d.type === "repressor" && colorOptimal) {
1466
+ if (d.type === "repressor" && grnState.colorOptimal) {
1419
1467
  if ((d.tanRatioMoveable > d.tanRatioFixed) || (d.target === d.source)) { // if horizontal repressor
1420
1468
  return "url(#repressorHorizontal" + selfRef + "_StrokeWidth" + d.strokeWidth + minimum + ")";
1421
1469
  } else { // otherwise vertical repressor
@@ -1439,7 +1487,7 @@ export var drawGraph = function (network, sliderController, nodeColoring) {
1439
1487
  }
1440
1488
 
1441
1489
  function normalize (d) {
1442
- return Math.abs(d.value / (d3.max(allWeights)));
1490
+ return Math.abs(d.value / maxWeight).toPrecision(4);
1443
1491
  }
1444
1492
 
1445
1493
  function dragstart (d) {
@@ -1455,57 +1503,13 @@ export var drawGraph = function (network, sliderController, nodeColoring) {
1455
1503
  d.fy = d3.event.y;
1456
1504
  }
1457
1505
 
1458
- // Configures sliderController
1459
- sliderController.addForce(simulation);
1460
- sliderController.configureForceHandlers();
1461
- sliderController.initializeDefaultForces();
1462
-
1463
- var changeSliderValue = function (slider, item) {
1464
- var value = slider === "link" ? linkDistValidator($(item).val()) :
1465
- chargeValidator($(item).val());
1466
- sliderController.modifyForceParameter(slider, value);
1467
- if (slider === "link") {
1468
- $(LINK_DISTANCE_VALUE).text(value);
1469
- $(LINK_DISTANCE_INPUT).val(value);
1470
- $(LINK_DISTANCE_MENU).val(value);
1471
- } else {
1472
- $(CHARGE_VALUE).text(value);
1473
- $(CHARGE_INPUT).val(value);
1474
- $(CHARGE_MENU).val(value);
1475
- }
1476
- };
1477
-
1478
- var LINK_DISTANCE_MENU = "#link-distance-menu";
1479
- var LINK_DISTANCE_INPUT = "#linkDistInput";
1480
- var LINK_DISTANCE_VALUE = "#linkDistVal";
1481
-
1482
- $(LINK_DISTANCE_MENU).on("change", function () {
1483
- changeSliderValue("link", LINK_DISTANCE_MENU);
1484
- });
1485
-
1486
- $(LINK_DISTANCE_INPUT).on("change", function () {
1487
- changeSliderValue("link", LINK_DISTANCE_INPUT);
1488
- });
1506
+ grnState.simulation = simulation;
1489
1507
 
1490
- var CHARGE_MENU = "#charge-menu";
1491
- var CHARGE_INPUT = "#chargeInput";
1492
- var CHARGE_VALUE = "#chargeVal";
1508
+ // The restrict graph state is sometimes carried over across reloads
1509
+ restrictGraphToViewport( $("input[name=viewport]").prop("checked"));
1493
1510
 
1494
- $(CHARGE_MENU).on("change", function () {
1495
- changeSliderValue("charge", CHARGE_MENU);
1496
- });
1497
-
1498
- $(CHARGE_INPUT).on("change", function () {
1499
- changeSliderValue("charge", CHARGE_INPUT);
1500
- });
1501
-
1502
- var linkDistValidator = function (value) {
1503
- return valueValidator(1, 1000, value);
1504
- };
1505
-
1506
- var chargeValidator = function (value) {
1507
- return valueValidator(-2000, 0, value);
1508
- };
1511
+ modifyChargeParameter(grnState.chargeSlider.currentVal);
1512
+ modifyLinkDistanceParameter(grnState.linkDistanceSlider.currentVal);
1509
1513
 
1510
1514
  $(".startDisabled").removeClass("disabled");
1511
1515
  };