diff --git a/.eslintrc.js b/.eslintrc.js
new file mode 100644
index 00000000..bb731a6e
--- /dev/null
+++ b/.eslintrc.js
@@ -0,0 +1,35 @@
+const errorInProduction = process.env.NODE_ENV === 'production' ? 'error' : 'off';
+const path = require('path');
+
+module.exports = {
+ root: true,
+ env: {
+ node: true,
+ browser: true
+ },
+ extends: [
+ ],
+ rules: {
+ 'no-console': 0, // errorInProduction,
+ 'no-debugger': errorInProduction,
+ 'brace-style': 0,
+ // 'brace-style': [2, 'stroustrup'],
+ 'padded-blocks': 0,
+ // 'indent': [2, 2, { 'SwitchCase': 1 }],
+ 'indent': 0,
+ 'spaced-comment': 0,
+ 'quotes': 0,
+ // 'quotes': ['error', 'single', { 'allowTemplateLiterals': true }],
+ 'global-require': 0,
+ 'no-unused-vars': [0, { 'argsIgnorePattern': '^_' }],
+ 'quote-props': [0],
+ 'prefer-destructuring': 0,
+ 'prefer-arrow-callback': 0,
+ 'prefer-template': 0,
+ 'comma-dangle': 0,
+ 'max-len': 0,
+ 'no-param-reassign': 0,
+ 'no-underscore-dangle': 0,
+ },
+};
+
diff --git a/.gitignore b/.gitignore
index 5691bc09..697722ad 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,11 +1,12 @@
## Ignore npm libs.
node_modules
+dist
## Ignore python behave tests generated libs
tests/behave/lib
tests/behave/bin
tests/behave/lib64
-tests/behave/include
+tests/behave/include
tests/behave/ghostdriver.log
## Others
diff --git a/.jshintrc b/.jshintrc
deleted file mode 100644
index fda19a6d..00000000
--- a/.jshintrc
+++ /dev/null
@@ -1,94 +0,0 @@
-{
- // JSHint Default Configuration File (as on JSHint website)
- // See http://jshint.com/docs/ for more details
-
- "maxerr" : 150, // {int} Maximum error before stopping
-
- // Enforcing
- "bitwise" : true, // true: Prohibit bitwise operators (&, |, ^, etc.)
- "camelcase" : false, // true: Identifiers must be in camelCase
- "curly" : true, // true: Require {} for every new block or scope
- "eqeqeq" : true, // true: Require triple equals (===) for comparison
- "forin" : false, // true: Require filtering for..in loops with obj.hasOwnProperty()
- "freeze" : true, // true: prohibits overwriting prototypes of native objects such as Array, Date etc.
- "immed" : false, // true: Require immediate invocations to be wrapped in parens e.g. `(function () { } ());`
- "indent" : 4, // {int} Number of spaces to use for indentation
- "latedef" : false, // true: Require variables/functions to be defined before being used
- "newcap" : false, // true: Require capitalization of all constructor functions e.g. `new F()`
- "noarg" : true, // true: Prohibit use of `arguments.caller` and `arguments.callee`
- "noempty" : true, // true: Prohibit use of empty blocks
- "nonbsp" : true, // true: Prohibit "non-breaking whitespace" characters.
- "nonew" : false, // true: Prohibit use of constructors for side-effects (without assignment)
- "plusplus" : false, // true: Prohibit use of `++` & `--`
- "quotmark" : false, // Quotation mark consistency:
- // false : do nothing (default)
- // true : ensure whatever is used is consistent
- // "single" : require single quotes
- // "double" : require double quotes
- "undef" : true, // true: Require all non-global variables to be declared (prevents global leaks)
- "unused" : true, // Unused variables:
- // true : all variables, last function parameter
- // "vars" : all variables only
- // "strict" : all variables, all function parameters
- "strict" : true, // true: Requires all functions run in ES5 Strict Mode
- "maxparams" : false, // {int} Max number of formal params allowed per function
- "maxdepth" : false, // {int} Max depth of nested blocks (within functions)
- "maxstatements" : false, // {int} Max number statements per function
- "maxcomplexity" : false, // {int} Max cyclomatic complexity per function
- "maxlen" : false, // {int} Max number of characters per line
- "varstmt" : false, // true: Disallow any var statements. Only `let` and `const` are allowed.
-
- // Relaxing
- "asi" : false, // true: Tolerate Automatic Semicolon Insertion (no semicolons)
- "boss" : false, // true: Tolerate assignments where comparisons would be expected
- "debug" : false, // true: Allow debugger statements e.g. browser breakpoints.
- "eqnull" : false, // true: Tolerate use of `== null`
- "es5" : false, // true: Allow ES5 syntax (ex: getters and setters)
- "esnext" : false, // true: Allow ES.next (ES6) syntax (ex: `const`)
- "moz" : false, // true: Allow Mozilla specific syntax (extends and overrides esnext features)
- // (ex: `for each`, multiple try/catch, function expression…)
- "evil" : false, // true: Tolerate use of `eval` and `new Function()`
- "expr" : false, // true: Tolerate `ExpressionStatement` as Programs
- "funcscope" : false, // true: Tolerate defining variables inside control statements
- "globalstrict" : false, // true: Allow global "use strict" (also enables 'strict')
- "iterator" : false, // true: Tolerate using the `__iterator__` property
- "lastsemic" : false, // true: Tolerate omitting a semicolon for the last statement of a 1-line block
- "laxbreak" : false, // true: Tolerate possibly unsafe line breakings
- "laxcomma" : false, // true: Tolerate comma-first style coding
- "loopfunc" : false, // true: Tolerate functions being defined in loops
- "multistr" : false, // true: Tolerate multi-line strings
- "noyield" : false, // true: Tolerate generator functions with no yield statement in them.
- "notypeof" : false, // true: Tolerate invalid typeof operator values
- "proto" : false, // true: Tolerate using the `__proto__` property
- "scripturl" : false, // true: Tolerate script-targeted URLs
- "shadow" : false, // true: Allows re-define variables later in code e.g. `var x=1; x=2;`
- "sub" : false, // true: Tolerate using `[]` notation when it can still be expressed in dot notation
- "supernew" : false, // true: Tolerate `new function () { ... };` and `new Object;`
- "validthis" : false, // true: Tolerate using this in a non-constructor function
-
- // Environments
- "browser" : true, // Web Browser (window, document, etc)
- "browserify" : true, // Browserify (node.js code in the browser)
- "couch" : false, // CouchDB
- "devel" : true, // Development/debugging (alert, confirm, etc)
- "dojo" : false, // Dojo Toolkit
- "jasmine" : false, // Jasmine
- "jquery" : false, // jQuery
- "mocha" : true, // Mocha
- "mootools" : false, // MooTools
- "node" : false, // Node.js
- "nonstandard" : false, // Widely adopted globals (escape, unescape, etc)
- "phantom" : false, // PhantomJS
- "prototypejs" : false, // Prototype and Scriptaculous
- "qunit" : false, // QUnit
- "rhino" : false, // Rhino
- "shelljs" : false, // ShellJS
- "typed" : false, // Globals for typed array constructions
- "worker" : false, // Web Workers
- "wsh" : false, // Windows Scripting Host
- "yui" : false, // Yahoo User Interface
-
- // Custom Globals
- "globals" : {} // additional predefined global variables
-}
-
diff --git a/.npmignore b/.npmignore
index fd97da54..7c3a6c5f 100644
--- a/.npmignore
+++ b/.npmignore
@@ -1,11 +1,12 @@
.travis.yml
.jshintrc
+.eslintrc.js
.gitignore
tests/behave/lib
tests/behave/bin
tests/behave/lib64
-tests/behave/include
+tests/behave/include
tests/behave/ghostdriver.log
diff --git a/.travis.yml b/.travis.yml
index 82661e94..561a663f 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -2,23 +2,39 @@ sudo: required
language: node_js
+env:
+ - MOZ_HEADLESS=1
+addons:
+ firefox: latest
+
node_js:
- - "0.12"
+ - "v8.12.0"
before_install:
- - "sudo pip install --upgrade pip"
- - "sudo pip install selenium"
- - "sudo pip install behave"
-
+ - wget https://github.com/mozilla/geckodriver/releases/download/v0.23.0/geckodriver-v0.23.0-linux64.tar.gz
+ - mkdir geckodriver
+ - tar -xzf geckodriver-v0.23.0-linux64.tar.gz -C geckodriver
+ - export PATH=$PATH:$PWD/geckodriver
+ - "cd tests/behave"
+ - "virtualenv env"
+ - "source env/bin/activate"
+ - "pip install selenium"
+ - "pip install --ignore-installed behave"
+ - "cd ../.."
+ # - "sudo pip install --upgrade pip"
+ # - "sudo pip install selenium"
+ # - "sudo pip install --ignore-installed behave"
+
install:
- "npm install"
-script:
- - "gulp bundle"
- - "gulp test"
+script:
+ - "npx gulp bundle"
+ - "npx gulp test"
- "PGDIR=`pwd`"
- "cd tests/behave"
- - "TARGET=file://$PGDIR BROWSER=phantomjs behave"
+ - "source env/bin/activate"
+ - "TARGET=file://$PGDIR DRAFT=6 behave"
# whitelist
branches:
diff --git a/README.md b/README.md
index 967d8288..6c2f113a 100644
--- a/README.md
+++ b/README.md
@@ -6,7 +6,7 @@
## About Phenogrid
-Phenogrid is a Javascript component that visualizes semantic similarity calculations provided by [OWLSim](https://github.com/owlcollab/owltools), as provided through APIs from the [Monarch Initiative](http://monarchinitiative.org/).
+Phenogrid is a Javascript component that visualizes semantic similarity calculations provided by [OWLSim](https://github.com/owlcollab/owltools), as provided through APIs from the [Monarch Initiative](https://monarchinitiative.org/).
Given an input list of phenotypes (you will see the sample input below) and parameters specified in config/phenogrid_config.js indicating desired source of matching models (humans, model organisms, etc.), the phenogrid will call the Monarch API to get OWLSim results and render them in your web browser in data visualization. And you may use the visualized data for your research.
@@ -281,7 +281,7 @@ var data = {
window.onload = function() {
// There are three species that are loaded and each of them has simsearch matches.
Phenogrid.createPhenogridForElement(document.getElementById('phenogrid_container'), {
- serverURL : "http://monarchinitiative.org",
+ serverURL : "https://monarchinitiative.org",
gridSkeletonData: data
});
}
@@ -303,7 +303,7 @@ window.onload = function() {
This URL should be pointed to the OWLSim URL server associated with your installation containing the Monarch web services. You have three options:
- Use http://beta.monarchinitiative.org to connect to the development/test web services. This server is less stable than the production server.
-- Use http://monarchinitiative.org to connect to the stable, production version of the web services (better uptime)
+- Use https://monarchinitiative.org to connect to the stable, production version of the web services (better uptime)
- If you are running the complete monarch-app, you can point it to http://localhost:8080, or whichever server/port you are using in your local installation.
diff --git a/config/phenogrid_config.js b/config/phenogrid_config.js
index 82290417..a295e79d 100644
--- a/config/phenogrid_config.js
+++ b/config/phenogrid_config.js
@@ -1,6 +1,5 @@
var configoptions = {
- serverURL: "https://beta.monarchinitiative.org",
+ serverURL: "https://monarchinitiative.org",
selectedCalculation: 0,
selectedSort: "Frequency"
};
-
\ No newline at end of file
diff --git a/dist/fonts/FontAwesome.otf b/dist/fonts/FontAwesome.otf
deleted file mode 100644
index 59853bcd..00000000
Binary files a/dist/fonts/FontAwesome.otf and /dev/null differ
diff --git a/dist/fonts/fontawesome-webfont.eot b/dist/fonts/fontawesome-webfont.eot
deleted file mode 100644
index 96f92f9b..00000000
Binary files a/dist/fonts/fontawesome-webfont.eot and /dev/null differ
diff --git a/dist/fonts/fontawesome-webfont.svg b/dist/fonts/fontawesome-webfont.svg
deleted file mode 100644
index 5a5f0ecd..00000000
--- a/dist/fonts/fontawesome-webfont.svg
+++ /dev/null
@@ -1,685 +0,0 @@
-
-
-
\ No newline at end of file
diff --git a/dist/fonts/fontawesome-webfont.ttf b/dist/fonts/fontawesome-webfont.ttf
deleted file mode 100644
index 86784df9..00000000
Binary files a/dist/fonts/fontawesome-webfont.ttf and /dev/null differ
diff --git a/dist/fonts/fontawesome-webfont.woff b/dist/fonts/fontawesome-webfont.woff
deleted file mode 100644
index c7faa19c..00000000
Binary files a/dist/fonts/fontawesome-webfont.woff and /dev/null differ
diff --git a/dist/fonts/fontawesome-webfont.woff2 b/dist/fonts/fontawesome-webfont.woff2
deleted file mode 100644
index cab8571d..00000000
Binary files a/dist/fonts/fontawesome-webfont.woff2 and /dev/null differ
diff --git a/dist/images/animated-overlay.gif b/dist/images/animated-overlay.gif
deleted file mode 100644
index d441f75e..00000000
Binary files a/dist/images/animated-overlay.gif and /dev/null differ
diff --git a/dist/images/ui-bg_flat_0_aaaaaa_40x100.png b/dist/images/ui-bg_flat_0_aaaaaa_40x100.png
deleted file mode 100644
index 5b5dab2a..00000000
Binary files a/dist/images/ui-bg_flat_0_aaaaaa_40x100.png and /dev/null differ
diff --git a/dist/images/ui-bg_flat_75_ffffff_40x100.png b/dist/images/ui-bg_flat_75_ffffff_40x100.png
deleted file mode 100644
index ac8b229a..00000000
Binary files a/dist/images/ui-bg_flat_75_ffffff_40x100.png and /dev/null differ
diff --git a/dist/images/ui-bg_glass_55_fbf9ee_1x400.png b/dist/images/ui-bg_glass_55_fbf9ee_1x400.png
deleted file mode 100644
index ad3d6346..00000000
Binary files a/dist/images/ui-bg_glass_55_fbf9ee_1x400.png and /dev/null differ
diff --git a/dist/images/ui-bg_glass_65_ffffff_1x400.png b/dist/images/ui-bg_glass_65_ffffff_1x400.png
deleted file mode 100644
index 42ccba26..00000000
Binary files a/dist/images/ui-bg_glass_65_ffffff_1x400.png and /dev/null differ
diff --git a/dist/images/ui-bg_glass_75_dadada_1x400.png b/dist/images/ui-bg_glass_75_dadada_1x400.png
deleted file mode 100644
index 5a46b47c..00000000
Binary files a/dist/images/ui-bg_glass_75_dadada_1x400.png and /dev/null differ
diff --git a/dist/images/ui-bg_glass_75_e6e6e6_1x400.png b/dist/images/ui-bg_glass_75_e6e6e6_1x400.png
deleted file mode 100644
index 86c2baa6..00000000
Binary files a/dist/images/ui-bg_glass_75_e6e6e6_1x400.png and /dev/null differ
diff --git a/dist/images/ui-bg_glass_95_fef1ec_1x400.png b/dist/images/ui-bg_glass_95_fef1ec_1x400.png
deleted file mode 100644
index 4443fdc1..00000000
Binary files a/dist/images/ui-bg_glass_95_fef1ec_1x400.png and /dev/null differ
diff --git a/dist/images/ui-bg_highlight-soft_75_cccccc_1x100.png b/dist/images/ui-bg_highlight-soft_75_cccccc_1x100.png
deleted file mode 100644
index 7c9fa6c6..00000000
Binary files a/dist/images/ui-bg_highlight-soft_75_cccccc_1x100.png and /dev/null differ
diff --git a/dist/images/ui-icons_222222_256x240.png b/dist/images/ui-icons_222222_256x240.png
deleted file mode 100644
index ee039dc0..00000000
Binary files a/dist/images/ui-icons_222222_256x240.png and /dev/null differ
diff --git a/dist/images/ui-icons_2e83ff_256x240.png b/dist/images/ui-icons_2e83ff_256x240.png
deleted file mode 100644
index 45e8928e..00000000
Binary files a/dist/images/ui-icons_2e83ff_256x240.png and /dev/null differ
diff --git a/dist/images/ui-icons_444444_256x240.png b/dist/images/ui-icons_444444_256x240.png
deleted file mode 100644
index 92214389..00000000
Binary files a/dist/images/ui-icons_444444_256x240.png and /dev/null differ
diff --git a/dist/images/ui-icons_454545_256x240.png b/dist/images/ui-icons_454545_256x240.png
deleted file mode 100644
index 7ec70d11..00000000
Binary files a/dist/images/ui-icons_454545_256x240.png and /dev/null differ
diff --git a/dist/images/ui-icons_555555_256x240.png b/dist/images/ui-icons_555555_256x240.png
deleted file mode 100644
index 4c372960..00000000
Binary files a/dist/images/ui-icons_555555_256x240.png and /dev/null differ
diff --git a/dist/images/ui-icons_777620_256x240.png b/dist/images/ui-icons_777620_256x240.png
deleted file mode 100644
index 3b4ce686..00000000
Binary files a/dist/images/ui-icons_777620_256x240.png and /dev/null differ
diff --git a/dist/images/ui-icons_777777_256x240.png b/dist/images/ui-icons_777777_256x240.png
deleted file mode 100644
index de6cf086..00000000
Binary files a/dist/images/ui-icons_777777_256x240.png and /dev/null differ
diff --git a/dist/images/ui-icons_888888_256x240.png b/dist/images/ui-icons_888888_256x240.png
deleted file mode 100644
index 5ba708c3..00000000
Binary files a/dist/images/ui-icons_888888_256x240.png and /dev/null differ
diff --git a/dist/images/ui-icons_cc0000_256x240.png b/dist/images/ui-icons_cc0000_256x240.png
deleted file mode 100644
index 6c64c85e..00000000
Binary files a/dist/images/ui-icons_cc0000_256x240.png and /dev/null differ
diff --git a/dist/images/ui-icons_cd0a0a_256x240.png b/dist/images/ui-icons_cd0a0a_256x240.png
deleted file mode 100644
index 7930a558..00000000
Binary files a/dist/images/ui-icons_cd0a0a_256x240.png and /dev/null differ
diff --git a/dist/images/ui-icons_ffffff_256x240.png b/dist/images/ui-icons_ffffff_256x240.png
deleted file mode 100644
index 4ab379a1..00000000
Binary files a/dist/images/ui-icons_ffffff_256x240.png and /dev/null differ
diff --git a/dist/phenogrid-bundle.css b/dist/phenogrid-bundle.css
deleted file mode 100644
index e3ad65df..00000000
--- a/dist/phenogrid-bundle.css
+++ /dev/null
@@ -1,11 +0,0 @@
-/*! normalize.css v4.1.1 | MIT License | github.com/necolas/normalize.css */html{font-family:sans-serif;-ms-text-size-adjust:100%;-webkit-text-size-adjust:100%}body{margin:0}article,aside,details,figcaption,figure,footer,header,main,menu,nav,section,summary{display:block}audio,canvas,progress,video{display:inline-block}audio:not([controls]){display:none;height:0}progress{vertical-align:baseline}[hidden],template{display:none}a{background-color:transparent;-webkit-text-decoration-skip:objects}a:active,a:hover{outline-width:0}abbr[title]{border-bottom:none;text-decoration:underline;text-decoration:underline dotted}b,strong{font-weight:inherit;font-weight:bolder}dfn{font-style:italic}h1{font-size:2em;margin:.67em 0}mark{background-color:#ff0;color:#000}small{font-size:80%}sub,sup{font-size:75%;line-height:0;position:relative;vertical-align:baseline}sub{bottom:-.25em}sup{top:-.5em}img{border-style:none}svg:not(:root){overflow:hidden}code,kbd,pre,samp{font-family:monospace,monospace;font-size:1em}figure{margin:1em 40px}hr{box-sizing:content-box;height:0;overflow:visible}button,input,select,textarea{font:inherit;margin:0}optgroup{font-weight:700}button,input{overflow:visible}button,select{text-transform:none}[type=reset],[type=submit],button,html [type=button]{-webkit-appearance:button}[type=button]::-moz-focus-inner,[type=reset]::-moz-focus-inner,[type=submit]::-moz-focus-inner,button::-moz-focus-inner{border-style:none;padding:0}[type=button]:-moz-focusring,[type=reset]:-moz-focusring,[type=submit]:-moz-focusring,button:-moz-focusring{outline:1px dotted ButtonText}fieldset{border:1px solid silver;margin:0 2px;padding:.35em .625em .75em}legend{box-sizing:border-box;color:inherit;display:table;max-width:100%;padding:0;white-space:normal}textarea{overflow:auto}[type=checkbox],[type=radio]{box-sizing:border-box;padding:0}[type=number]::-webkit-inner-spin-button,[type=number]::-webkit-outer-spin-button{height:auto}[type=search]{-webkit-appearance:textfield;outline-offset:-2px}[type=search]::-webkit-search-cancel-button,[type=search]::-webkit-search-decoration{-webkit-appearance:none}::-webkit-input-placeholder{color:inherit;opacity:.54}::-webkit-file-upload-button{-webkit-appearance:button;font:inherit}
-
-/*!
- * Font Awesome 4.6.1 by @davegandy - http://fontawesome.io - @fontawesome
- * License - http://fontawesome.io/license (Font: SIL OFL 1.1, CSS: MIT License)
- */@font-face{font-family:FontAwesome;src:url(fonts/fontawesome-webfont.eot?v=4.6.1);src:url(fonts/fontawesome-webfont.eot?#iefix&v=4.6.1) format("embedded-opentype"),url(fonts/fontawesome-webfont.woff2?v=4.6.1) format("woff2"),url(fonts/fontawesome-webfont.woff?v=4.6.1) format("woff"),url(fonts/fontawesome-webfont.ttf?v=4.6.1) format("truetype"),url(fonts/fontawesome-webfont.svg?v=4.6.1#fontawesomeregular) format("svg");font-weight:400;font-style:normal}.fa{display:inline-block;font:normal normal normal 14px/1 FontAwesome;font-size:inherit;text-rendering:auto;-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale}.fa-lg{font-size:1.33333333em;line-height:.75em;vertical-align:-15%}.fa-2x{font-size:2em}.fa-3x{font-size:3em}.fa-4x{font-size:4em}.fa-5x{font-size:5em}.fa-fw{width:1.28571429em;text-align:center}.fa-ul{padding-left:0;margin-left:2.14285714em;list-style-type:none}.fa-ul>li{position:relative}.fa-li{position:absolute;left:-2.14285714em;width:2.14285714em;top:.14285714em;text-align:center}.fa-li.fa-lg{left:-1.85714286em}.fa-border{padding:.2em .25em .15em;border:.08em solid #eee;border-radius:.1em}.fa-pull-left{float:left}.fa-pull-right{float:right}.fa.fa-pull-left{margin-right:.3em}.fa.fa-pull-right{margin-left:.3em}.pull-right{float:right}.pull-left{float:left}.fa.pull-left{margin-right:.3em}.fa.pull-right{margin-left:.3em}.fa-spin{-webkit-animation:a 2s infinite linear;animation:a 2s infinite linear}.fa-pulse{-webkit-animation:a 1s infinite steps(8);animation:a 1s infinite steps(8)}@-webkit-keyframes a{0%{-webkit-transform:rotate(0deg);transform:rotate(0deg)}to{-webkit-transform:rotate(359deg);transform:rotate(359deg)}}@keyframes a{0%{-webkit-transform:rotate(0deg);transform:rotate(0deg)}to{-webkit-transform:rotate(359deg);transform:rotate(359deg)}}.fa-rotate-90{-ms-filter:"progid:DXImageTransform.Microsoft.BasicImage(rotation=1)";-webkit-transform:rotate(90deg);transform:rotate(90deg)}.fa-rotate-180{-ms-filter:"progid:DXImageTransform.Microsoft.BasicImage(rotation=2)";-webkit-transform:rotate(180deg);transform:rotate(180deg)}.fa-rotate-270{-ms-filter:"progid:DXImageTransform.Microsoft.BasicImage(rotation=3)";-webkit-transform:rotate(270deg);transform:rotate(270deg)}.fa-flip-horizontal{-ms-filter:"progid:DXImageTransform.Microsoft.BasicImage(rotation=0, mirror=1)";-webkit-transform:scaleX(-1);transform:scaleX(-1)}.fa-flip-vertical{-ms-filter:"progid:DXImageTransform.Microsoft.BasicImage(rotation=2, mirror=1)";-webkit-transform:scaleY(-1);transform:scaleY(-1)}:root .fa-flip-horizontal,:root .fa-flip-vertical,:root .fa-rotate-90,:root .fa-rotate-180,:root .fa-rotate-270{filter:none}.fa-stack{position:relative;display:inline-block;width:2em;height:2em;line-height:2em;vertical-align:middle}.fa-stack-1x,.fa-stack-2x{position:absolute;left:0;width:100%;text-align:center}.fa-stack-1x{line-height:inherit}.fa-stack-2x{font-size:2em}.fa-inverse{color:#fff}.fa-glass:before{content:"\f000"}.fa-music:before{content:"\f001"}.fa-search:before{content:"\f002"}.fa-envelope-o:before{content:"\f003"}.fa-heart:before{content:"\f004"}.fa-star:before{content:"\f005"}.fa-star-o:before{content:"\f006"}.fa-user:before{content:"\f007"}.fa-film:before{content:"\f008"}.fa-th-large:before{content:"\f009"}.fa-th:before{content:"\f00a"}.fa-th-list:before{content:"\f00b"}.fa-check:before{content:"\f00c"}.fa-close:before,.fa-remove:before,.fa-times:before{content:"\f00d"}.fa-search-plus:before{content:"\f00e"}.fa-search-minus:before{content:"\f010"}.fa-power-off:before{content:"\f011"}.fa-signal:before{content:"\f012"}.fa-cog:before,.fa-gear:before{content:"\f013"}.fa-trash-o:before{content:"\f014"}.fa-home:before{content:"\f015"}.fa-file-o:before{content:"\f016"}.fa-clock-o:before{content:"\f017"}.fa-road:before{content:"\f018"}.fa-download:before{content:"\f019"}.fa-arrow-circle-o-down:before{content:"\f01a"}.fa-arrow-circle-o-up:before{content:"\f01b"}.fa-inbox:before{content:"\f01c"}.fa-play-circle-o:before{content:"\f01d"}.fa-repeat:before,.fa-rotate-right:before{content:"\f01e"}.fa-refresh:before{content:"\f021"}.fa-list-alt:before{content:"\f022"}.fa-lock:before{content:"\f023"}.fa-flag:before{content:"\f024"}.fa-headphones:before{content:"\f025"}.fa-volume-off:before{content:"\f026"}.fa-volume-down:before{content:"\f027"}.fa-volume-up:before{content:"\f028"}.fa-qrcode:before{content:"\f029"}.fa-barcode:before{content:"\f02a"}.fa-tag:before{content:"\f02b"}.fa-tags:before{content:"\f02c"}.fa-book:before{content:"\f02d"}.fa-bookmark:before{content:"\f02e"}.fa-print:before{content:"\f02f"}.fa-camera:before{content:"\f030"}.fa-font:before{content:"\f031"}.fa-bold:before{content:"\f032"}.fa-italic:before{content:"\f033"}.fa-text-height:before{content:"\f034"}.fa-text-width:before{content:"\f035"}.fa-align-left:before{content:"\f036"}.fa-align-center:before{content:"\f037"}.fa-align-right:before{content:"\f038"}.fa-align-justify:before{content:"\f039"}.fa-list:before{content:"\f03a"}.fa-dedent:before,.fa-outdent:before{content:"\f03b"}.fa-indent:before{content:"\f03c"}.fa-video-camera:before{content:"\f03d"}.fa-image:before,.fa-photo:before,.fa-picture-o:before{content:"\f03e"}.fa-pencil:before{content:"\f040"}.fa-map-marker:before{content:"\f041"}.fa-adjust:before{content:"\f042"}.fa-tint:before{content:"\f043"}.fa-edit:before,.fa-pencil-square-o:before{content:"\f044"}.fa-share-square-o:before{content:"\f045"}.fa-check-square-o:before{content:"\f046"}.fa-arrows:before{content:"\f047"}.fa-step-backward:before{content:"\f048"}.fa-fast-backward:before{content:"\f049"}.fa-backward:before{content:"\f04a"}.fa-play:before{content:"\f04b"}.fa-pause:before{content:"\f04c"}.fa-stop:before{content:"\f04d"}.fa-forward:before{content:"\f04e"}.fa-fast-forward:before{content:"\f050"}.fa-step-forward:before{content:"\f051"}.fa-eject:before{content:"\f052"}.fa-chevron-left:before{content:"\f053"}.fa-chevron-right:before{content:"\f054"}.fa-plus-circle:before{content:"\f055"}.fa-minus-circle:before{content:"\f056"}.fa-times-circle:before{content:"\f057"}.fa-check-circle:before{content:"\f058"}.fa-question-circle:before{content:"\f059"}.fa-info-circle:before{content:"\f05a"}.fa-crosshairs:before{content:"\f05b"}.fa-times-circle-o:before{content:"\f05c"}.fa-check-circle-o:before{content:"\f05d"}.fa-ban:before{content:"\f05e"}.fa-arrow-left:before{content:"\f060"}.fa-arrow-right:before{content:"\f061"}.fa-arrow-up:before{content:"\f062"}.fa-arrow-down:before{content:"\f063"}.fa-mail-forward:before,.fa-share:before{content:"\f064"}.fa-expand:before{content:"\f065"}.fa-compress:before{content:"\f066"}.fa-plus:before{content:"\f067"}.fa-minus:before{content:"\f068"}.fa-asterisk:before{content:"\f069"}.fa-exclamation-circle:before{content:"\f06a"}.fa-gift:before{content:"\f06b"}.fa-leaf:before{content:"\f06c"}.fa-fire:before{content:"\f06d"}.fa-eye:before{content:"\f06e"}.fa-eye-slash:before{content:"\f070"}.fa-exclamation-triangle:before,.fa-warning:before{content:"\f071"}.fa-plane:before{content:"\f072"}.fa-calendar:before{content:"\f073"}.fa-random:before{content:"\f074"}.fa-comment:before{content:"\f075"}.fa-magnet:before{content:"\f076"}.fa-chevron-up:before{content:"\f077"}.fa-chevron-down:before{content:"\f078"}.fa-retweet:before{content:"\f079"}.fa-shopping-cart:before{content:"\f07a"}.fa-folder:before{content:"\f07b"}.fa-folder-open:before{content:"\f07c"}.fa-arrows-v:before{content:"\f07d"}.fa-arrows-h:before{content:"\f07e"}.fa-bar-chart-o:before,.fa-bar-chart:before{content:"\f080"}.fa-twitter-square:before{content:"\f081"}.fa-facebook-square:before{content:"\f082"}.fa-camera-retro:before{content:"\f083"}.fa-key:before{content:"\f084"}.fa-cogs:before,.fa-gears:before{content:"\f085"}.fa-comments:before{content:"\f086"}.fa-thumbs-o-up:before{content:"\f087"}.fa-thumbs-o-down:before{content:"\f088"}.fa-star-half:before{content:"\f089"}.fa-heart-o:before{content:"\f08a"}.fa-sign-out:before{content:"\f08b"}.fa-linkedin-square:before{content:"\f08c"}.fa-thumb-tack:before{content:"\f08d"}.fa-external-link:before{content:"\f08e"}.fa-sign-in:before{content:"\f090"}.fa-trophy:before{content:"\f091"}.fa-github-square:before{content:"\f092"}.fa-upload:before{content:"\f093"}.fa-lemon-o:before{content:"\f094"}.fa-phone:before{content:"\f095"}.fa-square-o:before{content:"\f096"}.fa-bookmark-o:before{content:"\f097"}.fa-phone-square:before{content:"\f098"}.fa-twitter:before{content:"\f099"}.fa-facebook-f:before,.fa-facebook:before{content:"\f09a"}.fa-github:before{content:"\f09b"}.fa-unlock:before{content:"\f09c"}.fa-credit-card:before{content:"\f09d"}.fa-feed:before,.fa-rss:before{content:"\f09e"}.fa-hdd-o:before{content:"\f0a0"}.fa-bullhorn:before{content:"\f0a1"}.fa-bell:before{content:"\f0f3"}.fa-certificate:before{content:"\f0a3"}.fa-hand-o-right:before{content:"\f0a4"}.fa-hand-o-left:before{content:"\f0a5"}.fa-hand-o-up:before{content:"\f0a6"}.fa-hand-o-down:before{content:"\f0a7"}.fa-arrow-circle-left:before{content:"\f0a8"}.fa-arrow-circle-right:before{content:"\f0a9"}.fa-arrow-circle-up:before{content:"\f0aa"}.fa-arrow-circle-down:before{content:"\f0ab"}.fa-globe:before{content:"\f0ac"}.fa-wrench:before{content:"\f0ad"}.fa-tasks:before{content:"\f0ae"}.fa-filter:before{content:"\f0b0"}.fa-briefcase:before{content:"\f0b1"}.fa-arrows-alt:before{content:"\f0b2"}.fa-group:before,.fa-users:before{content:"\f0c0"}.fa-chain:before,.fa-link:before{content:"\f0c1"}.fa-cloud:before{content:"\f0c2"}.fa-flask:before{content:"\f0c3"}.fa-cut:before,.fa-scissors:before{content:"\f0c4"}.fa-copy:before,.fa-files-o:before{content:"\f0c5"}.fa-paperclip:before{content:"\f0c6"}.fa-floppy-o:before,.fa-save:before{content:"\f0c7"}.fa-square:before{content:"\f0c8"}.fa-bars:before,.fa-navicon:before,.fa-reorder:before{content:"\f0c9"}.fa-list-ul:before{content:"\f0ca"}.fa-list-ol:before{content:"\f0cb"}.fa-strikethrough:before{content:"\f0cc"}.fa-underline:before{content:"\f0cd"}.fa-table:before{content:"\f0ce"}.fa-magic:before{content:"\f0d0"}.fa-truck:before{content:"\f0d1"}.fa-pinterest:before{content:"\f0d2"}.fa-pinterest-square:before{content:"\f0d3"}.fa-google-plus-square:before{content:"\f0d4"}.fa-google-plus:before{content:"\f0d5"}.fa-money:before{content:"\f0d6"}.fa-caret-down:before{content:"\f0d7"}.fa-caret-up:before{content:"\f0d8"}.fa-caret-left:before{content:"\f0d9"}.fa-caret-right:before{content:"\f0da"}.fa-columns:before{content:"\f0db"}.fa-sort:before,.fa-unsorted:before{content:"\f0dc"}.fa-sort-desc:before,.fa-sort-down:before{content:"\f0dd"}.fa-sort-asc:before,.fa-sort-up:before{content:"\f0de"}.fa-envelope:before{content:"\f0e0"}.fa-linkedin:before{content:"\f0e1"}.fa-rotate-left:before,.fa-undo:before{content:"\f0e2"}.fa-gavel:before,.fa-legal:before{content:"\f0e3"}.fa-dashboard:before,.fa-tachometer:before{content:"\f0e4"}.fa-comment-o:before{content:"\f0e5"}.fa-comments-o:before{content:"\f0e6"}.fa-bolt:before,.fa-flash:before{content:"\f0e7"}.fa-sitemap:before{content:"\f0e8"}.fa-umbrella:before{content:"\f0e9"}.fa-clipboard:before,.fa-paste:before{content:"\f0ea"}.fa-lightbulb-o:before{content:"\f0eb"}.fa-exchange:before{content:"\f0ec"}.fa-cloud-download:before{content:"\f0ed"}.fa-cloud-upload:before{content:"\f0ee"}.fa-user-md:before{content:"\f0f0"}.fa-stethoscope:before{content:"\f0f1"}.fa-suitcase:before{content:"\f0f2"}.fa-bell-o:before{content:"\f0a2"}.fa-coffee:before{content:"\f0f4"}.fa-cutlery:before{content:"\f0f5"}.fa-file-text-o:before{content:"\f0f6"}.fa-building-o:before{content:"\f0f7"}.fa-hospital-o:before{content:"\f0f8"}.fa-ambulance:before{content:"\f0f9"}.fa-medkit:before{content:"\f0fa"}.fa-fighter-jet:before{content:"\f0fb"}.fa-beer:before{content:"\f0fc"}.fa-h-square:before{content:"\f0fd"}.fa-plus-square:before{content:"\f0fe"}.fa-angle-double-left:before{content:"\f100"}.fa-angle-double-right:before{content:"\f101"}.fa-angle-double-up:before{content:"\f102"}.fa-angle-double-down:before{content:"\f103"}.fa-angle-left:before{content:"\f104"}.fa-angle-right:before{content:"\f105"}.fa-angle-up:before{content:"\f106"}.fa-angle-down:before{content:"\f107"}.fa-desktop:before{content:"\f108"}.fa-laptop:before{content:"\f109"}.fa-tablet:before{content:"\f10a"}.fa-mobile-phone:before,.fa-mobile:before{content:"\f10b"}.fa-circle-o:before{content:"\f10c"}.fa-quote-left:before{content:"\f10d"}.fa-quote-right:before{content:"\f10e"}.fa-spinner:before{content:"\f110"}.fa-circle:before{content:"\f111"}.fa-mail-reply:before,.fa-reply:before{content:"\f112"}.fa-github-alt:before{content:"\f113"}.fa-folder-o:before{content:"\f114"}.fa-folder-open-o:before{content:"\f115"}.fa-smile-o:before{content:"\f118"}.fa-frown-o:before{content:"\f119"}.fa-meh-o:before{content:"\f11a"}.fa-gamepad:before{content:"\f11b"}.fa-keyboard-o:before{content:"\f11c"}.fa-flag-o:before{content:"\f11d"}.fa-flag-checkered:before{content:"\f11e"}.fa-terminal:before{content:"\f120"}.fa-code:before{content:"\f121"}.fa-mail-reply-all:before,.fa-reply-all:before{content:"\f122"}.fa-star-half-empty:before,.fa-star-half-full:before,.fa-star-half-o:before{content:"\f123"}.fa-location-arrow:before{content:"\f124"}.fa-crop:before{content:"\f125"}.fa-code-fork:before{content:"\f126"}.fa-chain-broken:before,.fa-unlink:before{content:"\f127"}.fa-question:before{content:"\f128"}.fa-info:before{content:"\f129"}.fa-exclamation:before{content:"\f12a"}.fa-superscript:before{content:"\f12b"}.fa-subscript:before{content:"\f12c"}.fa-eraser:before{content:"\f12d"}.fa-puzzle-piece:before{content:"\f12e"}.fa-microphone:before{content:"\f130"}.fa-microphone-slash:before{content:"\f131"}.fa-shield:before{content:"\f132"}.fa-calendar-o:before{content:"\f133"}.fa-fire-extinguisher:before{content:"\f134"}.fa-rocket:before{content:"\f135"}.fa-maxcdn:before{content:"\f136"}.fa-chevron-circle-left:before{content:"\f137"}.fa-chevron-circle-right:before{content:"\f138"}.fa-chevron-circle-up:before{content:"\f139"}.fa-chevron-circle-down:before{content:"\f13a"}.fa-html5:before{content:"\f13b"}.fa-css3:before{content:"\f13c"}.fa-anchor:before{content:"\f13d"}.fa-unlock-alt:before{content:"\f13e"}.fa-bullseye:before{content:"\f140"}.fa-ellipsis-h:before{content:"\f141"}.fa-ellipsis-v:before{content:"\f142"}.fa-rss-square:before{content:"\f143"}.fa-play-circle:before{content:"\f144"}.fa-ticket:before{content:"\f145"}.fa-minus-square:before{content:"\f146"}.fa-minus-square-o:before{content:"\f147"}.fa-level-up:before{content:"\f148"}.fa-level-down:before{content:"\f149"}.fa-check-square:before{content:"\f14a"}.fa-pencil-square:before{content:"\f14b"}.fa-external-link-square:before{content:"\f14c"}.fa-share-square:before{content:"\f14d"}.fa-compass:before{content:"\f14e"}.fa-caret-square-o-down:before,.fa-toggle-down:before{content:"\f150"}.fa-caret-square-o-up:before,.fa-toggle-up:before{content:"\f151"}.fa-caret-square-o-right:before,.fa-toggle-right:before{content:"\f152"}.fa-eur:before,.fa-euro:before{content:"\f153"}.fa-gbp:before{content:"\f154"}.fa-dollar:before,.fa-usd:before{content:"\f155"}.fa-inr:before,.fa-rupee:before{content:"\f156"}.fa-cny:before,.fa-jpy:before,.fa-rmb:before,.fa-yen:before{content:"\f157"}.fa-rouble:before,.fa-rub:before,.fa-ruble:before{content:"\f158"}.fa-krw:before,.fa-won:before{content:"\f159"}.fa-bitcoin:before,.fa-btc:before{content:"\f15a"}.fa-file:before{content:"\f15b"}.fa-file-text:before{content:"\f15c"}.fa-sort-alpha-asc:before{content:"\f15d"}.fa-sort-alpha-desc:before{content:"\f15e"}.fa-sort-amount-asc:before{content:"\f160"}.fa-sort-amount-desc:before{content:"\f161"}.fa-sort-numeric-asc:before{content:"\f162"}.fa-sort-numeric-desc:before{content:"\f163"}.fa-thumbs-up:before{content:"\f164"}.fa-thumbs-down:before{content:"\f165"}.fa-youtube-square:before{content:"\f166"}.fa-youtube:before{content:"\f167"}.fa-xing:before{content:"\f168"}.fa-xing-square:before{content:"\f169"}.fa-youtube-play:before{content:"\f16a"}.fa-dropbox:before{content:"\f16b"}.fa-stack-overflow:before{content:"\f16c"}.fa-instagram:before{content:"\f16d"}.fa-flickr:before{content:"\f16e"}.fa-adn:before{content:"\f170"}.fa-bitbucket:before{content:"\f171"}.fa-bitbucket-square:before{content:"\f172"}.fa-tumblr:before{content:"\f173"}.fa-tumblr-square:before{content:"\f174"}.fa-long-arrow-down:before{content:"\f175"}.fa-long-arrow-up:before{content:"\f176"}.fa-long-arrow-left:before{content:"\f177"}.fa-long-arrow-right:before{content:"\f178"}.fa-apple:before{content:"\f179"}.fa-windows:before{content:"\f17a"}.fa-android:before{content:"\f17b"}.fa-linux:before{content:"\f17c"}.fa-dribbble:before{content:"\f17d"}.fa-skype:before{content:"\f17e"}.fa-foursquare:before{content:"\f180"}.fa-trello:before{content:"\f181"}.fa-female:before{content:"\f182"}.fa-male:before{content:"\f183"}.fa-gittip:before,.fa-gratipay:before{content:"\f184"}.fa-sun-o:before{content:"\f185"}.fa-moon-o:before{content:"\f186"}.fa-archive:before{content:"\f187"}.fa-bug:before{content:"\f188"}.fa-vk:before{content:"\f189"}.fa-weibo:before{content:"\f18a"}.fa-renren:before{content:"\f18b"}.fa-pagelines:before{content:"\f18c"}.fa-stack-exchange:before{content:"\f18d"}.fa-arrow-circle-o-right:before{content:"\f18e"}.fa-arrow-circle-o-left:before{content:"\f190"}.fa-caret-square-o-left:before,.fa-toggle-left:before{content:"\f191"}.fa-dot-circle-o:before{content:"\f192"}.fa-wheelchair:before{content:"\f193"}.fa-vimeo-square:before{content:"\f194"}.fa-try:before,.fa-turkish-lira:before{content:"\f195"}.fa-plus-square-o:before{content:"\f196"}.fa-space-shuttle:before{content:"\f197"}.fa-slack:before{content:"\f198"}.fa-envelope-square:before{content:"\f199"}.fa-wordpress:before{content:"\f19a"}.fa-openid:before{content:"\f19b"}.fa-bank:before,.fa-institution:before,.fa-university:before{content:"\f19c"}.fa-graduation-cap:before,.fa-mortar-board:before{content:"\f19d"}.fa-yahoo:before{content:"\f19e"}.fa-google:before{content:"\f1a0"}.fa-reddit:before{content:"\f1a1"}.fa-reddit-square:before{content:"\f1a2"}.fa-stumbleupon-circle:before{content:"\f1a3"}.fa-stumbleupon:before{content:"\f1a4"}.fa-delicious:before{content:"\f1a5"}.fa-digg:before{content:"\f1a6"}.fa-pied-piper:before{content:"\f1a7"}.fa-pied-piper-alt:before{content:"\f1a8"}.fa-drupal:before{content:"\f1a9"}.fa-joomla:before{content:"\f1aa"}.fa-language:before{content:"\f1ab"}.fa-fax:before{content:"\f1ac"}.fa-building:before{content:"\f1ad"}.fa-child:before{content:"\f1ae"}.fa-paw:before{content:"\f1b0"}.fa-spoon:before{content:"\f1b1"}.fa-cube:before{content:"\f1b2"}.fa-cubes:before{content:"\f1b3"}.fa-behance:before{content:"\f1b4"}.fa-behance-square:before{content:"\f1b5"}.fa-steam:before{content:"\f1b6"}.fa-steam-square:before{content:"\f1b7"}.fa-recycle:before{content:"\f1b8"}.fa-automobile:before,.fa-car:before{content:"\f1b9"}.fa-cab:before,.fa-taxi:before{content:"\f1ba"}.fa-tree:before{content:"\f1bb"}.fa-spotify:before{content:"\f1bc"}.fa-deviantart:before{content:"\f1bd"}.fa-soundcloud:before{content:"\f1be"}.fa-database:before{content:"\f1c0"}.fa-file-pdf-o:before{content:"\f1c1"}.fa-file-word-o:before{content:"\f1c2"}.fa-file-excel-o:before{content:"\f1c3"}.fa-file-powerpoint-o:before{content:"\f1c4"}.fa-file-image-o:before,.fa-file-photo-o:before,.fa-file-picture-o:before{content:"\f1c5"}.fa-file-archive-o:before,.fa-file-zip-o:before{content:"\f1c6"}.fa-file-audio-o:before,.fa-file-sound-o:before{content:"\f1c7"}.fa-file-movie-o:before,.fa-file-video-o:before{content:"\f1c8"}.fa-file-code-o:before{content:"\f1c9"}.fa-vine:before{content:"\f1ca"}.fa-codepen:before{content:"\f1cb"}.fa-jsfiddle:before{content:"\f1cc"}.fa-life-bouy:before,.fa-life-buoy:before,.fa-life-ring:before,.fa-life-saver:before,.fa-support:before{content:"\f1cd"}.fa-circle-o-notch:before{content:"\f1ce"}.fa-ra:before,.fa-rebel:before{content:"\f1d0"}.fa-empire:before,.fa-ge:before{content:"\f1d1"}.fa-git-square:before{content:"\f1d2"}.fa-git:before{content:"\f1d3"}.fa-hacker-news:before,.fa-y-combinator-square:before,.fa-yc-square:before{content:"\f1d4"}.fa-tencent-weibo:before{content:"\f1d5"}.fa-qq:before{content:"\f1d6"}.fa-wechat:before,.fa-weixin:before{content:"\f1d7"}.fa-paper-plane:before,.fa-send:before{content:"\f1d8"}.fa-paper-plane-o:before,.fa-send-o:before{content:"\f1d9"}.fa-history:before{content:"\f1da"}.fa-circle-thin:before{content:"\f1db"}.fa-header:before{content:"\f1dc"}.fa-paragraph:before{content:"\f1dd"}.fa-sliders:before{content:"\f1de"}.fa-share-alt:before{content:"\f1e0"}.fa-share-alt-square:before{content:"\f1e1"}.fa-bomb:before{content:"\f1e2"}.fa-futbol-o:before,.fa-soccer-ball-o:before{content:"\f1e3"}.fa-tty:before{content:"\f1e4"}.fa-binoculars:before{content:"\f1e5"}.fa-plug:before{content:"\f1e6"}.fa-slideshare:before{content:"\f1e7"}.fa-twitch:before{content:"\f1e8"}.fa-yelp:before{content:"\f1e9"}.fa-newspaper-o:before{content:"\f1ea"}.fa-wifi:before{content:"\f1eb"}.fa-calculator:before{content:"\f1ec"}.fa-paypal:before{content:"\f1ed"}.fa-google-wallet:before{content:"\f1ee"}.fa-cc-visa:before{content:"\f1f0"}.fa-cc-mastercard:before{content:"\f1f1"}.fa-cc-discover:before{content:"\f1f2"}.fa-cc-amex:before{content:"\f1f3"}.fa-cc-paypal:before{content:"\f1f4"}.fa-cc-stripe:before{content:"\f1f5"}.fa-bell-slash:before{content:"\f1f6"}.fa-bell-slash-o:before{content:"\f1f7"}.fa-trash:before{content:"\f1f8"}.fa-copyright:before{content:"\f1f9"}.fa-at:before{content:"\f1fa"}.fa-eyedropper:before{content:"\f1fb"}.fa-paint-brush:before{content:"\f1fc"}.fa-birthday-cake:before{content:"\f1fd"}.fa-area-chart:before{content:"\f1fe"}.fa-pie-chart:before{content:"\f200"}.fa-line-chart:before{content:"\f201"}.fa-lastfm:before{content:"\f202"}.fa-lastfm-square:before{content:"\f203"}.fa-toggle-off:before{content:"\f204"}.fa-toggle-on:before{content:"\f205"}.fa-bicycle:before{content:"\f206"}.fa-bus:before{content:"\f207"}.fa-ioxhost:before{content:"\f208"}.fa-angellist:before{content:"\f209"}.fa-cc:before{content:"\f20a"}.fa-ils:before,.fa-shekel:before,.fa-sheqel:before{content:"\f20b"}.fa-meanpath:before{content:"\f20c"}.fa-buysellads:before{content:"\f20d"}.fa-connectdevelop:before{content:"\f20e"}.fa-dashcube:before{content:"\f210"}.fa-forumbee:before{content:"\f211"}.fa-leanpub:before{content:"\f212"}.fa-sellsy:before{content:"\f213"}.fa-shirtsinbulk:before{content:"\f214"}.fa-simplybuilt:before{content:"\f215"}.fa-skyatlas:before{content:"\f216"}.fa-cart-plus:before{content:"\f217"}.fa-cart-arrow-down:before{content:"\f218"}.fa-diamond:before{content:"\f219"}.fa-ship:before{content:"\f21a"}.fa-user-secret:before{content:"\f21b"}.fa-motorcycle:before{content:"\f21c"}.fa-street-view:before{content:"\f21d"}.fa-heartbeat:before{content:"\f21e"}.fa-venus:before{content:"\f221"}.fa-mars:before{content:"\f222"}.fa-mercury:before{content:"\f223"}.fa-intersex:before,.fa-transgender:before{content:"\f224"}.fa-transgender-alt:before{content:"\f225"}.fa-venus-double:before{content:"\f226"}.fa-mars-double:before{content:"\f227"}.fa-venus-mars:before{content:"\f228"}.fa-mars-stroke:before{content:"\f229"}.fa-mars-stroke-v:before{content:"\f22a"}.fa-mars-stroke-h:before{content:"\f22b"}.fa-neuter:before{content:"\f22c"}.fa-genderless:before{content:"\f22d"}.fa-facebook-official:before{content:"\f230"}.fa-pinterest-p:before{content:"\f231"}.fa-whatsapp:before{content:"\f232"}.fa-server:before{content:"\f233"}.fa-user-plus:before{content:"\f234"}.fa-user-times:before{content:"\f235"}.fa-bed:before,.fa-hotel:before{content:"\f236"}.fa-viacoin:before{content:"\f237"}.fa-train:before{content:"\f238"}.fa-subway:before{content:"\f239"}.fa-medium:before{content:"\f23a"}.fa-y-combinator:before,.fa-yc:before{content:"\f23b"}.fa-optin-monster:before{content:"\f23c"}.fa-opencart:before{content:"\f23d"}.fa-expeditedssl:before{content:"\f23e"}.fa-battery-4:before,.fa-battery-full:before{content:"\f240"}.fa-battery-3:before,.fa-battery-three-quarters:before{content:"\f241"}.fa-battery-2:before,.fa-battery-half:before{content:"\f242"}.fa-battery-1:before,.fa-battery-quarter:before{content:"\f243"}.fa-battery-0:before,.fa-battery-empty:before{content:"\f244"}.fa-mouse-pointer:before{content:"\f245"}.fa-i-cursor:before{content:"\f246"}.fa-object-group:before{content:"\f247"}.fa-object-ungroup:before{content:"\f248"}.fa-sticky-note:before{content:"\f249"}.fa-sticky-note-o:before{content:"\f24a"}.fa-cc-jcb:before{content:"\f24b"}.fa-cc-diners-club:before{content:"\f24c"}.fa-clone:before{content:"\f24d"}.fa-balance-scale:before{content:"\f24e"}.fa-hourglass-o:before{content:"\f250"}.fa-hourglass-1:before,.fa-hourglass-start:before{content:"\f251"}.fa-hourglass-2:before,.fa-hourglass-half:before{content:"\f252"}.fa-hourglass-3:before,.fa-hourglass-end:before{content:"\f253"}.fa-hourglass:before{content:"\f254"}.fa-hand-grab-o:before,.fa-hand-rock-o:before{content:"\f255"}.fa-hand-paper-o:before,.fa-hand-stop-o:before{content:"\f256"}.fa-hand-scissors-o:before{content:"\f257"}.fa-hand-lizard-o:before{content:"\f258"}.fa-hand-spock-o:before{content:"\f259"}.fa-hand-pointer-o:before{content:"\f25a"}.fa-hand-peace-o:before{content:"\f25b"}.fa-trademark:before{content:"\f25c"}.fa-registered:before{content:"\f25d"}.fa-creative-commons:before{content:"\f25e"}.fa-gg:before{content:"\f260"}.fa-gg-circle:before{content:"\f261"}.fa-tripadvisor:before{content:"\f262"}.fa-odnoklassniki:before{content:"\f263"}.fa-odnoklassniki-square:before{content:"\f264"}.fa-get-pocket:before{content:"\f265"}.fa-wikipedia-w:before{content:"\f266"}.fa-safari:before{content:"\f267"}.fa-chrome:before{content:"\f268"}.fa-firefox:before{content:"\f269"}.fa-opera:before{content:"\f26a"}.fa-internet-explorer:before{content:"\f26b"}.fa-television:before,.fa-tv:before{content:"\f26c"}.fa-contao:before{content:"\f26d"}.fa-500px:before{content:"\f26e"}.fa-amazon:before{content:"\f270"}.fa-calendar-plus-o:before{content:"\f271"}.fa-calendar-minus-o:before{content:"\f272"}.fa-calendar-times-o:before{content:"\f273"}.fa-calendar-check-o:before{content:"\f274"}.fa-industry:before{content:"\f275"}.fa-map-pin:before{content:"\f276"}.fa-map-signs:before{content:"\f277"}.fa-map-o:before{content:"\f278"}.fa-map:before{content:"\f279"}.fa-commenting:before{content:"\f27a"}.fa-commenting-o:before{content:"\f27b"}.fa-houzz:before{content:"\f27c"}.fa-vimeo:before{content:"\f27d"}.fa-black-tie:before{content:"\f27e"}.fa-fonticons:before{content:"\f280"}.fa-reddit-alien:before{content:"\f281"}.fa-edge:before{content:"\f282"}.fa-credit-card-alt:before{content:"\f283"}.fa-codiepie:before{content:"\f284"}.fa-modx:before{content:"\f285"}.fa-fort-awesome:before{content:"\f286"}.fa-usb:before{content:"\f287"}.fa-product-hunt:before{content:"\f288"}.fa-mixcloud:before{content:"\f289"}.fa-scribd:before{content:"\f28a"}.fa-pause-circle:before{content:"\f28b"}.fa-pause-circle-o:before{content:"\f28c"}.fa-stop-circle:before{content:"\f28d"}.fa-stop-circle-o:before{content:"\f28e"}.fa-shopping-bag:before{content:"\f290"}.fa-shopping-basket:before{content:"\f291"}.fa-hashtag:before{content:"\f292"}.fa-bluetooth:before{content:"\f293"}.fa-bluetooth-b:before{content:"\f294"}.fa-percent:before{content:"\f295"}.fa-gitlab:before{content:"\f296"}.fa-wpbeginner:before{content:"\f297"}.fa-wpforms:before{content:"\f298"}.fa-envira:before{content:"\f299"}.fa-universal-access:before{content:"\f29a"}.fa-wheelchair-alt:before{content:"\f29b"}.fa-question-circle-o:before{content:"\f29c"}.fa-blind:before{content:"\f29d"}.fa-audio-description:before{content:"\f29e"}.fa-volume-control-phone:before{content:"\f2a0"}.fa-braille:before{content:"\f2a1"}.fa-assistive-listening-systems:before{content:"\f2a2"}.fa-american-sign-language-interpreting:before,.fa-asl-interpreting:before{content:"\f2a3"}.fa-deaf:before,.fa-deafness:before,.fa-hard-of-hearing:before{content:"\f2a4"}.fa-glide:before{content:"\f2a5"}.fa-glide-g:before{content:"\f2a6"}.fa-sign-language:before,.fa-signing:before{content:"\f2a7"}.fa-low-vision:before{content:"\f2a8"}.fa-viadeo:before{content:"\f2a9"}.fa-viadeo-square:before{content:"\f2aa"}.fa-snapchat:before{content:"\f2ab"}.fa-snapchat-ghost:before{content:"\f2ac"}.fa-snapchat-square:before{content:"\f2ad"}.sr-only{position:absolute;width:1px;height:1px;padding:0;margin:-1px;overflow:hidden;clip:rect(0,0,0,0);border:0}.sr-only-focusable:active,.sr-only-focusable:focus{position:static;width:auto;height:auto;margin:0;overflow:visible;clip:auto}
-
-/*! jQuery UI - v1.10.4 - 2014-02-18
-* http://jqueryui.com
-* Includes: jquery.ui.core.css, jquery.ui.resizable.css, jquery.ui.selectable.css, jquery.ui.accordion.css, jquery.ui.autocomplete.css, jquery.ui.button.css, jquery.ui.datepicker.css, jquery.ui.dialog.css, jquery.ui.menu.css, jquery.ui.progressbar.css, jquery.ui.slider.css, jquery.ui.spinner.css, jquery.ui.tabs.css, jquery.ui.tooltip.css, jquery.ui.theme.css
-* Copyright 2014 jQuery Foundation and other contributors; Licensed MIT */.ui-helper-hidden{display:none}.ui-helper-hidden-accessible{border:0;clip:rect(0 0 0 0);height:1px;margin:-1px;overflow:hidden;padding:0;position:absolute;width:1px}.ui-helper-reset{margin:0;padding:0;border:0;outline:0;line-height:1.3;text-decoration:none;font-size:100%;list-style:none}.ui-helper-clearfix:after,.ui-helper-clearfix:before{content:"";display:table;border-collapse:collapse}.ui-helper-clearfix:after{clear:both}.ui-helper-clearfix{min-height:0}.ui-helper-zfix{width:100%;height:100%;top:0;left:0;position:absolute;opacity:0;filter:Alpha(Opacity=0)}.ui-front{z-index:3}.ui-state-disabled{cursor:default!important}.ui-icon{display:block;text-indent:-99999px;overflow:hidden;background-repeat:no-repeat}.ui-widget-overlay{position:fixed;top:0;left:0;width:100%;height:100%}.ui-resizable{position:relative}.ui-resizable-handle{position:absolute;font-size:.1px;display:block}.ui-resizable-autohide .ui-resizable-handle,.ui-resizable-disabled .ui-resizable-handle{display:none}.ui-resizable-n{cursor:n-resize;height:7px;width:100%;top:-5px;left:0}.ui-resizable-s{cursor:s-resize;height:7px;width:100%;bottom:-5px;left:0}.ui-resizable-e{cursor:e-resize;width:7px;right:-5px;top:0;height:100%}.ui-resizable-w{cursor:w-resize;width:7px;left:-5px;top:0;height:100%}.ui-resizable-se{cursor:se-resize;width:12px;height:12px;right:1px;bottom:1px}.ui-resizable-sw{cursor:sw-resize;width:9px;height:9px;left:-5px;bottom:-5px}.ui-resizable-nw{cursor:nw-resize;width:9px;height:9px;left:-5px;top:-5px}.ui-resizable-ne{cursor:ne-resize;width:9px;height:9px;right:-5px;top:-5px}.ui-selectable-helper{position:absolute;z-index:3;border:1px dotted #000}.ui-accordion .ui-accordion-header{display:block;cursor:pointer;position:relative;margin-top:2px;padding:.5em .5em .5em .7em;min-height:0}.ui-accordion .ui-accordion-icons{padding-left:2.2em}.ui-accordion .ui-accordion-noicons{padding-left:.7em}.ui-accordion .ui-accordion-icons .ui-accordion-icons{padding-left:2.2em}.ui-accordion .ui-accordion-header .ui-accordion-header-icon{position:absolute;left:.5em;top:50%;margin-top:-8px}.ui-accordion .ui-accordion-content{padding:1em 2.2em;border-top:0;overflow:auto}.ui-autocomplete{position:absolute;top:0;left:0;cursor:default}.ui-button{display:inline-block;position:relative;padding:0;line-height:normal;margin-right:.1em;cursor:pointer;vertical-align:middle;text-align:center;overflow:visible}.ui-button,.ui-button:active,.ui-button:hover,.ui-button:link,.ui-button:visited{text-decoration:none}.ui-button-icon-only{width:2.2em}button.ui-button-icon-only{width:2.4em}.ui-button-icons-only{width:3.4em}button.ui-button-icons-only{width:3.7em}.ui-button .ui-button-text{display:block;line-height:normal}.ui-button-text-only .ui-button-text{padding:.4em 1em}.ui-button-icon-only .ui-button-text,.ui-button-icons-only .ui-button-text{padding:.4em;text-indent:-9999999px}.ui-button-text-icon-primary .ui-button-text,.ui-button-text-icons .ui-button-text{padding:.4em 1em .4em 2.1em}.ui-button-text-icon-secondary .ui-button-text,.ui-button-text-icons .ui-button-text{padding:.4em 2.1em .4em 1em}.ui-button-text-icons .ui-button-text{padding-left:2.1em;padding-right:2.1em}input.ui-button{padding:.4em 1em}.ui-button-icon-only .ui-icon,.ui-button-icons-only .ui-icon,.ui-button-text-icon-primary .ui-icon,.ui-button-text-icon-secondary .ui-icon,.ui-button-text-icons .ui-icon{position:absolute;top:50%;margin-top:-8px}.ui-button-icon-only .ui-icon{left:50%;margin-left:-8px}.ui-button-icons-only .ui-button-icon-primary,.ui-button-text-icon-primary .ui-button-icon-primary,.ui-button-text-icons .ui-button-icon-primary{left:.5em}.ui-button-icons-only .ui-button-icon-secondary,.ui-button-text-icon-secondary .ui-button-icon-secondary,.ui-button-text-icons .ui-button-icon-secondary{right:.5em}.ui-buttonset{margin-right:7px}.ui-buttonset .ui-button{margin-left:0;margin-right:-.3em}button.ui-button::-moz-focus-inner,input.ui-button::-moz-focus-inner{border:0;padding:0}.ui-datepicker{width:17em;padding:.2em .2em 0;display:none}.ui-datepicker .ui-datepicker-header{position:relative;padding:.2em 0}.ui-datepicker .ui-datepicker-next,.ui-datepicker .ui-datepicker-prev{position:absolute;top:2px;width:1.8em;height:1.8em}.ui-datepicker .ui-datepicker-next-hover,.ui-datepicker .ui-datepicker-prev-hover{top:1px}.ui-datepicker .ui-datepicker-prev{left:2px}.ui-datepicker .ui-datepicker-next{right:2px}.ui-datepicker .ui-datepicker-prev-hover{left:1px}.ui-datepicker .ui-datepicker-next-hover{right:1px}.ui-datepicker .ui-datepicker-next span,.ui-datepicker .ui-datepicker-prev span{display:block;position:absolute;left:50%;margin-left:-8px;top:50%;margin-top:-8px}.ui-datepicker .ui-datepicker-title{margin:0 2.3em;line-height:1.8em;text-align:center}.ui-datepicker .ui-datepicker-title select{font-size:1em;margin:1px 0}.ui-datepicker select.ui-datepicker-month,.ui-datepicker select.ui-datepicker-year{width:49%}.ui-datepicker table{width:100%;font-size:.9em;border-collapse:collapse;margin:0 0 .4em}.ui-datepicker th{padding:.7em .3em;text-align:center;font-weight:700;border:0}.ui-datepicker td{border:0;padding:1px}.ui-datepicker td a,.ui-datepicker td span{display:block;padding:.2em;text-align:right;text-decoration:none}.ui-datepicker .ui-datepicker-buttonpane{background-image:none;margin:.7em 0 0;padding:0 .2em;border-left:0;border-right:0;border-bottom:0}.ui-datepicker .ui-datepicker-buttonpane button{float:right;margin:.5em .2em .4em;cursor:pointer;padding:.2em .6em .3em;width:auto;overflow:visible}.ui-datepicker .ui-datepicker-buttonpane button.ui-datepicker-current{float:left}.ui-datepicker.ui-datepicker-multi{width:auto}.ui-datepicker-multi .ui-datepicker-group{float:left}.ui-datepicker-multi .ui-datepicker-group table{width:95%;margin:0 auto .4em}.ui-datepicker-multi-2 .ui-datepicker-group{width:50%}.ui-datepicker-multi-3 .ui-datepicker-group{width:33.3%}.ui-datepicker-multi-4 .ui-datepicker-group{width:25%}.ui-datepicker-multi .ui-datepicker-group-last .ui-datepicker-header,.ui-datepicker-multi .ui-datepicker-group-middle .ui-datepicker-header{border-left-width:0}.ui-datepicker-multi .ui-datepicker-buttonpane{clear:left}.ui-datepicker-row-break{clear:both;width:100%;font-size:0}.ui-datepicker-rtl{direction:rtl}.ui-datepicker-rtl .ui-datepicker-prev{right:2px;left:auto}.ui-datepicker-rtl .ui-datepicker-next{left:2px;right:auto}.ui-datepicker-rtl .ui-datepicker-prev:hover{right:1px;left:auto}.ui-datepicker-rtl .ui-datepicker-next:hover{left:1px;right:auto}.ui-datepicker-rtl .ui-datepicker-buttonpane{clear:right}.ui-datepicker-rtl .ui-datepicker-buttonpane button{float:left}.ui-datepicker-rtl .ui-datepicker-buttonpane button.ui-datepicker-current,.ui-datepicker-rtl .ui-datepicker-group{float:right}.ui-datepicker-rtl .ui-datepicker-group-last .ui-datepicker-header,.ui-datepicker-rtl .ui-datepicker-group-middle .ui-datepicker-header{border-right-width:0;border-left-width:1px}.ui-dialog{overflow:hidden;position:absolute;top:0;left:0;padding:.2em;outline:0}.ui-dialog .ui-dialog-titlebar{padding:.4em 1em;position:relative}.ui-dialog .ui-dialog-title{float:left;margin:.1em 0;white-space:nowrap;width:90%;overflow:hidden;text-overflow:ellipsis}.ui-dialog .ui-dialog-titlebar-close{position:absolute;right:.3em;top:50%;width:20px;margin:-10px 0 0;padding:1px;height:20px}.ui-dialog .ui-dialog-content{position:relative;border:0;padding:.5em 1em;background:none;overflow:auto}.ui-dialog .ui-dialog-buttonpane{text-align:left;border-width:1px 0 0;background-image:none;margin-top:.5em;padding:.3em 1em .5em .4em}.ui-dialog .ui-dialog-buttonpane .ui-dialog-buttonset{float:right}.ui-dialog .ui-dialog-buttonpane button{margin:.5em .4em .5em 0;cursor:pointer}.ui-dialog .ui-resizable-se{width:12px;height:12px;right:-5px;bottom:-5px;background-position:16px 16px}.ui-draggable .ui-dialog-titlebar{cursor:move}.ui-menu{list-style:none;padding:2px;margin:0;display:block;outline:none}.ui-menu .ui-menu{margin-top:-3px;position:absolute}.ui-menu .ui-menu-item{margin:0;padding:0;width:100%;list-style-image:url(data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7)}.ui-menu .ui-menu-divider{margin:5px -2px;height:0;font-size:0;line-height:0;border-width:1px 0 0}.ui-menu .ui-menu-item a{text-decoration:none;display:block;padding:2px .4em;line-height:1.5;min-height:0;font-weight:400}.ui-menu .ui-menu-item a.ui-state-active,.ui-menu .ui-menu-item a.ui-state-focus{font-weight:400;margin:-1px}.ui-menu .ui-state-disabled{font-weight:400;margin:.4em 0 .2em;line-height:1.5}.ui-menu .ui-state-disabled a{cursor:default}.ui-menu-icons{position:relative}.ui-menu-icons .ui-menu-item a{position:relative;padding-left:2em}.ui-menu .ui-icon{position:absolute;top:.2em;left:.2em}.ui-menu .ui-menu-icon{position:static;float:right}.ui-progressbar{height:2em;text-align:left;overflow:hidden}.ui-progressbar .ui-progressbar-value{margin:-1px;height:100%}.ui-progressbar .ui-progressbar-overlay{background:url(images/animated-overlay.gif);height:100%;filter:alpha(opacity=25);opacity:.25}.ui-progressbar-indeterminate .ui-progressbar-value{background-image:none}.ui-slider{position:relative;text-align:left}.ui-slider .ui-slider-handle{position:absolute;z-index:2;width:1.2em;height:1.2em;cursor:default}.ui-slider .ui-slider-range{position:absolute;z-index:1;font-size:.7em;display:block;border:0;background-position:0 0}.ui-slider.ui-state-disabled .ui-slider-handle,.ui-slider.ui-state-disabled .ui-slider-range{filter:inherit}.ui-slider-horizontal{height:.8em}.ui-slider-horizontal .ui-slider-handle{top:-.3em;margin-left:-.6em}.ui-slider-horizontal .ui-slider-range{top:0;height:100%}.ui-slider-horizontal .ui-slider-range-min{left:0}.ui-slider-horizontal .ui-slider-range-max{right:0}.ui-slider-vertical{width:.8em;height:100px}.ui-slider-vertical .ui-slider-handle{left:-.3em;margin-left:0;margin-bottom:-.6em}.ui-slider-vertical .ui-slider-range{left:0;width:100%}.ui-slider-vertical .ui-slider-range-min{bottom:0}.ui-slider-vertical .ui-slider-range-max{top:0}.ui-spinner{position:relative;display:inline-block;overflow:hidden}.ui-spinner,.ui-spinner-input{padding:0;vertical-align:middle}.ui-spinner-input{border:none;background:none;color:inherit;margin:.2em 0;margin-left:.4em;margin-right:22px}.ui-spinner-button{width:16px;height:50%;font-size:.5em;padding:0;margin:0;text-align:center;position:absolute;cursor:default;display:block;overflow:hidden;right:0}.ui-spinner a.ui-spinner-button{border-top:none;border-bottom:none;border-right:none}.ui-spinner .ui-icon{position:absolute;margin-top:-8px;top:50%;left:0}.ui-spinner-up{top:0}.ui-spinner-down{bottom:0}.ui-spinner .ui-icon-triangle-1-s{background-position:-65px -16px}.ui-tabs{position:relative;padding:.2em}.ui-tabs .ui-tabs-nav{margin:0;padding:.2em .2em 0}.ui-tabs .ui-tabs-nav li{list-style:none;float:left;position:relative;top:0;margin:1px .2em 0 0;border-bottom-width:0;padding:0;white-space:nowrap}.ui-tabs .ui-tabs-nav .ui-tabs-anchor{float:left;padding:.5em 1em;text-decoration:none}.ui-tabs .ui-tabs-nav li.ui-tabs-active{margin-bottom:-1px;padding-bottom:1px}.ui-tabs .ui-tabs-nav li.ui-state-disabled .ui-tabs-anchor,.ui-tabs .ui-tabs-nav li.ui-tabs-active .ui-tabs-anchor,.ui-tabs .ui-tabs-nav li.ui-tabs-loading .ui-tabs-anchor{cursor:text}.ui-tabs-collapsible .ui-tabs-nav li.ui-tabs-active .ui-tabs-anchor{cursor:pointer}.ui-tabs .ui-tabs-panel{display:block;border-width:0;padding:1em 1.4em;background:none}.ui-tooltip{padding:8px;position:absolute;z-index:6;max-width:300px;box-shadow:0 0 5px #aaa}body .ui-tooltip{border-width:2px}.ui-widget{font-family:Verdana,Arial,sans-serif;font-size:1.1em}.ui-widget .ui-widget{font-size:1em}.ui-widget button,.ui-widget input,.ui-widget select,.ui-widget textarea{font-family:Verdana,Arial,sans-serif;font-size:1em}.ui-widget-content{border:1px solid #aaa;background:#fff url(images/ui-bg_flat_75_ffffff_40x100.png) 50% 50% repeat-x;color:#222}.ui-widget-content a{color:#222}.ui-widget-header{border:1px solid #aaa;background:#ccc url(images/ui-bg_highlight-soft_75_cccccc_1x100.png) 50% 50% repeat-x;color:#222;font-weight:700}.ui-widget-header a{color:#222}.ui-state-default,.ui-widget-content .ui-state-default,.ui-widget-header .ui-state-default{border:1px solid #d3d3d3;background:#e6e6e6 url(images/ui-bg_glass_75_e6e6e6_1x400.png) 50% 50% repeat-x;font-weight:400;color:#555}.ui-state-default a,.ui-state-default a:link,.ui-state-default a:visited{color:#555;text-decoration:none}.ui-state-focus,.ui-state-hover,.ui-widget-content .ui-state-focus,.ui-widget-content .ui-state-hover,.ui-widget-header .ui-state-focus,.ui-widget-header .ui-state-hover{border:1px solid #999;background:#dadada url(images/ui-bg_glass_75_dadada_1x400.png) 50% 50% repeat-x;font-weight:400;color:#212121}.ui-state-focus a,.ui-state-focus a:hover,.ui-state-focus a:link,.ui-state-focus a:visited,.ui-state-hover a,.ui-state-hover a:hover,.ui-state-hover a:link,.ui-state-hover a:visited{color:#212121;text-decoration:none}.ui-state-active,.ui-widget-content .ui-state-active,.ui-widget-header .ui-state-active{border:1px solid #aaa;background:#fff url(images/ui-bg_glass_65_ffffff_1x400.png) 50% 50% repeat-x;font-weight:400;color:#212121}.ui-state-active a,.ui-state-active a:link,.ui-state-active a:visited{color:#212121;text-decoration:none}.ui-state-highlight,.ui-widget-content .ui-state-highlight,.ui-widget-header .ui-state-highlight{border:1px solid #fcefa1;background:#fbf9ee url(images/ui-bg_glass_55_fbf9ee_1x400.png) 50% 50% repeat-x;color:#363636}.ui-state-highlight a,.ui-widget-content .ui-state-highlight a,.ui-widget-header .ui-state-highlight a{color:#363636}.ui-state-error,.ui-widget-content .ui-state-error,.ui-widget-header .ui-state-error{border:1px solid #cd0a0a;background:#fef1ec url(images/ui-bg_glass_95_fef1ec_1x400.png) 50% 50% repeat-x;color:#cd0a0a}.ui-state-error-text,.ui-state-error a,.ui-widget-content .ui-state-error-text,.ui-widget-content .ui-state-error a,.ui-widget-header .ui-state-error-text,.ui-widget-header .ui-state-error a{color:#cd0a0a}.ui-priority-primary,.ui-widget-content .ui-priority-primary,.ui-widget-header .ui-priority-primary{font-weight:700}.ui-priority-secondary,.ui-widget-content .ui-priority-secondary,.ui-widget-header .ui-priority-secondary{opacity:.7;filter:Alpha(Opacity=70);font-weight:400}.ui-state-disabled,.ui-widget-content .ui-state-disabled,.ui-widget-header .ui-state-disabled{opacity:.35;filter:Alpha(Opacity=35);background-image:none}.ui-state-disabled .ui-icon{filter:Alpha(Opacity=35)}.ui-icon{width:16px;height:16px}.ui-icon,.ui-widget-content .ui-icon,.ui-widget-header .ui-icon{background-image:url(images/ui-icons_222222_256x240.png)}.ui-state-default .ui-icon{background-image:url(images/ui-icons_888888_256x240.png)}.ui-state-active .ui-icon,.ui-state-focus .ui-icon,.ui-state-hover .ui-icon{background-image:url(images/ui-icons_454545_256x240.png)}.ui-state-highlight .ui-icon{background-image:url(images/ui-icons_2e83ff_256x240.png)}.ui-state-error-text .ui-icon,.ui-state-error .ui-icon{background-image:url(images/ui-icons_cd0a0a_256x240.png)}.ui-icon-blank{background-position:16px 16px}.ui-icon-carat-1-n{background-position:0 0}.ui-icon-carat-1-ne{background-position:-16px 0}.ui-icon-carat-1-e{background-position:-32px 0}.ui-icon-carat-1-se{background-position:-48px 0}.ui-icon-carat-1-s{background-position:-64px 0}.ui-icon-carat-1-sw{background-position:-80px 0}.ui-icon-carat-1-w{background-position:-96px 0}.ui-icon-carat-1-nw{background-position:-112px 0}.ui-icon-carat-2-n-s{background-position:-128px 0}.ui-icon-carat-2-e-w{background-position:-144px 0}.ui-icon-triangle-1-n{background-position:0 -16px}.ui-icon-triangle-1-ne{background-position:-16px -16px}.ui-icon-triangle-1-e{background-position:-32px -16px}.ui-icon-triangle-1-se{background-position:-48px -16px}.ui-icon-triangle-1-s{background-position:-64px -16px}.ui-icon-triangle-1-sw{background-position:-80px -16px}.ui-icon-triangle-1-w{background-position:-96px -16px}.ui-icon-triangle-1-nw{background-position:-112px -16px}.ui-icon-triangle-2-n-s{background-position:-128px -16px}.ui-icon-triangle-2-e-w{background-position:-144px -16px}.ui-icon-arrow-1-n{background-position:0 -32px}.ui-icon-arrow-1-ne{background-position:-16px -32px}.ui-icon-arrow-1-e{background-position:-32px -32px}.ui-icon-arrow-1-se{background-position:-48px -32px}.ui-icon-arrow-1-s{background-position:-64px -32px}.ui-icon-arrow-1-sw{background-position:-80px -32px}.ui-icon-arrow-1-w{background-position:-96px -32px}.ui-icon-arrow-1-nw{background-position:-112px -32px}.ui-icon-arrow-2-n-s{background-position:-128px -32px}.ui-icon-arrow-2-ne-sw{background-position:-144px -32px}.ui-icon-arrow-2-e-w{background-position:-160px -32px}.ui-icon-arrow-2-se-nw{background-position:-176px -32px}.ui-icon-arrowstop-1-n{background-position:-192px -32px}.ui-icon-arrowstop-1-e{background-position:-208px -32px}.ui-icon-arrowstop-1-s{background-position:-224px -32px}.ui-icon-arrowstop-1-w{background-position:-240px -32px}.ui-icon-arrowthick-1-n{background-position:0 -48px}.ui-icon-arrowthick-1-ne{background-position:-16px -48px}.ui-icon-arrowthick-1-e{background-position:-32px -48px}.ui-icon-arrowthick-1-se{background-position:-48px -48px}.ui-icon-arrowthick-1-s{background-position:-64px -48px}.ui-icon-arrowthick-1-sw{background-position:-80px -48px}.ui-icon-arrowthick-1-w{background-position:-96px -48px}.ui-icon-arrowthick-1-nw{background-position:-112px -48px}.ui-icon-arrowthick-2-n-s{background-position:-128px -48px}.ui-icon-arrowthick-2-ne-sw{background-position:-144px -48px}.ui-icon-arrowthick-2-e-w{background-position:-160px -48px}.ui-icon-arrowthick-2-se-nw{background-position:-176px -48px}.ui-icon-arrowthickstop-1-n{background-position:-192px -48px}.ui-icon-arrowthickstop-1-e{background-position:-208px -48px}.ui-icon-arrowthickstop-1-s{background-position:-224px -48px}.ui-icon-arrowthickstop-1-w{background-position:-240px -48px}.ui-icon-arrowreturnthick-1-w{background-position:0 -64px}.ui-icon-arrowreturnthick-1-n{background-position:-16px -64px}.ui-icon-arrowreturnthick-1-e{background-position:-32px -64px}.ui-icon-arrowreturnthick-1-s{background-position:-48px -64px}.ui-icon-arrowreturn-1-w{background-position:-64px -64px}.ui-icon-arrowreturn-1-n{background-position:-80px -64px}.ui-icon-arrowreturn-1-e{background-position:-96px -64px}.ui-icon-arrowreturn-1-s{background-position:-112px -64px}.ui-icon-arrowrefresh-1-w{background-position:-128px -64px}.ui-icon-arrowrefresh-1-n{background-position:-144px -64px}.ui-icon-arrowrefresh-1-e{background-position:-160px -64px}.ui-icon-arrowrefresh-1-s{background-position:-176px -64px}.ui-icon-arrow-4{background-position:0 -80px}.ui-icon-arrow-4-diag{background-position:-16px -80px}.ui-icon-extlink{background-position:-32px -80px}.ui-icon-newwin{background-position:-48px -80px}.ui-icon-refresh{background-position:-64px -80px}.ui-icon-shuffle{background-position:-80px -80px}.ui-icon-transfer-e-w{background-position:-96px -80px}.ui-icon-transferthick-e-w{background-position:-112px -80px}.ui-icon-folder-collapsed{background-position:0 -96px}.ui-icon-folder-open{background-position:-16px -96px}.ui-icon-document{background-position:-32px -96px}.ui-icon-document-b{background-position:-48px -96px}.ui-icon-note{background-position:-64px -96px}.ui-icon-mail-closed{background-position:-80px -96px}.ui-icon-mail-open{background-position:-96px -96px}.ui-icon-suitcase{background-position:-112px -96px}.ui-icon-comment{background-position:-128px -96px}.ui-icon-person{background-position:-144px -96px}.ui-icon-print{background-position:-160px -96px}.ui-icon-trash{background-position:-176px -96px}.ui-icon-locked{background-position:-192px -96px}.ui-icon-unlocked{background-position:-208px -96px}.ui-icon-bookmark{background-position:-224px -96px}.ui-icon-tag{background-position:-240px -96px}.ui-icon-home{background-position:0 -112px}.ui-icon-flag{background-position:-16px -112px}.ui-icon-calendar{background-position:-32px -112px}.ui-icon-cart{background-position:-48px -112px}.ui-icon-pencil{background-position:-64px -112px}.ui-icon-clock{background-position:-80px -112px}.ui-icon-disk{background-position:-96px -112px}.ui-icon-calculator{background-position:-112px -112px}.ui-icon-zoomin{background-position:-128px -112px}.ui-icon-zoomout{background-position:-144px -112px}.ui-icon-search{background-position:-160px -112px}.ui-icon-wrench{background-position:-176px -112px}.ui-icon-gear{background-position:-192px -112px}.ui-icon-heart{background-position:-208px -112px}.ui-icon-star{background-position:-224px -112px}.ui-icon-link{background-position:-240px -112px}.ui-icon-cancel{background-position:0 -128px}.ui-icon-plus{background-position:-16px -128px}.ui-icon-plusthick{background-position:-32px -128px}.ui-icon-minus{background-position:-48px -128px}.ui-icon-minusthick{background-position:-64px -128px}.ui-icon-close{background-position:-80px -128px}.ui-icon-closethick{background-position:-96px -128px}.ui-icon-key{background-position:-112px -128px}.ui-icon-lightbulb{background-position:-128px -128px}.ui-icon-scissors{background-position:-144px -128px}.ui-icon-clipboard{background-position:-160px -128px}.ui-icon-copy{background-position:-176px -128px}.ui-icon-contact{background-position:-192px -128px}.ui-icon-image{background-position:-208px -128px}.ui-icon-video{background-position:-224px -128px}.ui-icon-script{background-position:-240px -128px}.ui-icon-alert{background-position:0 -144px}.ui-icon-info{background-position:-16px -144px}.ui-icon-notice{background-position:-32px -144px}.ui-icon-help{background-position:-48px -144px}.ui-icon-check{background-position:-64px -144px}.ui-icon-bullet{background-position:-80px -144px}.ui-icon-radio-on{background-position:-96px -144px}.ui-icon-radio-off{background-position:-112px -144px}.ui-icon-pin-w{background-position:-128px -144px}.ui-icon-pin-s{background-position:-144px -144px}.ui-icon-play{background-position:0 -160px}.ui-icon-pause{background-position:-16px -160px}.ui-icon-seek-next{background-position:-32px -160px}.ui-icon-seek-prev{background-position:-48px -160px}.ui-icon-seek-end{background-position:-64px -160px}.ui-icon-seek-first,.ui-icon-seek-start{background-position:-80px -160px}.ui-icon-stop{background-position:-96px -160px}.ui-icon-eject{background-position:-112px -160px}.ui-icon-volume-off{background-position:-128px -160px}.ui-icon-volume-on{background-position:-144px -160px}.ui-icon-power{background-position:0 -176px}.ui-icon-signal-diag{background-position:-16px -176px}.ui-icon-signal{background-position:-32px -176px}.ui-icon-battery-0{background-position:-48px -176px}.ui-icon-battery-1{background-position:-64px -176px}.ui-icon-battery-2{background-position:-80px -176px}.ui-icon-battery-3{background-position:-96px -176px}.ui-icon-circle-plus{background-position:0 -192px}.ui-icon-circle-minus{background-position:-16px -192px}.ui-icon-circle-close{background-position:-32px -192px}.ui-icon-circle-triangle-e{background-position:-48px -192px}.ui-icon-circle-triangle-s{background-position:-64px -192px}.ui-icon-circle-triangle-w{background-position:-80px -192px}.ui-icon-circle-triangle-n{background-position:-96px -192px}.ui-icon-circle-arrow-e{background-position:-112px -192px}.ui-icon-circle-arrow-s{background-position:-128px -192px}.ui-icon-circle-arrow-w{background-position:-144px -192px}.ui-icon-circle-arrow-n{background-position:-160px -192px}.ui-icon-circle-zoomin{background-position:-176px -192px}.ui-icon-circle-zoomout{background-position:-192px -192px}.ui-icon-circle-check{background-position:-208px -192px}.ui-icon-circlesmall-plus{background-position:0 -208px}.ui-icon-circlesmall-minus{background-position:-16px -208px}.ui-icon-circlesmall-close{background-position:-32px -208px}.ui-icon-squaresmall-plus{background-position:-48px -208px}.ui-icon-squaresmall-minus{background-position:-64px -208px}.ui-icon-squaresmall-close{background-position:-80px -208px}.ui-icon-grip-dotted-vertical{background-position:0 -224px}.ui-icon-grip-dotted-horizontal{background-position:-16px -224px}.ui-icon-grip-solid-vertical{background-position:-32px -224px}.ui-icon-grip-solid-horizontal{background-position:-48px -224px}.ui-icon-gripsmall-diagonal-se{background-position:-64px -224px}.ui-icon-grip-diagonal-se{background-position:-80px -224px}.ui-corner-all,.ui-corner-left,.ui-corner-tl,.ui-corner-top{border-top-left-radius:4px}.ui-corner-all,.ui-corner-right,.ui-corner-top,.ui-corner-tr{border-top-right-radius:4px}.ui-corner-all,.ui-corner-bl,.ui-corner-bottom,.ui-corner-left{border-bottom-left-radius:4px}.ui-corner-all,.ui-corner-bottom,.ui-corner-br,.ui-corner-right{border-bottom-right-radius:4px}.ui-widget-overlay,.ui-widget-shadow{background:#aaa url(images/ui-bg_flat_0_aaaaaa_40x100.png) 50% 50% repeat-x;opacity:.3;filter:Alpha(Opacity=30)}.ui-widget-shadow{margin:-8px 0 0 -8px;padding:8px;border-radius:8px}.pg_message{padding:10px 20px;font-size:12px;color:#e27b67}.pg_container{margin:0;position:relative;font-family:Verdana,Geneva,sans-serif;font-size:12px}.pg_container .pg_controls{position:absolute;left:0;top:0}.pg_container .pg_controls_options{position:absolute;width:200px;height:434px;background:#f1f3f8;padding:20px;color:#111;border:1px solid #222;box-shadow:4px 4px 2px #ddd;z-index:4}.pg_container .pg_controls_options_arrow_border{top:-17px;border-color:transparent transparent #222}.pg_container .pg_controls_options_arrow,.pg_container .pg_controls_options_arrow_border{display:block;position:absolute;left:27px;border-style:solid;border-width:8px;height:0;width:0}.pg_container .pg_controls_options_arrow{top:-16px;border-color:transparent transparent #f1f3f8}.pg_container .pg_controls_close{float:right;margin-top:-20px;margin-right:-20px;color:#fff;border-left:1px solid #222;border-bottom:1px solid #222;background:#605f61;font-size:20px;display:inline-block;padding:2px 5px}.pg_container .pg_controls_close:hover,.pg_container .pg_unmatched_close:hover{cursor:pointer;background:#ea763b}.pg_container .pg_slide_btn{position:absolute;width:75px;font-size:14px;color:#44a293}.pg_container .pg_slide_btn:hover,.pg_container .pg_unmatched_btn:hover{cursor:pointer;color:#ea763b}.pg_container .pg_slide_open:hover,.pg_container .pg_unmatched_open:hover{cursor:default!important;color:#44a293!important}.pg_container .pg_ctrl_label{font-weight:700;font-size:12px;margin-bottom:3px}.pg_container input[type=checkbox],.pg_container input[type=radio]{width:11px;height:11px;margin:0 2px 0 0;vertical-align:-1px}.pg_container .pg_hr{clear:both;margin:10px 0;border-bottom:1px solid #cbcbcb}.pg_container .pg_select_item{margin:2px 0;padding:0;font-size:12px;text-overflow:ellipsis;width:200px;height:14px;white-space:nowrap;overflow:hidden}.pg_container .pg_unmatched{position:absolute;left:0;top:0}.pg_container .pg_unmatched_list{position:absolute;left:30px;background:#f1f3f8;padding:10px;margin-bottom:20px;color:#111;border:1px solid #222;box-shadow:4px 4px 2px #ddd;z-index:4}.pg_container .pg_unmatched_close{float:right;margin-top:-10px;margin-right:-10px;color:#fff;border-left:1px solid #222;border-bottom:1px solid #222;background:#605f61;font-size:20px;display:inline-block;padding:2px 5px}.pg_container .pg_unmatched_list_item a{padding:5px 10px;float:left;font-size:11px}.pg_container .pg_unmatched_list_arrow_border{top:-16px;border-color:transparent transparent #222}.pg_container .pg_unmatched_list_arrow,.pg_container .pg_unmatched_list_arrow_border{display:block;position:absolute;left:80px;border-style:solid;border-width:8px;height:0;width:0}.pg_container .pg_unmatched_list_arrow{top:-15px;border-color:transparent transparent #f1f3f8}.pg_container .pg_unmatched_btn{position:absolute;width:185px;text-align:right;font-size:14px;color:#44a293}.pg_container .pg_col_accent,.pg_container .pg_row_accent{stroke:#4ca092;stroke-width:2;fill:#4ca092;fill-opacity:.1}.pg_container .pg_scores_tip_icon{cursor:pointer}.pg_container .pg_tooltip{position:absolute;padding:5px;z-index:5}.pg_container .pg_tooltip_inner{box-shadow:5px 5px 8px #818181;-webkit-box-shadow:5px 5px 8px #818181;-moz-box-shadow:5px 5px 8px #818181;border:1px solid #000;background:#fff;padding:5px;font-family:Verdana,Geneva,sans-serif;font-size:11px}.ui-dialog-content{font-size:11px;font-family:arial;color:#000}.pg_faq_dialog_bg_color{background-color:#fff!important}.pg_container .cursor_pointer{cursor:pointer}.pg_container .pg_linethrough{text-decoration:line-through}.pg_container .pg_draggable{cursor:move}.pg_container .pg_dragging{fill:#ea763b!important}.clearfix:after,.clearfix:before{content:" ";display:table}.clearfix:after{content:" ";visibility:hidden;display:block;height:0;clear:both}.pg_container .pg_active{font-weight:700}.pg_container .pg_active,.pg_container .pg_related_active{font-family:Verdana,Geneva,sans-serif;font-size:11px;fill:blue}.pg_container .pg_expand_genotype,.pg_container .pg_expand_ontology{text-decoration:underline;color:#dc4945;cursor:pointer}.pg_container .pg_expand_genotype_icon,.pg_container .pg_expand_ontology_icon{margin-left:2px}.pg_container em.pg_ontology_tree_indent{width:12px;float:left;display:inline-block;height:1px}.pg_container .pg_cursor_pointer{cursor:pointer}.pg_container .pg_focusLine{fill:none;stroke:red;shape-rendering:crispEdges}.pg_container .pg_rowcolmatch{fill:red!important;border-style:solid;border-width:medium}.pg_container .pg_hide{display:none}.pg_container .pg_export:hover{cursor:pointer;color:#ea763b}
\ No newline at end of file
diff --git a/dist/phenogrid-bundle.js b/dist/phenogrid-bundle.js
deleted file mode 100644
index ff912d51..00000000
--- a/dist/phenogrid-bundle.js
+++ /dev/null
@@ -1,21 +0,0 @@
-!function t(e,i,n){function r(a,o){if(!i[a]){if(!e[a]){var l="function"==typeof require&&require;if(!o&&l)return l(a,!0);if(s)return s(a,!0);var u=new Error("Cannot find module '"+a+"'");throw u.code="MODULE_NOT_FOUND",u}var c=i[a]={exports:{}};e[a][0].call(c.exports,function(t){var i=e[a][1][t];return r(i?i:t)},c,c.exports,t,e,i,n)}return i[a].exports}for(var s="function"==typeof require&&require,a=0;at||t>this.groupLength())&&(t=0),this.renderStartPos=t},getRenderEndPos:function(){return this.renderEndPos},setRenderEndPos:function(t){t>this.groupLength()&&(t=this.groupLength()),this.renderEndPos=t},itemAt:function(t){var e=this.keys(),i=e[t];return this.get(i)},get:function(t){return this.items[t]},entries:function(){for(var t=Object.keys(this.items),e=[],i=this.renderStartPos;ii?-1:i>n?1:0}),this.items=[];for(var i in e)this.items[e[i].id]=e[i]},getScale:function(){var t=this.keys(),e=i.scale.ordinal().domain(t).rangeRoundBands([0,t.length]);return e}},e.exports=n}()},{d3:8}],2:[function(t,e,i){!function(){"use strict";var i=t("jquery"),n=t("./utils.js"),r=function(t,e,i){this.serverURL=t,this.simSearchQuery=e,this.qryString="",this.groupsNoMatch=[],this.limit=i,this.owlsimsData=[],this.origSourceList=[],this.maxMaxIC=0,this.targetData={},this.sourceData={},this.cellData={},this.ontologyCacheLabels=[],this.ontologyCache=[],this.loadedNewTargetGroupItems={},this.postDataLoadCallback=""};r.prototype={constructor:r,load:function(t,e,i,n){this.origSourceList=t,this.qryString=this.simSearchQuery.inputItemsString+t.join("+"),"undefined"!=typeof n&&(this.qryString+=this.simSearchQuery.limitString+n),this.postDataLoadCallback=i,this.process(e,this.qryString)},loadCompareData:function(t,e,n,r){this.postDataLoadCallback=r,this.origSourceList=e,this.qryString=this.serverURL+this.simSearchQuery.URL+"/"+e.join("+")+"/"+n.join(",");var s=this,a=i.ajax({url:this.qryString,method:"GET",async:!0,dataType:"json"});a.done(function(e){"undefined"==typeof e.b?s.groupsNoMatch.push(t):s.transform(t,e),s.postDataLoadCallback()}),a.fail(function(){console.log("Ajax error - loadCompareData()")})},loadCompareDataForVendor:function(t,e,i,n){this.origSourceList=t,this.qryString=this.serverURL+this.simSearchQuery.URL+"/"+t.join("+")+"/",this.postDataLoadCallback=i,this.processDataForVendor(e,this.qryString,n)},processDataForVendor:function(t,e,n){if(t.length>0){var r=t[0];t=t.slice(1);for(var s=[],a=0;a0){var i=t[0];t=t.slice(1);var n=e+this.simSearchQuery.targetSpeciesString+i.groupId,r=this.postSimsFetchCb;this.postFetch(this.serverURL+this.simSearchQuery.URL,i,t,r,n)}else this.postDataLoadCallback()},postFetch:function(t,e,n,r,s){var a=this;console.log("POST:"+t);var o=i.ajax({url:t,method:"POST",data:s,async:!0,timeout:6e4,dataType:"json"});o.done(function(t){r(a,e,n,t)}),o.fail(function(){console.log("Ajax error - postFetch()")})},postSimsFetchCb:function(t,e,i,n){(null!==n||"undefined"!=typeof n)&&("undefined"==typeof n.b?t.groupsNoMatch.push(e.groupName):(t.owlsimsData[e.groupName]=n,t.transform(e.groupName,n))),t.process(i,t.qryString)},transform:function(t,e){if("undefined"!=typeof e&&"undefined"!=typeof e.b){console.log("Transforming simsearch data of group: "+t),"undefined"!=typeof e.metadata&&(this.maxMaxIC=e.metadata.maxMaxIC),"undefined"==typeof this.cellData[t]&&(this.cellData[t]={}),"undefined"==typeof this.targetData[t]&&(this.targetData[t]={}),"undefined"==typeof this.sourceData[t]&&(this.sourceData[t]={});for(var i in e.b){var r=e.b[i],s=n.getConceptId(r.id),a=r.taxon.label;a&&"Not Specified"!==a||(a=t);var o={id:s,label:r.label,targetGroup:a,type:r.type,rank:parseInt(i)+1,score:r.score.score};"undefined"==typeof this.targetData[t][s]&&(this.targetData[t][s]={}),this.targetData[t][s]=o;var l,u,c,h,d,p,f=e.b[i].matches;if("undefined"!=typeof f&&f.length>0)for(var g in f){var m=0,v=0;l=f[g],h=n.getConceptId(l.a.id),d=n.getConceptId(l.b.id),p=n.getConceptId(l.lcs.id),u=n.normalizeIC(l,this.maxMaxIC);var y=this.sourceData[t][h];"undefined"==typeof y?(v++,m+=parseFloat(l.lcs.IC),c={id:h,label:l.a.label,IC:parseFloat(l.a.IC),count:v,sum:m,type:"phenotype"},this.sourceData[t][h]=c):(this.sourceData[t][h].count+=1,this.sourceData[t][h].sum+=parseFloat(l.lcs.IC)),c={source_id:h,target_id:s,targetGroup:t,value:u,a_IC:l.a.IC,a_label:l.a.label,subsumer_id:p,subsumer_label:l.lcs.label,subsumer_IC:parseFloat(l.lcs.IC),b_id:d,b_label:l.b.label,b_IC:parseFloat(l.b.IC),type:"cell"},"undefined"==typeof this.cellData[t][h]&&(this.cellData[t][h]={}),this.cellData[t][h][s]=c}}}},transformDataForVendor:function(t,e,i){if("undefined"!=typeof e&&"undefined"!=typeof e.b){console.log("Vendor Data transforming...");var r=(t.groupId,t.groupName);"undefined"!=typeof e.metadata&&(this.maxMaxIC=e.metadata.maxMaxIC),"undefined"==typeof this.targetData[r]&&(this.targetData[r]={}),"undefined"==typeof this.sourceData[r]&&(this.sourceData[r]={}),"undefined"==typeof this.cellData[r]&&(this.cellData[r]={});for(var s in e.b)for(var a in t.entities)if(e.b[s].id===t.entities[a].combinedList){e.b[s].newid=t.entities[a].id,e.b[s].label=t.entities[a].label,e.b[s].phenodigmScore=t.entities[a].score,e.b[s].info=t.entities[a].info;break}for(var o in e.b){var l=e.b[o],u=t.groupId+l.newid,c={id:u,label:l.label,targetGroup:r,type:"genotype",info:l.info,rank:parseInt(o)+1,score:Math.round(l.phenodigmScore.score)};"undefined"==typeof this.targetData[r][u]&&(this.targetData[r][u]={}),this.targetData[r][u]=c;var h,d,p,f,g,m,v=e.b[o].matches;if("undefined"!=typeof v&&v.length>0)for(var y in v){var _=0,b=0;h=v[y],f=n.getConceptId(h.a.id),g=n.getConceptId(h.b.id),m=n.getConceptId(h.lcs.id),d=n.normalizeIC(h,this.maxMaxIC);var x=this.sourceData[r][f];"undefined"==typeof x?(b++,_+=parseFloat(h.lcs.IC),p={id:f,label:h.a.label,IC:parseFloat(h.a.IC),count:b,sum:_,type:"phenotype"},this.sourceData[r][f]=p):(this.sourceData[r][f].count+=1,this.sourceData[r][f].sum+=parseFloat(h.lcs.IC)),p={source_id:f,target_id:u,targetGroup:r,value:d,a_IC:h.a.IC,a_label:h.a.label,subsumer_id:m,subsumer_label:h.lcs.label,subsumer_IC:parseFloat(h.lcs.IC),b_id:g,b_label:h.b.label,b_IC:parseFloat(h.b.IC),type:"cell"},"undefined"==typeof this.cellData[r][f]&&(this.cellData[r][f]={}),this.cellData[r][f][u]=p}}}},transformNewTargetGroupItems:function(t,e,i){if("undefined"!=typeof e&&"undefined"!=typeof e.b){console.log("transforming genotype data..."),"undefined"!=typeof e.metadata&&(this.maxMaxIC=e.metadata.maxMaxIC);for(var r in e.b){var s=e.b[r],a=n.getConceptId(s.id),o={id:a,label:s.label,targetGroup:s.taxon.label,type:s.type,parentGeneID:i,rank:parseInt(r)+1,score:s.score.score,visible:!0};"undefined"==typeof this.targetData[t][a]&&(this.targetData[t][a]={}),this.targetData[t][a]=o;var l,u,c,h,d,p,f=e.b[r].matches;if("undefined"!=typeof f&&f.length>0)for(var g in f){var m=0,v=0;l=f[g],h=n.getConceptId(l.a.id),d=n.getConceptId(l.b.id),p=n.getConceptId(l.lcs.id),u=n.normalizeIC(l,this.maxMaxIC),"undefined"==typeof this.sourceData[t]&&(this.sourceData[t]={});var y=this.sourceData[t][h];"undefined"==typeof y?(v++,m+=parseFloat(l.lcs.IC),c={id:h,label:l.a.label,IC:parseFloat(l.a.IC),count:v,sum:m,type:"phenotype"},this.sourceData[t][h]=c):(this.sourceData[t][h].count+=1,this.sourceData[t][h].sum+=parseFloat(l.lcs.IC)),c={source_id:h,target_id:a,target_type:"genotype",targetGroup:s.taxon.label,value:u,a_IC:l.a.IC,a_label:l.a.label,subsumer_id:p,subsumer_label:l.lcs.label,subsumer_IC:parseFloat(l.lcs.IC),b_id:d,b_label:l.b.label,b_IC:parseFloat(l.b.IC),type:"cell"},"undefined"==typeof this.cellData[t][h]&&(this.cellData[t][h]={}),this.cellData[t][h][a]=c}}}},refresh:function(t,e){var i=[],n=!1;if(e)for(var r in t)this.dataExists(t[r].name)===!1&&i.push(t[r]);else i=t;return i.length>0&&(this.load(this.origSourceList,i,this.postDataLoadCallback),n=!0),n},getFetch:function(t,e,n,r,s,a){console.log("GET:"+e);var o=i.ajax({url:e,method:"GET",async:!0,dataType:"json"});o.done(function(e){r(t,n,e,s,a)}),o.fail(function(){console.log("Ajax error - getFetch()")})},getOntology:function(t,e,i,n,r){var s=this,a=e,o=r.state.ontologyRelationship,l=i,u=this.serverURL+r.state.ontologyQuery+t+"/"+l+"/"+a+"/"+o+".json",c=this.postOntologyCb;this.getFetch(s,u,t,c,n,r)},getNewTargetGroupItems:function(t,e,i){var n=this,r=this.serverURL+"/gene/"+t+"/genotype_list.json",s=this.getNewTargetGroupItemsCb;this.getFetch(n,r,t,s,e,i)},getNewTargetGroupItemsCb:function(t,e,i,n,r){if("undefined"!=typeof i.genotype_list)if(i.genotype_list.length>0){var s=r.state.unstableTargetGroupItemPrefix;for(var a in i.genotype_list)for(var o in s)0===i.genotype_list[a].id.indexOf(s[o])&&i.genotype_list.splice(a,1);var l=i.genotype_list.slice(0,r.state.targetGroupItemExpandLimit),u=t.origSourceList.join("+"),c="";for(var a in l)c+=l[a].id+",";","===c.slice(-1)&&(c=c.slice(0,-1));var h=t.serverURL+r.state.compareQuery.URL+"/"+u+"/"+c,d=t.getNewTargetGroupItemsCbCb;t.getFetch(t,h,e,d,n,r)}else{var p={},f=r.state.messaging.noAssociatedGenotype;n(p,e,r,f)}},getNewTargetGroupItemsCbCb:function(t,e,i,n,r){var s=[];if("undefined"!=typeof i.b){for(var a=0;a=e)break}}return i},createCombinedSourceList:function(t){var e=[];for(var n in t){var r=this.getData("source",t[n].groupName);if("undefined"!=typeof r)for(var s in r){var a=r[s].id,o=e[a];if("undefined"==typeof o){var l={};e[a]=i.extend({},l,r[s])}}}for(var u in e){e[u].count=0,e[u].sum=0;for(var c in t){var h=this.getData("cellData",t[c].groupName);for(var d in h){var p=h[d];for(var f in p)e[u].id==p[f].source_id&&(e[u].count+=1,e[u].sum+=p[f].subsumer_IC)}}}return e},getOntologyLabel:function(t){return this.dataLoader.getOntologyLabel(t)},isExpanded:function(t){return"undefined"==typeof this.expandedItemList[t]?!1:!0},checkExpandedItemsLoaded:function(t,e){"undefined"==typeof this.reorderedTargetEntriesIndexArray[t]&&(this.reorderedTargetEntriesIndexArray[t]=[]);for(var i=0;i
What is the score shown at the top of the grid?
The score indicated at the top of each column, below the target label, is the overall similarity score between the query and target. Briefly, for each of the targets (columns) listed, the set of Q(1..n) phenotypes of the query are pairwise compared against all of the T(1..m) phenotypes in the target.
Then, for each pairwise comparison of phenotypes (q x P1...n), the best comparison is retained for each q and summed for all p1..n.
The raw score is then normalized against the maximal possible score, which is the query matching to itself. Therefore, range of scores displayed is 0..100. For more details, please see (Smedley et al, 2012 http://www.ncbi.nlm.nih.gov/pubmed/23660285 and http://www.owlsim.org)
\r\n",sorts:"
What are the different ways that phenotypes can be sorted?
The phenotypes that are shown on the left side of the grid may be sorted using one of three methods. More options may be available in the future.
Alphabetical - A-Z
Frequency and Rarity - Phenotypes are sorted by the sum of the phenotype values across all models/genes
Frequency (Default) - Phenotypes are sorted by the count of the number of model/gene matches per phenotype
",faq:"
Phenogrid Faq
How are the similar targets obtained?
We query our owlsim server to obtain the top 100 most-phenotypically similar targets for the selected organism. The grid defaults to showing mouse.
What are the possible targets for comparison?
Currently, the phenogrid is configured to permit comparisons between your query (typically a set of disease-phenotype associations) and one of:
human diseases
mouse genes
zebrafish genes
You can change the target organism by selecting a new organism. The grid will temporarily disappear, and reappear with the new target rendered.
Can I compare the phenotypes to human genes?
No, not yet. But that will be added soon.
Where does the data come from?
The phenotype annotations utilized to compute the phenotypic similarity are drawn from a number of sources:
Human disease-phenotype annotations were obtained from http://human-phenotype-ontology.org, which contains annotations for approx. 7,500 diseases.
Mouse gene-phenotype annotations were obtained from MGI (www.informatics.jax.org). The original annotations were made between genotypes and phenotypes. We then inferred the relationship between gene and phenotype based on the genes that were variant in each genotype. We only perform this inference for those genotypes that contain a single variant gene.
Zebrafish genotype-phenotype annotations were obtained from ZFIN (www.zfin.org). The original annotations were made between genotypes and phenotypes, with some of those genotypes created experimentally with the application of morpholino reagents. Like for mouse, we inferred the relationship between gene and phenotype based on the genes that were varied in each genotype. We only perform this inference for those genotypes that contain a single variant gene.
The grid depicts the comparison of a set of phenotypes in a query (such as those annotated to a disease or a gene) with one or more phenotypically similar targets. Each row is a phenotype that is annotated to the query (either directly or it is a less-specific phenotype that is inferred), and each column is an annotated target (such as a gene or disease). When a phenotype is shared between the query and target, the intersection is colored based on the selected calculation method (see What do the different calculation methods mean). You can hover over the intersection to get more information about what the original phenotype is of the target, and what is in-common between the two.
Where can I make suggestions for improvements or additions?
What happens to the phenotypes that are not shared?
Phenotypes that were part of your query but are not shared by any of the targets can be seen by clicking the View Unmatched Phenotype link.
Why do I sometimes see two targets that share the same phenotypes have very different overall scores?
This is usually because of some of the phenotypes that are not shared with the query. For example, if the top hit to a query matches each phenotype exactly, and the next hit matches all of them exactly plus it has 10 additional phenotypes that don't match it at all, it is penalized for those phenotypes are not in common, and thus ranks lower on the similarity scale. \r\n",calcs:"
What do the different calculation methods mean?
For each pairwise comparison of phenotypes from the query (q) and target (t), we can assess their individual similarities in a number of ways. First, we find the phenotype-in-common between each pair (called the lowest common subsumer or LCS). Then, we can leverage the Information Content (IC) of the phenotypes (q,t,lcs) in a variety of combinations to interpret the strength of the similarity.
**Uniqueness reflects how often the phenotype-in-common is annotated to all diseases and genes in the Monarch Initiative knowledgebase. This is simply a reflection of the IC normalized based on the maxIC. IC(PhenotypeInCommon)/maxIC(AllPhenotypes)
**Distance is the euclidian distance between the query, target, and phenotype-in-common, computed using IC scores.
d=(IC(q)-IC(lcs))2+(IC(t)-IC(lcs))2
This is normalized based on the maximal distance possible, which would be between two rarely annotated leaf nodes that only have the root node (phenotypic abnormality) in common. So what is depicted in the grid is 1-dmax(d)
**Ratio(q) is the proportion of shared information between a query phenotype and the phenotype-in-common with the target.
ratio(q)=IC(lcs)/IC(q)*100
**Ratio(t) is the proportion of shared information between the target phenotype and the phenotype-in-common with the query.
ratio(t)=IC(lcs)/IC(t)*100
\r\n"}},{}],5:[function(t,e,i){e.exports={logo:"data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0idXRmLTgiPz48IURPQ1RZUEUgc3ZnIFBVQkxJQyAiLS8vVzNDLy9EVEQgU1ZHIDEuMS8vRU4iICJodHRwOi8vd3d3LnczLm9yZy9HcmFwaGljcy9TVkcvMS4xL0RURC9zdmcxMS5kdGQiPjxzdmcgdmVyc2lvbj0iMS4xIiB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHhtbG5zOnhsaW5rPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5L3hsaW5rIiB4PSIwcHgiIHk9IjBweCIgd2lkdGg9IjIwMC40NjNweCIgaGVpZ2h0PSIxMzMuMDY1cHgiIHZpZXdCb3g9IjAgMCAyMDAuNDYzIDEzMy4wNjUiIGVuYWJsZS1iYWNrZ3JvdW5kPSJuZXcgMCAwIDIwMC40NjMgMTMzLjA2NSIgeG1sOnNwYWNlPSJwcmVzZXJ2ZSI+PGcgaWQ9ImFubm90YXRpb25zIiBkaXNwbGF5PSJub25lIj48ZyBkaXNwbGF5PSJpbmxpbmUiPjxkZWZzPjxsaW5lIGlkPSJTVkdJRF8xXyIgeDE9IjEwMy44MDMiIHkxPSI1OS41MzIiIHgyPSIxMDMuODEyIiB5Mj0iNTkuNTQxIi8+PC9kZWZzPjxjbGlwUGF0aCBpZD0iU1ZHSURfMl8iPjx1c2UgeGxpbms6aHJlZj0iI1NWR0lEXzFfIiAgb3ZlcmZsb3c9InZpc2libGUiLz48L2NsaXBQYXRoPjxnIGNsaXAtcGF0aD0idXJsKCNTVkdJRF8yXykiPjxkZWZzPjxsaW5lIGlkPSJTVkdJRF8zXyIgeDE9IjEwMy4zMjMiIHkxPSI2MC4xNjkiIHgyPSIxMDMuMzIzIiB5Mj0iNDguMjY1Ii8+PC9kZWZzPjxjbGlwUGF0aCBpZD0iU1ZHSURfNF8iPjx1c2UgeGxpbms6aHJlZj0iI1NWR0lEXzNfIiAgb3ZlcmZsb3c9InZpc2libGUiLz48L2NsaXBQYXRoPjwvZz48L2c+PGcgZGlzcGxheT0iaW5saW5lIj48ZGVmcz48cmVjdCBpZD0iU1ZHSURfNV8iIHg9Ii0wLjI0NSIgeT0iLTEyLjg3OSIgd2lkdGg9IjIwMS4zMjkiIGhlaWdodD0iMTYyLjE3NSIvPjwvZGVmcz48Y2xpcFBhdGggaWQ9IlNWR0lEXzZfIj48dXNlIHhsaW5rOmhyZWY9IiNTVkdJRF81XyIgIG92ZXJmbG93PSJ2aXNpYmxlIi8+PC9jbGlwUGF0aD48cGF0aCBjbGlwLXBhdGg9InVybCgjU1ZHSURfNl8pIiBmaWxsPSIjRTZFN0U4IiBkPSJNMTQ2Ljk1OSwxMzIuOTczdi0yLjM5NWgtMTkuNzg1Yy0zLjkyOCwwLTYuMzcxLTEuNDg1LTYuMzcxLTYuNzA3YzAtNS4yNywyLjc3OC02LjQxOSw4LTYuNDE5aDE4LjE1NnYtMi4zOTZoLTE4Ljg3NWMtNC4wMjIsMC03LjI4MS0xLjUzNC03LjI4MS02Ljc1NWMwLTUuMjcsMi4wNi02LjQxOSw4LTYuNDE5aDE4LjE1NnYtMi4zOTZoLTE4LjEwOGMtNi42MTEsMC0xMC4yNSwxLjcyNi0xMC4yNSw4LjU3NWMwLDMuMDY2LDAuNjIxLDUuNzQ5LDQuMTE4LDguMDQ5Yy0yLjM5NiwxLjAwNi00LjExOCwzLjQtNC4xMTgsNy41MjFjMCwzLjA2NiwwLjc2Niw1LjQxMywyLjc3Nyw2Ljg5OGwtMi4yNTIsMC4yMzh2Mi4yMDRIMTQ2Ljk1OXoiLz48cGF0aCBjbGlwLXBhdGg9InVybCgjU1ZHSURfNl8pIiBmaWxsPSIjRTZFN0U4IiBkPSJNMTQ2Ljk1OSwzMy44OHYtMi4zOTZoLTE5Ljc4NWMtMy45MjgsMC02LjM3MS0xLjQ4NC02LjM3MS02LjcwN2MwLTUuMjY5LDIuNzc4LTYuNDE5LDgtNi40MTloMTguMTU2di0yLjM5NWgtMTguODc1Yy00LjAyMiwwLTcuMjgxLTEuNTM0LTcuMjgxLTYuNzU1YzAtNS4yNzEsMi4wNi02LjQxOSw4LTYuNDE5aDE4LjE1NlYwLjM5NGgtMTguMTA4Yy02LjYxMSwwLTEwLjI1LDEuNzI2LTEwLjI1LDguNTc1YzAsMy4wNjYsMC42MjEsNS43NDksNC4xMTgsOC4wNDljLTIuMzk2LDEuMDA1LTQuMTE4LDMuNC00LjExOCw3LjUyMWMwLDMuMDY1LDAuNzY2LDUuNDEzLDIuNzc3LDYuODk3bC0yLjI1MiwwLjIzOXYyLjIwNEgxNDYuOTU5eiIvPjxwYXRoIGNsaXAtcGF0aD0idXJsKCNTVkdJRF82XykiIGZpbGw9IiNFNkU3RTgiIGQ9Ik0xNjcuNTk5LDgwLjU0NGgyLjM5NVY2MC43NmMwLTMuOTI5LDEuNDg1LTYuMzcxLDYuNzA3LTYuMzcxYzUuMjcsMCw2LjQxOSwyLjc3Nyw2LjQxOSw4djE4LjE1NWgyLjM5NlY2MS42NjljMC00LjAyMiwxLjUzMy03LjI4LDYuNzU0LTcuMjhjNS4yNzEsMCw2LjQyLDIuMDYsNi40Miw4djE4LjE1NWgyLjM5NlY2Mi40MzZjMC02LjYxMS0xLjcyNi0xMC4yNS04LjU3Ni0xMC4yNWMtMy4wNjUsMC01Ljc0OCwwLjYyMi04LjA0OSw0LjExOWMtMS4wMDUtMi4zOTYtMy40LTQuMTE5LTcuNTIxLTQuMTE5Yy0zLjA2NiwwLTUuNDEzLDAuNzY2LTYuODk3LDIuNzc3bC0wLjI0LTIuMjUyaC0yLjIwM1Y4MC41NDR6Ii8+PHBhdGggY2xpcC1wYXRoPSJ1cmwoI1NWR0lEXzZfKSIgZmlsbD0iI0U2RTdFOCIgZD0iTS0wLjI0NSw4MC41NDRIMi4xNVY2MC43NmMwLTMuOTI5LDEuNDg1LTYuMzcxLDYuNzA3LTYuMzcxYzUuMjY5LDAsNi40MTksMi43NzcsNi40MTksOHYxOC4xNTVoMi4zOTVWNjEuNjY5YzAtNC4wMjIsMS41MzQtNy4yOCw2Ljc1NS03LjI4YzUuMjcsMCw2LjQxOSwyLjA2LDYuNDE5LDh2MTguMTU1aDIuMzk2VjYyLjQzNmMwLTYuNjExLTEuNzI1LTEwLjI1LTguNTc1LTEwLjI1Yy0zLjA2NiwwLTUuNzQ5LDAuNjIyLTguMDQ5LDQuMTE5Yy0xLjAwNS0yLjM5Ni0zLjQtNC4xMTktNy41MjEtNC4xMTljLTMuMDY2LDAtNS40MTMsMC43NjYtNi44OTgsMi43NzdsLTAuMjM5LTIuMjUyaC0yLjIwNFY4MC41NDR6Ii8+PGxpbmUgY2xpcC1wYXRoPSJ1cmwoI1NWR0lEXzZfKSIgZmlsbD0ibm9uZSIgc3Ryb2tlPSIjNTg1OTVCIiBzdHJva2Utd2lkdGg9IjAuNSIgc3Ryb2tlLW1pdGVybGltaXQ9IjIiIHN0cm9rZS1kYXNoYXJyYXk9IjIuOTI0LDUuOTI0IiB4MT0iMTAwLjA0IiB5MT0iMTQ5LjI5NiIgeDI9IjEwMC4wNCIgeTI9Ii0xMi44NzkiLz48L2c+PC9nPjxnIGlkPSJGdWxsX0NvbG9yIj48Zz48cGF0aCBmaWxsPSIjNDZBNTk3IiBkPSJNMTQxLjc5MiwzNC43ODVjLTIuMDg4LTAuNDI5LTQuMjUtMC42NTQtNi40NjMtMC42NTRjLTguNTE0LDAtMTYuMjU4LDMuMzMyLTIyLjAwOCw4Ljc2Yy0wLjQ1MSwwLjQyNy0wLjg5LDAuODY1LTEuMzE2LDEuMzE3bC0xMS43NzEsMTEuNzcxTDg4LjQ2Miw0NC4yMDhjLTAuNDI2LTAuNDUyLTAuODY0LTAuODkxLTEuMzE2LTEuMzE3Yy01Ljc1LTUuNDI4LTEzLjQ5Ni04Ljc2LTIyLjAwNy04Ljc2Yy0yLjIxNSwwLTQuMzc2LDAuMjI2LTYuNDY0LDAuNjU0Yy0xNC42MDQsMy0yNS42MjEsMTUuOTUyLTI1LjYyMSwzMS40M2gwLjAxN2MwLDE1LjQ1OCwxMC45ODksMjguMzk3LDI1LjU2NSwzMS40MThjMi4xMDYsMC40MzgsNC4yODUsMC42NjgsNi41MTcsMC42NjhjOC4wMDksMCwxNS4zMzctMi45NTEsMjAuOTY0LTcuODJsMi42Ny0yLjY3bDAuMTU3LTAuMTU2YzAuMzktMC40MjYsMC42MjktMC45OTQsMC42MjktMS42MTljMC0xLjMzLTEuMDc3LTIuNDA2LTIuNDA3LTIuNDA2Yy0wLjYwNywwLTEuMTYyLDAuMjI3LTEuNTg0LDAuNkw4NS4zNiw4NC40NWwtMi41MTEsMi41MWMtNC45MzYsNC4yMTktMTEuMjE0LDYuNTQxLTE3LjY5Niw2LjU0MWMtMS44NjQsMC0zLjcyOS0wLjE5MS01LjU0My0wLjU2OGMtMTIuNTk1LTIuNjA5LTIxLjczOS0xMy44NDYtMjEuNzM5LTI2LjcxN2gwLjA3M2MwLTEyLjg5NSw5LjE2My0yNC4xMzUsMjEuNzg2LTI2LjcyOGwtMC4wMDQtMC4wMTdjMS43NzEtMC4zNTcsMy41OTEtMC41MzksNS40MTMtMC41MzljNi45NzYsMCwxMy42MjMsMi42NDYsMTguNzExLDcuNDQ5YzAuMzgzLDAuMzYyLDIzLjA2MiwyMy4yNTUsMjMuMDYyLDIzLjI1NWwwLjAwNC0wLjAwNGwwLjAxMywwLjAxMmwxNi41MjgtMTYuNTI1YzMuMTQyLTIuODQ4LDcuMzA2LTQuNTg3LDExLjg2OS00LjU4N2M5Ljc1MiwwLDE3LjY4NSw3LjkzMywxNy42ODUsMTcuNjg0YzAsOS43NS03LjkzMywxNy42ODQtMTcuNjg1LDE3LjY4NGMtNC4zMjYsMC04LjI5My0xLjU2Ni0xMS4zNjktNC4xNTRsLTIuMDE3LTIuMDE2bC0wLjM5LTAuMzkzYy0wLjQxMS0wLjMyNC0wLjkzLTAuNTIxLTEuNDk0LTAuNTIxYy0xLjMyOSwwLTIuNDA3LDEuMDgtMi40MDcsMi40MDhjMCwwLjcxMSwwLjMxMiwxLjM0OCwwLjgwMiwxLjc4OWwtMC4wOC0wLjA3MmwyLjMzNiwyLjM0NGwwLjE1OCwwLjEzM2M0LjA0NywzLjQwNiw5LjE4NCw1LjI4MywxNC40NjEsNS4yODNjMTIuMzk5LDAsMjIuNDg1LTEwLjA4NiwyMi40ODUtMjIuNDg1YzAtMTIuMzk4LTEwLjA4Ni0yMi40ODQtMjIuNDg1LTIyLjQ4NGMtNS41ODYsMC0xMC45NDcsMi4wNzEtMTUuMDk0LDUuODMybC0wLjA4NiwwLjA3OEwxMDYuOTQsNjIuODQ2bC0zLjM5NS0zLjM5MWwxMS44NTMtMTEuODUzbDAuMDk3LTAuMWMwLjM1OS0wLjM4MiwwLjczNy0wLjc2LDEuMTIxLTEuMTIyYzUuMDktNC44MDQsMTEuNzM0LTcuNDQ5LDE4LjcxMy03LjQ0OWMxLjg1MSwwLDMuNywwLjE4Nyw1LjQ5OCwwLjU1NmMxMi42MjMsMi41OTMsMjEuNzg1LDEzLjgzMywyMS43ODUsMjYuNzI4YzAsMTIuODcyLTkuMTQ1LDI0LjEwOC0yMS43MzgsMjYuNzE3Yy0xLjgxNCwwLjM3Ny0zLjY4LDAuNTY4LTUuNTQ1LDAuNTY4Yy02LjQ4MSwwLTEyLjc1OS0yLjMyMi0xNy42OTUtNi41NDFsLTIuNTEtMi41MWwtMC4yMjItMC4yMjFjLTAuNDIzLTAuMzczLTAuOTc4LTAuNi0xLjU4NS0wLjZjLTEuMzMsMC0yLjQwNiwxLjA3Ni0yLjQwNiwyLjQwNmMwLDAuNjI1LDAuMjM5LDEuMTkzLDAuNjI5LDEuNjE5bDAuMTU3LDAuMTU2bDIuNjY5LDIuNjdjNS42MjcsNC44NjksMTIuOTU1LDcuODIsMjAuOTYzLDcuODJjMi4yMzMsMCw0LjQxMi0wLjIzLDYuNTE5LTAuNjY4YzE0LjU3NS0zLjAyMSwyNS41NjUtMTUuOTYxLDI1LjU2NS0zMS40MThDMTY3LjQxMyw1MC43MzcsMTU2LjM5NSwzNy43ODUsMTQxLjc5MiwzNC43ODUiLz48cGF0aCBmaWxsPSIjNDZBNTk3IiBkPSJNODAuMjMyLDQ5LjU2M2MtNC4xMjQtMy43NDEtOS40NDgtNS44MDgtMTUuMDA0LTUuODN2LTAuMDAybC0wLjA1OCwwLjAwMWwtMC4wMzItMC4wMDFjLTEyLjM5OCwwLTIyLjQ4NCwxMC4wODYtMjIuNDg0LDIyLjQ4NGgwLjAxN2MwLDEyLjM5OSwxMC4wODYsMjIuNDg1LDIyLjQ4NCwyMi40ODVjNS4yNzgsMCwxMC40MTUtMS44NzUsMTQuNDYzLTUuMjgzbDAuMTU2LTAuMTMzbDIuMzM3LTIuMzQybC0wLjA4LDAuMDdjMC40ODktMC40MzksMC44MDEtMS4wNzgsMC44MDEtMS43OTFjMC0xLjMyOC0xLjA3OC0yLjQwNi0yLjQwNy0yLjQwNmMtMC41NjQsMC0xLjA4MywwLjE5Ny0xLjQ5MywwLjUyM2wtMC4zOTEsMC4zOTFsLTIuMDE3LDIuMDE2Yy0zLjA3NiwyLjU5LTcuMDQyLDQuMTU0LTExLjM2OSw0LjE1NGMtOS43NTEsMC0xNy42ODMtNy45MzQtMTcuNjgzLTE3LjY4NGgwLjA3M2MwLTkuNzM2LDcuOTA5LTE3LjY2LDE3LjYzOS0xNy42ODJjNC41NDYsMC4wMTEsOC42OTQsMS43NDcsMTEuODIzLDQuNTg1bDIzLjIyMiwyMy4yMjVsMy40MS0zLjQxTDgwLjQwMSw0OS43MjRMODAuMjMyLDQ5LjU2M3oiLz48L2c+PGc+PGRlZnM+PHBhdGggaWQ9IlNWR0lEXzdfIiBkPSJNMTEwLjM5Miw2Ni4xODJsMC4wMDQsMC4wMDRsMC45NzktMC45ODhMMTEwLjM5Miw2Ni4xODJ6IE0xMDYuOTc5LDYyLjc2OGwwLjAyLDAuMDJsMTAuMzY3LTEwLjM2N2wtMC4wMDctMC4wMDlMMTA2Ljk3OSw2Mi43Njh6Ii8+PC9kZWZzPjxjbGlwUGF0aCBpZD0iU1ZHSURfOF8iPjx1c2UgeGxpbms6aHJlZj0iI1NWR0lEXzdfIiAgb3ZlcmZsb3c9InZpc2libGUiLz48L2NsaXBQYXRoPjxnIGNsaXAtcGF0aD0idXJsKCNTVkdJRF84XykiPjxkZWZzPjxyZWN0IGlkPSJTVkdJRF85XyIgeD0iMTA2LjU5MiIgeT0iNTEuOTU2IiB3aWR0aD0iMTEuNTIxIiBoZWlnaHQ9IjE0Ljk3NiIvPjwvZGVmcz48Y2xpcFBhdGggaWQ9IlNWR0lEXzEwXyI+PHVzZSB4bGluazpocmVmPSIjU1ZHSURfOV8iICBvdmVyZmxvdz0idmlzaWJsZSIvPjwvY2xpcFBhdGg+PGcgdHJhbnNmb3JtPSJtYXRyaXgoMSAwIDAgMSA3LjYyOTM5NWUtMDYgMCkiIGNsaXAtcGF0aD0idXJsKCNTVkdJRF8xMF8pIj48aW1hZ2Ugb3ZlcmZsb3c9InZpc2libGUiIHdpZHRoPSIzMCIgaGVpZ2h0PSIzOSIgeGxpbms6aHJlZj0iZGF0YTppbWFnZS9qcGVnO2Jhc2U2NCwvOWovNEFBUVNrWkpSZ0FCQWdFQXV3QzdBQUQvN0FBUlJIVmphM2tBQVFBRUFBQUFIZ0FBLys0QUlVRmtiMkpsQUdUQUFBQUFBUU1BRUFNQ0F3WUFBQUdjQUFBQjNBQUFBaWYvMndDRUFCQUxDd3NNQ3hBTURCQVhEdzBQRnhzVUVCQVVHeDhYRnhjWEZ4OGVGeG9hR2hvWEhoNGpKU2NsSXg0dkx6TXpMeTlBUUVCQVFFQkFRRUJBUUVCQVFFQUJFUThQRVJNUkZSSVNGUlFSRkJFVUdoUVdGaFFhSmhvYUhCb2FKakFqSGg0ZUhpTXdLeTRuSnljdUt6VTFNREExTlVCQVAwQkFRRUJBUUVCQVFFQkFRUC9DQUJFSUFDb0FJZ01CSWdBQ0VRRURFUUgveEFCOUFBRUFBd0VCQVFBQUFBQUFBQUFBQUFBQUFnTUVBUVVHQVFFQkFBQUFBQUFBQUFBQUFBQUFBQUFBQVJBQUFRUUJCUUVBQUFBQUFBQUFBQUFBQXdBQkFnUUZFQ0F3RVJKQkVRQUJBZ1FIQVFFQUFBQUFBQUFBQUFBQkFBSWdZWUVERVNGQmNaR3gwY0V6RWdFQUFBQUFBQUFBQUFBQUFBQUFBQUF3LzlvQURBTUJBQUlSQXhFQUFBRDZMSk9tdGV1amRHeEllRjNUYU5pWUJtdTdJQUEvLzlvQUNBRUNBQUVGQU9ILzJnQUlBUU1BQVFVQTRmL2FBQWdCQVFBQkJRREs1QzRDNjJWeUx1Szlma2hXTGNsMjZ6TE8rUkNGQkNnaFhTeVkvVjhJVUVLaEJvc3JZL1ZzSVZDRFJiUWd2Um9RYUxhL2RuLy8yZ0FJQVFJQ0JqOEFILy9hQUFnQkF3SUdQd0FmLzlvQUNBRUJBUVkvQUgyN1YwdFlBM0FZRFVUQy9ZOE44V2Q0OER4WjNDYUR4VVZ6WnZRZ29ubVRlaEM0eUhVSk8wT24ySC8vMlE9PSIgdHJhbnNmb3JtPSJtYXRyaXgoMC4zODQgMCAwIC0wLjM4NCAxMDYuNTkyMyA2Ni45MzIxKSI+PC9pbWFnZT48L2c+PC9nPjwvZz48Zz48ZGVmcz48cmVjdCBpZD0iU1ZHSURfMTFfIiB4PSIxMDguOTA4IiB5PSI0Ni42MDciIHRyYW5zZm9ybT0ibWF0cml4KC0wLjY2OSAtMC43NDMzIDAuNzQzMyAtMC42NjkgMTQxLjU2NzggMTcxLjIzNjUpIiB3aWR0aD0iMC4wMTMiIGhlaWdodD0iMTQuOTc0Ii8+PC9kZWZzPjxjbGlwUGF0aCBpZD0iU1ZHSURfMTJfIj48dXNlIHhsaW5rOmhyZWY9IiNTVkdJRF8xMV8iICBvdmVyZmxvdz0idmlzaWJsZSIvPjwvY2xpcFBhdGg+PGcgY2xpcC1wYXRoPSJ1cmwoI1NWR0lEXzEyXykiPjxkZWZzPjxyZWN0IGlkPSJTVkdJRF8xM18iIHg9IjEwMy4xMzYiIHk9IjQ4LjExNiIgd2lkdGg9IjExLjUyMSIgaGVpZ2h0PSIxMS45MDQiLz48L2RlZnM+PGNsaXBQYXRoIGlkPSJTVkdJRF8xNF8iPjx1c2UgeGxpbms6aHJlZj0iI1NWR0lEXzEzXyIgIG92ZXJmbG93PSJ2aXNpYmxlIi8+PC9jbGlwUGF0aD48ZyB0cmFuc2Zvcm09Im1hdHJpeCgxIDAgMCAxIDAgMy44MTQ2OTdlLTA2KSIgY2xpcC1wYXRoPSJ1cmwoI1NWR0lEXzE0XykiPjxpbWFnZSBvdmVyZmxvdz0idmlzaWJsZSIgd2lkdGg9IjMwIiBoZWlnaHQ9IjMxIiB4bGluazpocmVmPSJkYXRhOmltYWdlL2pwZWc7YmFzZTY0LC85ai80QUFRU2taSlJnQUJBZ0VBdXdDN0FBRC83QUFSUkhWamEza0FBUUFFQUFBQUhnQUEvKzRBSVVGa2IySmxBR1RBQUFBQUFRTUFFQU1DQXdZQUFBR1dBQUFCdndBQUFnei8yd0NFQUJBTEN3c01DeEFNREJBWER3MFBGeHNVRUJBVUd4OFhGeGNYRng4ZUZ4b2FHaG9YSGg0akpTY2xJeDR2THpNekx5OUFRRUJBUUVCQVFFQkFRRUJBUUVBQkVROFBFUk1SRlJJU0ZSUVJGQkVVR2hRV0ZoUWFKaG9hSEJvYUpqQWpIaDRlSGlNd0t5NG5KeWN1S3pVMU1EQTFOVUJBUDBCQVFFQkFRRUJBUUVCQVFQL0NBQkVJQUNJQUh3TUJJZ0FDRVFFREVRSC94QUIvQUFFQUF3RUJBQUFBQUFBQUFBQUFBQUFBQVFNRUJRSUJBUUVBQUFBQUFBQUFBQUFBQUFBQUFBQUJFQUFDQWdJQ0F3QUFBQUFBQUFBQUFBQUJBd0lFQUNBUkJSQVNFeEVBQVFJREJRa0FBQUFBQUFBQUFBQUFBUUFDRVNFREVEQlJZZEV4UVlHeEVpSkNncklTQVFBQUFBQUFBQUFBQUFBQUFBQUFBQ0QvMmdBTUF3RUFBaEVERVFBQUFPL2ZUc3EvVkZrYzdiUHNBQUEvLzlvQUNBRUNBQUVGQU52LzJnQUlBUU1BQVFVQTIvL2FBQWdCQVFBQkJRQi9aZGhHMHE3ZmxpVzJaWUJQMVlubTBoR0xXSURQank5YXhBZUl3QU8vLzlvQUNBRUNBZ1kvQUYvLzJnQUlBUU1DQmo4QVgvL2FBQWdCQVFFR1B3Q3RUWlZneGxSeldqcGJzQklIaXU2ckgxYm9wdmp3R2lNVE9FbFdPTlIzMFVKTE94NXhjZWF6dEoza3h1UC8yUT09IiB0cmFuc2Zvcm09Im1hdHJpeCgwLjM4NCAwIDAgLTAuMzg0IDEwMy4xMzYyIDYwLjAyKSI+PC9pbWFnZT48L2c+PC9nPjwvZz48bGluZWFyR3JhZGllbnQgaWQ9IlNWR0lEXzE1XyIgZ3JhZGllbnRVbml0cz0idXNlclNwYWNlT25Vc2UiIHgxPSIxMDguNjk1OCIgeTE9IjY0LjQ4NjMiIHgyPSIxMTguODg1OSIgeTI9IjU0LjI5NjIiPjxzdG9wICBvZmZzZXQ9IjAiIHN0eWxlPSJzdG9wLWNvbG9yOiMwMDAwMDAiLz48c3RvcCAgb2Zmc2V0PSIwLjA4OTYiIHN0eWxlPSJzdG9wLWNvbG9yOiMwMDAwMDA7c3RvcC1vcGFjaXR5OjAuOTEwNCIvPjxzdG9wICBvZmZzZXQ9IjEiIHN0eWxlPSJzdG9wLWNvbG9yOiMwMDAwMDA7c3RvcC1vcGFjaXR5OjAiLz48L2xpbmVhckdyYWRpZW50Pjxwb2x5Z29uIG9wYWNpdHk9IjAuNSIgZmlsbD0idXJsKCNTVkdJRF8xNV8pIiBwb2ludHM9IjEwNi45OTksNjIuNzg4IDExMC4zOTIsNjYuMTgyIDExMS4zNzUsNjUuMTk4IDEyMC40MzMsNTYuMDY2IDExNy4zNjYsNTIuNDIxICIvPjxsaW5lYXJHcmFkaWVudCBpZD0iU1ZHSURfMTZfIiBncmFkaWVudFVuaXRzPSJ1c2VyU3BhY2VPblVzZSIgeDE9IjEwMS45MTk0IiB5MT0iNTcuNzE0NCIgeDI9IjExMi4xMDk5IiB5Mj0iNDcuNTIzOSI+PHN0b3AgIG9mZnNldD0iMCIgc3R5bGU9InN0b3AtY29sb3I6IzAwMDAwMCIvPjxzdG9wICBvZmZzZXQ9IjAuMDg5NiIgc3R5bGU9InN0b3AtY29sb3I6IzAwMDAwMDtzdG9wLW9wYWNpdHk6MC45MTA0Ii8+PHN0b3AgIG9mZnNldD0iMSIgc3R5bGU9InN0b3AtY29sb3I6IzAwMDAwMDtzdG9wLW9wYWNpdHk6MCIvPjwvbGluZWFyR3JhZGllbnQ+PHBvbHlnb24gb3BhY2l0eT0iMC41IiBmaWxsPSJ1cmwoI1NWR0lEXzE2XykiIHBvaW50cz0iMTAwLjI1LDU2LjAxNyAxMDMuNjE3LDU5LjM4MyAxMTQuMjA2LDQ4Ljc5NSAxMTAuOTcxLDQ1LjI5NSAiLz48L2c+PGcgaWQ9IkJXIiBkaXNwbGF5PSJub25lIj48ZyBkaXNwbGF5PSJpbmxpbmUiPjxwYXRoIGZpbGw9IiM5Mzk1OTgiIGQ9Ik0xNDEuNjc3LDM0Ljg5Yy0yLjA4OC0wLjQyOS00LjI1LTAuNjU0LTYuNDYzLTAuNjU0Yy04LjUxNCwwLTE2LjI1OCwzLjMzMi0yMi4wMDgsOC43NmMtMC40NTEsMC40MjctMC44OSwwLjg2NS0xLjMxNiwxLjMxN2wtMTEuNzcxLDExLjc3MWwtMTEuNzctMTEuNzcxYy0wLjQyNi0wLjQ1Mi0wLjg2NS0wLjg5MS0xLjMxNy0xLjMxN2MtNS43NS01LjQyOC0xMy40OTYtOC43Ni0yMi4wMDctOC43NmMtMi4yMTUsMC00LjM3NiwwLjIyNi02LjQ2NCwwLjY1NGMtMTQuNjA0LDMtMjUuNjIxLDE1Ljk1Mi0yNS42MjEsMzEuNDMxaDAuMDE3YzAsMTUuNDU3LDEwLjk4OSwyOC4zOTYsMjUuNTY1LDMxLjQxOGMyLjEwNiwwLjQzOCw0LjI4NSwwLjY2OCw2LjUxNywwLjY2OGM4LjAwOSwwLDE1LjMzNy0yLjk1MywyMC45NjQtNy44MmwyLjY2OS0yLjY3bDAuMTU4LTAuMTU2YzAuMzg5LTAuNDI4LDAuNjI5LTAuOTk2LDAuNjI5LTEuNjIxYzAtMS4zMjgtMS4wNzctMi40MDQtMi40MDctMi40MDRjLTAuNjA4LDAtMS4xNjMsMC4yMjctMS41ODUsMC42bC0wLjIyMSwwLjIxOWwtMi41MTEsMi41MWMtNC45MzYsNC4yMjEtMTEuMjE0LDYuNTQxLTE3LjY5Niw2LjU0MWMtMS44NjQsMC0zLjcyOS0wLjE4OS01LjU0My0wLjU2NkM0Ni45MDEsOTAuNDI2LDM3Ljc1Nyw3OS4xODksMzcuNzU3LDY2LjMyaDAuMDczYzAtMTIuODk2LDkuMTYzLTI0LjEzNiwyMS43ODYtMjYuNzI5bC0wLjAwNC0wLjAxN2MxLjc3MS0wLjM1NywzLjU5MS0wLjUzOSw1LjQxMy0wLjUzOWM2Ljk3NiwwLDEzLjYyMywyLjY0NiwxOC43MTEsNy40NDljMC4zODMsMC4zNjIsMjMuMDYyLDIzLjI1NSwyMy4wNjIsMjMuMjU1bDAuMDA0LTAuMDA0bDAuMDEzLDAuMDEzbDE2LjUyOC0xNi41MjZjMy4xNDItMi44NDgsNy4zMDYtNC41ODcsMTEuODY5LTQuNTg3YzkuNzUyLDAsMTcuNjg1LDcuOTMzLDE3LjY4NSwxNy42ODVjMCw5Ljc1LTcuOTMzLDE3LjY4Mi0xNy42ODUsMTcuNjgyYy00LjMyNiwwLTguMjkzLTEuNTY0LTExLjM2OS00LjE1MmwtMi4wMTctMi4wMThsLTAuMzktMC4zOTFjLTAuNDExLTAuMzI0LTAuOTMtMC41MjEtMS40OTQtMC41MjFjLTEuMzI5LDAtMi40MDcsMS4wNzgtMi40MDcsMi40MDZjMCwwLjcxMywwLjMxMiwxLjM1LDAuODAyLDEuNzkxbC0wLjA4LTAuMDcybDIuMzM2LDIuMzQybDAuMTU4LDAuMTM1YzQuMDQ3LDMuNDA2LDkuMTg0LDUuMjgxLDE0LjQ2MSw1LjI4MWMxMi4zOTksMCwyMi40ODUtMTAuMDg2LDIyLjQ4NS0yMi40ODJjMC0xMi4zOTgtMTAuMDg2LTIyLjQ4NC0yMi40ODUtMjIuNDg0Yy01LjU4NiwwLTEwLjk0NywyLjA3MS0xNS4wOTQsNS44MzJsLTAuMDg2LDAuMDc4bC0xMy4yMDcsMTMuMjA1bC0zLjM5NS0zLjM5MmwxMS44NTMtMTEuODUzbDAuMDk3LTAuMWMwLjM1OS0wLjM4MiwwLjczNy0wLjc2LDEuMTIxLTEuMTIyYzUuMDktNC44MDQsMTEuNzM0LTcuNDQ5LDE4LjcxMy03LjQ0OWMxLjg1MSwwLDMuNywwLjE4Nyw1LjQ5OCwwLjU1NmMxMi42MjMsMi41OTMsMjEuNzg1LDEzLjgzMywyMS43ODUsMjYuNzI5YzAsMTIuODY5LTkuMTQ1LDI0LjEwNS0yMS43MzgsMjYuNzE3Yy0xLjgxNCwwLjM3Ny0zLjY4LDAuNTY2LTUuNTQ1LDAuNTY2Yy02LjQ4MSwwLTEyLjc1OS0yLjMyLTE3LjY5NS02LjU0MWwtMi41MS0yLjUxbC0wLjIyMi0wLjIxOWMtMC40MjMtMC4zNzMtMC45NzgtMC42LTEuNTg1LTAuNmMtMS4zMywwLTIuNDA2LDEuMDc2LTIuNDA2LDIuNDA0YzAsMC42MjUsMC4yMzksMS4xOTMsMC42MjksMS42MjFsMC4xNTcsMC4xNTZsMi42NjksMi42N2M1LjYyNyw0Ljg2NywxMi45NTUsNy44MiwyMC45NjMsNy44MmMyLjIzMywwLDQuNDEyLTAuMjMsNi41MTktMC42NjhjMTQuNTc1LTMuMDIxLDI1LjU2NS0xNS45NjEsMjUuNTY1LTMxLjQxOEMxNjcuMjk4LDUwLjg0MiwxNTYuMjgxLDM3Ljg5LDE0MS42NzcsMzQuODkiLz48cGF0aCBmaWxsPSIjOTM5NTk4IiBkPSJNODAuMTE4LDQ5LjY2OGMtNC4xMjQtMy43NDEtOS40NDgtNS44MDgtMTUuMDA0LTUuODN2LTAuMDAybC0wLjA1OCwwLjAwMWwtMC4wMzItMC4wMDFjLTEyLjM5OCwwLTIyLjQ4NCwxMC4wODYtMjIuNDg0LDIyLjQ4M2gwLjAxN2MwLDEyLjM5OSwxMC4wODYsMjIuNDg1LDIyLjQ4NCwyMi40ODVjNS4yNzgsMCwxMC40MTUtMS44NzcsMTQuNDYzLTUuMjgzbDAuMTU2LTAuMTMzbDIuMzM3LTIuMzQ0bC0wLjA4LDAuMDdjMC40OS0wLjQzOSwwLjgwMS0xLjA3OCwwLjgwMS0xLjc4OWMwLTEuMzI4LTEuMDc4LTIuNDA2LTIuNDA3LTIuNDA2Yy0wLjU2NCwwLTEuMDgzLDAuMTk1LTEuNDkzLDAuNTIxbC0wLjM5MSwwLjM5M0w3Ni40MSw3OS44NWMtMy4wNzYsMi41ODgtNy4wNDIsNC4xNTQtMTEuMzY5LDQuMTU0Yy05Ljc1MSwwLTE3LjY4My03LjkzNi0xNy42ODMtMTcuNjg1aDAuMDczYzAtOS43MzUsNy45MDktMTcuNjU5LDE3LjYzOS0xNy42ODJjNC41NDYsMC4wMTEsOC42OTQsMS43NDcsMTEuODIzLDQuNTg1bDIzLjIyMiwyMy4yMjVsMy40MS0zLjQxTDgwLjI4Nyw0OS44MjhMODAuMTE4LDQ5LjY2OHoiLz48L2c+PGxpbmVhckdyYWRpZW50IGlkPSJTVkdJRF8xN18iIGdyYWRpZW50VW5pdHM9InVzZXJTcGFjZU9uVXNlIiB4MT0iMTA4LjU4MDYiIHkxPSI2NC4zODg3IiB4Mj0iMTE4Ljc3MDYiIHkyPSI1NC4xOTg2Ij48c3RvcCAgb2Zmc2V0PSIwIiBzdHlsZT0ic3RvcC1jb2xvcjojMDAwMDAwIi8+PHN0b3AgIG9mZnNldD0iMC4wODk2IiBzdHlsZT0ic3RvcC1jb2xvcjojMDAwMDAwO3N0b3Atb3BhY2l0eTowLjkxMDQiLz48c3RvcCAgb2Zmc2V0PSIxIiBzdHlsZT0ic3RvcC1jb2xvcjojMDAwMDAwO3N0b3Atb3BhY2l0eTowIi8+PC9saW5lYXJHcmFkaWVudD48cG9seWdvbiBkaXNwbGF5PSJpbmxpbmUiIG9wYWNpdHk9IjAuNSIgZmlsbD0idXJsKCNTVkdJRF8xN18pIiBwb2ludHM9IjEwNi44ODQsNjIuNjkgMTEwLjI3OCw2Ni4wODYgMTExLjI2MSw2NS4xMDIgMTIwLjMxOSw1NS45NyAxMTcuMjUxLDUyLjMyNCAiLz48bGluZWFyR3JhZGllbnQgaWQ9IlNWR0lEXzE4XyIgZ3JhZGllbnRVbml0cz0idXNlclNwYWNlT25Vc2UiIHgxPSIxMDEuODA1MiIgeTE9IjU3LjYxODIiIHgyPSIxMTEuOTk1NiIgeTI9IjQ3LjQyNzciPjxzdG9wICBvZmZzZXQ9IjAiIHN0eWxlPSJzdG9wLWNvbG9yOiMwMDAwMDAiLz48c3RvcCAgb2Zmc2V0PSIwLjA4OTYiIHN0eWxlPSJzdG9wLWNvbG9yOiMwMDAwMDA7c3RvcC1vcGFjaXR5OjAuOTEwNCIvPjxzdG9wICBvZmZzZXQ9IjEiIHN0eWxlPSJzdG9wLWNvbG9yOiMwMDAwMDA7c3RvcC1vcGFjaXR5OjAiLz48L2xpbmVhckdyYWRpZW50Pjxwb2x5Z29uIGRpc3BsYXk9ImlubGluZSIgb3BhY2l0eT0iMC41IiBmaWxsPSJ1cmwoI1NWR0lEXzE4XykiIHBvaW50cz0iMTAwLjEzNiw1NS45MiAxMDMuNTAyLDU5LjI4NiAxMTQuMDkxLDQ4LjY5OCAxMTAuODU3LDQ1LjE5OCAiLz48L2c+PGcgaWQ9InJldmVyc2UiIGRpc3BsYXk9Im5vbmUiPjxwYXRoIGRpc3BsYXk9ImlubGluZSIgZmlsbD0iIzkzOTU5OCIgZD0iTTAsMHYxMzMuMDY1aDIwMC40NjNWMEgweiBNMTAwLjExNSw3Ni40NDdMNzYuODkzLDUzLjIyM2MtMy4xMjktMi44MzgtNy4yNzctNC41NzQtMTEuODIzLTQuNTg1Yy05LjczLDAuMDIyLTE3LjYzOSw3Ljk0Ni0xNy42MzksMTcuNjgyaC0wLjA3M2MwLDkuNzQ5LDcuOTMyLDE3LjY4NSwxNy42ODMsMTcuNjg1YzQuMzI3LDAsOC4yOTMtMS41NjYsMTEuMzY5LTQuMTU0bDIuMDE3LTIuMDE2bDAuMzkxLTAuMzkzYzAuNDEtMC4zMjYsMC45MjktMC41MjEsMS40OTMtMC41MjFjMS4zMjksMCwyLjQwNywxLjA3OCwyLjQwNywyLjQwNmMwLDAuNzExLTAuMzExLDEuMzUtMC44MDEsMS43ODlsMC4wOC0wLjA3bC0yLjMzNywyLjM0NGwtMC4xNTYsMC4xMzNjLTQuMDQ4LDMuNDA2LTkuMTg1LDUuMjgzLTE0LjQ2Myw1LjI4M2MtMTIuMzk4LDAtMjIuNDg0LTEwLjA4Ni0yMi40ODQtMjIuNDg1SDQyLjU0YzAtMTIuMzk3LDEwLjA4Ni0yMi40ODMsMjIuNDg0LTIyLjQ4M2wwLjAzMiwwLjAwMWwwLjA1OC0wLjAwMXYwLjAwMmM1LjU1NiwwLjAyMiwxMC44OCwyLjA4OSwxNS4wMDQsNS44M2wwLjE2OSwwLjE2bDIzLjIzOCwyMy4yMDlMMTAwLjExNSw3Ni40NDd6IE0xNDEuNzMzLDk3LjczOGMtMi4xMDYsMC40MzgtNC4yODUsMC42NjgtNi41MTksMC42NjhjLTguMDA4LDAtMTUuMzM2LTIuOTUzLTIwLjk2My03LjgybC0yLjY2OS0yLjY3bC0wLjE1Ny0wLjE1NmMtMC4zOS0wLjQyOC0wLjYyOS0wLjk5Ni0wLjYyOS0xLjYyMWMwLTEuMzI4LDEuMDc2LTIuNDA0LDIuNDA2LTIuNDA0YzAuNjA3LDAsMS4xNjIsMC4yMjcsMS41ODUsMC42bDAuMjIyLDAuMjE5bDIuNTEsMi41MWM0LjkzNyw0LjIyMSwxMS4yMTQsNi41NDEsMTcuNjk1LDYuNTQxYzEuODY1LDAsMy43My0wLjE4OSw1LjU0NS0wLjU2NmMxMi41OTQtMi42MTEsMjEuNzM4LTEzLjg0OCwyMS43MzgtMjYuNzE3YzAtMTIuODk2LTkuMTYyLTI0LjEzNi0yMS43ODUtMjYuNzI5Yy0xLjc5OC0wLjM2OS0zLjY0Ny0wLjU1Ni01LjQ5OC0wLjU1NmMtNi45NzksMC0xMy42MjMsMi42NDYtMTguNzEzLDcuNDQ5Yy0wLjM4NCwwLjM2Mi0wLjc2MiwwLjc0LTEuMTIxLDEuMTIybC0wLjA5NywwLjFMMTAzLjQzMSw1OS41NmwzLjM5NSwzLjM5MmwxMy4yMDctMTMuMjA1bDAuMDg2LTAuMDc4YzQuMTQ2LTMuNzYxLDkuNTA4LTUuODMyLDE1LjA5NC01LjgzMmMxMi4zOTksMCwyMi40ODUsMTAuMDg2LDIyLjQ4NSwyMi40ODRjMCwxMi4zOTYtMTAuMDg2LDIyLjQ4Mi0yMi40ODUsMjIuNDgyYy01LjI3NywwLTEwLjQxNC0xLjg3NS0xNC40NjEtNS4yODFsLTAuMTU4LTAuMTM1bC0yLjMzNi0yLjM0MmwwLjA4LDAuMDcyYy0wLjQ5LTAuNDQxLTAuODAyLTEuMDc4LTAuODAyLTEuNzkxYzAtMS4zMjgsMS4wNzgtMi40MDYsMi40MDctMi40MDZjMC41NjQsMCwxLjA4MywwLjE5NywxLjQ5NCwwLjUyMWwwLjM5LDAuMzkxbDIuMDE3LDIuMDE4YzMuMDc2LDIuNTg4LDcuMDQzLDQuMTUyLDExLjM2OSw0LjE1MmM5Ljc1MiwwLDE3LjY4NS03LjkzMiwxNy42ODUtMTcuNjgyYzAtOS43NTItNy45MzMtMTcuNjg1LTE3LjY4NS0xNy42ODVjLTQuNTYzLDAtOC43MjgsMS43MzktMTEuODY5LDQuNTg3bC0xNi41MjgsMTYuNTI2bC0wLjAxMy0wLjAxM2wtMC4wMDQsMC4wMDRjMCwwLTIyLjY4LTIyLjg5My0yMy4wNjItMjMuMjU1Yy01LjA4OC00LjgwNC0xMS43MzUtNy40NDktMTguNzExLTcuNDQ5Yy0xLjgyMiwwLTMuNjQyLDAuMTgyLTUuNDEzLDAuNTM5bDAuMDA0LDAuMDE3QzQ2Ljk5Myw0Mi4xODUsMzcuODMsNTMuNDI1LDM3LjgzLDY2LjMyaC0wLjA3M2MwLDEyLjg2OSw5LjE0NCwyNC4xMDUsMjEuNzM5LDI2LjcxN2MxLjgxNCwwLjM3NywzLjY3OSwwLjU2Niw1LjU0MywwLjU2NmM2LjQ4MiwwLDEyLjc2LTIuMzIsMTcuNjk2LTYuNTQxbDIuNTExLTIuNTFsMC4yMjEtMC4yMTljMC40MjItMC4zNzMsMC45NzctMC42LDEuNTg1LTAuNmMxLjMzLDAsMi40MDcsMS4wNzYsMi40MDcsMi40MDRjMCwwLjYyNS0wLjI0LDEuMTkzLTAuNjI5LDEuNjIxbC0wLjE1OCwwLjE1NmwtMi42NjksMi42N2MtNS42MjcsNC44NjctMTIuOTU1LDcuODItMjAuOTY0LDcuODJjLTIuMjMyLDAtNC40MTEtMC4yMy02LjUxNy0wLjY2OEM0My45NDYsOTQuNzE3LDMyLjk1Nyw4MS43NzcsMzIuOTU3LDY2LjMySDMyLjk0YzAtMTUuNDc5LDExLjAxNy0yOC40MzEsMjUuNjIxLTMxLjQzMWMyLjA4OC0wLjQyOSw0LjI0OS0wLjY1NCw2LjQ2NC0wLjY1NGM4LjUxMSwwLDE2LjI1NywzLjMzMiwyMi4wMDcsOC43NmMwLjQ1MiwwLjQyNywwLjg5MSwwLjg2NSwxLjMxNywxLjMxN2wxMS43NywxMS43NzFsMTEuNzcxLTExLjc3MWMwLjQyNy0wLjQ1MiwwLjg2NS0wLjg5MSwxLjMxNi0xLjMxN2M1Ljc1LTUuNDI4LDEzLjQ5NC04Ljc2LDIyLjAwOC04Ljc2YzIuMjEzLDAsNC4zNzUsMC4yMjYsNi40NjMsMC42NTRjMTQuNjA0LDMsMjUuNjIxLDE1Ljk1MiwyNS42MjEsMzEuNDMxQzE2Ny4yOTgsODEuNzc3LDE1Ni4zMDgsOTQuNzE3LDE0MS43MzMsOTcuNzM4eiIvPjwvZz48L3N2Zz4="
-}},{}],6:[function(t,e,i){!function(){"use strict";t("jquery"),t("jquery-ui");var i=t("d3"),n=t("filesaver.js"),r=t("./axisgroup.js"),s=t("./dataloader.js"),a=t("./datamanager.js"),o=t("./utils.js"),l=t("./htmlnotes.json"),u=t("./images.json");!function(i){"object"==typeof e&&"object"==typeof e.exports?e.exports=i(t("jquery"),window,document):i($,window,document)}(function(t,e,c,h){var d=function(e,i){var n=t(e);n.phenogrid(i)};e.Phenogrid={createPhenogridForElement:d},t.widget("ui.phenogrid",{config:{serverURL:"https://monarchinitiative.org",gridSkeletonData:{},selectedCalculation:0,selectedSort:"Frequency",messaging:{misconfig:"Please fix your config to have at least one target group.",gridSkeletonDataError:"No phenotypes to compare.",noAssociatedGenotype:"This gene has no associated genotypes.",noSimSearchMatchForExpandedGenotype:"No similarity matches found between the provided phenotypes and expanded genotypes.",noSimSearchMatch:"No similarity matches found for {%groupName%} based on the provided phenotypes."},gridSkeletonDataVendor:"",owlSimFunction:"",targetSpecies:"",searchResultLimit:100,geneList:[]},internalOptions:{invertAxis:!1,simSearchQuery:{URL:"/simsearch/phenotype",inputItemsString:"input_items=",targetSpeciesString:"&target_species=",limitString:"&limit"},compareQuery:{URL:"/compare"},monarchInitiativeText:"Powered by The Monarch Initiative",unmatchedButtonLabel:"Unmatched Phenotypes",optionsBtnText:"Options",gridTitle:"Phenotype Similarity Comparison",singleTargetModeTargetLengthLimit:30,sourceLengthLimit:30,multiTargetsModeTargetLengthLimit:10,targetLabelCharLimit:23,ontologyDepth:10,ontologyDirection:"OUTGOING",ontologyRelationship:"subClassOf",ontologyQuery:"/neighborhood/",ontologyTreeAmounts:1,targetGroupItemExpandLimit:5,unstableTargetGroupItemPrefix:["MONARCH:","_:",":.well-known"],colorDomains:[0,.2,.4,.6,.8,1],colorRanges:["rgb(237,248,177)","rgb(199,233,180)","rgb(127,205,187)","rgb(65,182,196)","rgb(29,145,192)","rgb(34,94,168)"],minimap:{x:112,y:75,width:100,height:100,bgColor:"#fff",borderColor:"#666",borderThickness:2,miniCellSize:2,shadedAreaBgColor:"#666",shadedAreaOpacity:.5},scrollbar:{barToGridMargin:20,barThickness:1,barColor:"#ccc",sliderThickness:8,sliderColor:"#999"},targetGroupDividerLine:{color:"#EA763B",thickness:1,rotatedDividerLength:150},gridRegion:{x:240,y:200,cellPad:19,cellSize:12,rowLabelOffset:25,colLabelOffset:20,scoreOffset:5},gradientRegion:{width:240,height:5},optionsControls:{left:30,top:35,defaultButtonWidth:75},phenotypeSort:["Alphabetic","Frequency and Rarity","Frequency"],similarityCalculation:[{label:"Similarity",calc:0,high:"Max",low:"Min"},{label:"Ratio (q)",calc:1,high:"More Similar",low:"Less Similar"},{label:"Uniqueness",calc:2,high:"Highest",low:"Lowest"},{label:"Ratio (t)",calc:3,high:"More Similar",low:"Less Similar"}]},_destroy:function(){this.element.empty()},_create:function(){if(this.configoptions="undefined"!=typeof configoptions?configoptions:{},this.state=t.extend({},this.internalOptions,this.config,this.configoptions,this.options),this.state.targetGroupList=[],this.state.initialTargetGroupLoadList=[],this.state.selectedCompareTargetGroup=[],this._createPhenogridContainer(),this._showLoadingSpinner(),this.state.gridSkeletonData.xAxis.length>0&&this.state.gridSkeletonData.yAxis.length>0){"undefined"!=typeof this.state.gridSkeletonData.title&&""!==this.state.gridSkeletonData.title&&null!==this.state.gridSkeletonData.title&&(this.state.gridTitle=this.state.gridSkeletonData.title),this._parseGridSourceList();var e=this;this.state.asyncDataLoadingCallback=function(){e._asyncDataLoadingCB(e)},"IMPC"===this.state.gridSkeletonDataVendor?this._initGridSkeletonDataForVendor():"compare"===this.state.owlSimFunction&&0!==this.state.geneList.length?this._initCompare():"search"===this.state.owlSimFunction&&""!==this.state.targetSpecies?this._initSearch():this._initGridSkeletonData()}else this._showGridSkeletonDataErrorMsg()},_parseGridSourceList:function(){for(var t=[],e=0;e
"],_default:[0,"",""]};Pt.optgroup=Pt.option,Pt.tbody=Pt.tfoot=Pt.colgroup=Pt.caption=Pt.thead,Pt.th=Pt.td;var Ot=/<|?\w+;/;!function(){var t=Z.createDocumentFragment(),e=t.appendChild(Z.createElement("div")),i=Z.createElement("input");i.setAttribute("type","radio"),i.setAttribute("checked","checked"),i.setAttribute("name","t"),e.appendChild(i),nt.checkClone=e.cloneNode(!0).cloneNode(!0).lastChild.checked,e.innerHTML="",nt.noCloneChecked=!!e.cloneNode(!0).lastChild.defaultValue}();var Rt=/^key/,Ut=/^(?:mouse|pointer|contextmenu|drag|drop)|click/,Ft=/^([^.]*)(?:\.(.+)|)/;st.event={global:{},add:function(t,e,i,n,r){var s,a,o,l,u,c,h,d,p,f,g,m=Dt.get(t);if(m)for(i.handler&&(s=i,i=s.handler,r=s.selector),i.guid||(i.guid=st.guid++),(l=m.events)||(l=m.events={}),(a=m.handle)||(a=m.handle=function(e){return"undefined"!=typeof st&&st.event.triggered!==e.type?st.event.dispatch.apply(t,arguments):void 0}),e=(e||"").match(xt)||[""],u=e.length;u--;)o=Ft.exec(e[u])||[],p=g=o[1],f=(o[2]||"").split(".").sort(),p&&(h=st.event.special[p]||{},p=(r?h.delegateType:h.bindType)||p,h=st.event.special[p]||{},c=st.extend({type:p,origType:g,data:n,handler:i,guid:i.guid,selector:r,needsContext:r&&st.expr.match.needsContext.test(r),namespace:f.join(".")},s),(d=l[p])||(d=l[p]=[],d.delegateCount=0,h.setup&&h.setup.call(t,n,f,a)!==!1||t.addEventListener&&t.addEventListener(p,a)),h.add&&(h.add.call(t,c),c.handler.guid||(c.handler.guid=i.guid)),r?d.splice(d.delegateCount++,0,c):d.push(c),st.event.global[p]=!0)},remove:function(t,e,i,n,r){var s,a,o,l,u,c,h,d,p,f,g,m=Dt.hasData(t)&&Dt.get(t);if(m&&(l=m.events)){for(e=(e||"").match(xt)||[""],u=e.length;u--;)if(o=Ft.exec(e[u])||[],p=g=o[1],f=(o[2]||"").split(".").sort(),p){for(h=st.event.special[p]||{},p=(n?h.delegateType:h.bindType)||p,d=l[p]||[],o=o[2]&&new RegExp("(^|\\.)"+f.join("\\.(?:.*\\.|)")+"(\\.|$)"),a=s=d.length;s--;)c=d[s],!r&&g!==c.origType||i&&i.guid!==c.guid||o&&!o.test(c.namespace)||n&&n!==c.selector&&("**"!==n||!c.selector)||(d.splice(s,1),c.selector&&d.delegateCount--,h.remove&&h.remove.call(t,c));a&&!d.length&&(h.teardown&&h.teardown.call(t,f,m.handle)!==!1||st.removeEvent(t,p,m.handle),delete l[p])}else for(p in l)st.event.remove(t,p+e[u],i,n,!0);st.isEmptyObject(l)&&Dt.remove(t,"handle events")}},dispatch:function(t){t=st.event.fix(t);var e,i,n,r,s,a=[],o=X.call(arguments),l=(Dt.get(this,"events")||{})[t.type]||[],u=st.event.special[t.type]||{};if(o[0]=t,t.delegateTarget=this,!u.preDispatch||u.preDispatch.call(this,t)!==!1){for(a=st.event.handlers.call(this,t,l),e=0;(r=a[e++])&&!t.isPropagationStopped();)for(t.currentTarget=r.elem,i=0;(s=r.handlers[i++])&&!t.isImmediatePropagationStopped();)(!t.rnamespace||t.rnamespace.test(s.namespace))&&(t.handleObj=s,t.data=s.data,n=((st.event.special[s.origType]||{}).handle||s.handler).apply(r.elem,o),void 0!==n&&(t.result=n)===!1&&(t.preventDefault(),t.stopPropagation()));return u.postDispatch&&u.postDispatch.call(this,t),t.result}},handlers:function(t,e){var i,n,r,s,a=[],o=e.delegateCount,l=t.target;if(o&&l.nodeType&&("click"!==t.type||isNaN(t.button)||t.button<1))for(;l!==this;l=l.parentNode||this)if(1===l.nodeType&&(l.disabled!==!0||"click"!==t.type)){for(n=[],i=0;o>i;i++)s=e[i],r=s.selector+" ",void 0===n[r]&&(n[r]=s.needsContext?st(r,this).index(l)>-1:st.find(r,this,null,[l]).length),n[r]&&n.push(s);n.length&&a.push({elem:l,handlers:n})}return o]*)\/>/gi,Wt=/
-
+ -->
+
+
+
+
diff --git a/js/axisgroup.js b/js/axisgroup.js
index 6668eb37..52fc4be4 100644
--- a/js/axisgroup.js
+++ b/js/axisgroup.js
@@ -8,18 +8,18 @@ var d3 = require('d3');
Namespace: phenogrid.axisgroup
- Constructor: AxisGroup
+ Constructor: AxisGroup
an object routine that will wrap data required for axis rendering
Parameters:
renderStartPos - render starting position
renderEndPos - render end position
- items - item list to use for the axis display
+ items - item list to use for the axis display
*/
var AxisGroup = function(renderStartPos, renderEndPos, items) {
this.renderStartPos = renderStartPos;
this.renderEndPos = renderEndPos;
-
+
// remove all invisible genotypes
for (var index in items) {
if (items[index].type === 'genotype' && items[index].visible === false) {
@@ -30,7 +30,7 @@ var AxisGroup = function(renderStartPos, renderEndPos, items) {
};
/*
- Class: AxisGroup
+ Class: AxisGroup
an object routine that will wrap data required for axis rendering
*/
AxisGroup.prototype = {
@@ -60,7 +60,7 @@ AxisGroup.prototype = {
Return:
position index
- */
+ */
getRenderEndPos: function() {
return this.renderEndPos;
},
@@ -68,12 +68,12 @@ AxisGroup.prototype = {
// don't let the postion go pass the max group size
if (position > this.groupLength()) {
position = this.groupLength();
- }
+ }
this.renderEndPos = position;
- },
+ },
/*
Function: itemAt
- gets a single item at a specified index position within the rendered axisgroup
+ gets a single item at a specified index position within the rendered axisgroup
Parameters:
index
@@ -89,13 +89,13 @@ AxisGroup.prototype = {
/*
Function: get
- gets a single item element using a key from the axis
-
+ gets a single item element using a key from the axis
+
Parameters:
key
Returns:
item element
- */
+ */
get: function(key) {
return this.items[key];
},
@@ -106,7 +106,7 @@ AxisGroup.prototype = {
Return:
array of objects of items
- */
+ */
entries: function() {
var keys = Object.keys(this.items);
var a = [];
@@ -125,16 +125,16 @@ AxisGroup.prototype = {
Return:
array of objects of items
- */
+ */
getItems: function() {
/* if (typeof(this.items) !== 'undefined' && this.items != null) {
-
-
+
+
var a = this.items.map(function(d) { return d;} );
-
+
return a.slice(this.renderStartPos, this.renderEndPos);
- }
-*/
+ }
+*/
var keys = Object.keys(this.items);
var a = [];
// loop through only those that are rendered
@@ -143,19 +143,19 @@ AxisGroup.prototype = {
var el = this.items[key];
a[key] = el;
}
- return a;
+ return a;
},
-
+
/*
Function: keys
returns a list of key (id) values
-
+
Returns:
array of key ids
*/
keys: function () {
var renderedList = this.getItems();
- return Object.keys(renderedList);
+ return Object.keys(renderedList);
},
/*
@@ -164,7 +164,7 @@ AxisGroup.prototype = {
Return:
length
- */
+ */
displayLength: function() {
return (this.renderEndPos - this.renderStartPos);
},
@@ -175,7 +175,7 @@ AxisGroup.prototype = {
Return:
length
- */
+ */
groupLength: function() {
return Object.keys(this.items).length;
},
@@ -183,7 +183,7 @@ AxisGroup.prototype = {
/*
Function: position
gets the relative position a key within the rendered or viewable range
-
+
Parameters:
key - a key value to locate
@@ -191,7 +191,7 @@ AxisGroup.prototype = {
index value, -1 if item not found within rendered range
*/
position: function(key) {
- var renderedList = this.keys();
+ var renderedList = this.keys();
return renderedList.indexOf(key);
},
@@ -201,7 +201,7 @@ AxisGroup.prototype = {
Return:
array
- */
+ */
groupIDs: function() {
return Object.keys(this.items);
},
@@ -211,7 +211,7 @@ AxisGroup.prototype = {
Return:
array of objects of items
- */
+ */
groupEntries: function() {
var keys = Object.keys(this.items);
var a = [];
@@ -226,14 +226,14 @@ AxisGroup.prototype = {
/*
Function: contains
- determines if a item element is contained within the axis
-
+ determines if a item element is contained within the axis
+
Parameters:
key - key id to locate
Returns:
boolean
- */
+ */
contains: function(key) {
if (typeof(this.get(key)) != 'undefined') {
@@ -245,19 +245,19 @@ AxisGroup.prototype = {
},
/*
Function: sort
- sorts the data on the axis
-
+ sorts the data on the axis
+
Parameters:
by - specifies the the sort type
- */
+ */
sort: function(by) {
var temp = this.groupEntries();
if (by === 'Frequency') {
//sortFunc = self._sortByFrequency;
//this.items.sort(function(a,b) {
- temp.sort(function(a,b) {
+ temp.sort(function(a,b) {
var diff = b.count - a.count;
if (diff === 0) {
diff = a.id.localeCompare(b.id);
@@ -266,13 +266,13 @@ AxisGroup.prototype = {
});
} else if (by === 'Frequency and Rarity') {
//this.items.sort(function(a,b) {
- temp.sort(function(a,b) {
+ temp.sort(function(a,b) {
return b.sum - a.sum;
});
} else if (by === 'Alphabetic') {
//this.items.sort(function(a,b) {
- temp.sort(function(a,b) {
- var labelA = a.label,
+ temp.sort(function(a,b) {
+ var labelA = a.label,
labelB = b.label;
if (labelA < labelB) {return -1;}
if (labelA > labelB) {return 1;}
@@ -290,17 +290,17 @@ AxisGroup.prototype = {
/*
Function: getScale
- creates a d3 scale from the data on the axis
-
+ creates a d3 scale from the data on the axis
+
Parameters:
d3 scale
- */
+ */
getScale: function() {
var values = this.keys();
- var scale = d3.scale.ordinal()
+ var scale = d3.scaleBand()
.domain(values)
- .rangeRoundBands([0, values.length]);
+ .rangeRound([0, values.length]);
return scale;
}
};
diff --git a/js/dataloader.js b/js/dataloader.js
index 4bd53af7..25505786 100644
--- a/js/dataloader.js
+++ b/js/dataloader.js
@@ -11,17 +11,81 @@ var Utils = require('./utils.js');
Class: DataLoader
handles all loading of the data external
servers, transformations.
-
+
Parameters:
serverUrl - sim server url
- simSearchQuery - sim search query specific url string
+ useSimSearchQuery - whether to perform compare (false) or search (true)
limit - cutoff number
*/
-
+
+function isBioLinkServer(serverURL) {
+ return serverURL.indexOf('https://api.monarchinitiative.org') === 0;
+}
+
+function buildSearchQuery(useBioLink, inputItemsString, qrySourceList) {
+ // console.log('buildSearchQuery', useBioLink, inputItemsString, qrySourceList);
+ if (useBioLink) {
+ var result = inputItemsString + qrySourceList.join("&id=");
+ // console.log('bsq', inputItemsString, qrySourceList, result);
+ }
+ else {
+ var result = inputItemsString + qrySourceList.join("+");
+ }
+ return result;
+}
+
+function buildCompareQuery(useBioLink, sourceList, geneList) {
+ console.log('buildCompareQuery', useBioLink, sourceList, geneList);
+ if (useBioLink) {
+ var result = sourceList.join("+") + '/' + geneList.join(",");
+ }
+ else {
+ var result = sourceList.join("+") + '/' + geneList.join(",");
+ }
+ return result;
+}
+
// Define the DataLoader constructor using an object constructor function
-var DataLoader = function(serverURL, simSearchQuery, limit) {
- this.serverURL = serverURL;
- this.simSearchQuery = simSearchQuery; // object
+var DataLoader = function(serverURL, useSimSearchQuery, limit) {
+ this.serverURL = serverURL;
+ var useBioLink = isBioLinkServer(serverURL);
+
+ if (useSimSearchQuery) {
+ if (useBioLink) {
+ // https://api.monarchinitiative.org/api/sim/search?id=HP%3A0000006&id=HP%3A0000174&limit=100
+ this.simQuery = { // HTTP POST
+ URL: '/api/sim/search',
+ inputItemsString: 'id=', // HTTP POST, body parameter
+ targetSpeciesString: '&taxon=', // HTTP POST, body parameter
+ limitString: '&limit'
+ };
+ }
+ else {
+ this.simQuery = { // HTTP POST
+ URL: '/simsearch/phenotype',
+ inputItemsString: 'input_items=', // HTTP POST, body parameter
+ targetSpeciesString: '&target_species=', // HTTP POST, body parameter
+ limitString: '&limit'
+ };
+ }
+ }
+ else {
+ if (useBioLink) {
+ //
+ // Not Yet Implemented
+ // Should use https://api.monarchinitiative.org/api/sim/compare
+ //
+ this.simQuery = {
+ URL: '/BioLinkCompareEndpointNotUsedYet'
+ };
+ }
+ else {
+ this.simQuery = { // compare API takes HTTP GET, so no body parameters
+ URL: '/compare' // used for owlSimFunction === 'compare' and genotype expansion compare simsearch - Joe
+ };
+ }
+ }
+
this.qryString = '';
this.groupsNoMatch = []; // contains group names that don't have simsearch matches
this.limit = limit;
@@ -46,7 +110,7 @@ DataLoader.prototype = {
fetch and load data from external source (i.e., owlsims)
- Parameters:
+ Parameters:
qrySourceList - list of source items to query
targetGroupList - list of targetGroups, array
limit - value to limit targets returned
@@ -56,12 +120,17 @@ DataLoader.prototype = {
// The qrySourceList has already had all duplicated IDs removed in _parseQuerySourceList() of phenogrid.js - Joe
this.origSourceList = qrySourceList;
- this.qryString = this.simSearchQuery.inputItemsString + qrySourceList.join("+");
+ var useBioLink = isBioLinkServer(this.serverURL);
+ // this.qryString = this.simQuery.inputItemsString + qrySourceList.join("+");
+ this.qryString = buildSearchQuery(
+ useBioLink,
+ this.simQuery.inputItemsString,
+ qrySourceList);
// limit is used in analyze/phenotypes search mode
// can also be used in general simsearch query - Joe
if (typeof(limit) !== 'undefined') {
- this.qryString += this.simSearchQuery.limitString + limit;
+ this.qryString += this.simQuery.limitString + limit;
}
this.postDataLoadCallback = asyncDataLoadingCallback;
@@ -75,52 +144,57 @@ DataLoader.prototype = {
fetch and load data from the monarch compare api
- Parameters:
+ Parameters:
qrySourceList - list of source items to query
geneList - combined list of genes
asyncDataLoadingCallback - callback
*/
loadCompareData: function(targetGroup, qrySourceList, geneList, asyncDataLoadingCallback) {
this.postDataLoadCallback = asyncDataLoadingCallback;
-
+
// save the original source listing
// The qrySourceList has already had all duplicated IDs removed in _parseQuerySourceList() of phenogrid.js - Joe
this.origSourceList = qrySourceList;
// example: monarchinitiative.org/compare/HP:0000726+HP:0000746+HP:0001300/NCBIGene:388552,NCBIGene:12166
- this.qryString = this.serverURL + this.simSearchQuery.URL + '/' + qrySourceList.join("+") + '/' + geneList.join(",");
+ // this.qryString = this.serverURL + this.simQuery.URL + '/' + qrySourceList.join("+") + '/' + geneList.join(",");
+ var useBioLink = isBioLinkServer(this.serverURL);
+ this.qryString = buildCompareQuery(
+ useBioLink,
+ qrySourceList,
+ geneList);
var self = this;
-
+
// Separate the ajax request with callbacks
var jqxhr = $.ajax({
url: this.qryString,
- method: 'GET',
+ method: 'GET',
async : true,
dataType : 'json'
});
-
+
jqxhr.done(function(data) {
//console.log('compare data loaded:');
//console.log(data);
-
+
// sometimes the compare api doesn't find any matches, we need to stop here - Joe
if (typeof (data.b) === 'undefined') {
// Add the 'compare' name to the groupsNoMatch array
self.groupsNoMatch.push(targetGroup);
} else {
// use 'compare' as the key of the named array
- self.transform(targetGroup, data);
+ self.transform(targetGroup, data);
}
-
- self.postDataLoadCallback();
+
+ self.postDataLoadCallback();
});
-
- jqxhr.fail(function () {
+
+ jqxhr.fail(function () {
console.log('Ajax error - loadCompareData()')
});
},
-
+
// In progress 03/09/2016
loadCompareDataForVendor: function(qrySourceList, targetGroupList, asyncDataLoadingCallback, multiTargetsModeTargetLengthLimit) {
// save the original source listing
@@ -129,7 +203,7 @@ DataLoader.prototype = {
// use the default comma to separate each list into each genotype profile
// example: monarchinitiative.org/compare/HP:0000726+HP:0000746+HP:0001300/MP+MP+MP,MP+MP,MP+MP+MP+MP...
- this.qryString = this.serverURL + this.simSearchQuery.URL + '/' + qrySourceList.join("+") + '/';
+ this.qryString = this.serverURL + this.simQuery.URL + '/' + qrySourceList.join("+") + '/';
this.postDataLoadCallback = asyncDataLoadingCallback;
@@ -141,8 +215,8 @@ DataLoader.prototype = {
if (targetGrpList.length > 0) {
var target = targetGrpList[0]; // pull off the first to start processing
targetGrpList = targetGrpList.slice(1);
-
-
+
+
var listOfListsPerTargetGroup = [];
for (var j = 0; j < target.entities.length; j++) {
var eachList = [];
@@ -151,58 +225,58 @@ DataLoader.prototype = {
}
// add new property
target.entities[j].combinedList = eachList.join('+');
-
+
// default separator of array.join(separator) is comma
// join all the MP inside each MP list with plus sign, and join each list with default comma
listOfListsPerTargetGroup.push(target.entities[j].combinedList);
}
-
-
+
+
// use the default comma to separate each list into each genotype profile
// example: monarchinitiative.org/compare/HP:0000726+HP:0000746+HP:0001300/MP+MP+MP,MP+MP,MP+MP+MP+MP...
var qryStringPerTargetGroup = qryString + listOfListsPerTargetGroup.join();
var self = this;
-
+
// Separate the ajax request with callbacks
var jqxhr = $.ajax({
url: qryStringPerTargetGroup,
- method: 'GET',
+ method: 'GET',
async : true,
dataType : 'json'
});
-
+
jqxhr.done(function(data) {
//console.log('compare data loaded:');
//console.log(data);
-
+
// sometimes the compare api doesn't find any matches, we need to stop here - Joe
if (typeof (data.b) === 'undefined') {
// Add the target.groupName to the groupsNoMatch array
self.groupsNoMatch.push(target.groupName);
} else {
// Will use target.groupName as the key of the named array
- self.transformDataForVendor(target, data, multiTargetsModeTargetLengthLimit);
+ self.transformDataForVendor(target, data, multiTargetsModeTargetLengthLimit);
}
-
+
// iterative back to process to make sure we processed all the targetGrpList
self.processDataForVendor(targetGrpList, self.qryString, multiTargetsModeTargetLengthLimit);
});
-
- jqxhr.fail(function () {
+
+ jqxhr.fail(function () {
console.log('Ajax error - processDataForVendor()')
});
} else {
this.postDataLoadCallback(); // make a call back to post data init function
}
},
-
+
/*
Function: process
process routine being async query to load data from external source (i.e., owlsims)
- Parameters:
+ Parameters:
targetGrpList - list of target Group items (i.e., group)
qryString - query list url parameters, which includes list of sources
*/
@@ -210,20 +284,30 @@ DataLoader.prototype = {
if (targetGrpList.length > 0) {
var target = targetGrpList[0]; // pull off the first to start processing
targetGrpList = targetGrpList.slice(1);
-
- // need to add on target targetGroup groupId
- var postData = qryString + this.simSearchQuery.targetSpeciesString + target.groupId;
-
- var postFetchCallback = this.postSimsFetchCb;
- this.postFetch(this.serverURL + this.simSearchQuery.URL, target, targetGrpList, postFetchCallback, postData);
+ // // need to add on target targetGroup groupId
+ // var postData = qryString + this.simQuery.targetSpeciesString + target.groupId;
+ var isBioLink = isBioLinkServer(this.serverURL);
+ var postFetchCallback = isBioLink ?
+ this.postSimsFetchBioLinkCb :
+ this.postSimsFetchCb;
+
+ this.postFetch(
+ this.serverURL,
+ this.simQuery.URL,
+ target,
+ targetGrpList,
+ postFetchCallback,
+ qryString,
+ this.simQuery.targetSpeciesString,
+ target.groupId);
} else {
this.postDataLoadCallback(); // make a call back to post data init function
}
},
/*
- Function: postFetch
+ Function: postFetch
generic ajax call for all POST queries
Parameters:
@@ -232,37 +316,111 @@ DataLoader.prototype = {
targets - target list
callback
postData - data to be posted
- */
- postFetch: function (url, target, targets, callback, postData) {
+ */
+ postFetch: function (url, queryURL, target, targets, callback, qryString, targetSpeciesString, targetGroupId) {
var self = this;
- console.log('POST:' + url);
+ if (isBioLinkServer('https://api.monarchinitiative.org')) {
+ // console.log('POST:', url, queryURL, target, targets, qryString);
+ var getData = qryString + targetSpeciesString + targetGroupId;
+ // Separate the ajax request with callbacks
+ var jqxhr = $.ajax({
+ url: url + queryURL,
+ method: 'GET',
+ data: getData,
+ async : true,
+ timeout: 60000,
+ dataType : 'json'
+ });
+ jqxhr.done(function(data) {
+ // console.log('doneget', target, targets, data);
+ callback(self, target, targets, data);
+ });
+
+ jqxhr.fail(function(jqXHR, textStatus, errorThrown) {
+ console.log('Ajax error - postFetch()', jqXHR, textStatus, errorThrown);
+ });
+ }
+ else {
+ var postData = qryString + targetSpeciesString + targetGroupId;
+ // Separate the ajax request with callbacks
+ var jqxhr = $.ajax({
+ url: url + queryURL,
+ method: 'POST',
+ data: postData,
+ async : true,
+ timeout: 60000,
+ dataType : 'json'
+ });
+ jqxhr.done(function(data) {
+ // console.log('donepost', target, targets, data);
+ callback(self, target, targets, data);
+ });
+
+ jqxhr.fail(function(jqXHR, textStatus, errorThrown) {
+ console.log('Ajax error - postFetch()', jqXHR, textStatus, errorThrown);
+ });
+ }
- // Separate the ajax request with callbacks
- var jqxhr = $.ajax({
- url: url,
- method: 'POST',
- data: postData,
- async : true,
- timeout: 60000,
- dataType : 'json'
- });
-
- jqxhr.done(function(data) {
- callback(self, target, targets, data);
- });
-
- jqxhr.fail(function () {
- console.log('Ajax error - postFetch()')
- });
},
-
+
/*
- Function: postSimsFetchCb
+ Function: postSimsFetchBioLinkCb
Callback function for the post async ajax call
*/
+ postSimsFetchBioLinkCb: function(self, target, targetGrpList, data) {
+ // console.log('postSimsFetchBioLinkCb', target, targetGrpList, data);
+
+ function legacyMatches(bioLinkMatches) {
+ return bioLinkMatches.map(function(e) {
+ var legacy = Object.assign({}, e);
+ legacy.matches = [];
+ legacy.pairwise_match.forEach(function(m) {
+ legacy.matches.push({
+ a: m.reference,
+ b: m.match,
+ lcs: m.lcs
+ });
+ });
+ delete legacy.pairwise_match;
+ legacy.score = {
+ score: legacy.score
+ };
+ return legacy;
+ });
+ }
+
+ var legacyMetadata = {
+ 'maxMaxIC': data.metadata.max_max_ic // '14.87790',
+ };
+ var legacyData = {
+ a: data.query.ids.map(function(i) {
+ return i.id;
+ }),
+ b: legacyMatches(data.matches),
+ metadata: legacyMetadata,
+ ids: data.query.ids
+ };
+
+ if (legacyData !== null || typeof(legacyData) !== 'undefined') {
+ // legacyData.b contains all the matches, if not present, then no matches - Joe
+ if (typeof(legacyData.b) === 'undefined' || legacyData.b.length === 0) {
+ // Add the group name to the groupsNoMatch array
+ self.groupsNoMatch.push(target.groupName);
+ } else {
+ // save the original raw owlsim data
+ self.owlsimsData[target.groupName] = legacyData;
+ // now transform data to there basic data structures
+ self.transform(target.groupName, legacyData);
+ }
+ }
+ // iterative back to process to make sure we processed all the targetGrpList
+ self.process(targetGrpList, self.qryString);
+ },
+
postSimsFetchCb: function(self, target, targetGrpList, data) {
+ // console.log('postSimsFetchCb', target, targetGrpList, data);
if (data !== null || typeof(data) !== 'undefined') {
// data.b contains all the matches, if not present, then no matches - Joe
if (typeof(data.b) === 'undefined') {
@@ -272,7 +430,7 @@ DataLoader.prototype = {
// save the original raw owlsim data
self.owlsimsData[target.groupName] = data;
// now transform data to there basic data structures
- self.transform(target.groupName, data);
+ self.transform(target.groupName, data);
}
}
// iterative back to process to make sure we processed all the targetGrpList
@@ -286,27 +444,31 @@ DataLoader.prototype = {
For a given model, extract the sim search data including IC scores and the triple:
The a column, b column, and lowest common subsumer for the triple's IC score, use the LCS score
-
+
Parameters:
targetGroup - targetGroup name
data - owlsims structured data
*/
- transform: function(targetGroup, data) {
+ transform: function(targetGroup, data) {
+ // console.log('transform', targetGroup, data.a.length, data.b.length, this.sourceData);
if (typeof(data) !== 'undefined' && typeof (data.b) !== 'undefined') {
- console.log("Transforming simsearch data of group: " + targetGroup);
+ // console.log("Transforming simsearch data of group: " + targetGroup);
// sometimes the 'metadata' field might be missing from the JSON - Joe
// extract the maxIC score; ugh!
if (typeof (data.metadata) !== 'undefined') {
this.maxMaxIC = data.metadata.maxMaxIC;
}
-
+ if (typeof (data.ids) !== 'undefined') {
+ this.ids = data.ids;
+ }
+
// just initialize the specific targetGroup
-
+
// Here we don't reset the cellData, targetData, and sourceData every time,
// because we want to append the genotype expansion data - Joe
- // No need to redefine this in transformNewTargetGroupItems() - Joe
+ // No need to redefine this in transformNewTargetGroupItems() - Joe
if (typeof(this.cellData[targetGroup]) === 'undefined') {
this.cellData[targetGroup] = {};
}
@@ -328,20 +490,20 @@ DataLoader.prototype = {
}
// build the target list
var targetVal = {
- "id":targetID,
- "label": item.label,
- "targetGroup": species ,
+ "id":targetID,
+ "label": item.label,
+ "targetGroup": species ,
//"targetGroup": targetGroup, // sometimes item.taxon.label is missing from result, use targetGroup instead - Joe
- "type": item.type,
+ "type": item.type,
"rank": parseInt(idx)+1, // start with 1 not zero
"score": item.score.score
- };
-
+ };
+
// We need to define this here since the targetID is newly added here, doesn't exist before - Joe
if (typeof(this.targetData[targetGroup][targetID]) === 'undefined') {
this.targetData[targetGroup][targetID] = {};
}
-
+
this.targetData[targetGroup][targetID] = targetVal;
var matches = data.b[idx].matches;
@@ -349,9 +511,9 @@ DataLoader.prototype = {
var sourceID_a, currID_b, currID_lcs;
if (typeof(matches) !== 'undefined' && matches.length > 0) {
for (var matchIdx in matches) {
- // E.g., matches[i].b is one of the input phenotypes, witch matches to matches[i].a in the mouse
+ // E.g., matches[i].b is one of the input phenotypes, witch matches to matches[i].a in the mouse
// via the least common subumser (lcs) match[i].lcs. - Joe
- var sum = 0, count = 0;
+ var sum = 0, count = 0;
curr_row = matches[matchIdx];
sourceID_a = Utils.getConceptId(curr_row.a.id);
currID_b = Utils.getConceptId(curr_row.b.id);
@@ -369,50 +531,48 @@ DataLoader.prototype = {
// create a new source object
dataVals = {
- "id":sourceID_a,
- "label": curr_row.a.label,
+ "id":sourceID_a,
+ "label": curr_row.a.label,
"IC": parseFloat(curr_row.a.IC),
- "count": count,
- "sum": sum,
+ "count": count,
+ "sum": sum,
"type": "phenotype"
};
-
+
this.sourceData[targetGroup][sourceID_a] = dataVals;
} else {
this.sourceData[targetGroup][sourceID_a].count += 1;
- this.sourceData[targetGroup][sourceID_a].sum += parseFloat(curr_row.lcs.IC);
+ this.sourceData[targetGroup][sourceID_a].sum += parseFloat(curr_row.lcs.IC);
}
-
// building cell data points
dataVals = {
- "source_id": sourceID_a,
- "target_id": targetID,
- "targetGroup": targetGroup,
- "value": lcs,
- "a_IC" : curr_row.a.IC,
+ "source_id": sourceID_a,
+ "target_id": targetID,
+ "targetGroup": targetGroup,
+ "value": lcs,
+ "a_IC" : curr_row.a.IC,
"a_label" : curr_row.a.label,
- "subsumer_id": currID_lcs,
- "subsumer_label": curr_row.lcs.label,
- "subsumer_IC": parseFloat(curr_row.lcs.IC),
+ "subsumer_id": currID_lcs,
+ "subsumer_label": curr_row.lcs.label,
+ "subsumer_IC": parseFloat(curr_row.lcs.IC),
"b_id": currID_b,
- "b_label": curr_row.b.label,
+ "b_label": curr_row.b.label,
"b_IC": parseFloat(curr_row.b.IC),
"type": 'cell'
};
-
- // we need to define this before adding the data to named array, otherwise will get 'cannot set property of undefined' error
- // No need to redefine this in transformNewTargetGroupItems() - Joe
+
+ // we need to define this before adding the data to named array, otherwise will get 'cannot set property of undefined' error
+ // No need to redefine this in transformNewTargetGroupItems() - Joe
if (typeof(this.cellData[targetGroup][sourceID_a]) === 'undefined') {
this.cellData[targetGroup][sourceID_a] = {};
}
-
this.cellData[targetGroup][sourceID_a][targetID] = dataVals;
}
}
}
}
- },
-
+ },
+
/*
Function: transformDataForVendor
@@ -421,31 +581,31 @@ DataLoader.prototype = {
For a given model, extract the sim search data including IC scores and the triple:
The a column, b column, and lowest common subsumer for the triple's IC score, use the LCS score
-
+
Parameters:
target - target group data
data - owlsims structured data
multiTargetsModeTargetLengthLimit - default target length limit per group
*/
- transformDataForVendor: function(target, data, multiTargetsModeTargetLengthLimit) {
+ transformDataForVendor: function(target, data, multiTargetsModeTargetLengthLimit) {
if (typeof(data) !== 'undefined' && typeof (data.b) !== 'undefined') {
console.log("Vendor Data transforming...");
var targetGroupId = target.groupId;
var targetGroup = target.groupName;
-
+
// sometimes the 'metadata' field might be missing from the JSON - Joe
// extract the maxIC score; ugh!
if (typeof (data.metadata) !== 'undefined') {
this.maxMaxIC = data.metadata.maxMaxIC;
}
-
+
// just initialize the specific targetGroup
// Here we don't reset the cellData, targetData, and sourceData every time,
// because we want to append the genotype expansion data - Joe
- // No need to redefine this in transformNewTargetGroupItems() - Joe
-
+ // No need to redefine this in transformNewTargetGroupItems() - Joe
+
if (typeof(this.targetData[targetGroup]) === 'undefined') {
this.targetData[targetGroup] = {};
}
@@ -471,7 +631,7 @@ DataLoader.prototype = {
data.b[i].phenodigmScore = target.entities[j].score;
// add info for tooltip rendering
data.b[i].info = target.entities[j].info;
-
+
break;
}
}
@@ -486,20 +646,20 @@ DataLoader.prototype = {
// build the target list
var targetVal = {
- "id": targetID,
- "label": item.label,
+ "id": targetID,
+ "label": item.label,
"targetGroup": targetGroup, // Mouse
"type": "genotype", // Needs to be dynamic instead of hard coded - Joe
"info": item.info, // for tooltip rendering
"rank": parseInt(idx)+1, // start with 1 not zero
"score": Math.round(item.phenodigmScore.score) // rounded to the nearest integer, used in _createTextScores() in phenogrid.js
- };
+ };
// We need to define this here since the targetID is newly added here, doesn't exist before - Joe
if (typeof(this.targetData[targetGroup][targetID]) === 'undefined') {
this.targetData[targetGroup][targetID] = {};
}
-
+
this.targetData[targetGroup][targetID] = targetVal;
var matches = data.b[idx].matches;
@@ -507,9 +667,9 @@ DataLoader.prototype = {
var sourceID_a, currID_b, currID_lcs;
if (typeof(matches) !== 'undefined' && matches.length > 0) {
for (var matchIdx in matches) {
- // E.g., matches[i].b is one of the input phenotypes, witch matches to matches[i].a in the mouse
+ // E.g., matches[i].b is one of the input phenotypes, witch matches to matches[i].a in the mouse
// via the least common subumser (lcs) match[i].lcs. - Joe
- var sum = 0, count = 0;
+ var sum = 0, count = 0;
curr_row = matches[matchIdx];
sourceID_a = Utils.getConceptId(curr_row.a.id);
currID_b = Utils.getConceptId(curr_row.b.id);
@@ -527,50 +687,50 @@ DataLoader.prototype = {
// create a new source object
dataVals = {
- "id":sourceID_a,
- "label": curr_row.a.label,
- "IC": parseFloat(curr_row.a.IC),
- "count": count,
- "sum": sum,
+ "id":sourceID_a,
+ "label": curr_row.a.label,
+ "IC": parseFloat(curr_row.a.IC),
+ "count": count,
+ "sum": sum,
"type": "phenotype"
};
-
+
this.sourceData[targetGroup][sourceID_a] = dataVals;
} else {
this.sourceData[targetGroup][sourceID_a].count += 1;
- this.sourceData[targetGroup][sourceID_a].sum += parseFloat(curr_row.lcs.IC);
+ this.sourceData[targetGroup][sourceID_a].sum += parseFloat(curr_row.lcs.IC);
}
// building cell data points
dataVals = {
- "source_id": sourceID_a,
- "target_id": targetID,
- "targetGroup": targetGroup,
- "value": lcs,
- "a_IC" : curr_row.a.IC,
+ "source_id": sourceID_a,
+ "target_id": targetID,
+ "targetGroup": targetGroup,
+ "value": lcs,
+ "a_IC" : curr_row.a.IC,
"a_label" : curr_row.a.label,
- "subsumer_id": currID_lcs,
- "subsumer_label": curr_row.lcs.label,
- "subsumer_IC": parseFloat(curr_row.lcs.IC),
+ "subsumer_id": currID_lcs,
+ "subsumer_label": curr_row.lcs.label,
+ "subsumer_IC": parseFloat(curr_row.lcs.IC),
"b_id": currID_b,
- "b_label": curr_row.b.label,
+ "b_label": curr_row.b.label,
"b_IC": parseFloat(curr_row.b.IC),
"type": 'cell'
};
-
- // we need to define this before adding the data to named array, otherwise will get 'cannot set property of undefined' error
- // No need to redefine this in transformNewTargetGroupItems() - Joe
+
+ // we need to define this before adding the data to named array, otherwise will get 'cannot set property of undefined' error
+ // No need to redefine this in transformNewTargetGroupItems() - Joe
if (typeof(this.cellData[targetGroup][sourceID_a]) === 'undefined') {
this.cellData[targetGroup][sourceID_a] = {};
}
this.cellData[targetGroup][sourceID_a][targetID] = dataVals;
}
- }
- }
- }
- },
-
+ }
+ }
+ }
+ },
+
/*
Function: transformNewTargetGroupItems
@@ -578,16 +738,16 @@ DataLoader.prototype = {
For a given model, extract the sim search data including IC scores and the triple:
The a column, b column, and lowest common subsumer for the triple's IC score, use the LCS score
-
+
Parameters:
targetGroup - targetGroup name
data - owlsims structured data
parentGeneID - the parent gene ID that these genotypes are associated with
*/
- transformNewTargetGroupItems: function(targetGroup, data, parentGeneID) {
+ transformNewTargetGroupItems: function(targetGroup, data, parentGeneID) {
if (typeof(data) !== 'undefined' && typeof (data.b) !== 'undefined') {
-
+
console.log("transforming genotype data...");
// extract the maxIC score; ugh!
@@ -603,22 +763,22 @@ DataLoader.prototype = {
// build the target list
var targetVal = {
- "id":targetID,
- "label": item.label,
+ "id":targetID,
+ "label": item.label,
"targetGroup": item.taxon.label, // item.taxon.label is 'Not Specified' for fish sometimes
//"targetGroup": targetGroup, // we use the provided targetGroup as a quick fix - Joe
- "type": item.type,
+ "type": item.type,
'parentGeneID': parentGeneID, // added this for each added genotype so it knows which gene to be associated with - Joe
"rank": parseInt(idx)+1, // start with 1 not zero
"score": item.score.score,
"visible": true // set all newly added genotypes as visible, and update this when removing them from axis - Joe
- };
+ };
// We need to define this again here since the targetID is newly added here, doesn't exist before - Joe
if (typeof(this.targetData[targetGroup][targetID]) === 'undefined') {
this.targetData[targetGroup][targetID] = {};
}
-
+
this.targetData[targetGroup][targetID] = targetVal;
var matches = data.b[idx].matches;
@@ -626,7 +786,7 @@ DataLoader.prototype = {
var sourceID_a, currID_b, currID_lcs;
if (typeof(matches) !== 'undefined' && matches.length > 0) {
for (var matchIdx in matches) {
- var sum = 0, count = 0;
+ var sum = 0, count = 0;
curr_row = matches[matchIdx];
sourceID_a = Utils.getConceptId(curr_row.a.id);
currID_b = Utils.getConceptId(curr_row.b.id);
@@ -638,7 +798,7 @@ DataLoader.prototype = {
if(typeof(this.sourceData[targetGroup]) === 'undefined') {
this.sourceData[targetGroup] = {};
}
-
+
var srcElement = this.sourceData[targetGroup][sourceID_a]; // this checks to see if source already exists
// build a unique list of sources
@@ -648,35 +808,35 @@ DataLoader.prototype = {
// create a new source object
dataVals = {
- "id":sourceID_a,
- "label": curr_row.a.label,
- "IC": parseFloat(curr_row.a.IC),
- "count": count,
- "sum": sum,
+ "id":sourceID_a,
+ "label": curr_row.a.label,
+ "IC": parseFloat(curr_row.a.IC),
+ "count": count,
+ "sum": sum,
"type": "phenotype"
};
-
+
this.sourceData[targetGroup][sourceID_a] = dataVals;
} else {
this.sourceData[targetGroup][sourceID_a].count += 1;
- this.sourceData[targetGroup][sourceID_a].sum += parseFloat(curr_row.lcs.IC);
+ this.sourceData[targetGroup][sourceID_a].sum += parseFloat(curr_row.lcs.IC);
}
// building cell data points
dataVals = {
- "source_id": sourceID_a,
- "target_id": targetID,
+ "source_id": sourceID_a,
+ "target_id": targetID,
"target_type": 'genotype', // to mark this cell is generated for genotype expansion - Joe
- "targetGroup": item.taxon.label,
+ "targetGroup": item.taxon.label,
//"targetGroup": targetGroup,
- "value": lcs,
- "a_IC" : curr_row.a.IC,
+ "value": lcs,
+ "a_IC" : curr_row.a.IC,
"a_label" : curr_row.a.label,
- "subsumer_id": currID_lcs,
- "subsumer_label": curr_row.lcs.label,
- "subsumer_IC": parseFloat(curr_row.lcs.IC),
+ "subsumer_id": currID_lcs,
+ "subsumer_label": curr_row.lcs.label,
+ "subsumer_IC": parseFloat(curr_row.lcs.IC),
"b_id": currID_b,
- "b_label": curr_row.b.label,
+ "b_label": curr_row.b.label,
"b_IC": parseFloat(curr_row.b.IC),
"type": 'cell'
};
@@ -685,20 +845,20 @@ DataLoader.prototype = {
if (typeof(this.cellData[targetGroup][sourceID_a]) === 'undefined') {
this.cellData[targetGroup][sourceID_a] = {};
}
-
+
this.cellData[targetGroup][sourceID_a][targetID] = dataVals;
}
} //if
} // for
} // if
- },
+ },
+
-
/*
Function: refresh
- freshes the data
-
+ freshes the data
+
Parameters:
targetGroup - list of targetGroup (aka group) to fetch
lazy - performs a lazy load of the data checking for existing data
@@ -722,23 +882,23 @@ DataLoader.prototype = {
return reloaded;
},
-
+
getFetch: function (self, url, target, callback, finalCallback, parent) {
console.log('GET:' + url);
-
+
// Separate the ajax request with callbacks
var jqxhr = $.ajax({
url: url,
- method: 'GET',
+ method: 'GET',
async : true,
dataType : 'json',
});
-
+
jqxhr.done(function(data) {
callback(self, target, data, finalCallback, parent);
});
-
- jqxhr.fail(function () {
+
+ jqxhr.fail(function () {
console.log('Ajax error - getFetch()')
});
},
@@ -747,7 +907,7 @@ DataLoader.prototype = {
Function: getOntology
gets the ontology for a given id; wraps scigraph call
-
+
Parameters:
id - id
ontologyDirection - which direction to search for relationships
@@ -760,7 +920,7 @@ DataLoader.prototype = {
var relationship = parent.state.ontologyRelationship;
var depth = ontologyDepth;
- // http://monarchinitiative.org/neighborhood/HP_0003273/2/OUTGOING/subClassOf.json is the URL path - Joe
+ // https://monarchinitiative.org/neighborhood/HP_0003273/2/OUTGOING/subClassOf.json is the URL path - Joe
var url = this.serverURL + parent.state.ontologyQuery + id + "/" + depth + "/" + direction + "/" + relationship + ".json";
@@ -772,8 +932,8 @@ DataLoader.prototype = {
/*
Function: getNewTargetGroupItems
- get genotypes of a specific gene
-
+ get genotypes of a specific gene
+
Parameters:
id - id
finalCallback - final callback name
@@ -781,17 +941,17 @@ DataLoader.prototype = {
*/
getNewTargetGroupItems: function(id, finalCallback, parent) {
var self = this;
- // http://monarchinitiative.org/gene/MGI:98297/genotype_list.json
+ // https://monarchinitiative.org/gene/MGI:98297/genotype_list.json
var url = this.serverURL + "/gene/" + id + "/genotype_list.json";
var cb = this.getNewTargetGroupItemsCb;
// ajax get all the genotypes of this gene id
this.getFetch(self, url, id, cb, finalCallback, parent);
},
-
+
/*
Function: getNewTargetGroupItemsCb
send the compare request to get all the matches data
-
+
Parameters:
self - immediate parent
id - id which was searched
@@ -816,13 +976,13 @@ DataLoader.prototype = {
// remove that genotype with unstable prefix
results.genotype_list.splice(i, 1);
}
- }
+ }
}
-
+
// Now only get the first parent.state.targetGroupItemExpandLimit genotypes in the list
var genotype_list = results.genotype_list.slice(0, parent.state.targetGroupItemExpandLimit);
var phenotype_id_list = self.origSourceList.join("+");
- var genotype_id_list = '';
+ var genotype_id_list = '';
for (var i in genotype_list) {
genotype_id_list += genotype_list[i].id + ",";
}
@@ -843,11 +1003,11 @@ DataLoader.prototype = {
}
}
},
-
+
/*
Function: getNewTargetGroupItemsCb
return results(matches data) back to final callback (_fetchGenotypesCb() in phenogrid.js)
-
+
Parameters:
self - immediate parent
id - id which was searched
@@ -856,13 +1016,13 @@ DataLoader.prototype = {
parent - top level parent
*/
getNewTargetGroupItemsCbCb: function(self, id, results, finalCallback, parent) {
- // don't encode labels into html entities here, otherwise the tooltip content is good,
+ // don't encode labels into html entities here, otherwise the tooltip content is good,
// but genotype labels on x axis will have the encoded characters
// we just need to encode the labels for tooltip use - Joe
-
+
// save the expanded gene id for later
var genotype_id_list = [];
-
+
// there's no results.b is no matches found in the simsearch - Joe
if (typeof(results.b) !== 'undefined') {
for (var i = 0; i < results.b.length; i++) {
@@ -871,7 +1031,7 @@ DataLoader.prototype = {
// for reactivation
self.loadedNewTargetGroupItems[id] = genotype_id_list;
-
+
// this `results` is the simsearch resulting JSON
finalCallback(results, id, parent);
} else {
@@ -881,12 +1041,12 @@ DataLoader.prototype = {
finalCallback(simsearchResults, id, parent, errorMsg);
}
},
-
+
/*
Function: postOntologyCb
post callback from async call to gets the ontology for a given id
-
+
Parameters:
self - immediate parent
id - id which was searched
@@ -894,7 +1054,7 @@ DataLoader.prototype = {
parent - top level parent
*/
postOntologyCb: function(self, id, results, finalCallback, parent) {
- var ontologyInfo = [];
+ var ontologyInfo = [];
var nodes, edges;
if (typeof (results) !== 'undefined') {
@@ -940,14 +1100,14 @@ DataLoader.prototype = {
// save the ontology in cache for later
var ontoData = {"edges": ontologyInfo, "active": 1};
self.ontologyCache[id] = ontoData;
-
+
// return results back to final callback
finalCallback(ontoData, id, parent);
},
getOntologyLabel: function(id) {
return this.ontologyCacheLabels[id];
- },
+ },
getTargets: function() {
return this.targetData;
@@ -969,7 +1129,7 @@ DataLoader.prototype = {
Function: dataExists
convenient function to check the cell data for a given target group (i.e., group)
-
+
Parameters:
targetGroup - target Group label
*/
@@ -985,7 +1145,7 @@ DataLoader.prototype = {
Function: checkOntologyCache
convenient function to check the ontology cache for a given id
-
+
Parameters:
id - id to check
*/
diff --git a/js/datamanager.js b/js/datamanager.js
index 70d14209..24994185 100644
--- a/js/datamanager.js
+++ b/js/datamanager.js
@@ -52,6 +52,7 @@ DataManager.prototype = {
array of objects
*/
getData: function(dataset, targetGroup) {
+ // console.log('getData', dataset, targetGroup, this[dataset][targetGroup]);
return this[dataset][targetGroup];
},
diff --git a/js/phenogrid.js b/js/phenogrid.js
index ae04b9f1..c1a07ee8 100644
--- a/js/phenogrid.js
+++ b/js/phenogrid.js
@@ -20,10 +20,63 @@
// jquery is commonsJS compliant as of 2.1.0 - Joe
-require('jquery'); // Browserify encapsulates every module into its own scope - Joe
-require('jquery-ui');
+var jQuery = require('jquery'); // Browserify encapsulates every module into its own scope - Joe
+global.jQuery = global.$ = jQuery;
+global.jQueryUI = require('jquery-ui');
+
+// require('jquery-ui/ui/core');
+// require('jquery-ui/ui/widget');
+require('jquery-ui/ui/widgets/button');
+require('jquery-ui/ui/widgets/menu');
+require('jquery-ui/ui/widgets/selectmenu');
+require('jquery-ui/ui/widgets/tabs');
+require('jquery-ui/ui/widgets/dialog');
+require('jquery-ui/ui/data');
+require('jquery-ui/ui/position');
+require('jquery-ui/ui/focusable');
+require('jquery-ui/ui/keycode');
+require('jquery-ui/ui/tabbable');
+require('jquery-ui/ui/safe-blur');
+require('jquery-ui/ui/unique-id');
+require('jquery-ui/ui/safe-active-element');
+require('jquery-ui/ui/widgets/tooltip');
+
+/*
+
+// require('jquery-ui/ui/core');
+// require('jquery-ui/ui/data');
+// require('jquery-ui/ui/disable-selection');
+// require('jquery-ui/ui/effect');
+// require('jquery-ui/ui/escape-selector');
+// require('jquery-ui/ui/focusable');
+// require('jquery-ui/ui/form-reset-mixin');
+// require('jquery-ui/ui/form');
+// require('jquery-ui/ui/keycode');
+// require('jquery-ui/ui/labels');
+// require('jquery-ui/ui/plugin');
+// require('jquery-ui/ui/position');
+// require('jquery-ui/ui/safe-active-element');
+// require('jquery-ui/ui/safe-blur');
+// require('jquery-ui/ui/scroll-parent');
+// require('jquery-ui/ui/tabbable');
+// require('jquery-ui/ui/unique-id');
+// require('jquery-ui/ui/widget');
+
+require('jquery-ui/ui/widgets/accordion');
+require('jquery-ui/ui/widgets/draggable');
+require('jquery-ui/ui/widgets/tooltip');
+require('jquery-ui/ui/widgets/button');
+require('jquery-ui/ui/widgets/menu');
+require('jquery-ui/ui/widgets/selectmenu');
+require('jquery-ui/ui/widgets/tabs');
+require('jquery-ui/ui/widgets/dialog');
+
+*/
+
var d3 = require('d3');
-var filesaver = require('filesaver.js');
+global.d3 = d3;
+
+var filesaver = require('file-saver');
// load other non-npm dependencies - Joe
// need to specify the relative path ./ and .js extension
@@ -38,6 +91,21 @@ var htmlnotes = require('./htmlnotes.json');
// images in data uri format, only monarch logo so far
var images = require('./images.json');
+// From https://stackoverflow.com/a/47768164
+function getOffset(element)
+{
+ var bound = element.getBoundingClientRect();
+ var html = document.documentElement;
+
+ return {
+ top: bound.top + window.pageYOffset - html.clientTop,
+ left: bound.left + window.pageXOffset - html.clientLeft
+ };
+}
+
+function isBioLinkServer(serverURL) {
+ return serverURL.indexOf('https://api.monarchinitiative.org') === 0;
+}
(function(factory) {
// If there is a variable named module and it has an exports property,
@@ -67,11 +135,11 @@ var images = require('./images.json');
// Widget factory API documentation https://api.jqueryui.com/jquery.widget/ - Joe
$.widget("ui.phenogrid", {
// Public API, can be overwritten in Phenogrid constructor
- config: {
+ config: {
serverURL: "https://monarchinitiative.org", // will be overwritten by phenogrid_config.js, and Phenogrid constructor
gridSkeletonData: {},
selectedCalculation: 0, // index 0 is Similarity by default. (0 - Similarity, 1 - Ratio (q), 2 - Uniqueness, 3- Ratio (t))
- selectedSort: "Frequency", // sort method of sources: "Alphabetic", "Frequency and Rarity", "Frequency"
+ selectedSort: "Frequency", // sort method of sources: "Alphabetic", "Frequency and Rarity", "Frequency"
messaging: {
misconfig: 'Please fix your config to have at least one target group.',
gridSkeletonDataError: 'No phenotypes to compare.',
@@ -84,30 +152,21 @@ var images = require('./images.json');
// hooks to the monarch app's Analyze/phenotypes page - Joe
owlSimFunction: '', // 'compare', 'search'
targetSpecies: '', // quoted 'taxon number' or 'all'
- searchResultLimit: 100, // the limit field under analyze/phenotypes search section in search mode, default 100, will be overwritten by user-input limit
- geneList: [] // an array of gene IDs to be used in compare mode, already contains orthologs and paralogs when provided
+ searchResultLimit: 100, // the limit field under analyze/phenotypes search section in search mode, default 100, will be overwritten by user-input limit
+ geneList: [] // an array of gene IDs to be used in compare mode, already contains orthologs and paralogs when provided
},
// Supposed to be used by developers for deeper customization
// can not be overwritten from constructor
internalOptions: {
invertAxis: false,
- simSearchQuery: { // HTTP POST
- URL: '/simsearch/phenotype',
- inputItemsString: 'input_items=', // HTTP POST, body parameter
- targetSpeciesString: '&target_species=', // HTTP POST, body parameter
- limitString: '&limit'
- },
- compareQuery: { // compare API takes HTTP GET, so no body parameters
- URL: '/compare' // used for owlSimFunction === 'compare' and genotype expansion compare simsearch - Joe
- },
monarchInitiativeText: 'Powered by The Monarch Initiative',
unmatchedButtonLabel: 'Unmatched Phenotypes',
optionsBtnText: 'Options',
- gridTitle: 'Phenotype Similarity Comparison',
+ gridTitle: 'Phenotype Similarity Comparison',
singleTargetModeTargetLengthLimit: 30, // defines the limit of the number of targets to display
sourceLengthLimit: 30, // defines the limit of the number of sources to display
- multiTargetsModeTargetLengthLimit: 10, // the number of visible targets per group to be displayed in cross compare mode
+ multiTargetsModeTargetLengthLimit: 10, // the number of visible targets per group to be displayed in cross compare mode
targetLabelCharLimit : 23,
ontologyDepth: 10, // Numerical value that determines how far to go up the tree in relations.
ontologyDirection: "OUTGOING", // String that determines what direction to go in relations. Default is "out".
@@ -115,20 +174,20 @@ var images = require('./images.json');
ontologyQuery: "/neighborhood/", // Keep the slashes
ontologyTreeAmounts: 1, // Allows you to decide how many HPO Trees to render. Once a tree hits the high-level parent, it will count it as a complete tree. Additional branchs or seperate trees count as seperate items
// [vaa12] DO NOT CHANGE UNTIL THE DISPLAY HPOTREE FUNCTIONS HAVE BEEN CHANGED. WILL WORK ON SEPERATE TREES, BUT BRANCHES MAY BE INACCURATE
- targetGroupItemExpandLimit: 5, // sets the limit for the number of genotype expanded on grid
+ targetGroupItemExpandLimit: 5, // sets the limit for the number of genotype expanded on grid
unstableTargetGroupItemPrefix: ['MONARCH:', '_:', ':.well-known'], //https://github.com/monarch-initiative/monarch-app/issues/1024#issuecomment-163733837
colorDomains: [0, 0.2, 0.4, 0.6, 0.8, 1],
colorRanges: [ // each color sets the stop color based on the stop points in colorDomains - Joe
'rgb(237,248,177)',
'rgb(199,233,180)',
'rgb(127,205,187)',
- 'rgb(65,182,196)',
+ 'rgb(65,182,196)',
'rgb(29,145,192)',
'rgb(34,94,168)'
], // stop colors for corresponding stop points - Joe
minimap: {
- x:112,
- y: 75,
+ x:112,
+ y: 75,
width:100, // the actual width will be calculated based on the number of x count - Joe
height:100, // the actual height will be calculated based on the number of y count - Joe
bgColor: '#fff',
@@ -151,7 +210,7 @@ var images = require('./images.json');
rotatedDividerLength: 150 // the length of the divider line for the rotated labels
},
gridRegion: {
- x:240,
+ x:240,
y:200, // origin coordinates for grid region (matrix)
cellPad:19, // distance from the first cell to the next cell, odd number(19 - 12 = 7) makes the divider line entered perfectly - Joe
cellSize:12, // grid cell width/height
@@ -169,13 +228,13 @@ var images = require('./images.json');
defaultButtonWidth: 75 // the width of the 'Options' button
},
phenotypeSort: [
- "Alphabetic",
- "Frequency and Rarity",
- "Frequency"
+ "Alphabetic",
+ "Frequency and Rarity",
+ "Frequency"
],
similarityCalculation: [
- {label: "Similarity", calc: 0, high: "Max", low: "Min"},
- {label: "Ratio (q)", calc: 1, high: "More Similar", low: "Less Similar"},
+ {label: "Similarity", calc: 0, high: "Max", low: "Min"},
+ {label: "Ratio (q)", calc: 1, high: "More Similar", low: "Less Similar"},
{label: "Uniqueness", calc: 2, high: "Highest", low: "Lowest"},
{label: "Ratio (t)", calc: 3, high: "More Similar", low: "Less Similar"}
]
@@ -185,21 +244,21 @@ var images = require('./images.json');
_destroy: function() {
this.element.empty();
},
-
- // The _create() method is the jquery UI widget's constructor.
+
+ // The _create() method is the jquery UI widget's constructor.
// There are no parameters, but this.element and this.options are already set.
// According to this article, http://www.erichynds.com/blog/tips-for-developing-jquery-ui-widgets
- // the widget factory automatically fires the _create() and _init() methods during initialization,
- // in that order. At first glance it appears that the effort is duplicated, but there is a sight difference
- // between the two. Because the widget factory protects against multiple instantiations on the same element,
- // _create() will be called a maximum of one time for each widget instance,
+ // the widget factory automatically fires the _create() and _init() methods during initialization,
+ // in that order. At first glance it appears that the effort is duplicated, but there is a sight difference
+ // between the two. Because the widget factory protects against multiple instantiations on the same element,
+ // _create() will be called a maximum of one time for each widget instance,
// whereas _init() will be called each time the widget is called without arguments.
_create: function() {
// Loaded from a separate file config/phenogrid_config.js IF PROVIDED
if (typeof(configoptions) !== 'undefined') {
this.configoptions = configoptions;
} else {
- this.configoptions = {}; // Define as an empty object
+ this.configoptions = {}; // Define as an empty object
}
// Merge into one object
@@ -209,39 +268,39 @@ var images = require('./images.json');
// taxon is used by dataLoader to specify 'target_species' in query URL - Joe
this.state.targetGroupList = [];
-
+
// Create new arrays for later use
// initialTargetGroupLoadList is used for loading the simsearch data for the first time
this.state.initialTargetGroupLoadList = [];
-
+
// selectedCompareTargetGroup is used to control what group are loaded
// it's possible that a group is in initialTargetGroupLoadList but there's no simsearch data returned - Joe
this.state.selectedCompareTargetGroup = [];
// create the Phenogrid div
this._createPhenogridContainer();
-
+
// show loading spinner - Joe
this._showLoadingSpinner();
- // We need some basic data validation here to make sure
+ // We need some basic data validation here to make sure
// there are at least two phenotypes (one from each axis) to send off to the compare API before trying
- if (this.state.gridSkeletonData.xAxis.length > 0 && this.state.gridSkeletonData.yAxis.length > 0) {
+ if (this.state.gridSkeletonData.xAxis.length > 0 && this.state.gridSkeletonData.yAxis.length > 0) {
// Use provided grid title if there's one, otherwise use default
if (typeof(this.state.gridSkeletonData.title) !== 'undefined' && this.state.gridSkeletonData.title !== '' && this.state.gridSkeletonData.title !== null) {
this.state.gridTitle = this.state.gridSkeletonData.title;
}
-
+
// Parse to get unique source phenotype ID list
this._parseGridSourceList();
-
+
// Specify the final data loading callback to create all the display components
var self = this;
// no change to the callback - Joe
this.state.asyncDataLoadingCallback = function() {
- self._asyncDataLoadingCB(self);
+ self._asyncDataLoadingCB(self);
};
-
+
// Vendor data integration
if (this.state.gridSkeletonDataVendor === 'IMPC') {
this._initGridSkeletonDataForVendor();
@@ -256,7 +315,7 @@ var images = require('./images.json');
}
}
} else {
- // No need to compose the compare API call
+ // No need to compose the compare API call
this._showGridSkeletonDataErrorMsg();
}
},
@@ -272,7 +331,7 @@ var images = require('./images.json');
// Remove duplicated source IDs and add this gridSourceList to the global state variable - Joe
this.state.gridSourceList = this._removeDuplicatedSourceId(gridSourceList);
},
-
+
// Monarch use case
_initGridSkeletonData: function() {
// Compose the target group list based on gridSkeletonData.xAxis
@@ -283,15 +342,15 @@ var images = require('./images.json');
// load the target targetGroup list
this._parseTargetGroupList();
-
+
// initialize data processing class for simsearch query
- this.state.dataLoader = new DataLoader(this.state.serverURL, this.state.simSearchQuery);
-
+ this.state.dataLoader = new DataLoader(this.state.serverURL, true);
+
// starting loading the data from simsearch
//optional parm: this.limit
this.state.dataLoader.load(this.state.gridSourceList, this.state.initialTargetGroupLoadList, this.state.asyncDataLoadingCallback);
},
-
+
// Monarch analyze/phenotype compare use case
_initCompare: function() {
// overwrite the this.state.targetGroupList with only 'compare'
@@ -300,12 +359,12 @@ var images = require('./images.json');
this.state.targetGroupList = [
{groupName: compare, groupId: compare}
];
-
+
// load the target targetGroup list
- this._parseTargetGroupList();
+ this._parseTargetGroupList();
// initialize data processing class for compare query
- this.state.dataLoader = new DataLoader(this.state.serverURL, this.state.compareQuery);
+ this.state.dataLoader = new DataLoader(this.state.serverURL, false);
// starting loading the data from compare api
// NOTE: the owlsim data returned form the ajax GET may be empty (no matches), we'll handle this in the callback - Joe
@@ -320,21 +379,21 @@ var images = require('./images.json');
// load the target targetGroup list
this._parseTargetGroupList();
- } else {
+ } else {
// when single group is selected (taxon is passed in via this.state.targetSpecies)
// load just the one selected from the dropdown menu - Joe
for (var i = 0; i < this.state.gridSkeletonData.xAxis.length; i++) {
if (this.state.gridSkeletonData.xAxis[i].groupId === this.state.targetSpecies) {
this.state.targetGroupList.push(this.state.gridSkeletonData.xAxis[i]);
- this._parseTargetGroupList();
+ this._parseTargetGroupList();
break;
- }
+ }
}
}
-
+
// initialize data processing class for simsearch query
- this.state.dataLoader = new DataLoader(this.state.serverURL, this.state.simSearchQuery);
-
+ this.state.dataLoader = new DataLoader(this.state.serverURL, true);
+
// starting loading the data from simsearch
this.state.dataLoader.load(this.state.gridSourceList, this.state.initialTargetGroupLoadList, this.state.asyncDataLoadingCallback, this.state.searchResultLimit);
},
@@ -351,7 +410,7 @@ var images = require('./images.json');
this._parseTargetGroupList();
// initialize data processing class for compare query
- this.state.dataLoader = new DataLoader(this.state.serverURL, this.state.compareQuery);
+ this.state.dataLoader = new DataLoader(this.state.serverURL, false);
// starting loading the owlsim data from compare api for this vendor
if (this._isCrossComparisonView()) {
@@ -360,18 +419,18 @@ var images = require('./images.json');
this.state.dataLoader.loadCompareDataForVendor(this.state.gridSourceList, this.state.initialTargetGroupLoadList, this.state.asyncDataLoadingCallback);
}
},
-
+
_showGridSkeletonDataErrorMsg: function() {
this.state.pgContainer.html(this.state.messaging.gridSkeletonDataError);
},
-
+
// when not work with monarch's analyze/phenotypes page
_parseTargetGroupList: function() {
for (var idx in this.state.targetGroupList) {
- this.state.initialTargetGroupLoadList.push(this.state.targetGroupList[idx]);
+ this.state.initialTargetGroupLoadList.push(this.state.targetGroupList[idx]);
this.state.selectedCompareTargetGroup.push(this.state.targetGroupList[idx]);
- }
-
+ }
+
// Then we init the flag obj for group target item (genotype) expansion
this._createTargetGroupItemExpansionFlag();
},
@@ -380,26 +439,26 @@ var images = require('./images.json');
// Genotype expansion flags - named/associative array
// flag used for switching between single group and multi-group mode
var targetGroupItemExpansionFlag = {};
- // Add new group here, human disease doesn't have genotype expansion,
+ // Add new group here, human disease doesn't have genotype expansion,
// but we still have that group in the flag obj - Joe
for (var i = 0; i < this.state.initialTargetGroupLoadList.length; i++) {
// Add all group names as properties on this flag obj
targetGroupItemExpansionFlag[this.state.initialTargetGroupLoadList[i].groupName] = false;
}
- // NOTE: without using jquery's extend(), all the new flags are referenced
+ // NOTE: without using jquery's extend(), all the new flags are referenced
// to the config object, not actual copy - Joe
this.state.expandedTargetGroupItems = $.extend({}, this.state.targetGroupItemExpansionFlag);
-
+
// genotype flags to mark every genotype expansion on/off in each group
this.state.newTargetGroupItems = $.extend({}, this.state.targetGroupItemExpansionFlag);
-
+
this.state.removedTargetGroupItems = $.extend({}, this.state.targetGroupItemExpansionFlag);
-
+
// flag to mark if hidden genotypes need to be reactivated
this.state.reactivateTargetGroupItems = $.extend({}, this.state.targetGroupItemExpansionFlag);
},
-
+
// Phenogrid container div
_createPhenogridContainer: function() {
// ID of base containing div of each instance
@@ -414,7 +473,7 @@ var images = require('./images.json');
var spinner = $('
Loading Phenogrid Widget...
');
spinner.appendTo(this.state.pgContainer);
},
-
+
// callback to handle the loaded owlsim data
_asyncDataLoadingCB: function(self) {
// add dataManager to this.state
@@ -422,11 +481,11 @@ var images = require('./images.json');
// No need to update in owlSimFunction === 'compare' mode
// since compare only loads data once - Joe
- if (self.state.owlSimFunction !== 'compare') {
+ if (self.state.owlSimFunction !== 'compare') {
// need to update the selectedCompareTargetGroup list depending on if we loaded all the data
self._updateSelectedCompareTargetGroup();
}
-
+
// This removes the loading spinner, otherwise the spinner will be always there - Joe
self.state.pgContainer.html('');
@@ -458,10 +517,10 @@ var images = require('./images.json');
i--; // Need to go back to the first element of updated array
}
}
- },
+ },
- // for genotype expansion, we need to update the target list
+ // for genotype expansion, we need to update the target list
// for each group if they have added genotypes - Joe
_updateTargetAxisRenderingGroup: function(group_name) {
var targetList = [];
@@ -472,23 +531,25 @@ var images = require('./images.json');
targetList = this.state.dataManager.reorderedTargetEntriesNamedArray[group_name];
} else if (this.state.removedTargetGroupItems[group_name]) {
// get the reordered target list in the format of a named array, has all added genotype data
- targetList = this.state.dataManager.getReorderedTargetEntriesNamedArray(group_name);
+ targetList = this.state.dataManager.getReorderedTargetEntriesNamedArray(group_name);
} else if (this.state.reactivateTargetGroupItems[group_name]) {
- targetList = this.state.dataManager.getReorderedTargetEntriesNamedArray(group_name);
+ targetList = this.state.dataManager.getReorderedTargetEntriesNamedArray(group_name);
} else {
// unordered target list in the format of a named array, has all added genotype data
targetList = this.state.dataManager.getData("target", group_name);
- }
-
+ }
+
// update target axis group
var targetAxisRenderStartPos = this.state.targetAxis.getRenderStartPos();
var targetAxisRenderEndPos = this.state.targetAxis.getRenderEndPos();
+
+ console.log('targetaxis2', targetAxisRenderStartPos, targetAxisRenderEndPos, targetList);
this.state.targetAxis = new AxisGroup(targetAxisRenderStartPos, targetAxisRenderEndPos, targetList);
this._setAxisRenderers();
},
- /* create the groups to contain the rendering
+ /* create the groups to contain the rendering
information for x and y axes. Use already loaded data
in various hashes, etc. to create objects containing
information for axis rendering. Then, switch source and target
@@ -496,10 +557,9 @@ var images = require('./images.json');
_createAxisRenderingGroups: function() {
var targetList = [], sourceList = [];
- if (this._isCrossComparisonView()) {
+ if (this._isCrossComparisonView()) {
// create a combined list of targets
- sourceList = this.state.dataManager.createCombinedSourceList(this.state.selectedCompareTargetGroup);
-
+ sourceList = this.state.dataManager.createCombinedSourceList(this.state.selectedCompareTargetGroup);
// get the length of the sourceList, this sets that limit since we are in comparison mode
// only the multiTargetsModeTargetLengthLimit is set, which provides the overall display limit
this.state.sourceDisplayLimit = Object.keys(sourceList).length;
@@ -507,7 +567,7 @@ var images = require('./images.json');
// create a combined list of targets
// show as many columns as possible within the multiTargetsModeTargetLengthLimit
// only show multiTargetsModeTargetLengthLimit columns if there are more columns
- var targetLengthPerGroup = [];
+ var targetLengthPerGroup = [];
//this variable captures to total number of targets returned per group (groupTargetLength is limited
//to be less than or equal to the multiTargetsModeTargetLengthLimit
var targetReturnedLength = [];
@@ -519,10 +579,10 @@ var images = require('./images.json');
singleTargetReturnedLength = {
groupName: this.state.selectedCompareTargetGroup[i].groupName,
targetLength: Object.keys(targetDataPerGroup).length
-
+
};
targetReturnedLength.push(singleTargetReturnedLength);
-
+
if (Object.keys(targetDataPerGroup).length <= this.state.multiTargetsModeTargetLengthLimit) {
groupTargetLength = {
groupName: this.state.selectedCompareTargetGroup[i].groupName,
@@ -536,39 +596,38 @@ var images = require('./images.json');
}
targetLengthPerGroup.push(groupTargetLength);
}
-
+
// Also make it available in the global scope
// to be used when creating divider lines
this.state.targetLengthPerGroup = targetLengthPerGroup;
this.state.targetTotalReturnedPerGroup = targetReturnedLength;
- targetList = this.state.dataManager.createCombinedTargetList(this.state.selectedCompareTargetGroup, this.state.multiTargetsModeTargetLengthLimit);
-
+ targetList = this.state.dataManager.createCombinedTargetList(this.state.selectedCompareTargetGroup, this.state.multiTargetsModeTargetLengthLimit);
// get the length of the targetlist, this sets that limit since we are in comparison mode
this.state.targetDisplayLimit = Object.keys(targetList).length;
} else if (this.state.selectedCompareTargetGroup.length === 1) {
- // just get the target group name
+ // just get the target group name
// in analyze/phenotypes compare mode, the singleTargetGroupName will be 'compare' - Joe
var singleTargetGroupName = this.state.selectedCompareTargetGroup[0].groupName;
-
+
sourceList = this.state.dataManager.getData("source", singleTargetGroupName);
// set default display limits based on displaying sourceLengthLimit
this.state.sourceDisplayLimit = this.state.dataManager.length("source", singleTargetGroupName);
-
+
// display all the expanded genotypes when we switch back from multi-group mode to single-group mode
// at this point, this.state.expandedTargetGroupItems is true, and this.state.newTargetGroupItems is false - Joe
if (this.state.expandedTargetGroupItems[singleTargetGroupName]) {
//targetList = this.state.dataManager.reorderedTargetEntriesNamedArray[singleTargetGroupName];
- targetList = this.state.dataManager.getReorderedTargetEntriesNamedArray(singleTargetGroupName);
+ targetList = this.state.dataManager.getReorderedTargetEntriesNamedArray(singleTargetGroupName);
} else {
// unordered target list in the format of a named array, has all added genotype data
targetList = this.state.dataManager.getData("target", singleTargetGroupName);
}
-
+
this.state.targetDisplayLimit = this.state.dataManager.length("target", singleTargetGroupName);
-
+
// Determines the count shown in the column header
// ie mouse (>100)
this.state.targetTotalReturnedPerGroup = [{
@@ -579,7 +638,7 @@ var images = require('./images.json');
// In single target mode, use singleTargetModeTargetLengthLimit if more than that
if (this.state.targetDisplayLimit > this.state.singleTargetModeTargetLengthLimit) {
this.state.targetDisplayLimit = this.state.singleTargetModeTargetLengthLimit;
- }
+ }
}
// check to make sure the display limits are not over the default display limits
@@ -589,9 +648,9 @@ var images = require('./images.json');
// creates AxisGroup with full source and target lists with default rendering range
this.state.sourceAxis = new AxisGroup(0, this.state.sourceDisplayLimit, sourceList);
-
+
// sort source with default sorting type
- this.state.sourceAxis.sort(this.state.selectedSort);
+ this.state.sourceAxis.sort(this.state.selectedSort);
//create target axis group
this.state.targetAxis = new AxisGroup(0, this.state.targetDisplayLimit, targetList);
@@ -639,64 +698,65 @@ var images = require('./images.json');
this._showConfigErrorMsg();
}
},
-
+
_showConfigErrorMsg: function() {
this.state.pgContainer.html(this.state.messaging.misconfig);
},
-
+
_createDisplayComponents: function() {
// initialize axis groups
this._createAxisRenderingGroups();
-
+
// create the display as usual if there's 'b' and 'metadata' fields found - Joe
this._createColorScalePerSimilarityCalculation();
-
+
// No need to recreate this tooltip on _updateDisplay() - Joe
this._createTooltipStub();
-
+
this._createSvgComponents();
// Create and postion HTML sections
// Unmatched sources
this._createUnmatchedSources();
-
+
// calculate the button padding
// whitespace size between left boundry to unmatched button, same as the options button to right boundry
- this.state.btnPadding = this.state.gridRegion.x - $('#' + this.state.pgInstanceId + '_unmatched_btn').width() - this.state.gridRegion.rowLabelOffset;
-
+ var unmatchedButton = $('#' + this.state.pgInstanceId + '_unmatched_btn');
+ this.state.btnPadding = this.state.gridRegion.x - unmatchedButton.width() - this.state.gridRegion.rowLabelOffset;
+
// Must after the this.state.btnPadding calculation since we use the padding to position the unmatched button - Joe
this._positionUnmatchedSources();
-
+
// Options menu
this._createPhenogridControls();
this._positionPhenogridControls();
this._togglePhenogridControls();
-
+
// Must after calling this._createPhenogridControls()
- // since it uses the control options width to
+ // since it uses the control options width to
// calculate the gridWidth of the final group grid width - Joe
this._scalableCutoffGroupLabel();
-
+
this._setSvgSize();
},
-
+
// Recreates the SVG content and leave the HTML sections unchanged
_updateDisplay: function() {
// Only remove the #pg_svg node and leave #this.state.pgInstanceId_controls there
// since #this.state.pgInstanceId_controls is HTML not SVG - Joe
this.element.find('#' + this.state.pgInstanceId + '_svg').remove();
-
+
this._createSvgComponents();
// Recalculate the button padding
// whitespace size between left boundry to unmatched button, same as the options button to right boundry
this.state.btnPadding = this.state.gridRegion.x - $('#' + this.state.pgInstanceId + '_unmatched_btn').width() - this.state.gridRegion.rowLabelOffset;
-
+
// Reposition HTML sections
this._positionUnmatchedSources();
this._positionPhenogridControls();
-
+
this._setSvgSize();
},
@@ -714,17 +774,17 @@ var images = require('./images.json');
// this must be called here so the tooltip disappears when we mouseout the current element - Joe
this._relinkTooltip();
},
-
+
// the svg container
_createSvgContainer: function() {
this.state.pgContainer.append('');
-
- // Define a font-family for all SVG texts
+
+ // Define a font-family for all SVG texts
// so we don't have to apply font-family separately for each SVG text - Joe
this.state.svg = d3.select('#' + this.state.pgInstanceId + '_svg_group')
.style("font-family", "Verdana, Geneva, sans-serif");
},
-
+
// if no owlsim data returned for that group
_showGroupsNoMatch: function() {
var output = '';
@@ -746,17 +806,17 @@ var images = require('./images.json');
groups = groups.slice(0, n) + groups.slice(n).replace(', ', conjunctionJunction);
}
output = this.state.messaging.noSimSearchMatch.replace(/{%groupName%}/, groups) + ' ';
- // Insert the error messages before the container div, so it won't mess up the alignment of
+ // Insert the error messages before the container div, so it won't mess up the alignment of
// unmatched and options that are aligned relatively to the container
$('
' + output + '
').insertBefore(this.state.pgContainer);
},
-
+
// Positioned next to the grid region bottom
- _addLogoImage: function() {
+ _addLogoImage: function() {
var gridRegion = this.state.gridRegion;
-
+
var x = (gridRegion.x + this.state.singleTargetModeTargetLengthLimit*gridRegion.cellPad + gridRegion.rowLabelOffset)/2;
-
+
var self = this;
this.state.svg.append("svg:image")
.attr("xlink:href", images.logo)
@@ -770,16 +830,16 @@ var images = require('./images.json');
window.open(self.state.serverURL, '_blank');
});
},
-
+
_createTargetGroupLabels: function () {
if (this.state.owlSimFunction !== 'compare') {
var self = this;
// targetGroupList is an array that contains all the selected targetGroup names
- var targetGroupList = this.state.selectedCompareTargetGroup.map(function(d){return d.groupName;});
+ var targetGroupList = this.state.selectedCompareTargetGroup.map(function(d){return d.groupName;});
var titleXPerGroup = [];
var titleYPerGroup = [];
-
+
if (this._isCrossComparisonView()) {
var totalColumns = 0;
var columnsCounter = [];
@@ -797,7 +857,7 @@ var images = require('./images.json');
// add 30 to rotatedDividerLength
var x = this.state.gridRegion.x + this.state.gridRegion.cellPad*(columnsCounter[i] - this.state.targetLengthPerGroup[i].targetLength) + (this.state.targetGroupDividerLine.rotatedDividerLength)*Math.sin(Math.PI/4) - (this.state.gridRegion.cellPad - this.state.gridRegion.cellSize)/2;
}
-
+
var y = this.state.gridRegion.y + this.state.gridRegion.cellPad*(columnsCounter[i] - this.state.targetLengthPerGroup[i].targetLength/2);
titleXPerGroup.push(x);
titleYPerGroup.push(y);
@@ -808,14 +868,14 @@ var images = require('./images.json');
titleXPerGroup.push(x);
titleYPerGroup.push(y);
}
-
+
// No group labels after inverting axis
- if ( ! this.state.invertAxis) {
+ if ( ! this.state.invertAxis) {
this.state.svg.selectAll(".pg_targetGroup_name")
.data(targetGroupList)
.enter()
.append("text")
- .attr("x", function(d, i){
+ .attr("x", function(d, i){
return titleXPerGroup[i];
})
.attr("y", function(d, i) {
@@ -826,15 +886,15 @@ var images = require('./images.json');
return self.state.gridRegion.y - 135 - 12; // Move up 12px
}
})
- .attr("class", "pg_targetGroup_name")
+ .attr("class", "pg_targetGroup_name")
.attr("id", function(d, i) {
return self.state.pgInstanceId + "_groupName_" + (i + 1);
- })
+ })
.text(function(d, i){
var totalTargetCount = 0;
//display the total number of targets found per group
for (var x = 0;x < self.state.targetTotalReturnedPerGroup.length; x++) {
- if (self.state.targetTotalReturnedPerGroup[x].groupName === d) {
+ if (self.state.targetTotalReturnedPerGroup[x].groupName === d) {
totalTargetCount = self.state.targetTotalReturnedPerGroup[x].targetLength;
if (totalTargetCount >= self.state.searchResultLimit) {
totalTargetCount = ">" + totalTargetCount;
@@ -844,7 +904,7 @@ var images = require('./images.json');
}
return targetGroupList[i] + " (" + totalTargetCount + ")";
})
- .style("font-size", '11px')
+ .style("font-size", '11px')
.attr("text-anchor", function(d, i) {
if (i === 0) {
return 'middle';
@@ -859,7 +919,7 @@ var images = require('./images.json');
//otherwise, show all the available taxa
var elem = $('#' + self.state.pgInstanceId + '_targetGroup');
var items = elem.children();
- //if no other taxon is expanded...
+ //if no other taxon is expanded...
if (!self.state.taxonExpanded) {
self.state.taxonExpanded = true;
for (var idx = 0; idx < items.length; idx++) {
@@ -877,7 +937,7 @@ var images = require('./images.json');
//check to make sure the checkbox is enabled before checking it
if (!items[idx].childNodes[0].disabled) {
items[idx].childNodes[0].checked = true;
- }
+ }
}
}
//the above "click" event basically mimics a user selecting one organism
@@ -885,7 +945,7 @@ var images = require('./images.json');
$('#' + self.state.pgInstanceId + '_targetGroup').trigger("change");
})
- .on("mouseover", function(d) {
+ .on("mouseover", function(d) {
// self is the global widget this
// this passed to _mouseover refers to the current element
// _mouseover() highlights and matching x/y labels, and creates crosshairs on current grid cell
@@ -899,9 +959,9 @@ var images = require('./images.json');
self._mouseout();
});
}
- }
+ }
},
-
+
// Use a scalable cutoff for group label length based on the group size
// Adjust the group name length once it's rendered
// Only applied to the multi group mode
@@ -909,7 +969,7 @@ var images = require('./images.json');
if (this._isCrossComparisonView()) {
for (var i = 0; i < this.state.selectedCompareTargetGroup.length; i++) {
var groupNameWidth = $('#' + this.state.pgInstanceId + ' .pg_targetGroup_name')[i].getBoundingClientRect().width;
-
+
// For the first group, scale the label based on the grid width and the divider line's projection on X coordinate.
if (i === 0) {
var groupGridWidth = this.state.targetLengthPerGroup[i].targetLength * this.state.gridRegion.cellPad - (this.state.gridRegion.cellPad - this.state.gridRegion.cellSize)/2 + (this.state.targetGroupDividerLine.rotatedDividerLength)*Math.sin(Math.PI/4);
@@ -935,9 +995,9 @@ var images = require('./images.json');
},
// Create minimap and scrollbars based on needs
- // In single group mode, only show mini map
+ // In single group mode, only show mini map
// when there are more sources than the default limit or more targets than default limit
- // In cross comparison mode, only show mini map
+ // In cross comparison mode, only show mini map
// when there are more sources than the default limit
// Create scrollbars accordingly
_createNavigation: function() {
@@ -945,7 +1005,7 @@ var images = require('./images.json');
var yCount = this.state.yAxisRender.groupLength();
var width = this.state.minimap.width;
var height = this.state.minimap.height;
-
+
// check xCount based on yCount
if ( ! this.state.invertAxis) {
if ( ! this._isCrossComparisonView()) {
@@ -971,19 +1031,19 @@ var images = require('./images.json');
this._createMinimap(width, height);
// only create horizontal scrollbar
this._createScrollbars(true, false);
- }
-
+ }
+
// No need to create the mini map if both xCount and yCount are within the default limit
}
} else {
// No need to check xCount since the max x limit per group is set to multiTargetsModeTargetLengthLimit
- if (yCount > this.state.sourceLengthLimit) {
+ if (yCount > this.state.sourceLengthLimit) {
// just use the default mini map width and height
this._createMinimap(width, height);
// only create vertical scrollbar
this._createScrollbars(false, true);
- }
-
+ }
+
// No need to create the mini map if yCount is within the default limit
}
} else {
@@ -1008,23 +1068,23 @@ var images = require('./images.json');
}
}
} else {
- if (xCount > this.state.sourceLengthLimit) {
+ if (xCount > this.state.sourceLengthLimit) {
this._createMinimap(width, height);
// only create horizontal scrollbar
this._createScrollbars(true, false);
- }
- }
- }
+ }
+ }
+ }
},
-
- // For the selection area, see if you can convert the selection to the idx of the x and y then redraw the bigger grid
+
+ // For the selection area, see if you can convert the selection to the idx of the x and y then redraw the bigger grid
_createMinimap: function(width, height) {
// display counts and total counts on each axis
- var xDisplayCount = this.state.xAxisRender.displayLength();
- var yDisplayCount = this.state.yAxisRender.displayLength();
- var xTotalCount = this.state.xAxisRender.groupLength();
- var yTotalCount = this.state.yAxisRender.groupLength();
-
+ var xDisplayCount = this.state.xAxisRender.displayLength();
+ var yDisplayCount = this.state.yAxisRender.displayLength();
+ var xTotalCount = this.state.xAxisRender.groupLength();
+ var yTotalCount = this.state.yAxisRender.groupLength();
+
// these translations from the top-left of the rectangular region give the absolute coordinates
var overviewX = this.state.minimap.x;
var overviewY = this.state.minimap.y;
@@ -1033,7 +1093,7 @@ var images = require('./images.json');
// Group the overview region and text together - Joe
var globalviewGrp = this.state.svg.append("g")
.attr("id", this.state.pgInstanceId + "_navigator");
-
+
// rectangular border for overview map
// border color and thickness are defined inline so it can be used by exported svg - Joe
globalviewGrp.append("rect")
@@ -1052,7 +1112,7 @@ var images = require('./images.json');
// this should be the full set of cellData
var xvalues = this.state.xAxisRender.groupEntries();
//console.log(JSON.stringify(xvalues));
- var yvalues = this.state.yAxisRender.groupEntries();
+ var yvalues = this.state.yAxisRender.groupEntries();
// in compare mode, the targetGroup will be 'compare' instead of actual group name - Joe
// each element in data contains source_id, targetGroup, target_id, type ('cell'), xpos, and ypos
@@ -1067,10 +1127,10 @@ var images = require('./images.json');
var miniCellsGrp = this.state.svg.select('#' + this.state.pgInstanceId + '_navigator').append('g')
.attr("id", this.state.pgInstanceId + "_mini_cells_container")
.attr("transform", "translate(" + overviewX + "," + overviewY + ")");
-
+
var self = this; // to be used in callback
-
- // Add cells to the miniCellsGrp
+
+ // Add cells to the miniCellsGrp
var cell_rects = miniCellsGrp.selectAll(".mini_cell")
.data(data, function(d) {
return d.source_id + d.target_id;
@@ -1078,30 +1138,30 @@ var images = require('./images.json');
.enter()
.append("rect")
.attr("class", "mini_cell")
- .attr("x", function(d) {
- return self.state.smallXScale(d.target_id) + self.state.minimap.miniCellSize / 2;
+ .attr("x", function(d) {
+ return self.state.smallXScale(d.target_id) + self.state.minimap.miniCellSize / 2;
})
- .attr("y", function(d) {
+ .attr("y", function(d) {
return self.state.smallYScale(d.source_id) + self.state.minimap.miniCellSize / 2;
})
- .attr("width", this.state.minimap.miniCellSize)
- .attr("height", this.state.minimap.miniCellSize)
+ .attr("width", this.state.minimap.miniCellSize)
+ .attr("height", this.state.minimap.miniCellSize)
.attr("fill", function(d) {
var el = self.state.dataManager.getCellDetail(d.source_id, d.target_id, d.targetGroup);
- return self._getCellColor(el.value[self.state.selectedCalculation]);
+ return self._getCellColor(el.value[self.state.selectedCalculation]);
});
-
+
// start point (x, y) of the shaded draggable area
- var startYId = this.state.yAxisRender.itemAt(0).id; // start point should always be 0 - Joe
- var startXId = this.state.xAxisRender.itemAt(0).id; // start point should always be 0 - Joe
-
+ var startYId = this.state.yAxisRender.itemAt(0).id; // start point should always be 0 - Joe
+ var startXId = this.state.xAxisRender.itemAt(0).id; // start point should always be 0 - Joe
+
var selectRectX = this.state.smallXScale(startXId);
var selectRectY = this.state.smallYScale(startYId);
-
+
// Calculate the width and height of the shaded draggable area
var selectRectHeight = height * (yDisplayCount/yTotalCount);
var selectRectWidth = width * (xDisplayCount/xTotalCount);
-
+
// Also add the shaded area in the pg_navigator group - Joe
this.state.highlightRect = this.state.svg.select('#' + this.state.pgInstanceId + '_navigator').append("rect")
.attr("x", overviewX + selectRectX)
@@ -1112,12 +1172,12 @@ var images = require('./images.json');
.attr("class", "pg_draggable")
.style("fill", this.state.minimap.shadedAreaBgColor)
.style("opacity", this.state.minimap.shadedAreaOpacity)
- .call(d3.behavior.drag() // Constructs a new drag behavior
- .on("dragstart", self._dragstarted) // self._dragstarted() won't work - Joe
+ .call(d3.drag() // Constructs a new drag behavior
+ .on("start", self._dragstarted) // self._dragstarted() won't work - Joe
.on("drag", function() {
// Random movement while dragging triggers mouseover on labels and cells (luckily, only crosshairs show up in this case)
self._crossHairsOff();
-
+
/*
* drag the highlight in the overview window
* notes: account for the width of the rectangle in my x and y calculations
@@ -1158,23 +1218,23 @@ var images = require('./images.json');
.attr("x", newX)
.attr("y", newY);
- // update the position of slider in each scrollbar accordingly
+ // update the position of slider in each scrollbar accordingly
self.state.svg.select('#' + self.state.pgInstanceId + '_horizontal_scrollbar_slider')
.attr("x", function() {
var factor = (newX - overviewX) / width;
var horizontal_scrollbar_width = self._gridWidth();
return self.state.gridRegion.x + horizontal_scrollbar_width*factor;
});
-
+
self.state.svg.select('#' + self.state.pgInstanceId + '_vertical_scrollbar_slider')
.attr("y", function() {
var factor = (newY - overviewY) / height;
var vertical_scrollbar_height = self._gridHeight();
return self.state.gridRegion.y + vertical_scrollbar_height*factor;
});
-
-
-
+
+
+
// adjust x back to have 0,0 as base instead of overviewX, overviewY
newX = newX - overviewX;
newY = newY - overviewY;
@@ -1186,7 +1246,7 @@ var images = require('./images.json');
// grid region needs to be updated accordingly
self._updateGrid(newXPos, newYPos);
})
- .on("dragend", self._dragended) // self._dragended() won't work - Joe
+ .on("end", self._dragended) // self._dragended() won't work - Joe
);
},
@@ -1194,7 +1254,7 @@ var images = require('./images.json');
// define the dragging color in CSS since it won't show in the exported SVG
_dragstarted: function() {
// change the slider color while dragging
- d3.select(this).classed("pg_dragging", true);
+ d3.select(this).classed("pg_dragging", true);
},
// when the drag gesture finishes
@@ -1202,11 +1262,11 @@ var images = require('./images.json');
// remove the dragging color
d3.select(this).classed("pg_dragging", false);
},
-
+
// Create horizontal and vertical scrollbars based on needs
_createScrollbars: function(horizontal, vertical) {
var self = this;
-
+
// variables for scrollbar and slider SVG rendering
var scrollbar = this.state.scrollbar;
var barToGridMargin = scrollbar.barToGridMargin;
@@ -1217,28 +1277,28 @@ var images = require('./images.json');
// create the scales based on the scrollbar size
this._createScrollbarScales(this._gridWidth(), this._gridHeight());
-
+
// variables for creating horizontal bar
- var xDisplayCount = this.state.xAxisRender.displayLength();
- var xTotalCount = this.state.xAxisRender.groupLength();
- var startXId = this.state.xAxisRender.itemAt(0).id; // start point should always be 0 - Joe
+ var xDisplayCount = this.state.xAxisRender.displayLength();
+ var xTotalCount = this.state.xAxisRender.groupLength();
+ var startXId = this.state.xAxisRender.itemAt(0).id; // start point should always be 0 - Joe
var defaultX = this.state.gridRegion.x;
var sliderRectX = this.state.horizontalScrollbarScale(startXId);
var sliderWidth = this._gridWidth() * (xDisplayCount/xTotalCount);
-
+
// variables for creating vertical scrollbar
- var yDisplayCount = this.state.yAxisRender.displayLength();
- var yTotalCount = this.state.yAxisRender.groupLength();
- var startYId = this.state.yAxisRender.itemAt(0).id; // start point should always be 0 - Joe
+ var yDisplayCount = this.state.yAxisRender.displayLength();
+ var yTotalCount = this.state.yAxisRender.groupLength();
+ var startYId = this.state.yAxisRender.itemAt(0).id; // start point should always be 0 - Joe
var defaultY = this.state.gridRegion.y;
var sliderRectY = this.state.verticalScrollbarScale(startYId);
var sliderHeight = this._gridHeight() * (yDisplayCount/yTotalCount);
-
+
// horizontal scrollbar
if (horizontal === true) {
var horizontalScrollbarGrp = this.state.svg.append("g")
.attr("id", this.state.pgInstanceId + "_horizontal_scrollbar_group");
-
+
// scrollbar line
horizontalScrollbarGrp.append("line")
.attr("x1", this.state.gridRegion.x)
@@ -1258,30 +1318,30 @@ var images = require('./images.json');
.attr("width", sliderWidth)
.style("fill", sliderColor)
.attr("class", "pg_draggable")
- .call(d3.behavior.drag()
- .on("dragstart", self._dragstarted)
+ .call(d3.drag()
+ .on("start", self._dragstarted)
.on("drag", function() {
// Random movement while dragging triggers mouseover on labels and cells (luckily, only crosshairs show up in this case)
self._crossHairsOff();
-
+
var newX = parseFloat(d3.select(this).attr("x")) + d3.event.dx;
-
+
// Make sure the slider moves within the scrollbar horizontally - Joe
// left
if (newX < defaultX) {
newX = defaultX;
}
-
+
// right
if ((newX + sliderWidth) > (self.state.gridRegion.x + self._gridWidth())) {
newX = self.state.gridRegion.x + self._gridWidth() - sliderWidth;
}
-
+
// update the position of slider
self.state.svg.select('#' + self.state.pgInstanceId + '_horizontal_scrollbar_slider')
.attr("x", newX);
-
- // update the shaded area in mini map accordingly
+
+ // update the shaded area in mini map accordingly
self.state.svg.select('#' + self.state.pgInstanceId + '_navigator_shaded_area')
.attr("x", function() {
// NOTE: d3 returns string so we need to use parseFloat()
@@ -1289,19 +1349,19 @@ var images = require('./images.json');
var minimap_width = parseFloat(d3.select('#' + self.state.pgInstanceId + '_globalview').attr("width")) - 2*self.state.minimap.borderThickness;
return self.state.minimap.x + minimap_width*factor;
});
-
+
// adjust
newX = newX - defaultX;
-
+
var newXPos = self._invertDragPosition(self.state.horizontalScrollbarScale, newX) + xDisplayCount;
-
+
// Horizontal grid region needs to be updated accordingly
self._updateHorizontalGrid(newXPos);
})
- .on("dragend", self._dragended)
+ .on("end", self._dragended)
);
}
-
+
// vertical scrollbar
if (vertical === true) {
var verticalScrollbarGrp = this.state.svg.append("g")
@@ -1319,118 +1379,118 @@ var images = require('./images.json');
// slider rect
verticalScrollbarGrp.append("rect")
- .attr("x", this._positionVerticalScrollbarRect(sliderThickness, barToGridMargin))
+ .attr("x", this._positionVerticalScrollbarRect(sliderThickness, barToGridMargin))
.attr("y", defaultY + sliderRectY) // sets the slider to the desired position after inverting axis - Joe
.attr("id", this.state.pgInstanceId + "_vertical_scrollbar_slider")
.attr("height", sliderHeight)
.attr("width", sliderThickness)
.style("fill", sliderColor)
.attr("class", "pg_draggable")
- .call(d3.behavior.drag()
- .on("dragstart", self._dragstarted)
+ .call(d3.drag()
+ .on("start", self._dragstarted)
.on("drag", function() {
// Random movement while dragging triggers mouseover on labels and cells (luckily, only crosshairs show up in this case)
// Adding this in _dragstarted() won't work since the crosshair lines are being created during dragging
self._crossHairsOff();
-
+
var newY = parseFloat(d3.select(this).attr("y")) + d3.event.dy;
-
+
// Make sure the slider moves within the scrollbar vertically - Joe
// top
if (newY < defaultY) {
newY = defaultY;
}
-
+
// bottom
if ((newY + sliderHeight) > (self.state.gridRegion.y + self._gridHeight())) {
newY = self.state.gridRegion.y + self._gridHeight() - sliderHeight;
}
-
+
// update the position of slider
self.state.svg.select('#' + self.state.pgInstanceId + '_vertical_scrollbar_slider')
.attr("y", newY);
-
- // update the shaded area in mini map accordingly
+
+ // update the shaded area in mini map accordingly
self.state.svg.select('#' + self.state.pgInstanceId + '_navigator_shaded_area')
.attr("y", function() {
// NOTE: d3 returns string so we need to use parseFloat()
var factor = (newY - defaultY) / self._gridHeight();
- var minimap_height = parseFloat(d3.select('#' + self.state.pgInstanceId + '_globalview').attr("height")) - 2*self.state.minimap.borderThickness;
+ var minimap_height = parseFloat(d3.select('#' + self.state.pgInstanceId + '_globalview').attr("height")) - 2*self.state.minimap.borderThickness;
return self.state.minimap.y + minimap_height*factor;
});
-
-
+
+
// adjust
newY = newY - defaultY;
var newYPos = self._invertDragPosition(self.state.verticalScrollbarScale, newY) + yDisplayCount;
-
+
// Vertical grid region needs to be updated accordingly
self._updateVerticalGrid(newYPos);
})
- .on("dragend", self._dragended)
+ .on("end", self._dragended)
);
}
},
-
+
_positionVerticalScrollbarLine: function(sliderThickness, barToGridMargin) {
var x;
-
+
if (this._isCrossComparisonView()) {
x = this.state.gridRegion.x + this.state.multiTargetsModeTargetLengthLimit*this.state.selectedCompareTargetGroup.length*this.state.gridRegion.cellPad - sliderThickness/2 + barToGridMargin
} else {
x = this.state.gridRegion.x + this.state.singleTargetModeTargetLengthLimit*this.state.gridRegion.cellPad - sliderThickness/2 + barToGridMargin;
}
-
+
return x;
},
-
+
_positionVerticalScrollbarRect: function(sliderThickness, barToGridMargin) {
var x;
-
+
if (this._isCrossComparisonView()) {
x = this.state.gridRegion.x + this.state.multiTargetsModeTargetLengthLimit*this.state.selectedCompareTargetGroup.length*this.state.gridRegion.cellPad + barToGridMargin - sliderThickness;
} else {
x = this.state.gridRegion.x + this.state.singleTargetModeTargetLengthLimit*this.state.gridRegion.cellPad + barToGridMargin - sliderThickness;
}
-
+
return x;
},
-
+
// for scrollbars
_createScrollbarScales: function(width, height) {
// create list of all item ids within each axis
var sourceList = this.state.yAxisRender.groupIDs();
var targetList = this.state.xAxisRender.groupIDs();
- this.state.verticalScrollbarScale = d3.scale.ordinal()
+ this.state.verticalScrollbarScale = d3.scalePoint()
.domain(sourceList.map(function(d) {
- return d;
+ return d;
}))
- .rangePoints([0, height]);
+ .range([0, height]);
- this.state.horizontalScrollbarScale = d3.scale.ordinal()
+ this.state.horizontalScrollbarScale = d3.scalePoint()
.domain(targetList.map(function(d) {
- return d;
+ return d;
}))
- .rangePoints([0, width]);
+ .range([0, width]);
},
-
+
_setSvgSize: function() {
// Update the width and height of #pg_svg
var svgWidth;
-
+
if (this._isCrossComparisonView()) {
svgWidth = this.state.gridRegion.x + this.state.multiTargetsModeTargetLengthLimit*this.state.selectedCompareTargetGroup.length*this.state.gridRegion.cellPad + this.state.gridRegion.rowLabelOffset + this.state.btnPadding;
} else {
svgWidth = this.state.gridRegion.x + this.state.singleTargetModeTargetLengthLimit*this.state.gridRegion.cellPad + this.state.gridRegion.rowLabelOffset + this.state.btnPadding;
}
-
+
d3.select('#' + this.state.pgInstanceId + '_svg')
.attr('width', svgWidth)
- .attr('height', this.state.gridRegion.y + this._gridHeight() + 100);
+ .attr('height', this.state.gridRegion.y + this._gridHeight() + 100);
},
-
+
// Click the setting button to open the control options
// Click the cross mark to close when it's open
_togglePhenogridControls: function() {
@@ -1445,13 +1505,13 @@ var images = require('./images.json');
$(this).addClass("pg_slide_open");
}
});
-
+
$('#' + this.state.pgInstanceId + '_controls_close').click(function() {
$('#' + self.state.pgInstanceId + '_controls_options').fadeOut();
$('#' + self.state.pgInstanceId + '_slide_btn').removeClass("pg_slide_open");
});
},
-
+
// Click the setting button to open unmatched sources
// Click the cross mark to close when it's open
_toggleUnmatchedSources: function() {
@@ -1465,16 +1525,16 @@ var images = require('./images.json');
$(this).addClass("pg_unmatched_open");
}
});
-
+
$('#' + this.state.pgInstanceId + '_unmatched_close').click(function() {
$('#' + self.state.pgInstanceId + '_unmatched_list').fadeOut();
$('#' + self.state.pgInstanceId + '_unmatched_btn').removeClass("pg_unmatched_open");
});
},
-
+
_crossHairsOff: function() {
- this.state.svg.selectAll(".pg_focusLine").remove();
+ this.state.svg.selectAll(".pg_focusLine").remove();
},
// direction: 'vertical' or 'horizontal' or 'both'
@@ -1482,25 +1542,24 @@ var images = require('./images.json');
var xScale = this.state.xAxisRender.getScale();
var xs = xScale(id);
-
- var gridRegion = this.state.gridRegion;
+ var gridRegion = this.state.gridRegion;
var x = gridRegion.x + (xs * gridRegion.cellPad) + 5; // magic number to make sure it goes through the middle of the cell
- var y = gridRegion.y + (ypos * gridRegion.cellPad) + 5;
+ var y = gridRegion.y + (ypos * gridRegion.cellPad) + 5;
if (direction === 'vertical') {
this._createFocusLineVertical(x, gridRegion.y, x, gridRegion.y + this._gridHeight());
} else if (direction === 'horizontal') {
- this._createFocusLineHorizontal(gridRegion.x, y, gridRegion.x + this._gridWidth(), y);
+ this._createFocusLineHorizontal(gridRegion.x, y, gridRegion.x + this._gridWidth(), y);
} else {
// creating 4 lines around the cell, this way there's no svg elements overlapped - Joe
this._createFocusLineVertical(x, gridRegion.y, x, gridRegion.y + gridRegion.cellPad*ypos); // vertical line above cell
this._createFocusLineVertical(x, gridRegion.y + gridRegion.cellPad*ypos + gridRegion.cellSize, x, gridRegion.y + this._gridHeight()); // vertical line under cell
this._createFocusLineHorizontal(gridRegion.x, y, gridRegion.x + gridRegion.cellPad*xs, y); // horizontal line on the left of cell
- this._createFocusLineHorizontal(gridRegion.x + gridRegion.cellPad*xs + gridRegion.cellSize, y, gridRegion.x + this._gridWidth(), y); // horizontal line on the right of cell
+ this._createFocusLineHorizontal(gridRegion.x + gridRegion.cellPad*xs + gridRegion.cellSize, y, gridRegion.x + this._gridWidth(), y); // horizontal line on the right of cell
}
},
-
- _createFocusLineVertical: function(x1, y1, x2, y2) {
+
+ _createFocusLineVertical: function(x1, y1, x2, y2) {
this.state.svg.append('line')
.attr('class', 'pg_focusLine')
.attr('x1', x1)
@@ -1509,7 +1568,7 @@ var images = require('./images.json');
.attr('y2', y2);
},
- _createFocusLineHorizontal: function(x1, y1, x2, y2) {
+ _createFocusLineHorizontal: function(x1, y1, x2, y2) {
this.state.svg.append('line')
.attr('class', 'pg_focusLine')
.attr('x1', x1)
@@ -1523,21 +1582,21 @@ var images = require('./images.json');
_mouseover: function (elem, d) {
// show matching highlighting and crosshairs on mouseover lebel/cell
this._showEffectsOnMouseover(elem, d);
-
+
// render tooltip data
var data;
-
- if (d.type === 'cell') {
- data = this.state.dataManager.getCellDetail(d.source_id, d.target_id, d.targetGroup);
+
+ if (d.type === 'cell') {
+ data = this.state.dataManager.getCellDetail(d.source_id, d.target_id, d.targetGroup);
} else if (elem.classList.contains("pg_targetGroup_name")) {
data = new Array();
data["type"] = "targetgroup";
data["name"] = d;
} else {
- data = d;
+ data = d;
}
this._createHoverBox(data);
-
+
// show tooltip
// elem is a native DOM element
this._showTooltip($('#' + this.state.pgInstanceId + '_tooltip'), elem, d);
@@ -1554,20 +1613,20 @@ var images = require('./images.json');
_showEffectsOnMouseover: function(elem, d) {
if (elem.classList.contains("pg_targetGroup_name")) {
d3.select("#" + elem.id).classed("pg_active", true);
- } else if (d.type === 'cell') {
+ } else if (d.type === 'cell') {
// d.xpos and d.ypos only appear for cell - Joe
// hightlight row/col labels
d3.select("#" + this.state.pgInstanceId + "_grid_row_" + d.ypos +" text")
.classed("pg_active", true);
d3.select("#" + this.state.pgInstanceId + "_grid_col_" + d.xpos +" text")
.classed("pg_active", true);
-
+
// hightlight the cell
d3.select("#" + this.state.pgInstanceId + "_cell_" + d.ypos +"_" + d.xpos)
- .classed("pg_rowcolmatch", true);
+ .classed("pg_rowcolmatch", true);
// show crosshairs
- this._crossHairsOn(d.target_id, d.ypos, 'both');
+ this._crossHairsOn(d.target_id, d.ypos, 'both');
} else if (d.type === 'phenotype') {
this._createMatchingHighlight(elem, d);
// show crosshair
@@ -1580,7 +1639,7 @@ var images = require('./images.json');
var xpos = xScale(d.id);
this._crossHairsOn(d.id, xpos, 'vertical');
}
- } else {
+ } else {
this._createMatchingHighlight(elem, d);
// show crosshair
if ( ! this.state.invertAxis) {
@@ -1594,7 +1653,7 @@ var images = require('./images.json');
}
}
},
-
+
// removes the highlighted x/y labels as well as highlighted grid cell
_removeMatchingHighlight: function() {
// remove highlighting for row/col
@@ -1602,8 +1661,8 @@ var images = require('./images.json');
d3.selectAll(".pg_targetGroup_name").classed("pg_active", false);
d3.selectAll(".column text").classed("pg_active", false);
d3.selectAll(".row text").classed("pg_related_active", false);
- d3.selectAll(".column text").classed("pg_related_active", false);
- d3.selectAll(".cell").classed("pg_rowcolmatch", false);
+ d3.selectAll(".column text").classed("pg_related_active", false);
+ d3.selectAll(".cell").classed("pg_rowcolmatch", false);
},
_createMatchingHighlight: function(elem, data) {
@@ -1620,8 +1679,8 @@ var images = require('./images.json');
for (var k=0; k < matches.length; k++) {
d3.select("#" + this.state.pgInstanceId + "_grid_row_" + matches[k].ypos +" text").classed("pg_related_active", true);
}
- }
- d3.select("#" + this.state.pgInstanceId + "_grid_col_" + currenPos +" text").classed("pg_active", true);
+ }
+ d3.select("#" + this.state.pgInstanceId + "_grid_col_" + currenPos +" text").classed("pg_active", true);
} else { // hovered over a row
hightlightSources = false;
var matches = this.state.dataManager.getMatrixSourceTargetMatches(currenPos, hightlightSources);
@@ -1630,35 +1689,35 @@ var images = require('./images.json');
for (var k=0; k < matches.length; k++) {
d3.select("#" + this.state.pgInstanceId + "_grid_col_" + matches[k].xpos +" text").classed("pg_related_active", true);
}
- }
+ }
d3.select("#" + this.state.pgInstanceId + "_grid_row_" + currenPos +" text").classed("pg_active", true);
- }
+ }
},
_gridWidth: function() {
- var gridRegion = this.state.gridRegion;
+ var gridRegion = this.state.gridRegion;
var gridWidth = (gridRegion.cellPad * this.state.xAxisRender.displayLength()) - (gridRegion.cellPad - gridRegion.cellSize);
return gridWidth;
},
_gridHeight: function() {
- var gridRegion = this.state.gridRegion;
- var height = (gridRegion.cellPad * this.state.yAxisRender.displayLength()) - (gridRegion.cellPad - gridRegion.cellSize);
+ var gridRegion = this.state.gridRegion;
+ var height = (gridRegion.cellPad * this.state.yAxisRender.displayLength()) - (gridRegion.cellPad - gridRegion.cellSize);
return height;
},
_createTextScores: function () {
var self = this;
- var gridRegion = self.state.gridRegion;
+ var gridRegion = self.state.gridRegion;
var list, scale, axRender;
if (self.state.invertAxis) {
- list = self.state.yAxisRender.keys();
+ list = self.state.yAxisRender.keys();
scale = self.state.yAxisRender.getScale();
axRender = self.state.yAxisRender;
} else {
- list = self.state.xAxisRender.keys();
+ list = self.state.xAxisRender.keys();
scale = self.state.xAxisRender.getScale();
axRender = self.state.xAxisRender;
}
@@ -1667,27 +1726,27 @@ var images = require('./images.json');
.enter().append("g")
.attr("class", "pg_score_text");
- scores.append("text")
+ scores.append("text")
.attr("text-anchor", "start")
.style("font-size", "9px")
.style("fill", "#8763A3")
- .text(function(d, i) {
+ .text(function(d, i) {
var el = axRender.itemAt(i);
- return el.score;
+ return el.score;
});
if (self.state.invertAxis) { // score are render vertically
scores
- .attr("transform", function(d) {
- return "translate(" + (gridRegion.x - gridRegion.cellPad) +", " + (gridRegion.y+scale(d)*gridRegion.cellPad + 9) + ")";
+ .attr("transform", function(d) {
+ return "translate(" + (gridRegion.x - gridRegion.cellPad) +", " + (gridRegion.y+scale(d)*gridRegion.cellPad + 9) + ")";
});
} else {
- scores
- .attr("transform", function(d) {
+ scores
+ .attr("transform", function(d) {
return "translate(" + (gridRegion.x + scale(d)*gridRegion.cellPad) + "," + (gridRegion.y - gridRegion.scoreOffset) +")";
});
}
- },
+ },
_getCellColor: function(score) {
var selectedScale = this.state.colorScale[this.state.selectedCalculation];
@@ -1697,7 +1756,7 @@ var images = require('./images.json');
// Tip info icon for more info on those text scores
_createScoresTipIcon: function() {
- var self = this; // Used in the anonymous function
+ var self = this; // Used in the anonymous function
this.state.svg.append("text")
.attr('font-family', 'FontAwesome')
@@ -1712,7 +1771,7 @@ var images = require('./images.json');
self._populateDialog(htmlnotes.scores);
});
},
-
+
// for overview mini map
_createSmallScales: function(width, height) {
@@ -1720,25 +1779,26 @@ var images = require('./images.json');
var sourceList = this.state.yAxisRender.groupIDs();
var targetList = this.state.xAxisRender.groupIDs();
- this.state.smallYScale = d3.scale.ordinal()
+ this.state.smallYScale = d3.scalePoint()
.domain(sourceList.map(function (d) {
- return d;
+ return d;
}))
- .rangePoints([0, height]);
+ .range([0, height]);
- this.state.smallXScale = d3.scale.ordinal()
+ this.state.smallXScale = d3.scalePoint()
.domain(targetList.map(function (d) {
- return d;
+ return d;
}))
- .rangePoints([0, width]);
+ .range([0, width]);
},
// Used by minimap and scrollbars
_invertDragPosition: function(scale, value) {
var leftEdges = scale.range();
- var size = scale.rangeBand();
+ var size = scale.bandwidth();
+
var j;
- for (j = 0; value > (leftEdges[j] + size); j++) {}
+ for (j = 0; value > (leftEdges[j] + size); j++) {}
// iterate until leftEdges[j]+size is past value
return j;
},
@@ -1749,8 +1809,8 @@ var images = require('./images.json');
return this.state.yAxisRender.get(key);
} else if (this.state.xAxisRender.contains(key)) {
return this.state.xAxisRender.get(key);
- } else {
- return null;
+ } else {
+ return null;
}
},
@@ -1759,8 +1819,8 @@ var images = require('./images.json');
return this.state.yAxisRender.position(key);
} else if (this.state.xAxisRender.contains(key)) {
return this.state.xAxisRender.position(key);
- } else {
- return -1;
+ } else {
+ return -1;
}
},
@@ -1770,17 +1830,17 @@ var images = require('./images.json');
this.state.colorScale = []; // One color scale per calculation method - Joe
// 4 different calculations (0 - Similarity, 1 - Ration (q), 2 - Uniqueness, 3- Ratio (t))
var len = this.state.similarityCalculation.length;
-
+
for (var i = 0; i < len; i++) {
// Except Uniqueness (index is 2), all other three methods use 100 as the maxScore
var maxScore = 100;
if (i === 2) {
- maxScore = this.state.dataManager.maxMaxIC; // Uniqueness
+ maxScore = this.state.dataManager.maxMaxIC; // Uniqueness
}
-
- // Constructs a new linear scale with the default domain [0,1] and the default range [0,1].
- var cs = d3.scale.linear();
- // Simply put: scales transform a number in a certain interval (called the domain)
+
+ // Constructs a new linear scale with the default domain [0,1] and the default range [0,1].
+ var cs = d3.scaleLinear();
+ // Simply put: scales transform a number in a certain interval (called the domain)
// into a number in another interval (called the range).
// transform a score domain to a color domain, then transform a color domain into an actual color range
@@ -1798,7 +1858,7 @@ var images = require('./images.json');
}
},
- // add a tooltip div stub, this is used to dynamically set a tooltip info
+ // add a tooltip div stub, this is used to dynamically set a tooltip info
_createTooltipStub: function() {
var pg_tooltip = $("
")
.attr("id", this.state.pgInstanceId + '_tooltip')
@@ -1815,7 +1875,7 @@ var images = require('./images.json');
// Hide the tooltip div by default
this._hideTooltip(pg_tooltip);
},
-
+
// Bind tooltip to SVG X and Y labels as well as grid cells for mouseout - Joe
_relinkTooltip: function() {
var self = this;
@@ -1828,34 +1888,45 @@ var images = require('./images.json');
}
self._hideTooltip($tooltip);
-
+
// this hides the tooltip when we move mouse out the current element
- $targets.mouseout(function(e){
+ $targets.mouseout(function(e){
var elem = e.relatedTarget || e.toElement || e.fromElement;
if (typeof(elem) !== 'undefined' ) {
- if (elem.id !== (self.state.pgInstanceId + '_tooltip') && elem.id !== "") {
+ if (elem.id !== (self.state.pgInstanceId + '_tooltip') && elem.id !== "") {
self._hideTooltip($tooltip);
}
}
});
});
},
-
+
// tooltip is a jquery element
// elem is the mouseover element, native DOM element - Joe
- _showTooltip: function(tooltip, elem, d) {
+ _showTooltip: function(tooltip, elem, d) {
// Firstly we need to position the tooltip
-
- // The .offset() method allows us to retrieve the current position of an element relative to the document.
- // get the position of the x/y label or cell where the mouse event happened
+
+ // The .offset() method allows us to retrieve the current position of an element relative to the document.
+ // get the position of the x/y label or cell where the mouse event happened
// .offset() is a jquery method, so we need to use $(elem) - Joe
- var pos = $(elem).offset();
+ // var jqElem = $(elem);
+ // var pos = jqElem.offset();
+ // pos = elem.getBoundingClientRect();
+ var pos = getOffset(elem);
+
// position of the pg_container
var pgContainerPos = this.state.pgContainer.offset();
// Calculate the absolute x and y position of the tooltip,
// otherwise, the tooltip will be incorrectly position when run phenogrid inside monarch-app - Joe
var leftPos = pos.left - pgContainerPos.left;
- var topPos = pos.top - pgContainerPos.top;
+ var topPos = pos.top - pgContainerPos.top;
+
+ // console.log('ttbug elem/jq', pos, elem, jqElem);
+ // console.log(jqElem.offset);
+ // console.log('ttbug',
+ // elem.parentNode.id.indexOf('grid_row'),
+ // elem.getBoundingClientRect(),
+ // leftPos, topPos, pos.left, pos.top, pgContainerPos.left, pgContainerPos.top);
// When we hover over a grid row (label text or grid cell), place the tooltip on the far right of the element
if (elem.parentNode.id.indexOf('grid_row') > -1) {
@@ -1867,7 +1938,7 @@ var images = require('./images.json');
//adjust the tooltip for group headers
} else if (elem.classList.contains('pg_targetGroup_name')) {
topPos += elem.getBoundingClientRect().height;
- } else {
+ } else {
// shift overlap for y label mouseover - Joe
leftPos += 10;
}
@@ -1877,7 +1948,7 @@ var images = require('./images.json');
tooltip.show();
// Secondly we need to add add mouseover and mouseleave events to tooltip
-
+
// Remove all event handlers from #pg_tooltip to prevent duplicated mouseover/mouseleave
// without using this, the previously added mouseover/mouseleave enent will stay there - Joe
// https://api.jqueryui.com/jquery.widget/#method-_off
@@ -1886,7 +1957,7 @@ var images = require('./images.json');
// jquery-ui widget factory api: _on()
// https://api.jqueryui.com/jquery.widget/#method-_on
- // _on() maintains proper this context inside the handlers
+ // _on() maintains proper this context inside the handlers
// so we can reuse this._showEffectsOnMouseover() wihout using `var self = this;`
this._on(tooltip, {
"mouseover": function() {
@@ -1894,7 +1965,7 @@ var images = require('./images.json');
this._showEffectsOnMouseover(elem, d);
}
});
-
+
// Attach mouseleave event to tooltip
// mouseout doesn't work - Joe
// The mouseout event triggers when the mouse pointer leaves any child elements as well the selected element.
@@ -1954,14 +2025,14 @@ var images = require('./images.json');
}
// format data for rendering in a tooltip
- var retData = this._renderTooltip(id, data);
+ var retData = this._renderTooltip(id, data);
// update the stub pg_tooltip div dynamically to display
$("#" + this.state.pgInstanceId + "_tooltip_inner").empty();
$("#" + this.state.pgInstanceId + "_tooltip_inner").html(retData);
- // For phenotype ontology tree
- // For phenotype ontology tree
+ // For phenotype ontology tree
+ // For phenotype ontology tree
if (data.type === 'phenotype') {
// https://api.jqueryui.com/jquery.widget/#method-_on
// Binds click event to the ontology tree expand icon - Joe
@@ -1975,7 +2046,7 @@ var images = require('./images.json');
}
});
}
-
+
// For genotype expansion
if (data.type === 'gene') {
// In renderTooltip(), the font awesome icon element follows the form of id="this.state.pgInstanceId_insert_genotypes_MGI:98297"
@@ -1986,7 +2057,7 @@ var images = require('./images.json');
this._insertExpandedItems(id);
}
});
-
+
var remove = $('.pg_expand_genotype');
this._on(remove, {
"click": function(event) {
@@ -1998,7 +2069,7 @@ var images = require('./images.json');
_phenotypeTooltip: function(id, data) {
var htmlContent = '';
-
+
// phenotype tooltip shows type, id, sum, frequency, and ontology expansion
var tooltipType = (typeof(data.type) !== 'undefined' ? "" + Utils.capitalizeString(data.type) + ": " + this._encodeTooltipHref(data.type, id, data.label) + " " : "");
var ic = (typeof(data.IC) !== 'undefined' ? "IC: " + data.IC.toFixed(2)+" " : "");
@@ -2006,7 +2077,7 @@ var images = require('./images.json');
var frequency = (typeof(data.count) !== 'undefined' ? "Frequency: " + data.count +" " : "");
htmlContent = tooltipType + ic + sum + frequency;
-
+
var expanded = false;
var ontologyData = " ";
@@ -2025,34 +2096,35 @@ var images = require('./images.json');
ontologyData += "Classification hierarchy:" + tree;
}
}
-
+
if (expanded){
htmlContent += ontologyData;
- } else {
+ }
+ else if (!isBioLinkServer(this.state.serverURL)) {
htmlContent += '
Expand classification hierarchy
';
}
-
+
// Finally return the rendered HTML result
return htmlContent;
},
-
+
_targetTooltip: function(id, data) {
var htmlContent = '';
-
+
var msg = "Show only " + data.name + " results";
if (this.state.taxonExpanded || this.state.selectedCompareTargetGroup.length === 1) {
msg = "Show results for all species";
}
htmlContent = msg;
-
+
// Finally return the rendered HTML result
return htmlContent;
},
-
+
_cellTooltip: function(id, data) {
var htmlContent = '';
-
+
var suffix = "";
var selCalc = this.state.selectedCalculation;
@@ -2060,16 +2132,16 @@ var images = require('./images.json');
sourceId = data.source_id;
targetId = data.target_id;
-
+
if (this.state.invertAxis) {
- targetInfo = this.state.yAxisRender.get(data.target_id);
- sourceInfo = this.state.xAxisRender.get(data.source_id);
+ targetInfo = this.state.yAxisRender.get(data.target_id);
+ sourceInfo = this.state.xAxisRender.get(data.source_id);
} else {
- targetInfo = this.state.xAxisRender.get(data.target_id);
- sourceInfo = this.state.yAxisRender.get(data.source_id);
+ targetInfo = this.state.xAxisRender.get(data.target_id);
+ sourceInfo = this.state.yAxisRender.get(data.source_id);
}
- for (var idx in this.state.similarityCalculation) {
+ for (var idx in this.state.similarityCalculation) {
if (this.state.similarityCalculation[idx].calc === this.state.selectedCalculation) {
prefix = this.state.similarityCalculation[idx].label;
break;
@@ -2081,20 +2153,20 @@ var images = require('./images.json');
suffix = '%';
}
- htmlContent = "" + Utils.capitalizeString(sourceInfo.type) + " "
- + this._encodeTooltipHref(sourceInfo.type, sourceId, data.a_label ) + " " + Utils.formatScore(data.a_IC.toFixed(2)) + "
"
+ + "Match "
+ this._encodeTooltipHref(sourceInfo.type, data.b_id, data.b_label ) + Utils.formatScore(data.b_IC.toFixed(2));
-
+
// Finally return the rendered HTML result
return htmlContent;
},
-
+
_vendorTooltip: function(id, data) {
var htmlContent = '';
-
+
// Tooltip rendering for Vendor
for (var idx in data.info) {
// null is a javascript object
@@ -2121,18 +2193,18 @@ var images = require('./images.json');
// Finally return the rendered HTML result
return htmlContent.slice(0, -4); // Trim off the last ' '
},
-
+
_defaultTooltip: function(id, data) {
var htmlContent = '';
-
+
// disease and gene/genotype share common items
var tooltipType = (typeof(data.type) !== 'undefined' ? "" + Utils.capitalizeString(data.type) + ": " + this._encodeTooltipHref(data.type, id, data.label) + " " : "");
var rank = (typeof(data.rank) !== 'undefined' ? "Rank: " + data.rank+" " : "");
- var score = (typeof(data.score) !== 'undefined' ? "Score: " + data.score+" " : "");
+ var score = (typeof(data.score) !== 'undefined' ? "Score: " + data.score+" " : "");
var group = (typeof(data.targetGroup) !== 'undefined' ? "Species: " + data.targetGroup+" " : "");
htmlContent = tooltipType + rank + score + group;
-
+
// Add genotype expansion link to genes
// genotype expansion won't work with owlSimFunction === 'compare' since we use
// 'compare' as the key of the named array, while the added genotypes are named based on their group - Joe
@@ -2143,42 +2215,42 @@ var images = require('./images.json');
var expanded = this.state.dataManager.isExpanded(id); // gene id
if (expanded){
- htmlContent += '
Remove associated genotypes
';
+ htmlContent += '
Remove associated genotypes
';
} else {
- htmlContent += '
Insert associated genotypes
';
+ htmlContent += '
Insert associated genotypes
';
}
}
}
-
+
// Finally return the rendered HTML result
return htmlContent;
},
-
+
// main method for rendering tooltip content
_renderTooltip: function(id, data) {
var htmlContent = '';
-
+
if (data.type === 'phenotype') {
- htmlContent = this._phenotypeTooltip(id, data);
+ htmlContent = this._phenotypeTooltip(id, data);
} else if (data.type === 'cell') {
- htmlContent = this._cellTooltip(id, data);
+ htmlContent = this._cellTooltip(id, data);
} else if (this.state.gridSkeletonDataVendor === 'IMPC') {
- htmlContent = this._vendorTooltip(id, data);
+ htmlContent = this._vendorTooltip(id, data);
} else if (data.type === 'targetgroup') {
- htmlContent = this._targetTooltip(id, data);
+ htmlContent = this._targetTooltip(id, data);
} else {
- htmlContent = this._defaultTooltip(id, data);
+ htmlContent = this._defaultTooltip(id, data);
}
-
+
// Finally return the rendered HTML result
return htmlContent;
},
-
+
// also encode the labels into html entities, otherwise they will mess up the tooltip content format
_encodeTooltipHref: function(type, id, label) {
return '' + Utils.encodeHtmlEntity(label) + '';
},
-
+
// This builds the string to show the relations of the ontology nodes. It recursively cycles through the edges and in the end returns the full visual structure displayed in the phenotype hover
_buildOntologyTree: function(id, edges, level) {
var results = "";
@@ -2203,7 +2275,7 @@ var images = require('./images.json');
} else {
results += nextResult + " " + this._buildIndentMark(this.state.ontologyTreeHeight - nextLevel) + this._buildOntologyHyperLink(edges[j].obj);
}
-
+
if (level === 0){
results += " " + this._buildIndentMark(this.state.ontologyTreeHeight) + this.state.dataManager.getOntologyLabel(id) + " ";
this.state.ontologyTreeHeight = 0;
@@ -2224,7 +2296,7 @@ var images = require('./images.json');
for (var i = 1; i < treeHeight; i++){
indent += '';
}
-
+
return indent + '↳'; // HTML entity - Joe
},
@@ -2238,7 +2310,7 @@ var images = require('./images.json');
// Only create divider lines in multi-group mode
if (this._isCrossComparisonView()) {
-
+
var totalColumns = 0;
var columnsCounter = [];
// No need to get the last group length
@@ -2248,15 +2320,15 @@ var images = require('./images.json');
totalColumns += this.state.targetLengthPerGroup[i].targetLength;
columnsCounter.push(totalColumns);
}
-
+
for (var i = 1; i < this.state.selectedCompareTargetGroup.length; i++) {
if (this.state.invertAxis) {
// gridRegion.colLabelOffset: offset the line to reach the labels
- var y = gridRegion.y + gridRegion.cellPad * columnsCounter[i-1] - (gridRegion.cellPad - gridRegion.cellSize)/2;
+ var y = gridRegion.y + gridRegion.cellPad * columnsCounter[i-1] - (gridRegion.cellPad - gridRegion.cellSize)/2;
// render horizontal divider line
- this.state.svg.append("line")
- .attr("class", "pg_target_grp_divider")
+ this.state.svg.append("line")
+ .attr("class", "pg_target_grp_divider")
.attr("x1", gridRegion.x - gridRegion.rowLabelOffset)
.attr("y1", y)
.attr("x2", gridRegion.x + this._gridWidth()) // adjust this for to go beyond the row label
@@ -2266,11 +2338,11 @@ var images = require('./images.json');
.style("shape-rendering", "crispEdges");
} else {
// Perfectly center the first divider line between the 10th and 11th cell, same rule for the second line ...
- var x = gridRegion.x + gridRegion.cellPad * columnsCounter[i-1] - (gridRegion.cellPad - gridRegion.cellSize)/2;
+ var x = gridRegion.x + gridRegion.cellPad * columnsCounter[i-1] - (gridRegion.cellPad - gridRegion.cellSize)/2;
// render vertical divider line
- this.state.svg.append("line")
- .attr("class", "pg_target_grp_divider")
+ this.state.svg.append("line")
+ .attr("class", "pg_target_grp_divider")
.attr("x1", x)
.attr("y1", gridRegion.y - gridRegion.colLabelOffset)
.attr("x2", x)
@@ -2280,15 +2352,15 @@ var images = require('./images.json');
.style("shape-rendering", "crispEdges");
// render the slanted line between targetGroup (targetGroup) columns
- this.state.svg.append("line")
+ this.state.svg.append("line")
.attr("class", "pg_target_grp_divider")
// rotate( [])
- // The optional cx and cy values represent the unitless coordinates of the point used as a center of rotation.
- // If cx and cy are not provided, the rotation is about the origin of the current user coordinate system.
- .attr("transform", "rotate(-45 " + x + " " + (gridRegion.y - gridRegion.colLabelOffset) + ")")
+ // The optional cx and cy values represent the unitless coordinates of the point used as a center of rotation.
+ // If cx and cy are not provided, the rotation is about the origin of the current user coordinate system.
+ .attr("transform", "rotate(-45 " + x + " " + (gridRegion.y - gridRegion.colLabelOffset) + ")")
.attr("x1", x)
.attr("y1", gridRegion.y - gridRegion.colLabelOffset)
- .attr("x2", x + this.state.targetGroupDividerLine.rotatedDividerLength) // extend the line out to underline the labels
+ .attr("x2", x + this.state.targetGroupDividerLine.rotatedDividerLength) // extend the line out to underline the labels
.attr("y2", gridRegion.y - gridRegion.colLabelOffset)
.style("stroke", this.state.targetGroupDividerLine.color)
.style("stroke-width", this.state.targetGroupDividerLine.thickness)
@@ -2304,9 +2376,9 @@ var images = require('./images.json');
// xvalues and yvalues are index arrays that contain the current x and y items, not all of them
// if any items are added genotypes, they only contain visible genotypes
// Axisgroup's constructor does the data filtering - Joe
- var xvalues = this.state.xAxisRender.entries();
+ var xvalues = this.state.xAxisRender.entries();
var yvalues = this.state.yAxisRender.entries();
- var gridRegion = this.state.gridRegion;
+ var gridRegion = this.state.gridRegion;
var xScale = this.state.xAxisRender.getScale();
var yScale = this.state.yAxisRender.getScale();
@@ -2316,7 +2388,7 @@ var images = require('./images.json');
} else {
var matrix = this.state.dataManager.buildMatrix(xvalues, yvalues, false);
}
-
+
// create column labels first, so the added genotype cells will overwrite the background color - Joe
// create columns using the xvalues (targets)
var column = this.state.svg.selectAll(".column")
@@ -2324,20 +2396,20 @@ var images = require('./images.json');
.enter()
.append("g")
.attr("class", 'column')
- .style("font-size", '11px')
- .attr("id", function(d, i) {
+ .style("font-size", '11px')
+ .attr("id", function(d, i) {
return self.state.pgInstanceId + "_grid_col_" + i;
- })
- .attr("transform", function(d, i) {
+ })
+ .attr("transform", function(d, i) {
var offset = gridRegion.colLabelOffset;
// i starts from 0
- return "translate(" + (gridRegion.x + (i*gridRegion.cellPad)) + "," + (gridRegion.y - offset) + ")rotate(-45)";
+ return "translate(" + (gridRegion.x + (i*gridRegion.cellPad)) + "," + (gridRegion.y - offset) + ")rotate(-45)";
}); //-45
// create column labels
column.append("text")
.attr("x", 0)
- .attr("y", xScale.rangeBand()+2) //2
+ .attr("y", xScale.bandwidth()+2) //2
.attr("dy", ".32em")
.style('fill', function(d) { // add different color to genotype labels
// Only added genotypes have this `parentGeneID` property
@@ -2347,12 +2419,12 @@ var images = require('./images.json');
return '';
}
})
- .attr("data-tooltip", this.state.pgInstanceId + "_tooltip")
+ .attr("data-tooltip", this.state.pgInstanceId + "_tooltip")
.attr("text-anchor", "start")
- .text(function(d) {
- return Utils.getShortLabel(d.label, self.state.targetLabelCharLimit);
+ .text(function(d) {
+ return Utils.getShortLabel(d.label, self.state.targetLabelCharLimit);
})
- .on("mouseover", function(d) {
+ .on("mouseover", function(d) {
// self is the global widget this
// this passed to _mouseover refers to the current element
// _mouseover() highlights and matching x/y labels, and creates crosshairs on current grid cell
@@ -2362,12 +2434,12 @@ var images = require('./images.json');
// _mouseout() removes the matching highlighting as well as the crosshairs - Joe
self._mouseout();
});
-
+
// grey background for added genotype columns - Joe
// no need to add this grey background for multi group or owlSimFunction === 'compare' - Joe
if (this.state.selectedCompareTargetGroup.length === 1 && this.state.selectedCompareTargetGroup[0].groupName !== 'compare') {
column.append("rect")
- .attr("y", xScale.rangeBand() - 1 + gridRegion.colLabelOffset)
+ .attr("y", xScale.bandwidth() - 1 + gridRegion.colLabelOffset)
.attr('width', gridRegion.cellSize)
.attr('height', self._gridHeight())
.style('fill', function(d){
@@ -2375,41 +2447,41 @@ var images = require('./images.json');
if (d.type === 'genotype' && typeof(d.parentGeneID) !== 'undefined') {
return '#ededed'; // fill color needs to be here instead of CSS, for SVG export purpose - Joe
} else {
- return 'none'; // transparent
+ return 'none'; // transparent
}
})
.style('opacity', 0.8)
- .attr("transform", function(d) {
- return "rotate(45)";
+ .attr("transform", function(d) {
+ return "rotate(45)";
}); //45
}
-
+
// add the scores for labels
self._createTextScores();
// create a row, the matrix contains an array of rows (yscale) with an array of columns (xscale)
var row = this.state.svg.selectAll(".row")
.data(matrix)
- .enter().append("g")
- .attr("class", "row")
- .attr("id", function(d, i) {
+ .enter().append("g")
+ .attr("class", "row")
+ .attr("id", function(d, i) {
return self.state.pgInstanceId + "_grid_row_" + i;
})
- .attr("transform", function(d, i) {
+ .attr("transform", function(d, i) {
var y = self.state.gridRegion.y;
var ypad = self.state.gridRegion.cellPad;
- return "translate(" + gridRegion.x +"," + (y + (i*ypad)) + ")";
+ return "translate(" + gridRegion.x +"," + (y + (i*ypad)) + ")";
});
// create row labels
row.append("text")
- .attr("x", -gridRegion.rowLabelOffset) // shift a bit to the left to create some white spaces for inverting
+ .attr("x", -gridRegion.rowLabelOffset) // shift a bit to the left to create some white spaces for inverting
.attr("y", function(d, i) {
- var rb = yScale.rangeBand(i)/2;
+ var rb = yScale.bandwidth()/2;
return rb;
- })
- .attr("dy", ".80em") // this makes small adjustment in position
+ })
+ .attr("dy", ".80em") // this makes small adjustment in position
.attr("text-anchor", "end")
.style("font-size", "11px")
// the d.type is cell instead of genotype because the d refers to cell data
@@ -2423,12 +2495,12 @@ var images = require('./images.json');
return '';
}
})
- .attr("data-tooltip", this.state.pgInstanceId + "_tooltip")
- .text(function(d, i) {
+ .attr("data-tooltip", this.state.pgInstanceId + "_tooltip")
+ .text(function(d, i) {
var el = self.state.yAxisRender.itemAt(i);
- return Utils.getShortLabel(el.label);
+ return Utils.getShortLabel(el.label);
})
- .on("mouseover", function(d, i) {
+ .on("mouseover", function(d, i) {
var data = self.state.yAxisRender.itemAt(i); // d is really an array of data points, not individual data pt
// self is the global widget this
// this passed to _mouseover refers to the current element
@@ -2438,7 +2510,7 @@ var images = require('./images.json');
})
.on("mouseout", function() {
// _mouseout() removes the matching highlighting as well as the crosshairs - Joe
- self._mouseout();
+ self._mouseout();
});
// no need to add this grey background for multi groups or owlSimFunction === 'compare' - Joe
@@ -2452,14 +2524,14 @@ var images = require('./images.json');
if (el.type === 'genotype' && typeof(el.parentGeneID) !== 'undefined') {
return '#ededed'; // fill color needs to be here instead of CSS, for SVG export purpose - Joe
} else {
- return 'none'; // transparent
+ return 'none'; // transparent
}
})
.style('opacity', 0.8);
}
-
+
// create the grid cells after appending all the background rects
- // so they can overwrite the row background for those added genotype rows - Joe
+ // so they can overwrite the row background for those added genotype rows - Joe
row.each(createrow);
// callback for row.each()
@@ -2468,26 +2540,26 @@ var images = require('./images.json');
var cell = d3.select(this).selectAll(".cell")
.data(row)
.enter().append("rect")
- .attr("id", function(d) {
- return self.state.pgInstanceId + "_cell_"+ d.ypos + "_" + d.xpos;
+ .attr("id", function(d) {
+ return self.state.pgInstanceId + "_cell_"+ d.ypos + "_" + d.xpos;
})
.attr("class", "cell")
- .attr("x", function(d) {
+ .attr("x", function(d) {
return d.xpos * gridRegion.cellPad;
})
.attr("width", gridRegion.cellSize)
- .attr("height", gridRegion.cellSize)
- .attr("data-tooltip", "tooltip")
- .style("fill", function(d) {
+ .attr("height", gridRegion.cellSize)
+ .attr("data-tooltip", "tooltip")
+ .style("fill", function(d) {
var el = self.state.dataManager.getCellDetail(d.source_id, d.target_id, d.targetGroup);
return self._getCellColor(el.value[self.state.selectedCalculation]);
})
- .on("mouseover", function(d) {
+ .on("mouseover", function(d) {
// self is the global widget this
// this passed to _mouseover refers to the current element
// _mouseover() highlights and matching x/y labels, and creates crosshairs on current grid cell
// _mouseover() also triggers the tooltip popup as well as the tooltip mouseover/mouseleave - Joe
- self._mouseover(this, d, self);
+ self._mouseover(this, d, self);
})
.on("mouseout", function() {
// _mouseout() removes the matching highlighting as well as the crosshairs - Joe
@@ -2496,7 +2568,7 @@ var images = require('./images.json');
}
},
-
+
/*
* Change the list of phenotypes and filter the models accordingly.
*/
@@ -2515,11 +2587,11 @@ var images = require('./images.json');
this.state.yAxisRender.setRenderStartPos(this.state.currYIdx - this.state.yAxisRender.displayLength());
this.state.yAxisRender.setRenderEndPos(this.state.currYIdx);
-
+
this._recreateGrid();
},
-
- // used by vertical scrollbar
+
+ // used by vertical scrollbar
_updateVerticalGrid: function(newYPos){
var ySize = this.state.yAxisRender.groupLength();
@@ -2527,11 +2599,11 @@ var images = require('./images.json');
this.state.yAxisRender.setRenderStartPos(this.state.currYIdx - this.state.yAxisRender.displayLength());
this.state.yAxisRender.setRenderEndPos(this.state.currYIdx);
-
+
this._recreateGrid();
},
-
- // used by horizontal scrollbar
+
+ // used by horizontal scrollbar
_updateHorizontalGrid: function(newXPos){
var xSize = this.state.xAxisRender.groupLength();
@@ -2541,10 +2613,10 @@ var images = require('./images.json');
// so, the starting render position is this size minus the display limit
this.state.xAxisRender.setRenderStartPos(this.state.currXIdx - this.state.xAxisRender.displayLength());
this.state.xAxisRender.setRenderEndPos(this.state.currXIdx);
-
+
this._recreateGrid();
},
-
+
_recreateGrid: function() {
this._clearGrid();
this._createGrid();
@@ -2557,11 +2629,11 @@ var images = require('./images.json');
this.state.svg.selectAll("g.column").remove();
this.state.svg.selectAll("g.pg_score_text").remove();
},
-
+
_populateDialog: function(text) {
var SplitText = "Title";
var $dialog = $('')
- .html(SplitText )
+ // .html(SplitText)
.dialog({
modal: true,
width: 400,
@@ -2573,7 +2645,7 @@ var images = require('./images.json');
draggable: true,
dialogClass: "pg_faq_dialog_bg_color",
position: {
- my: "top",
+ my: "top",
at: "top+25%",
of: '#' + this.state.pgContainerId
},
@@ -2589,7 +2661,7 @@ var images = require('./images.json');
*/
_createGradientLegend: function(){
var gridRegion = this.state.gridRegion;
-
+
if (this._isCrossComparisonView()) {
var x = gridRegion.x + this.state.multiTargetsModeTargetLengthLimit*this.state.selectedCompareTargetGroup.length*gridRegion.cellPad/2;
} else {
@@ -2601,9 +2673,9 @@ var images = require('./images.json');
.attr('id', this.state.pgInstanceId + '_gradient_legend');
// The element is used to define a linear gradient background - Joe
- // The element must be nested within a tag.
+ // The element must be nested within a tag.
// The tag is short for definitions and contains definition of special elements (such as gradients)
- var gradient = gradientGrp.append("svg:defs").append("svg:linearGradient")
+ var gradient = gradientGrp.append("svg:defs").append("svg:linearGradient")
.attr("id", this.state.pgInstanceId + "_gradient_legend_fill") // this id is used for the fill attribute - Joe
.attr("x1", "0")
.attr("x2", "100%")
@@ -2615,7 +2687,7 @@ var images = require('./images.json');
if ( ! this.state.colorDomains.hasOwnProperty(j)) {
break;
}
-
+
gradient.append("svg:stop") // SVG stop element
.attr("offset", this.state.colorDomains[j]) // The offset attribute is used to define where the gradient color begin and end
.style("stop-color", this.state.colorRanges[j]);
@@ -2627,17 +2699,17 @@ var images = require('./images.json');
.attr("y", gridRegion.y + this._gridHeight() + 60) // use x and y instead of transform since rect has x and y, 60 is margin - Joe
.attr("id", this.state.pgInstanceId + "_gradient_legend_rect")
.attr("width", this.state.gradientRegion.width)
- .attr("height", this.state.gradientRegion.height)
+ .attr("height", this.state.gradientRegion.height)
.attr("fill", "url(#" + this.state.pgInstanceId + "_gradient_legend_fill)"); // The fill attribute links the element to the gradient defined in svg:linearGradient - Joe
-
+
// Now create the label texts
var lowText, highText, labelText;
// Texts are based on the calculation method
- for (var idx in this.state.similarityCalculation) {
+ for (var idx in this.state.similarityCalculation) {
if ( ! this.state.similarityCalculation.hasOwnProperty(idx)) {
break;
- }
+ }
if (this.state.similarityCalculation[idx].calc === this.state.selectedCalculation) {
lowText = this.state.similarityCalculation[idx].low;
highText = this.state.similarityCalculation[idx].high;
@@ -2650,7 +2722,7 @@ var images = require('./images.json');
var gradientTextGrp = this.state.svg.select('#' + this.state.pgInstanceId + '_gradient_legend').append("g")
.attr('id', this.state.pgInstanceId + '_gradient_legend_texts')
.style('font-size', '11px');
-
+
// Dynamicly change, relative to grid region - Joe
var yTexts = gridRegion.y + this._gridHeight() + 57; // 57 is margin - Joe
@@ -2664,15 +2736,15 @@ var images = require('./images.json');
// create and position the display type label
gradientTextGrp.append("svg:text")
.attr("x", x)
- .attr("y", yTexts)
- .style('text-anchor', 'middle') // This renders the middle of the text string as the current text position x - Joe
+ .attr("y", yTexts)
+ .style('text-anchor', 'middle') // This renders the middle of the text string as the current text position x - Joe
.text(labelText);
// create and position the high label
gradientTextGrp.append("svg:text")
- .attr("x", x + this.state.gradientRegion.width/2)
- .attr("y", yTexts)
- .style('text-anchor', 'end') // This renders the end of the text to align the end of the rect - Joe
+ .attr("x", x + this.state.gradientRegion.width/2)
+ .attr("y", yTexts)
+ .style('text-anchor', 'end') // This renders the end of the text to align the end of the rect - Joe
.text(highText);
},
@@ -2688,18 +2760,18 @@ var images = require('./images.json');
// Not in the #this.state.pgInstanceId_svg_group div since it's HTML - Joe
this.state.pgContainer.append(pg_unmatched);
-
+
// Need to put .pg_unmatched_list_arrow_border span before .pg_unmatched_list_arrow span - Joe
var pg_unmatched_list = '
';
-
+
// Hide/show unmatched - button - Joe
var pg_unmatched_btn ='
' + this.state.unmatchedButtonLabel + '
';
-
+
pg_unmatched.append(pg_unmatched_list);
pg_unmatched.append(pg_unmatched_btn);
$('#' + this.state.pgInstanceId + '_unmatched_list').hide(); // Hide by default
-
+
// Vendor data ships will all HP labels, no need to grab via ajax - Joe
if (this.state.gridSkeletonDataVendor === 'IMPC') {
var vendorDataUnmatchedSources = [];
@@ -2732,21 +2804,21 @@ var images = require('./images.json');
// Phengrid controls/options
_createPhenogridControls: function() {
- var self = this; // Use self inside anonymous functions
-
+ var self = this; // Use self inside anonymous functions
+
var phenogridControls = $('');
// Not in the #pg_svg_group div since it's HTML - Joe
this.state.pgContainer.append(phenogridControls);
-
+
// Need to put .pg_controls_options_arrow_border span before .pg_controls_options_arrow span - Joe
var optionhtml = '
';
-
+
// Hide/show panel - button - Joe
var slideBtn = '
' + this.state.optionsBtnText + '
';
-
+
var options = $(optionhtml);
-
+
// only show the Organism(s) option when we have at least two speices
if (this.state.initialTargetGroupLoadList.length > 1) {
var targetGroupSelection = this._createTargetGroupSelection();
@@ -2759,21 +2831,21 @@ var images = require('./images.json');
options.append(calcSel);
var axisSel = this._createAxisSelection();
options.append(axisSel);
-
+
var exportBtn = this._createExportPhenogridButton();
options.append(exportBtn);
-
+
var aboutPhenogrid = this._createAboutPhenogrid();
options.append(aboutPhenogrid);
-
+
phenogridControls.append(options);
-
+
// Append slide button - Joe
phenogridControls.append(slideBtn);
-
+
// Hide options menu by default
- $('#' + this.state.pgInstanceId + '_controls_options').hide();
-
+ $('#' + this.state.pgInstanceId + '_controls_options').hide();
+
// add the handler for the checkboxes control
$('#' + this.state.pgInstanceId + '_targetGroup').change(function(d) {
var items = this.childNodes; // this refers to $("#pg_targetGroup") object - Joe
@@ -2784,9 +2856,9 @@ var images = require('./images.json');
temp.push(rec);
}
}
-
+
if (temp.length > 0) {
- self.state.selectedCompareTargetGroup = temp;
+ self.state.selectedCompareTargetGroup = temp;
} else {
alert("You must have at least 1 target group selected.");
}
@@ -2799,7 +2871,7 @@ var images = require('./images.json');
// Remove the HTML if created from the former load
$('#' + self.state.pgInstanceId + '_unmatched').remove();
self._createUnmatchedSources();
-
+
self._updateDisplay();
});
@@ -2813,43 +2885,43 @@ var images = require('./images.json');
self.state.selectedSort = d.target.value;
// sort source with default sorting type
if (self.state.invertAxis){
- self.state.xAxisRender.sort(self.state.selectedSort);
+ self.state.xAxisRender.sort(self.state.selectedSort);
} else {
- self.state.yAxisRender.sort(self.state.selectedSort);
+ self.state.yAxisRender.sort(self.state.selectedSort);
}
-
+
self._updateDisplay();
});
- $("#" + this.state.pgInstanceId + "_invert_axis").click(function() {
+ $("#" + this.state.pgInstanceId + "_invert_axis").click(function() {
var $this = $(this);
- // $this will contain a reference to the checkbox
+ // $this will contain a reference to the checkbox
if ($this.is(':checked')) {
self.state.invertAxis = true;
} else {
self.state.invertAxis = false;
}
-
+
self._setAxisRenderers();
self._updateDisplay();
});
// Click save button to export the current phenogrid view as a SVG file - Joe
- $("#" + this.state.pgInstanceId + "_export").click(function() {
+ $("#" + this.state.pgInstanceId + "_export").click(function() {
// SVG styles are applied with D3, not in CSS for this exporting purpose
var svgElementClone = $('#' + self.state.pgInstanceId + '_svg').clone(); // clone the svg to manipulate
// Use data uri for svg logo
svgElementClone.find('#' + self.state.pgInstanceId + '_logo').attr('href', images.logo);
svgElementClone.find('#' + self.state.pgInstanceId + '_scores_tip_icon').remove(); // remove fontawesome icon
svgElementClone.find('#' + self.state.pgInstanceId + '_monarchinitiative_text').removeClass('pg_hide'); // Show text in exported SVG
-
+
var svgStr = '';
- // The standard W3C File API Blob interface is not available in all browsers.
+ // The standard W3C File API Blob interface is not available in all browsers.
// Blob.js is a cross-browser Blob implementation that solves this.
var blob = new Blob([svgStr], {type: "image/svg+xml"});
filesaver.saveAs(blob, "phenogrid.svg");
});
-
+
// FAQ popups
$('#' + this.state.pgInstanceId + '_sorts_faq').click("click", function() {
self._populateDialog(htmlnotes.sorts);
@@ -2858,8 +2930,8 @@ var images = require('./images.json');
$('#' + this.state.pgInstanceId + '_calcs_faq').click(function(){
self._populateDialog(htmlnotes.calcs);
});
-
- $('#' + this.state.pgInstanceId + '_about_phenogrid').click(function() {
+
+ $('#' + this.state.pgInstanceId + '_about_phenogrid').click(function() {
self._populateDialog(htmlnotes.faq);
});
},
@@ -2867,7 +2939,7 @@ var images = require('./images.json');
// Recognition text and monarch logo
_createMonarchInitiativeRecognition: function() {
var gridRegion = this.state.gridRegion;
-
+
if (this._isCrossComparisonView()) {
var x = gridRegion.x + this.state.multiTargetsModeTargetLengthLimit*this.state.selectedCompareTargetGroup.length*gridRegion.cellPad/2;
} else {
@@ -2876,8 +2948,8 @@ var images = require('./images.json');
// Create a group for text and logo
var recognitionGrp = this.state.svg.append("g")
- .attr('id', this.state.pgInstanceId + '_recognition');
-
+ .attr('id', this.state.pgInstanceId + '_recognition');
+
// Add text
recognitionGrp.append("text")
.attr("x", x)
@@ -2885,7 +2957,7 @@ var images = require('./images.json');
.attr("id", this.state.pgInstanceId + "_monarchinitiative_text")
.style('font-size', '10px')
.text(this.state.monarchInitiativeText);
-
+
// Add logo
var self = this;
recognitionGrp.append("svg:image")
@@ -2899,21 +2971,21 @@ var images = require('./images.json');
.on('click', function() {
window.open(self.state.serverURL, '_blank');
});
-
+
var recognitionGrpWidth = $("#" + this.state.pgInstanceId + "_recognition")[0].getBoundingClientRect().width;
// Center the group by left shift half the width of the group element
recognitionGrp.attr("transform", "translate(" + -recognitionGrpWidth/2 + "0)");
},
-
+
// Position the control panel when the gridRegion changes
_positionPhenogridControls: function() {
// Note: CANNOT use this inside _createPhenogridControls() since the _createGrid() is called after it
// we won't have the _gridHeight() by that time - Joe
- var gridRegion = this.state.gridRegion;
- var marginTop = 17; // Create some whitespace between the button and the y labels
+ var gridRegion = this.state.gridRegion;
+ var marginTop = 17; // Create some whitespace between the button and the y labels
//$('#' + this.state.pgInstanceId + '_slide_btn').css('top', gridRegion.y + this._gridHeight() + marginTop);
$('#' + this.state.pgInstanceId + '_slide_btn').css('top', this.state.optionsControls.top);
-
+
// The height of .pg_controls_options defined in phenogrid.css - Joe
var pg_ctrl_options = $('#' + this.state.pgInstanceId + '_controls_options');
// shrink the height when we don't show the group selection
@@ -2923,21 +2995,21 @@ var images = require('./images.json');
// options div has an down arrow, -10 to create some space between the down arrow and the button - Joe
//pg_ctrl_options.css('top', gridRegion.y + this._gridHeight() - pg_ctrl_options.outerHeight() - 10 + marginTop);
pg_ctrl_options.css('top', this.state.optionsControls.top + 30);
- pg_ctrl_options.css('left', this.state.optionsControls.left);
+ pg_ctrl_options.css('left', this.state.optionsControls.left);
$('#' + this.state.pgInstanceId + '_slide_btn').css('left', this.state.optionsControls.left);
/* // Place the options button to the right of the default limit of columns
if (this._isCrossComparisonView()) {
- pg_ctrl_options.css('left', gridRegion.x + this.state.multiTargetsModeTargetLengthLimit*this.state.selectedCompareTargetGroup.length*gridRegion.cellPad + gridRegion.rowLabelOffset);
+ pg_ctrl_options.css('left', gridRegion.x + this.state.multiTargetsModeTargetLengthLimit*this.state.selectedCompareTargetGroup.length*gridRegion.cellPad + gridRegion.rowLabelOffset);
$('#' + this.state.pgInstanceId + '_slide_btn').css('left', gridRegion.x + this.state.multiTargetsModeTargetLengthLimit*this.state.selectedCompareTargetGroup.length*gridRegion.cellPad + gridRegion.rowLabelOffset);
} else {
- pg_ctrl_options.css('left', gridRegion.x + this.state.singleTargetModeTargetLengthLimit*gridRegion.cellPad + gridRegion.rowLabelOffset);
+ pg_ctrl_options.css('left', gridRegion.x + this.state.singleTargetModeTargetLengthLimit*gridRegion.cellPad + gridRegion.rowLabelOffset);
$('#' + this.state.pgInstanceId + '_slide_btn').css('left', gridRegion.x + this.state.singleTargetModeTargetLengthLimit*gridRegion.cellPad + gridRegion.rowLabelOffset);
}
*/
- },
-
+ },
+
_createTargetGroupSelection: function() {
- var optionhtml = "
Target Group(s)
" +
+ var optionhtml = "
Target Group(s)
" +
"
";
for (var idx in this.state.targetGroupList) {
if ( ! this.state.targetGroupList.hasOwnProperty(idx)) {
@@ -2950,7 +3022,7 @@ var images = require('./images.json');
if (this._isTargetGroupSelected(this, this.state.targetGroupList[idx].groupName)) {
checked = "checked";
}
- // If there is no data for a given group, even if it's set as active in config,
+ // If there is no data for a given group, even if it's set as active in config,
// it should not be shown in the group selector - Joe
if (this.state.dataManager.length('target', this.state.targetGroupList[idx].groupName) === 0) {
disabled = "disabled";
@@ -2974,7 +3046,7 @@ var images = require('./images.json');
for (var idx in this.state.similarityCalculation) {
if ( ! this.state.similarityCalculation.hasOwnProperty(idx)) {
break;
- }
+ }
var checked = "";
if (this.state.similarityCalculation[idx].calc === this.state.selectedCalculation) {
checked = "checked";
@@ -2988,7 +3060,7 @@ var images = require('./images.json');
// create the html necessary for selecting the sort
_createSortPhenotypeSelection: function () {
- var optionhtml ="
Sort Phenotypes" +
+ var optionhtml ="
Sort Phenotypes" +
'
' + // FontAwesome - Joe
'
';
@@ -3014,22 +3086,22 @@ var images = require('./images.json');
if (this.state.invertAxis) {
checked = "checked";
}
- var optionhtml = '
Invert Axis
';
+ var optionhtml = '
Invert Axis
';
return $(optionhtml);
},
// create about phenogrid FAQ inside the controls/options - Joe
_createAboutPhenogrid: function () {
- var html = '
About Phenogrid
';
+ var html = '
About Phenogrid
';
return $(html);
},
-
+
// Export current state of phenogrid as SVG file to be used in publications
_createExportPhenogridButton: function() {
var btn = '
';
$('#' + self.state.pgInstanceId + '_unmatched_list_data').append(pg_unmatched_list_item);
-
+
// iterative back to process to make sure we processed all the targets
self._formatUnmatchedSources(targets);
},
-
+
// ajax
_fetchUnmatchedLabel: function(target, targets, callback) {
var self = this;
-
- // Note: phenotype label is not in the unmatched array when this widget runs as a standalone app,
- // so we need to fetch each label from the monarch-app server
- // Sample output: http://monarchinitiative.org/phenotype/HP:0000746.json
- // Separate the ajax request with callbacks
- var jqxhr = $.ajax({
- url: this.state.serverURL + "/phenotype/" + target + ".json",
- async: true,
- method: 'GET',
- dataType: 'json'
- });
-
- jqxhr.done(function(data) {
- callback(self, target, targets, data); // callback needs self for reference to global this - Joe
- });
-
- jqxhr.fail(function () {
- console.log('Ajax error - _fetchUnmatchedLabel()')
- });
+
+ if (isBioLinkServer(this.state.serverURL)) {
+ var ids = this.state.dataLoader.ids;
+ var targetEntry = ids.find(function(i) {
+ return i.id === target;
+ });
+ var targetLabel = targetEntry ? targetEntry.label : target;
+
+ var labelData =
+ {
+ "labels": [
+ targetLabel
+ ],
+ "curie": target,
+ "categories": [
+ "Phenotype"
+ ],
+ "synonyms": [
+ target
+ ],
+ "id": target,
+ "label": targetLabel,
+ };
+
+ callback(this, target, targets, labelData);
+ }
+ else {
+ // Note: phenotype label is not in the unmatched array when this widget runs as a standalone app,
+ // so we need to fetch each label from the monarch-app server
+ // Sample output: https://monarchinitiative.org/phenotype/HP:0000746.json
+ // Separate the ajax request with callbacks
+ var jqxhr = $.ajax({
+ url: this.state.serverURL + "/phenotype/" + target + ".json",
+ async: true,
+ method: 'GET',
+ dataType: 'json'
+ });
+
+ jqxhr.done(function(data) {
+ callback(self, target, targets, data); // callback needs self for reference to global this - Joe
+ });
+
+ jqxhr.fail(function () {
+ console.log('Ajax error - _fetchUnmatchedLabel()')
+ });
+ }
},
-
+
_formatUnmatchedSources: function(targetGrpList) {
if (targetGrpList.length > 0) {
var target = targetGrpList[0]; // pull off the first to start processing
@@ -3123,7 +3222,7 @@ var images = require('./images.json');
}
},
-
+
/*
* Given an array of phenotype objects edit the object array.
* items are objects of the form { "id": "HP:0000174", "term": "Abnormality of the palate"}
@@ -3143,7 +3242,7 @@ var images = require('./images.json');
// since JavaScript Array push() doesn't remove duplicates,
// we need to get rid of the duplicates. There are many duplicates from the monarch-app returned json - Joe
// Based on "Smart" but naïve way - http://stackoverflow.com/questions/9229645/remove-duplicates-from-javascript-array - Joe
- // filter() calls a provided callback function once for each element in an array,
+ // filter() calls a provided callback function once for each element in an array,
// and constructs a new array of all the values for which callback returns a true value or a value that coerces to true.
newlist = newlist.filter(function(item) {
return filteredList.hasOwnProperty(item) ? false : (filteredList[item] = true);
@@ -3159,7 +3258,7 @@ var images = require('./images.json');
if (typeof(cache) === 'undefined') {
var cb = this._postExpandOntologyCB;
- this.state.dataLoader.getOntology(id, this.state.ontologyDirection, this.state.ontologyDepth, cb, this);
+ this.state.dataLoader.getOntology(id, this.state.ontologyDirection, this.state.ontologyDepth, cb, this);
} else {
this._postExpandOntologyCB(cache, id, this);
}
@@ -3169,7 +3268,7 @@ var images = require('./images.json');
// Must use parent to pass this - Joe
_postExpandOntologyCB: function(d, id, parent) {
parent.state.ontologyTreesDone = 0;
- parent.state.ontologyTreeHeight = 0;
+ parent.state.ontologyTreeHeight = 0;
var info = parent._getAxisData(id);
var hrefLink = '' + info.label + '';
var ontologyData = "Phenotype: " + hrefLink + " ";
@@ -3193,42 +3292,42 @@ var images = require('./images.json');
// change the plus icon to spinner to indicate the loading
$('.pg_expand_genotype_icon').removeClass('fa-plus-circle');
$('.pg_expand_genotype_icon').addClass('fa-spinner fa-pulse');
-
+
// When we can expand a gene, we must be in the single group mode,
// and there must be only one group in this.state.selectedCompareTargetGroup - Joe
var group_name = this.state.selectedCompareTargetGroup[0].groupName;
var loaded = this.state.dataManager.checkExpandedItemsLoaded(group_name, id);
- // when we can see the insert genotypes link in tooltip,
+ // when we can see the insert genotypes link in tooltip,
// the genotypes are either haven't been loaded or have already been loaded but then removed(invisible)
if (loaded) {
// change those associated genotypes to 'visible' and render them
// array of genotype id list
var associated_genotype_ids = this.state.dataLoader.loadedNewTargetGroupItems[id];
-
+
// reactivating by changing 'visible' to true
for (var i = 0; i < associated_genotype_ids.length; i++) {
var genotype_id = associated_genotype_ids[i];
-
+
// update the underlying data (not ordered) in dataLoader
- // In dataManager, reorderedTargetEntriesNamedArray and reorderedTargetEntriesIndexArray are also updated once we update the
- // underlying data in dataLoader, because variable reference in javascript, not actual copy/clone - Joe
- this.state.dataLoader.targetData[group_name][genotype_id].visible = true;
+ // In dataManager, reorderedTargetEntriesNamedArray and reorderedTargetEntriesIndexArray are also updated once we update the
+ // underlying data in dataLoader, because variable reference in javascript, not actual copy/clone - Joe
+ this.state.dataLoader.targetData[group_name][genotype_id].visible = true;
}
-
+
this.state.reactivateTargetGroupItems[group_name] = true;
-
+
this._updateTargetAxisRenderingGroup(group_name);
-
+
this.state.reactivateTargetGroupItems[group_name] = false;
-
+
this._updateDisplay();
-
+
// Remove the spinner icon
$('.pg_expand_genotype_icon').removeClass('fa-spinner fa-pulse');
$('.pg_expand_genotype_icon').addClass('fa-plus-circle');
-
+
// Tell dataManager that the loaded genotypes of this gene have been expanded
this.state.dataManager.expandedItemList[id] = this.state.dataLoader.loadedNewTargetGroupItems[id];
} else {
@@ -3238,9 +3337,9 @@ var images = require('./images.json');
this.state.dataLoader.getNewTargetGroupItems(id, cb, this);
}
},
-
+
// this cb has all the matches info returned from the compare
- // e.g., http://monarchinitiative.org/compare/:id1+:id2/:id3,:id4,...idN
+ // e.g., https://monarchinitiative.org/compare/:id1+:id2/:id3,:id4,...idN
// parent refers to the global `this` and we have to pass it
_insertExpandedItemsCb: function(results, id, parent, errorMsg) {
console.log(results);
@@ -3255,17 +3354,17 @@ var images = require('./images.json');
// transform raw owlsims into simplified format
// append the genotype matches data to targetData[targetGroup]/sourceData[targetGroup]/cellData[targetGroup]
- parent.state.dataLoader.transformNewTargetGroupItems(group_name, results, id);
-
+ parent.state.dataLoader.transformNewTargetGroupItems(group_name, results, id);
+
// call this before reordering the target list
// to update this.state.targetAxis so it has the newly added genotype data in the format of named array
// when we call parent.state.targetAxis.groupEntries()
parent._updateTargetAxisRenderingGroup(group_name);
-
+
if (typeof(parent.state.dataManager.reorderedTargetEntriesIndexArray[group_name]) === 'undefined') {
parent.state.dataManager.reorderedTargetEntriesIndexArray[group_name] = [];
}
-
+
// for the first time, just get the unordered groupEntries()
// starting from second time, append the genotype data of following expansions to the already ordered target list
if (parent.state.dataManager.reorderedTargetEntriesIndexArray[group_name].length === 0) {
@@ -3273,16 +3372,16 @@ var images = require('./images.json');
} else {
var updatedTargetEntries = parent.state.dataManager.appendNewItemsToOrderedTargetList(group_name, results.b);
}
-
+
// Now we update the target list in dataManager
// and place those genotypes right after their parent gene
var newItemsData = {
- targetEntries: updatedTargetEntries,
- genotypes: results.b,
+ targetEntries: updatedTargetEntries,
+ genotypes: results.b,
parentGeneID: id,
group: group_name
};
-
+
// this will give us a reordered target list in two formats.
// one is associative/named array(reorderedTargetEntriesNamedArray), the other is number indexed array(reorderedTargetEntriesIndexArray)
parent.state.dataManager.updateTargetList(newItemsData);
@@ -3290,28 +3389,28 @@ var images = require('./images.json');
// we set the genotype flag before calling _updateTargetAxisRenderingGroup() again
// _updateTargetAxisRenderingGroup() uses this flag for creating this.state.targetAxis
parent.state.newTargetGroupItems[group_name] = true;
-
+
// call this again after the target list gets updated
// so this.state.targetAxis gets updated with the reordered target list (reorderedTargetEntriesNamedArray)
// as well as the new start position and end position
parent._updateTargetAxisRenderingGroup(group_name);
-
+
// then reset the flag to false so it can still grab the newly added genotypes of another gene
// and add them to the unordered target list.
- // without resetting this flag, we'll just get reorderedTargetEntriesNamedArray from dataManager and
- // reorderedTargetEntriesNamedArray hasn't been updated with the genotypes of the new expansion
+ // without resetting this flag, we'll just get reorderedTargetEntriesNamedArray from dataManager and
+ // reorderedTargetEntriesNamedArray hasn't been updated with the genotypes of the new expansion
parent.state.newTargetGroupItems[group_name] = false;
-
- // flag, indicates that we have expanded genotypes for this group,
+
+ // flag, indicates that we have expanded genotypes for this group,
// so they show up when we switch from multi-group mode back to single group mode
parent.state.expandedTargetGroupItems[group_name] = true;
parent._updateDisplay();
-
+
// Remove the spinner icon
$('.pg_expand_genotype_icon').removeClass('fa-spinner fa-pulse');
$('.pg_expand_genotype_icon').addClass('fa-plus-circle');
-
+
// Tell dataManager that the loaded genotypes of this gene have been expanded
parent.state.dataManager.expandedItemList[id] = parent.state.dataLoader.loadedNewTargetGroupItems[id];
}
@@ -3327,35 +3426,35 @@ var images = require('./images.json');
// When we can expand a gene, we must be in the single group mode,
// and there must be only one group in this.state.selectedCompareTargetGroup - Joe
var group_name = this.state.selectedCompareTargetGroup[0].groupName;
-
+
// array of genotype id list
var associated_genotype_ids = this.state.dataLoader.loadedNewTargetGroupItems[id];
-
- // change 'visible' to false
+
+ // change 'visible' to false
for (var i = 0; i < associated_genotype_ids.length; i++) {
var genotype_id = associated_genotype_ids[i];
// update the underlying data
- // In dataManager, reorderedTargetEntriesNamedArray and reorderedTargetEntriesIndexArray are also updated once we update the
- // underlying data in dataLoader, because variable reference in javascript, not actual copy/clone - Joe
- this.state.dataLoader.targetData[group_name][genotype_id].visible = false;
+ // In dataManager, reorderedTargetEntriesNamedArray and reorderedTargetEntriesIndexArray are also updated once we update the
+ // underlying data in dataLoader, because variable reference in javascript, not actual copy/clone - Joe
+ this.state.dataLoader.targetData[group_name][genotype_id].visible = false;
}
-
- // Tell dataManager that the loaded genotypes of this gene have been collapsed from display
+
+ // Tell dataManager that the loaded genotypes of this gene have been collapsed from display
delete this.state.dataManager.expandedItemList[id];
-
+
// set the flag
this.state.removedTargetGroupItems[group_name] = true;
-
+
// update the target list for axis render
this._updateTargetAxisRenderingGroup(group_name);
// reset flag
this.state.removedTargetGroupItems[group_name] = false;
-
+
// update display
this._updateDisplay();
- },
-
+ },
+
_isTargetGroupSelected: function(self, name) {
for (var i in self.state.selectedCompareTargetGroup) {
if (self.state.selectedCompareTargetGroup[i].groupName === name) {
diff --git a/package.json b/package.json
index f7e9e7f3..fb244aed 100644
--- a/package.json
+++ b/package.json
@@ -1,6 +1,6 @@
{
"name": "phenogrid",
- "version": "1.3.11",
+ "version": "1.4.0",
"description": "Monarch PhenoGrid widget",
"repository": {
"type": "git",
@@ -17,33 +17,46 @@
"bugs": {
"url": "https://github.com/monarch-initiative/phenogrid/issues"
},
+ "files": [
+ "dist/",
+ "js/",
+ "README.md"
+ ],
"homepage": "https://github.com/monarch-initiative/phenogrid",
+ "scripts": {
+ "clean": "rm -rf dist/",
+ "rmdsstore": "find . -name '.DS_Store' -print -delete",
+ "lint": "eslint js/",
+ "build": "gulp bundle",
+ "devbuild": "gulp dev-bundle",
+ "buildandserve": "npm run devbuild && http-server -c-1",
+ "pack": "npm run rmdsstore && npm run build && npm pack"
+ },
"dependencies": {
- "d3": "3.5.17",
- "filesaver.js": "0.2.0",
- "font-awesome": "4.6.1",
- "jquery": "2.2.4",
- "jquery-ui": "1.10.5",
- "normalize.css": "4.1.1"
+ "d3": "5.7.0",
+ "font-awesome": "4.7.0",
+ "jquery": "^3.3.1",
+ "jquery-ui": "1.12.1",
+ "normalize.css": "8.0.1"
},
"devDependencies": {
- "browserify": "13.0.0",
- "chai": "3.5.0",
- "gulp": "3.9.0",
- "gulp-concat": "2.5.2",
- "gulp-cssnano": "2.1.0",
- "gulp-file-include": "0.13.5",
- "gulp-jshint": "2.0.0",
- "gulp-mocha": "2.0.1",
- "gulp-pandoc": "0.2.1",
- "gulp-rename": "1.2.2",
- "gulp-replace": "0.5.4",
+ "browserify": "16.2.3",
+ "chai": "4.2.0",
+ "eslint": "^5.12.0",
+ "file-saver": "^2.0.0",
+ "gulp": "^3.9.1",
+ "gulp-concat": "2.6.1",
+ "gulp-cssnano": "2.1.3",
+ "gulp-eslint": "^5.0.0",
+ "gulp-file-include": "2.0.1",
+ "gulp-mocha": "6.0.0",
+ "gulp-rename": "1.4.0",
+ "gulp-replace": "1.0.0",
"gulp-streamify": "1.0.2",
- "gulp-uglify": "1.2.0",
- "jshint": "2.9.1",
- "jshint-stylish": "2.0.1",
- "lodash": "4.11.1",
- "vinyl-source-stream": "1.1.0"
+ "gulp-uglify": "3.0.1",
+ "http-server": "^0.11.1",
+ "lodash": "4.17.11",
+ "vinyl-source-stream": "2.0.0"
},
"main": "js/phenogrid.js"
}
diff --git a/tests/behave/environment.py b/tests/behave/environment.py
index ebae9820..b7ab488b 100644
--- a/tests/behave/environment.py
+++ b/tests/behave/environment.py
@@ -1,7 +1,7 @@
##############################################################################
#
# Setup gross testing environment.
-#
+#
# UI instance target base URL and browser type (Chrome, firefox, and PhantomJS).
# This software is subject to the provisions of the MIT license at
# https://pythonhosted.org/behave/tutorial.html#environmental-controls
@@ -12,23 +12,61 @@
import sys
import time
from selenium import webdriver
+from selenium.webdriver.common.desired_capabilities import DesiredCapabilities
+# from selenium.webdriver.firefox.firefox_binary import FirefoxBinary
+from selenium.webdriver import Firefox
+from selenium.webdriver.firefox.options import Options
+
+window_width = 1200
+window_height = 825
# Run this before anything else.
def before_all(context):
- # Determine the target path. Can either be file path or base URL.
+ print("context", context)
+ # Determine the target path. Can either be file path or base URL.
if 'TARGET' in os.environ:
context.target = os.environ['TARGET']
else:
print("Please specify the Phenogrid file path or base URL with 'TARGET=' format")
sys.exit(1)
-
+
# Check to see which browser to use, default to use Firefox
if 'BROWSER' in os.environ and os.environ['BROWSER'] == 'phantomjs':
context.browser = webdriver.PhantomJS()
print("# Using PhantomJS")
- else:
- context.browser = webdriver.Firefox()
- print("# Using Firefox")
+ else:
+ options = Options()
+ options.add_argument('-headless')
+ context.browser = Firefox(firefox_options=options)
+
+ # print("# Using Firefox")
+ # d = DesiredCapabilities.FIREFOX
+ # d['marionette'] = True
+ # # d['binary'] = '/Applications/Firefox.app/Contents/MacOS/firefox-bin'
+ # d['loggingPrefs'] = {'browser': 'ALL', 'client': 'ALL', 'driver': 'ALL', 'performance': 'ALL', 'server': 'ALL'}
+ # fp = webdriver.FirefoxProfile()
+ # fp.set_preference('devtools.jsonview.enabled', False)
+ # fp.set_preference('javascript.options.showInConsole', True)
+ # fp.set_preference('browser.dom.window.dump.enabled', True)
+ # fp.set_preference('devtools.chrome.enabled', True)
+ # fp.set_preference("devtools.webconsole.persistlog", True)
+
+ # fp.set_preference("devtools.browserconsole.filter.jslog", True)
+ # fp.set_preference("devtools.browserconsole.filter.jswarn", True)
+ # fp.set_preference("devtools.browserconsole.filter.error", True)
+ # fp.set_preference("devtools.browserconsole.filter.warn", True)
+ # fp.set_preference("devtools.browserconsole.filter.info", True)
+ # fp.set_preference("devtools.browserconsole.filter.log", True)
+
+ # fp.set_preference("devtools.webconsole.filter.jslog", True)
+ # fp.set_preference("devtools.webconsole.filter.jswarn", True)
+ # fp.set_preference("devtools.webconsole.filter.error", True)
+ # fp.set_preference("devtools.webconsole.filter.warn", True)
+ # fp.set_preference("devtools.webconsole.filter.info", True)
+ # fp.set_preference("devtools.webconsole.filter.log", True)
+
+ # context.browser = webdriver.Firefox(capabilities=d, firefox_profile=fp, executable_path='/usr/local/bin/geckodriver')
+ # context.browser._is_remote = False
# Set a 30 second implicit wait - http://selenium-python.readthedocs.org/en/latest/waits.html#implicit-waits
# Once set, the implicit wait is set for the life of the WebDriver object instance.
@@ -38,3 +76,8 @@ def before_all(context):
# Do this after completing everything.
def after_all(context):
context.browser.quit()
+
+# def before_scenario(context, scenario):
+# context.browser.set_window_size(window_width, window_height)
+# time.sleep(5)
+# pass
diff --git a/tests/behave/generic.feature b/tests/behave/generic.feature
index a24488b7..df401590 100644
--- a/tests/behave/generic.feature
+++ b/tests/behave/generic.feature
@@ -7,7 +7,7 @@ Feature: Generic features work when running Phenogrid as a standalone widget
Scenario Outline: Visible items once the widget is loaded
- Given I go to page "/index.html"
+ Given I go to slow page "/index.html" and wait for id "phenogrid_container1_slide_btn"
Then the id "" should contain ""
Examples: - list
| id | text |
@@ -15,7 +15,7 @@ Feature: Generic features work when running Phenogrid as a standalone widget
Scenario Outline: Visible items after open options panel
- Given I go to page "/index.html"
+ Given I go to slow page "/index.html" and wait for id "phenogrid_container1_slide_btn"
And I click the "phenogrid_container1_slide_btn"
Then the id "" should contain ""
Examples: - list
@@ -35,18 +35,18 @@ Feature: Generic features work when running Phenogrid as a standalone widget
Scenario: Model scores popup dialog
- Given I go to page "/index.html"
+ Given I go to slow page "/index.html" and wait for id "phenogrid_container1_slide_btn"
And I click the "phenogrid_container1_scores_tip_icon"
Then the document should contain "What is the score shown at the top of the grid?"
-
+
Scenario: Appearance of vertical scrollbar slider
- Given I go to page "/index.html"
+ Given I go to slow page "/index.html" and wait for id "phenogrid_container1_slide_btn"
Then the document should contain id "phenogrid_container1_vertical_scrollbar_slider"
-
-
+
+
Scenario: Appearance of horizontal scrollbar slider after inverting axis
- Given I go to page "/index.html"
+ Given I go to slow page "/index.html" and wait for id "phenogrid_container1_slide_btn"
And I click the "phenogrid_container1_slide_btn"
And I click the "phenogrid_container1_invert_axis"
Then the document should contain id "phenogrid_container1_horizontal_scrollbar_slider"
\ No newline at end of file
diff --git a/tests/behave/steps/phenogrid.py b/tests/behave/steps/phenogrid.py
index 9ecbe2fb..23b682b0 100644
--- a/tests/behave/steps/phenogrid.py
+++ b/tests/behave/steps/phenogrid.py
@@ -2,7 +2,7 @@
#
# A set of basic steps.
# https://selenium-python.readthedocs.org/
-#
+#
##############################################################################
from behave import *
@@ -10,7 +10,7 @@
from urllib.parse import urlparse
except ImportError:
from urlparse import urlparse
-
+
import time
import datetime
from selenium import webdriver
@@ -32,8 +32,8 @@ def step_impl(context, page):
@given('I go to slow page "{page}" and wait for id "{id}"')
def step_impl(context, page, id):
context.browser.get(context.target + page)
- #time.sleep(30)
- element = WebDriverWait(context.browser, 60).until(EC.presence_of_element_located((By.ID, id)))
+ # time.sleep(10)
+ element = WebDriverWait(context.browser, 30).until(EC.presence_of_element_located((By.ID, id)))
# try:
# print(id)
# element = WebDriverWait(context.browser, 30).until(EC.presence_of_element_located((By.ID, id)))
@@ -41,7 +41,7 @@ def step_impl(context, page, id):
# print("FINALLY")
# #context.browser.quit()
-# Click
+# Click
@given('I click the "{id}"')
def step_impl(context, id):
webelt = context.browser.find_element_by_id(id)
@@ -63,7 +63,7 @@ def step_impl(context, text):
radio_btn = context.browser.find_element_by_css_selector(target)
radio_btn.click()
context.browser.implicitly_wait(30)
-
+
# The document body should contain a certain piece of text.
@then('the document should contain "{text}"')
def step_impl(context, text):
@@ -82,7 +82,7 @@ def step_impl(context, id):
webelt = context.browser.find_element_by_id(id)
assert webelt.get_attribute('id') == id
-
+
# The document body should contain a hyperlink with text.
@then('the document should contain link with "{text}"')
def step_impl(context, text):
@@ -94,7 +94,7 @@ def step_impl(context, text):
isFound = False
assert isFound
-# A given id should contain a given piece of text/content.
+# A given id should contain a given piece of text/content.
# Not generably usable by non-dev test writers.
@then('the id "{id}" should contain "{text}"')
def step_impl(context, id, text):