diff --git a/gulpfile.js b/gulpfile.js index 931292a..68de15b 100644 --- a/gulpfile.js +++ b/gulpfile.js @@ -20,6 +20,7 @@ var watchify = require('watchify'); var uglify = require('gulp-uglify'); var minifyCss = require('gulp-minify-css'); var concat = require('gulp-concat'); +var less = require('gulp-less'); // testing var mocha = require('gulp-mocha'); @@ -120,7 +121,7 @@ gulp.task('build-test',['init'], function() { }); gulp.task('test-watch', function() { - gulp.watch(['./src/**/*.js','./lib/**/*.js', './test/**/*.js', './style/main.css'], ['test']); + gulp.watch(['./src/**/*.js','./lib/**/*.js', './test/**/*.js', './less/**/*.less', './style/main.css'], ['test']); }); //build tasks @@ -136,17 +137,34 @@ gulp.task('init', ['clean'], function() { }); }); +gulp.task('compile-css', ['init'], function() { + return gulp.src("./less/*.less") + .pipe(less({ + paths: [ path.join(__dirname, 'less', 'includes') ] + })) + .pipe(gulp.dest('./style/')); +}); + gulp.task('copy-resources', ['init'], function() { gulp.src(["./font/**/*.*"]) .pipe(gulp.dest(buildDir + '/font/')); - return gulp.src("./style/*.css") - .pipe(minifyCss({compatibility: 'ie8'})) - .pipe(concat('main.css')) - .pipe(gulp.dest(buildDir + '/css/')); +}); + +gulp.task('build-css', ['copy-resources', 'compile-css'], function() { + return gulp.src(["./style/main.css", "./style/fontello.css"]) + .pipe(minifyCss({compatibility: 'ie8'})) + .pipe(concat('main.css')) + .pipe(gulp.dest(buildDir + '/css/')); +}); + +gulp.task('build-css-theme', ['compile-css'], function() { + return gulp.src(["./style/*theme*.css"]) + .pipe(minifyCss({compatibility: 'ie8'})) + .pipe(gulp.dest(buildDir + '/css/')); }); // browserify debug -gulp.task('build-browser',['copy-resources'], function() { +gulp.task('build-browser',['init', 'build-css', 'build-css-theme'], function() { var b = browserify({debug: true,hasExports: true}); exposeBundles(b); return b.bundle() diff --git a/less/fv-theme-grey.less b/less/fv-theme-grey.less new file mode 100644 index 0000000..431fdbd --- /dev/null +++ b/less/fv-theme-grey.less @@ -0,0 +1,177 @@ +@import "includes/defaults"; + +/* +d3.scale.category20() +*/ +@c1: #1f77b4; +@c2: #aec7e8; +@c3: #ff7f0e; +@c4: #ffbb78; +@c5: #2ca02c; +@c6: #98df8a; +@c7: #d62728; +@c8: #ff9896; +@c9: #9467bd; +@c10: #c5b0d5; +@c11: #8c564b; +@c12: #c49c94; +@c13: #e377c2; +@c14: #f7b6d2; +@c15: #7f7f7f; +@c16: #c7c7c7; +@c17: #bcbd22; +@c18: #dbdb8d; +@c19: #17becf; +@c20: #9edae5; + +@c_chain: @c1; +@c_transit: @c2; +@c_init_met: @c3; +@c_propep: @c4; +@c_peptide: @c5; +@c_signal: @c6; +@c_turn: @c7; +@c_strand: @c8; +@c_helix: @c9; +@c_crosslnk: @c10; +@c_disulfid: @c11; +@c_region: @c12; +@c_coiled: @c13; +@c_motif: @c14; +@c_repeat: @c15; +@c_ca_bind: @c16; +@c_dna_bind: @c17; +@c_domain: @c18; +@c_zn_fing: @c19; +@c_np_bind: @c20; +@c_metal: @c1; +@c_site: @c2; +@c_binding: @c3; +@c_act_site: @c4; +@c_mod_res: @c5; +@c_lipid: @c6; +@c_carbohyd: @c7; +@c_compbias: @c8; +@c_mutagen: @c9; +@c_conflict: @c10; +@c_non_cons: @c11; +@c_non_ter: @c12; +@c_unsure: @c13; +@c_non_std: @c14; +@c_topo_dom: @c15; +@c_transmem: @c16; +@c_intramem: @c17; + +.pv-theme-grey { + .up_pftv_navruler svg { + background-color: transparent; + } + + .up_pftv_buttons { + padding: 0; + margin: 0; + width: 100%; + text-align: center; + } + + .up_pftv_buttons span { + background-color: #eee; + border: 1px solid #ccc; + border-radius: 3px 3px; + padding: 2px; + margin: 2px; + } + + .up_pftv_buttons span:hover { + background-color: #fff; + border-color: #999; + } + + .up_pftv_aaviewer .background, .up_pftv_viewport .background { + fill: white; + } + + .up_pftv_container text.domain-label { + font-size: 12px !important; + } + + .up_pftv_container, .up_pftv_aaviewer text { + font-family: Helvetica, sans-serif; + } + + .up_pftv_container .up_pftv_navruler .resize { + font-size: 20px; + fill: #999; + fill-opacity: 1.0; + } + + .up_pftv_category-container { + border-right: 1px solid #ddd; + border-top: 1px solid #ddd; + } + + .up_pftv_category { + margin-bottom: 0; + border-bottom: 1px solid #ddd; + } + + .up_pftv_category-name { + background-color: #eee; + text-transform: uppercase; + } + + .up_pftv_category a:hover { + background-color: #ddd; + } + + .up_pftv_track-header { + background-color: #f3f3f3; + text-align: right; + text-transform: uppercase; + font-size: 12px; + font-style: italic; + } + + .up_pftv_track { + border-top: 1px solid #eee; + } + + .up_pftv_navruler path.trapezoid { + } + + .up_pftv_container .up_pftv_navruler .axis .tick, .up_pftv_container .up_pftv_navruler .axis .domain { + stroke: black; + } + .up_pftv_container .up_pftv_navruler .axis text { + stroke: none; + fill: black; + } + + .up_pftv_navruler .extent { + fill: steelblue; + stroke: none; + } + + .up_pftv_navruler .handle { + fill: #eee; + fill-opacity: 1.0; + stroke: #999; + stroke-opacity: 1.0; + stroke-width: 1; + } + + .up_pftv_navruler text.domain-label { + fill: black; + } + + .up_pftv_navruler .handle:hover { + fill: #ccc; + } + + .up_pftv_disulphid { + } + + @svg_features(); +} + + diff --git a/less/includes/defaults.less b/less/includes/defaults.less new file mode 100644 index 0000000..88aa553 --- /dev/null +++ b/less/includes/defaults.less @@ -0,0 +1,86 @@ +@c_chain: #CC9933; +@c_transit: #009966; +@c_init_met: #996633; +@c_propep: #99CCCC; +@c_peptide: #006699; +@c_signal: #CC0033; +@c_turn: #0571AF; +@c_strand: #FFCC00; +@c_helix: #FF0066; +@c_crosslnk: #FF6600; +@c_disulfid: #23B14D; +@c_region: #B33E00; +@c_coiled: #006699; +@c_motif: #402060; +@c_repeat: #9900FF; +@c_ca_bind: #FF3399; +@c_dna_bind: #009933; +@c_domain: #9999FF; +@c_zn_fing: #990066; +@c_np_bind: #FF9900; +@c_metal: #009900; +@c_site: #660033; +@c_binding: #006699; +@c_act_site: #FF6666; +@c_mod_res: #000066; +@c_lipid: #99CC33; +@c_carbohyd: #CC3366; +@c_compbias: #FF3366; +@c_mutagen: #FF9900; +@c_conflict: #6633CC; +@c_non_cons: #FF0033; +@c_non_ter: #339933; +@c_unsure: #33FF00; +@c_non_std: #330066; +@c_topo_dom: #CC0000; +@c_transmem: #CC00CC; +@c_intramem: #0000CC; + +@c_variant: #fc3133; +@c_unique: #fc3133; +@c_non_unique: #8585fc; + +/* Features */ +@svg_features: { + svg .up_pftv_chain { stroke: @c_chain; fill: @c_chain; } + svg .up_pftv_transit { stroke: @c_transit; fill: @c_transit; } + svg .up_pftv_init_met { stroke: @c_init_met; fill: @c_init_met; } + svg .up_pftv_propep { stroke: @c_propep; fill: @c_propep; } + svg .up_pftv_peptide { stroke: @c_peptide; fill: @c_peptide; } + svg .up_pftv_signal { stroke: @c_signal; fill: @c_signal; } + svg .up_pftv_crosslnk { stroke: @c_crosslnk; fill: @c_crosslnk; } + svg .up_pftv_disulfid { stroke: @c_disulfid; fill: @c_disulfid; } + svg .up_pftv_region { stroke: @c_region; fill: @c_region; } + svg .up_pftv_coiled { stroke: @c_coiled; fill: @c_coiled; } + svg .up_pftv_motif { stroke: @c_motif; fill: @c_motif; } + svg .up_pftv_repeat { stroke: @c_repeat; fill: @c_repeat; } + svg .up_pftv_ca_bind { stroke: @c_ca_bind; fill: @c_ca_bind; } + svg .up_pftv_dna_bind { stroke: @c_dna_bind; fill: @c_dna_bind; } + svg .up_pftv_domain { stroke: @c_domain; fill: @c_domain; } + svg .up_pftv_zn_fing { stroke: @c_zn_fing; fill: @c_zn_fing; } + svg .up_pftv_np_bind { stroke: @c_np_bind; fill: @c_np_bind; } + svg .up_pftv_metal { stroke: @c_metal; fill: @c_metal; } + svg .up_pftv_binding { stroke: @c_binding; fill: @c_binding; } + svg .up_pftv_act_site { stroke: @c_act_site; fill: @c_act_site; } + svg .up_pftv_mod_res { stroke: @c_mod_res; fill: @c_mod_res; } + svg .up_pftv_lipid { stroke: @c_lipid; fill: @c_lipid; } + svg .up_pftv_carbohyd { stroke: @c_carbohyd; fill: @c_carbohyd; } + svg .up_pftv_variant { stroke: @c_variant; fill: @c_variant; } + svg .up_pftv_compbias { stroke: @c_compbias; fill: @c_compbias; } + svg .up_pftv_mutagen { stroke: @c_mutagen; fill: @c_mutagen; } + svg .up_pftv_conflict { stroke: @c_conflict; fill: @c_conflict; } + svg .up_pftv_non_cons { stroke: @c_non_cons; fill: @c_non_cons; } + svg .up_pftv_non_ter { stroke: @c_non_ter; fill: @c_non_ter; } + svg .up_pftv_unsure { stroke: @c_unsure; fill: @c_unsure; } + svg .up_pftv_non_std { stroke: @c_non_std; fill: @c_non_std; } + svg .up_pftv_topo_dom { stroke: @c_topo_dom; fill: @c_topo_dom; } + svg .up_pftv_transmem { stroke: @c_transmem; fill: @c_transmem; } + svg .up_pftv_intramem { stroke: @c_intramem; fill: @c_intramem; } + svg .up_pftv_unique { stroke: @c_unique; fill: @c_unique; } + svg .up_pftv_non_unique { stroke: @c_non_unique; fill: @c_non_unique; } + + svg .up_pftv_turn { stroke: @c_turn; fill: @c_turn; } + svg .up_pftv_strand { stroke: @c_strand; fill: @c_strand; } + svg .up_pftv_helix { stroke: @c_helix; fill: @c_helix; } +}; + diff --git a/less/main.less b/less/main.less new file mode 100644 index 0000000..3b95549 --- /dev/null +++ b/less/main.less @@ -0,0 +1,538 @@ +@import "includes/defaults"; + +.up_pftv_container svg { + overflow: hidden; +} + +.up_pftv_container rect { + shape-rendering: crispEdges; +} + +.up_pftv_buttons { + display: inline-block; + padding: 0 .5em 0 .5em; + width: 180px; + text-align: right; + vertical-align: bottom; + position: relative; +} + +.up_pftv_buttons a { + color: inherit; + text-decoration: none; +} +.up_pftv_buttons > span { + cursor: pointer; + font-size: 18px; + opacity: 0.8; +} + +.up_pftv_buttons > span:hover { + opacity: 1; +} + +.up_pftv_category-container { + position: relative; + border-right: 2px solid #b2f5ff; + border-top: 1px solid #b2f5ff; +} + +.up_pftv_category-container svg { + cursor: move; +} + +.up_pftv_container { + position: relative; + width: 960px; + color:#557071 !important; + font-family: 'Helvetica neue', Helvetica, Arial, sans-serif !important; + font-size: 13px !important; + line-height: 1.5 !important; +} + +.up_pftv_row { + width: 100%; + clear: both; + padding: 0; +} +.up_pftv_mainpanel { + margin-left: 200px; + padding: 0; +} +.up_pftv_leftpanel { + float: left; + width: 200px; + padding: 0; +} + +.up_pftv_category-name { + padding: 0; +} + +.up_pftv_container a { + color:#557071 !important; + border:none !important; +} + +.up_pftv_container h4 { + font-size: 1em !important; + font-weight: 500 !important; + border-bottom: 1px solid #cacaca; + margin-bottom: .7em; +} + +.up_pftv_keepWithPrevious { + margin-top: 0px; +} + +.up_ptfv_pp-container { + display: inline-block; + margin: 5em 0 0 0.5em; + vertical-align: top; +} + +.up_pftv_navruler svg { + vertical-align: bottom; + font-size: 14px; +} + +.up_pftv_aaviewer .background, .up_pftv_viewport .background { + fill: white; +} + +/*Category and type tracks*/ +.up_pftv_category { + margin-bottom: .1em; + border-bottom: .1em solid #b2f5ff; +} + +.up_pftv_category-name, .up_pftv_track-header { + font-weight: 400; + vertical-align: bottom; + display: block; + border: none; + text-overflow: ellipsis; + white-space: nowrap; + overflow: hidden; + height: 40px; + line-height: 40px; + padding: 0 5px; +} + +.up_pftv_category-name { + background-color: #b2f5ff; +} +.up_pftv_track-header { +} + +.up_pftv_category a:hover { + background-color: #80EEFF; + text-decoration: none; +} + +.up_pftv_track-header { + background-color: #d9faff; +} + +.up_pftv_navruler .extent { + fill: #ccc; + stroke: none; +} + +.up_pftv_navruler .handle { + fill: #999; + fill-opacity: 1.0; + stroke: #999; + stroke-opacity: 1.0; + stroke-width: 1; +} + +.up_pftv_category-viewer, .up_pftv_track, .up_pftv_aaviewer { + position: relative; + display: inline-block; + vertical-align: bottom; + width: 760px; +} + +.up_pftv_category-viewer svg, .up_pftv_track svg, .up_pftv_aaviewer svg { + vertical-align: bottom; +} + +.up_pftv_category-name { + cursor: pointer; +} + +.up_pftv_track-header-container { + vertical-align: top; + margin-top: 1px; + background-color: #D9FAFF; +} + +.up_pftv_track { + border-top: 1px #b2f5ff solid; +} + +.up_pftv_category-name.up_pftv_arrow-right:before { + content: ' '; + display: inline-block; + width: 0; + height: 0; + border-top: 5px solid transparent; + border-bottom: 5px solid transparent; + border-left: 5px solid #333; + margin-right: 5px; +} + +.up_pftv_category-name.up_pftv_arrow-down:before { + content: ' '; + display: inline-block; + width: 0; + height: 0; + border-left: 5px solid transparent; + border-right: 5px solid transparent; + border-top: 5px solid #333; + margin-right: 5px; +} + +.up_pftv_category-disabled { + opacity: 0; + display: none; +} + +/* Ruler */ +.up_pftv_container .axis { + font-size: 12px; +} + +.up_pftv_container .axis line, .up_pftv_container .axis path { + fill: none; + stroke: #666; +} + +.up_pftv_container .axis text { + fill: #666; +} + +.up_pftv_navruler { pointer-events: none } + +.up_pftv_shadow, .up_pftv_amino_acid_selector { + fill: #FFE999; + fill-opacity: 1; +} + +.fv-zoom-gradient-start { + stop-color: #eee; + stop-opacity: 1.0; +} +.fv-zoom-gradient-end { + stop-color: #eee; + stop-opacity: 1.0; +} + +.up_pftv_aaviewer text { + font-family: "Lucida Console", Monaco, monospace; + font-size: 12px; +} + +.up_pftv_feature { + cursor: pointer; + fill-opacity: .6; +} + +.up_pftv_feature:hover { + fill-opacity: .9; +} + +.up_pftv_activeFeature { + fill-opacity: .9 !important; +} + +.up_pftv_variation-chart .up_pftv_block-area { + fill: #ccc; +} + +.up_pftv_variation-chart .up_pftv_block-area:hover { + fill: #999; + cursor: pointer; +} + +.up_pftv_variation-chart .up_pftv_line { + stroke: #999; + stroke-width: 1px; +} + +.up_pftv_variation-chart .up_pftv_line:hover, .up_pftv_variation-chart .up_pftv_line:hover + .up_pftv_block-area { + stroke: darkslategrey; + cursor: pointer; +} + +/* Dialogs */ + +.up_pftv_dialog-container { + margin:0;padding:0; +} + +.up_pftv_dialog-container li a { + display: block; + line-height: 2em; + padding: 0 .2em; + cursor: pointer; +} + +.up_pftv_legend { + vertical-align: sub; + width: 1.5em; + display: inline-block; + height: 1em; + border-radius: .4em; + border: 2px solid #ABABAB; + margin-right: .5em; +} + +.up_pftv_legend_double { + height: 3em !important; +} + +.up_pftv_legend_text { + font-size: 12px; + line-height: 2em; + display: inline-block; + vertical-align: top; +} + +/* Close button */ +.up_pftv_tooltip-close { + color: #FFF; + background-color: #333333; + position: absolute; + top: -10px; + right: -10px; + cursor: pointer; + border-radius: 20px; + width: 20px; + height: 20px; + font-size: 14px !important; + text-align: center; + border: 1px solid #fff; +} + +/* Tooltip */ +.up_pftv_tooltip-pin-container { + display: inline-block; + float: left; + padding: 4px 10px 0 0; +} + +.up_pftv_iconContainer-unpinned { + background-color: lightgrey !important; + color: black !important; +} + +.up_pftv_iconContainer-unpinned:hover { + background-color: grey !important; +} + +.up_pftv_iconContainer-pinned { + background-color: black !important; + color: white !important; +} + +.up_pftv_tooltip-container, .up_pftv_popupDialog-container { + z-index: 50000; + position: absolute; + opacity: 0; + display: none; +} + +.up_pftv_tooltip-container { + min-width: 220px; +} + +.up_pftv_popupDialog-container { + border: 1px solid #557071; + background-color: #ffffff; + padding: 0.5em; + min-width: 210px; + text-align: left; + font-size: 13px; +} + +.up_pftv_tooltip-container table { + font-size: 13px; + border: 2px solid #CCC; + border-spacing: 0; + border-collapse: collapse; + background-color: #FFF; + -webkit-box-shadow: 5px 5px 2px 0px rgba(0, 0, 0, 0.1); + -moz-box-shadow: 5px 5px 2px 0px rgba(0, 0, 0, 0.1); + box-shadow: 5px 5px 2px 0px rgba(0, 0, 0, 0.1); + width: 100%; +} + +.up_pftv_tooltip-container table th { + text-transform: capitalize; + font-weight: 400; + white-space: nowrap; + letter-spacing: .05em; + font-size: 1.1em; + background-color: #595959; + line-height: 2em; + padding: 0 .7em; + color: #FFF; + text-align: center; +} + +.up_pftv_tooltip-container table a { + text-decoration: underline !important; +} + +.up_pftv_tooltip-container table td { + padding: .4em; + margin: 0; + border: 1px solid #CCC; +} + +.up_pftv_tooltip-container td:first-child { + font-weight: bold; +} + +.up_pftv_tooltip-container .up_pftv_evidence-col { + background-color: #FFF; +} + +.up_pftv_tooltip-container .up_pftv_evidence-source { + background-color: #FFF; +} + +.up_pftv_tooltip-container .up_pftv_section { + background-color: #B3B3B3; + text-align: center; +} + +.up_pftv_tooltip-container .up_pftv_subsection { + color: black; +} + +@svg_features(); + +svg .up_pftv_variant { + stroke-width: 2; +} + +svg .up_pftv_unique { + stroke: #fc3133; + fill: #fd5a5d; +} + +svg .up_pftv_non_unique { + stroke: #8585fc; + fill: #5a60fb; +} + + +/* Variation */ +.up_pftv_variants-svg .axis path { + display: none; +} + +.up_pftv_variants-svg .axis text { + font-size: 10px; + font-family: 'Helvetica neue', Helvetica, Arial, sans-serif; +} + +.up_pftv_variants-svg .variation-y.axis line { + stroke: #ccc; + opacity: .4; +} + +.up_pftv_variants-svg .variation-x.axis line { + stroke: #000; +} + +.up_pftv_variants-svg circle.main-seq { + fill: #fff; + stroke: steelblue; + stroke-width: 1.5px; + /*display: none;*/ +} + +.up_pftv_variants-svg .main-sequence { + fill: green; +} + +.up_pftv_variants-svg circle { + cursor: pointer; + fill-opacity: .6; +} + +.up_pftv_variants-svg .up_pftv_variant_hidden { + cursor: default; + opacity: 0; +} + +.up_pftv_variants-svg circle:hover { + fill-opacity: 0.9; +} + +.up_pftv_dialog_checkboxLabel { + opacity: 0.9; +} + +.up_pftv_dialog_checkboxLabel:hover { + cursor: pointer; + opacity: 1; +} + +.up_pftv_message_wrapper { + margin: .7em; +} + +/*Taken from http://www.karimnassar.com/code/web/css-icons/*/ +.up_pftv_icon, .up_pftv_icon::before, .up_pftv_icon::after +{ + position: relative; + padding: 0; + margin: 0; +} + +.up_pftv_icon { + font-size: 20px; + color: transparent; +} +.up_pftv_icon.up_pftv_warning { + display: inline-block; + top: 0.225em; + width: 1.15em; + height: 1.15em; + overflow: hidden; + border: none; + background-color: transparent; + border-radius: 0.625em; +} + +.up_pftv_icon.up_pftv_warning::before { + content: ""; + display: block; + top: -0.08em; + left: 0.0em; + position: absolute; + border: transparent 0.6em solid; + border-bottom-color: #fd3; + border-bottom-width: 1em; + border-top-width: 0; + box-shadow: #999 0 1px 1px; +} + +.up_pftv_icon.up_pftv_warning::after { + display: block; + position: absolute; + top: 0.3em; + left: 0; + width: 100%; + padding: 0 1px; + text-align: center; + content: "!"; + font-size: 0.65em; + font-weight: bold; + color: #333; +} diff --git a/package.json b/package.json index 1e64d70..d86d925 100644 --- a/package.json +++ b/package.json @@ -60,6 +60,7 @@ "gulp-gzip": "0.0.8", "gulp-istanbul": "^0.10.0", "gulp-jshint": "^1.8.4", + "gulp-less": "^3.0.0", "gulp-minify-css": "^1.2.0", "gulp-mocha": "^1.0.0", "gulp-mocha-phantomjs": "^0.5.0", @@ -87,7 +88,9 @@ }, "sniper": { "js": [ - "/build/protvista.js" + "/build/protvista.js", + "/node_modules/d3/d3.js", + "/node_modules/jquery/dist/jquery.min.js" ], "css": [ "/build/css/main.css" diff --git a/snippets/with_theme.js b/snippets/with_theme.js new file mode 100644 index 0000000..24d2bbd --- /dev/null +++ b/snippets/with_theme.js @@ -0,0 +1,24 @@ + +var app = require("ProtVista"); + +var themeClassName = 'pv-theme-grey'; + +var body = d3.select("body"); + +// add container without theme +body.append("h2").text("No Theme") +body.append("div") + .attr("id", "app-no-theme") + +// add container with theme +body.append("h2").text("With Theme") +body.append("div") + .attr("id", "app-with-theme") + .attr("class", themeClassName); + +// app instance without theme +var app1 = new app({el: document.getElementById('app-no-theme'), text: 'biojs', uniprotacc : 'P05067'}); + +// app instance with theme +var app2 = new app({el: document.getElementById('app-with-theme'), text: 'biojs', uniprotacc : 'P05067'}); + diff --git a/snippets/with_theme.json b/snippets/with_theme.json new file mode 100644 index 0000000..0abd6a7 --- /dev/null +++ b/snippets/with_theme.json @@ -0,0 +1,3 @@ +{ + "css": ["build/css/fv-theme-grey.css"] +} \ No newline at end of file diff --git a/src/CategoryFactory.js b/src/CategoryFactory.js index 3ddef37..dc46e24 100644 --- a/src/CategoryFactory.js +++ b/src/CategoryFactory.js @@ -24,8 +24,11 @@ var Category = function(name, data, catInfo, fv, container) { category.categoryViewer = undefined; category.categoryContainer = container.append('div') - .attr('class', 'up_pftv_category'); - category.header = category.categoryContainer.append('a') + .attr('class', 'up_pftv_row up_pftv_category'); + + category.header = category.categoryContainer.append('div') + .attr('class', 'up_pftv_leftpanel') + .append('a') .attr('class', 'up_pftv_category-name up_pftv_arrow-right') .attr('title', category.name) .text(catInfo.label) @@ -35,9 +38,13 @@ var Category = function(name, data, catInfo, fv, container) { }); category.viewerContainer = category.categoryContainer.append('div') + .attr('class', 'up_pftv_mainpanel') + .append('div') .attr('class', 'up_pftv_category-viewer'); category.tracksContainer = category.categoryContainer.append('div') + .attr('class', 'up_pftv_row') + .append('div') .attr('class', 'up_pftv_category-tracks') .style('display','none'); }; diff --git a/src/DownloadDataLoader.js b/src/DownloadDataLoader.js index fd95048..c719e78 100644 --- a/src/DownloadDataLoader.js +++ b/src/DownloadDataLoader.js @@ -57,7 +57,7 @@ var DownloadDataLoader = function() { zip.generateAsync({type:"base64"}).then(function (base64) { window.location = "data:application/zip;base64," + base64; }, function (err) { - console.log('Error: ', err) + console.log('Error: ', err); }); } }); diff --git a/src/DownloadDialog.js b/src/DownloadDialog.js index 3399fd2..f9b71e1 100644 --- a/src/DownloadDialog.js +++ b/src/DownloadDialog.js @@ -10,7 +10,7 @@ var Constants = require("./Constants"); var DownloadDataLoader = require("./DownloadDataLoader"); var populateDialog = function (fv, wrapper) { - var isSafari = navigator.vendor.indexOf("Apple")==0 && /\sSafari\//.test(navigator.userAgent); + var isSafari = navigator.vendor.indexOf("Apple") === 0 && /\sSafari\//.test(navigator.userAgent); var selected = true; var allFormats = Constants.getExternalDataSource() === undefined diff --git a/src/FeaturesViewer.js b/src/FeaturesViewer.js index fbf2a10..f173c7e 100644 --- a/src/FeaturesViewer.js +++ b/src/FeaturesViewer.js @@ -108,15 +108,16 @@ var resetZoomAndSelection = function(fv) { }; var createZoom = function(fv) { + var scale = fv.xZoomScale; var zoom = d3.behavior.zoom() - .x(fv.xScale) + .x(scale) // .scaleExtent([1,1]) .on('zoom', function() { - if (fv.xScale.domain()[0] < 1) { - var tempX = zoom.translate()[0] - fv.xScale(1) + fv.xScale.range()[0]; + if (scale.domain()[0] < 1) { + var tempX = zoom.translate()[0] - scale(1) + scale.range()[0]; zoom.translate([tempX, 0]); - } else if (fv.xScale.domain()[1] > fv.maxPos) { - var translatedX = zoom.translate()[0] - fv.xScale(fv.maxPos) + fv.xScale.range()[1]; + } else if (scale.domain()[1] > fv.maxPos) { + var translatedX = zoom.translate()[0] - scale(fv.maxPos) + scale.range()[1]; zoom.translate([translatedX, 0]); } update(fv); @@ -142,11 +143,13 @@ var closeTooltipAndPopup = function(fv) { }; var createNavRuler = function(fv, container) { - var navHeight = 40, navWithTrapezoid = 50; - - var navXScale = d3.scale.linear() - .domain([1,fv.maxPos]) - .range([fv.padding.left, fv.width - fv.padding.right]); + var navHeight = 30, + trapezoidHeight = 40, + navdragHandleWidth = 10, + navdragHandleHeight = 30, + trapezoidCurveOffset = 30, + extentHeight = 6, + resizeLabelWidth = 15; var svg = container .append('div') @@ -154,18 +157,22 @@ var createNavRuler = function(fv, container) { .append('svg') .attr('id','up_pftv_svg-navruler') .attr('width', fv.width) - .attr('height', (navWithTrapezoid)); + .attr('height', trapezoidHeight + (navdragHandleHeight/2)); + + var defs = svg.append('defs'); + var gradient = defs.append('linearGradient').attr({id: "fv-zoom-gradient", x1: 0, x2: 0, y1: 0, y2: 1}); + gradient.append('stop').attr('class', 'fv-zoom-gradient-start').attr('offset', '0%'); + gradient.append('stop').attr('class', 'fv-zoom-gradient-end').attr('offset', '90%'); + + // move everything down to give space for brush handles + var canvas = svg.append('g').attr('transform', 'translate(0,'+navdragHandleHeight/2+')'); var navXAxis = d3.svg.axis() - .scale(fv.xScale) + .scale(fv.xZoomScale) .orient('bottom'); - svg.append('g') - .attr('class', 'x axis') - .call(navXAxis); - var viewport = d3.svg.brush() - .x(navXScale) + .x(fv.xZoomScale) .on("brush", function() { var s = d3.event.target.extent(); if((s[1] - s[0]) < fv.maxZoomSize) { @@ -182,51 +189,66 @@ var createNavRuler = function(fv, container) { viewport.on("brushend", function () { updateZoomFromChart(fv); var navigator = fv.globalContainer.select('.up_pftv_navruler .extent'); - if (+navigator.attr('width') >= fv.width - fv.padding.left - fv.padding.right) { + if (+navigator.attr('width') >= fv.width - fv.zoomMargin.left - fv.zoomMargin.right) { updateZoomButton(fv, 'fv-icon-zoom-out', 'fv-icon-zoom-in', 'Zoom in to sequence view'); } }); - var arc = d3.svg.arc() - .outerRadius(navHeight / 4) - .startAngle(0) - .endAngle(function(d, i) { return i ? -Math.PI : Math.PI; }); - - svg.append("g") + var vp = canvas.append("g") .attr("class", "up_pftv_viewport") - .call(viewport) - .selectAll("rect") + .call(viewport); + + var handles = vp.selectAll('.resize'); + + vp.selectAll("rect") .attr("height", navHeight); - viewport.trapezoid = svg.append("g") - .selectAll("path") - .data([0]).enter().append("path") - .classed("up_pftv_trapezoid", true); - - viewport.domainStartLabel = svg.append("text") - .attr('class', 'domain-label') - .attr('x',0) - .attr('y',navHeight); - - viewport.domainEndLabel = svg.append("text") - .attr('class', 'domain-label') - .attr('x',fv.width) - .attr('y',navHeight) - .attr('text-anchor','end'); - - svg.selectAll(".resize").append("path") - .attr("transform", "translate(0," + ((navHeight / 2) - 5) + ")") - .attr('class','handle') - .attr("d", arc); + // add axis to the background + vp.insert('g', ':first-child') + .attr('class', 'x axis') + .call(navXAxis); + // then add 'background' to the background + viewport.trapezoid = vp.insert("path", ":first-child") + .attr("fill", "url(#fv-zoom-gradient)") + .classed("trapezoid", true); + + var resizeLabels = vp.selectAll('.resize').append("text") + .attr('class', 'domain-label'); + + viewport.domainStartLabel = vp.select('.resize.w .domain-label') + .attr('text-anchor', 'end') + .attr('transform', 'translate(' + (-(navdragHandleWidth+2)) + ',-3)'); + + viewport.domainEndLabel = vp.select('.resize.e .domain-label') + .attr('text-anchor', 'start') + .attr('transform', 'translate(' + (navdragHandleWidth+2) + ',-3)'); + + handles.select('rect') + .classed('handle', true) + .attr('x', -(navdragHandleWidth/2)) + .attr('y', -(navdragHandleHeight/2)) + .attr('width', navdragHandleWidth ) + .attr('height', navdragHandleHeight ) + .style({visibility: 'visible'}); + + vp.select('.extent') + .attr('height', extentHeight) + .attr('transform', 'translate(0,'+(-extentHeight/2)+')'); + viewport.updateTrapezoid = function() { var begin = fv.globalContainer.select(".up_pftv_navruler .extent").attr("x"); var tWidth = fv.globalContainer.select(".up_pftv_navruler .extent").attr("width"); + var end = (+begin) + (+tWidth); - var path = "M0," + (navWithTrapezoid) + "L0" + "," + (navWithTrapezoid-2) - + "L" + begin + "," + (navHeight-12) + "L" + begin + "," + navHeight - + "L" + end + "," + navHeight + "L" + end + "," + (navHeight-12) - + "L" + fv.width + "," + (navWithTrapezoid-2) + "L" + fv.width + "," + (navWithTrapezoid) + "Z"; + + var path = "M" + [0,trapezoidHeight].join(',') + // C x1 y1 x2 y2 x y + + "C" + [0,trapezoidHeight-trapezoidCurveOffset, begin,0+trapezoidCurveOffset, begin,0 ].join(',') // left + + "L" + [end,0].join(',') // top + + "C" + [end,0+trapezoidCurveOffset, fv.width,trapezoidHeight-trapezoidCurveOffset, fv.width,trapezoidHeight ].join(',') // right + + "L" + [0,trapezoidHeight].join(',') // bottom + + "Z"; this.trapezoid.attr("d", path); this.domainStartLabel.text(Math.round(fv.xScale.domain()[0])); this.domainEndLabel.text(Math.min(Math.round(fv.xScale.domain()[1]), fv.maxPos)); @@ -278,7 +300,8 @@ var createButtons = function(fv, data, container) { }; var createAAViewer = function(fv, container, sequence) { - var aaViewer = {}, aaViewWidth = fv.width, aaViewHeight = 30; + var aaViewer = {}, aaViewWidth = fv.width, aaViewHeight = 30, aaViewFontSize=12; + var svg = container .append('div') .attr('class','up_pftv_aaviewer') @@ -286,6 +309,11 @@ var createAAViewer = function(fv, container, sequence) { .attr('width', aaViewWidth) .attr('height',aaViewHeight); + svg.append('rect') + .attr("width", aaViewWidth) + .attr("height", aaViewHeight) + .classed('background', true); + //amino acids selector var aaSelectorPlot = function(){ var series, aminoAcids; @@ -310,17 +338,20 @@ var createAAViewer = function(fv, container, sequence) { var selectorSeries = aaSelectorPlot(); var selectorGroup = svg.append('g') - .attr('clip-path','url(#aaSelectorViewClip)') + //.attr('clip-path','url(#aaSelectorViewClip)') .style('opacity',1); selectorGroup.datum([{"feature": {"begin": -10, "end": -10}}]) .call(selectorSeries); //scale + var xAxis = d3.svg.axis() - .scale(fv.xScale); + .scale(fv.xScale) + .orient( 'bottom' ); + var gAxis = svg.append("g") - .attr("class", "x axis") - .attr('transform','translate(0, -7)') - .call(xAxis); + .attr("class", "x axis") + .call(xAxis); + //amino acids var aaPlot = function(){ var series, aminoAcids; @@ -330,7 +361,6 @@ var createAAViewer = function(fv, container, sequence) { aminoAcids = series.selectAll('.up_pftv_amino-acid').data(data); aminoAcids.enter().append('text') .style('text-anchor','middle') - .attr('y', aaViewHeight / 2) .text(function(d) { return d.toUpperCase(); }) @@ -353,7 +383,7 @@ var createAAViewer = function(fv, container, sequence) { var g = svg.append('g') .attr('class','up_pftv_aa-text') .attr('clip-path','url(#aaViewClip)') - .attr('transform','translate(0,' + aaViewHeight/5 + ')') + .attr('transform','translate(0,' + aaViewHeight + ')') .style('opacity',0); g.datum(sequence.split('')).call(series); @@ -473,19 +503,23 @@ var FeaturesViewer = function(opts) { fv.dispatcher = d3.dispatch("featureSelected", "featureDeselected", "ready", "noDataAvailable", "noDataRetrieved", "notFound", "notConfigRetrieved"); - fv.width = 760; + fv.width = opts.width !== undefined && opts.width >= 760 ? opts.width : 760; fv.maxZoomSize = 30; fv.selectedFeature = undefined; fv.selectedFeatureElement = undefined; fv.sequence = ""; fv.categories = []; fv.filterCategories = []; - fv.padding = {top:2, right:10, bottom:2, left:10}; + fv.margin = {top:0, right:0, bottom:0, left:0}; + fv.zoomMargin = {top:10, right:50, bottom:10, left:50}; fv.data = []; fv.uniprotacc = opts.uniprotacc; fv.overwritePredictions = opts.overwritePredictions; fv.defaultSource = opts.defaultSources !== undefined ? opts.defaultSources : true; + fv.xScale = d3.scale.linear(); + fv.xZoomScale = d3.scale.linear(); + fv.load = function() { initSources(opts); var dataSources = Constants.getDataSources(); @@ -617,19 +651,19 @@ FeaturesViewer.prototype.initLayout = function(opts, d) { closeTooltipAndPopup(fv); }); - fv.header = fvContainer.append('div'); + fv.header = fvContainer.append('div') + .classed('up_pftv_header up_pftv_row', true); - fv.container = fvContainer - .append('div') - .attr('class', 'up_pftv_category-container'); + fv.container = fvContainer.append('div') + .classed('up_pftv_category-container', true); fv.ontheFlyContainer = fv.container.append('div').classed('up_pftv_category_on_the_fly', true); _.each(Constants.getCategoryNamesInOrder(), function(catInfo) { - fv.container.append('div').classed('up_pftv_category_' + catInfo.name, true); + fv.container.append('div').classed('up_pftv_category_row up_pftv_category_' + catInfo.name, true); }); - fv.footer = fvContainer.append('div').attr('class','bottom-aa-container'); + fv.footer = fvContainer.append('div').attr('class', 'up_pftv_footer up_pftv_row'); }; FeaturesViewer.prototype.loadZoom = function(d) { @@ -640,14 +674,22 @@ FeaturesViewer.prototype.loadZoom = function(d) { fv.xScale = d3.scale.linear() .domain([1, d.sequence.length + 1]) - .range([fv.padding.left, fv.width - fv.padding.right]); + .range([fv.margin.left, fv.width - fv.margin.right]); + + fv.xZoomScale = d3.scale.linear() + .domain([1, d.sequence.length + 1]) + .range([fv.zoomMargin.left, fv.width - fv.zoomMargin.right]); + + var buttonsContainer = fv.header.append('div').classed('up_pftv_leftpanel', true); + var scaleContainer = fv.header.append('div').classed('up_pftv_mainpanel', true); + var bottomScaleContainer = fv.footer.append('div').classed('up_pftv_mainpanel', true); - fv.viewport = createNavRuler(fv, fv.header); - createButtons(fv, d, fv.header); - fv.aaViewer = createAAViewer(fv, fv.header, d.sequence); + fv.viewport = createNavRuler(fv, scaleContainer); + createButtons(fv, d, buttonsContainer); + fv.aaViewer = createAAViewer(fv, scaleContainer, d.sequence); fv.zoom = createZoom(fv); - fv.aaViewer2 = createAAViewer(fv, fv.footer, d.sequence); + fv.aaViewer2 = createAAViewer(fv, bottomScaleContainer, d.sequence); updateViewportFromChart(fv); updateZoomFromChart(fv); }; diff --git a/src/TrackFactory.js b/src/TrackFactory.js index 9bccf09..76ae0bf 100644 --- a/src/TrackFactory.js +++ b/src/TrackFactory.js @@ -19,10 +19,13 @@ var Track = function(typeFeatures, category) { track.category = category; track.id = track.type + '_track'; - track.titleContainer = category.tracksContainer.append('div').style('display', 'inline-block'); + track.titleContainer = category.tracksContainer + .append('div').attr('class', 'up_pftv_leftpanel') + .append('div').attr('class', 'up_pftv_track-header'); - track.trackContainer = category.tracksContainer.append('div') - .attr('class', 'up_pftv_track'); + track.trackContainer = category.tracksContainer + .append('div').attr('class', 'up_pftv_mainpanel') + .append('div').attr('class', 'up_pftv_track'); }; Track.prototype.update = function() { diff --git a/src/VariantCategoryViewer.js b/src/VariantCategoryViewer.js index b4e1422..e0cbf91 100644 --- a/src/VariantCategoryViewer.js +++ b/src/VariantCategoryViewer.js @@ -34,22 +34,14 @@ var VariantCategoryViewer = function(category) { .y(function(d) { return varYScale(d); }) - .interpolate('linear'); + .interpolate('step-after'); this.init = function () { var varCatViewer = this; + varCatViewer.varChart.append("path") .data(varCatViewer.features) - .attr("class","up_pftv_block-area") - .attr("d",line(varCatViewer.variationCountArray)) - .on('click', function(){ - category.toggle(); - }) - .append('title').text('Number of variants per position'); - - varCatViewer.varChart.append("path") - .data(varCatViewer.features) - .attr("class","up_pftv_line") + .attr("class","up_pftv_block-area up_pftv_line") .attr("d",line(varCatViewer.variationCountArray)) .on('click', function(){ category.toggle(); diff --git a/style/README.md b/style/README.md new file mode 100644 index 0000000..483a226 --- /dev/null +++ b/style/README.md @@ -0,0 +1,15 @@ +# WARNING + +The .css files in this directory are compiled from the LESS source files. Any changes here will be overwritten. + +If you want to make changes then edit the approrpiate LESS file: + + ./less/ + +Then rebuild: + + $ gulp build + +The final, minified CSS files get written out to: + + ./build/css/ \ No newline at end of file diff --git a/style/fv-theme-grey.css b/style/fv-theme-grey.css new file mode 100644 index 0000000..aa1fe21 --- /dev/null +++ b/style/fv-theme-grey.css @@ -0,0 +1,203 @@ +/* +specific colours +*/ +/* +d3.scale.category20() +*/ +.pv-theme-grey .up_pftv_navruler svg { + background-color: transparent; +} +.pv-theme-grey .up_pftv_aaviewer .background, +.pv-theme-grey .up_pftv_viewport .background { + fill: white; +} +.pv-theme-grey .up_pftv_container text.domain-label { + font-size: 12px !important; +} +.pv-theme-grey .up_pftv_container, +.pv-theme-grey .up_pftv_aaviewer text { + font-family: Helvetica, sans-serif; +} +.pv-theme-grey .up_pftv_container .up_pftv_navruler .resize { + font-size: 20px; + fill: #999; + fill-opacity: 1.0; +} +.pv-theme-grey .up_pftv_category-container { + border-right: 1px solid #ddd; + border-top: 1px solid #ddd; +} +.pv-theme-grey .up_pftv_category { + border-bottom-color: #ddd; +} +.pv-theme-grey .up_pftv_category-name { + background-color: #eee; + text-transform: uppercase; +} +.pv-theme-grey .up_pftv_category a:hover { + background-color: #eeeeee; +} +.pv-theme-grey .up_pftv_track-header { + background-color: #f3f3f3; + text-align: right; + text-transform: uppercase; +} +.pv-theme-grey .up_pftv_track { + border-top: 1px solid #eee; +} +.pv-theme-grey svg .up_pftv_chain { + stroke: #1f77b4; + fill: #1f77b4; +} +.pv-theme-grey svg .up_pftv_transit { + stroke: #aec7e8; + fill: #aec7e8; +} +.pv-theme-grey svg .up_pftv_init_met { + stroke: #ff7f0e; + fill: #ff7f0e; +} +.pv-theme-grey svg .up_pftv_propep { + stroke: #ffbb78; + fill: #ffbb78; +} +.pv-theme-grey svg .up_pftv_peptide { + stroke: #2ca02c; + fill: #2ca02c; +} +.pv-theme-grey svg .up_pftv_signal { + stroke: #98df8a; + fill: #98df8a; +} +.pv-theme-grey svg .up_pftv_crosslnk { + stroke: #c5b0d5; + fill: #c5b0d5; +} +.pv-theme-grey svg .up_pftv_disulfid { + stroke: #8c564b; + fill: #8c564b; +} +.pv-theme-grey svg .up_pftv_region { + stroke: #c49c94; + fill: #c49c94; +} +.pv-theme-grey svg .up_pftv_coiled { + stroke: #e377c2; + fill: #e377c2; +} +.pv-theme-grey svg .up_pftv_motif { + stroke: #f7b6d2; + fill: #f7b6d2; +} +.pv-theme-grey svg .up_pftv_repeat { + stroke: #7f7f7f; + fill: #7f7f7f; +} +.pv-theme-grey svg .up_pftv_ca_bind { + stroke: #c7c7c7; + fill: #c7c7c7; +} +.pv-theme-grey svg .up_pftv_dna_bind { + stroke: #bcbd22; + fill: #bcbd22; +} +.pv-theme-grey svg .up_pftv_domain { + stroke: #dbdb8d; + fill: #dbdb8d; +} +.pv-theme-grey svg .up_pftv_zn_fing { + stroke: #17becf; + fill: #17becf; +} +.pv-theme-grey svg .up_pftv_np_bind { + stroke: #9edae5; + fill: #9edae5; +} +.pv-theme-grey svg .up_pftv_metal { + stroke: #1f77b4; + fill: #1f77b4; +} +.pv-theme-grey svg .up_pftv_binding { + stroke: #aec7e8; + fill: #aec7e8; +} +.pv-theme-grey svg .up_pftv_act_site { + stroke: #ff7f0e; + fill: #ff7f0e; +} +.pv-theme-grey svg .up_pftv_mod_res { + stroke: #ffbb78; + fill: #ffbb78; +} +.pv-theme-grey svg .up_pftv_lipid { + stroke: #2ca02c; + fill: #2ca02c; +} +.pv-theme-grey svg .up_pftv_carbohyd { + stroke: #98df8a; + fill: #98df8a; +} +.pv-theme-grey svg .up_pftv_variant { + stroke: #d62728; + fill: #d62728; +} +.pv-theme-grey svg .up_pftv_compbias { + stroke: #ff9896; + fill: #ff9896; +} +.pv-theme-grey svg .up_pftv_mutagen { + stroke: #9467bd; + fill: #9467bd; +} +.pv-theme-grey svg .up_pftv_conflict { + stroke: #c5b0d5; + fill: #c5b0d5; +} +.pv-theme-grey svg .up_pftv_non_cons { + stroke: #8c564b; + fill: #8c564b; +} +.pv-theme-grey svg .up_pftv_non_ter { + stroke: #c49c94; + fill: #c49c94; +} +.pv-theme-grey svg .up_pftv_unsure { + stroke: #e377c2; + fill: #e377c2; +} +.pv-theme-grey svg .up_pftv_non_std { + stroke: #f7b6d2; + fill: #f7b6d2; +} +.pv-theme-grey svg .up_pftv_topo_dom { + stroke: #7f7f7f; + fill: #7f7f7f; +} +.pv-theme-grey svg .up_pftv_transmem { + stroke: #c7c7c7; + fill: #c7c7c7; +} +.pv-theme-grey svg .up_pftv_intramem { + stroke: #bcbd22; + fill: #bcbd22; +} +.pv-theme-grey svg .up_pftv_unique { + stroke: #dbdb8d; + fill: #dbdb8d; +} +.pv-theme-grey svg .up_pftv_non_unique { + stroke: #17becf; + fill: #17becf; +} +.pv-theme-grey svg .up_pftv_turn { + stroke: #0571AF; + fill: #0571AF; +} +.pv-theme-grey svg .up_pftv_strand { + stroke: #FFCC00; + fill: #FFCC00; +} +.pv-theme-grey svg .up_pftv_helix { + stroke: #FF0066; + fill: #FF0066; +}