mirror of
https://github.com/galaxyproject/galaxy.git
synced 2026-09-24 16:30:27 +08:00
Merge remote-tracking branch 'upstream/dev' into slow_query_logging_tweaks
This commit is contained in:
@@ -7,15 +7,16 @@ else
|
||||
fi
|
||||
|
||||
export ACK_OPTIONS=" --type python \
|
||||
--ignore-dir=.git/ \
|
||||
--ignore-dir=.tox/ \
|
||||
--ignore-dir=.venv/ \
|
||||
--ignore-dir=client/node_modules/ \
|
||||
--ignore-dir=database/ \
|
||||
--ignore-dir=doc/build/ \
|
||||
--ignore-dir=eggs/ \
|
||||
--ignore-dir=static/maps/ \
|
||||
--ignore-dir=static/scripts/"
|
||||
--ignore-dir=.git \
|
||||
--ignore-dir=.tox \
|
||||
--ignore-dir=.venv \
|
||||
--ignore-dir=.venv3 \
|
||||
--ignore-dir=client/node_modules \
|
||||
--ignore-dir=database \
|
||||
--ignore-dir=doc/build \
|
||||
--ignore-dir=eggs \
|
||||
--ignore-dir=static/maps \
|
||||
--ignore-dir=static/scripts"
|
||||
|
||||
PYTHON2_ONLY_MODULES="__builtin__ _winreg BaseHTTPServer CGIHTTPServer \
|
||||
ConfigParser Cookie cookielib copy_reg cPickle cStringIO Dialog dummy_thread \
|
||||
|
||||
@@ -1,8 +1,12 @@
|
||||
.venv/
|
||||
.tox/
|
||||
client/node_modules/
|
||||
database/
|
||||
.git
|
||||
.tox
|
||||
.venv
|
||||
.venv3
|
||||
client/node_modules
|
||||
database
|
||||
doc/build
|
||||
doc/source/conf.py
|
||||
eggs/
|
||||
eggs
|
||||
lib/galaxy/util/jstree.py
|
||||
lib/galaxy/web/proxy/js/node_modules/
|
||||
static/maps
|
||||
static/scripts
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
|
||||
set -e
|
||||
|
||||
flake8 --exclude `paste -sd, .ci/flake8_blacklist.txt` .
|
||||
flake8 --exclude $(paste -sd, .ci/flake8_blacklist.txt) .
|
||||
|
||||
# Apply stricter rules for the directories shared with Pulsar
|
||||
flake8 --ignore=D --max-line-length=150 lib/galaxy/jobs/runners/util/
|
||||
|
||||
@@ -15,7 +15,7 @@ args="--ignore=D --select=D100,D201,D202,D206,D207,D208,D209,D211,D3,D403"
|
||||
|
||||
if [ "$1" = "--include" ];
|
||||
then
|
||||
flake8 $args `paste .ci/flake8_docstrings_include_list.txt`
|
||||
flake8 $args $(paste -sd' ' .ci/flake8_docstrings_include_list.txt)
|
||||
else
|
||||
flake8 $args --exclude `paste -sd, .ci/flake8_blacklist.txt` .
|
||||
flake8 $args --exclude $(paste -sd, .ci/flake8_blacklist.txt) .
|
||||
fi
|
||||
|
||||
+2
-2
@@ -7,6 +7,7 @@ scripts/scramble/archives
|
||||
|
||||
# Python virtualenv
|
||||
.venv
|
||||
.venv3
|
||||
|
||||
# Database stuff
|
||||
database/beaker_sessions
|
||||
@@ -123,9 +124,8 @@ tool-data/shared/jars/
|
||||
sprite-*.less
|
||||
|
||||
# JS, Local node_modules, and bower_components directories
|
||||
static/scripts/bundled/[0-9]*
|
||||
static/scripts
|
||||
static/maps
|
||||
static/scripts/bundled/*.map
|
||||
node_modules
|
||||
bower_components
|
||||
client/galaxy/scripts/libs/*
|
||||
|
||||
+13
-6
@@ -8,13 +8,13 @@ env:
|
||||
- TOX_ENV=py27-lint
|
||||
- TOX_ENV=py27-unit
|
||||
- TOX_ENV=qunit
|
||||
- TOX_ENV=first_startup
|
||||
- TOX_ENV=py27-lint-docstring-include-list
|
||||
- TOX_ENV=check-python-dependencies
|
||||
- TOX_ENV=py27-first_startup
|
||||
- TOX_ENV=py27-lint_docstring_include_list
|
||||
- TOX_ENV=check_python_dependencies
|
||||
|
||||
matrix:
|
||||
include:
|
||||
- env: TOX_ENV=validate-test-tools
|
||||
- env: TOX_ENV=validate_test_tools
|
||||
addons:
|
||||
apt:
|
||||
packages:
|
||||
@@ -25,12 +25,19 @@ matrix:
|
||||
packages:
|
||||
- ack-grep
|
||||
allow_failures:
|
||||
- env: TOX_ENV=check-python-dependencies
|
||||
- env: TOX_ENV=check_python_dependencies
|
||||
|
||||
|
||||
install:
|
||||
- set -e
|
||||
- pip install tox
|
||||
- if [ "$TOX_ENV" == "first_startup" ]; then bash -c "bash scripts/common_startup.sh && wget -q https://github.com/jmchilton/galaxy-downloads/raw/master/db_gx_rev_0127.sqlite && mv db_gx_rev_0127.sqlite database/universe.sqlite && bash manage_db.sh -c ./config/galaxy.yml.sample upgrade"; fi
|
||||
- |
|
||||
if [ "$TOX_ENV" == "py27-first_startup" ]; then
|
||||
sh scripts/common_startup.sh
|
||||
wget -q https://github.com/jmchilton/galaxy-downloads/raw/master/db_gx_rev_0127.sqlite
|
||||
mv db_gx_rev_0127.sqlite database/universe.sqlite
|
||||
sh manage_db.sh -c ./config/galaxy.yml.sample upgrade
|
||||
fi
|
||||
|
||||
script: tox -e $TOX_ENV
|
||||
|
||||
|
||||
@@ -17,7 +17,6 @@ DOC_SOURCE_DIR=$(DOCS_DIR)/source
|
||||
SLIDESHOW_DIR=$(DOC_SOURCE_DIR)/slideshow
|
||||
OPEN_RESOURCE=bash -c 'open $$0 || xdg-open $$0'
|
||||
SLIDESHOW_TO_PDF?=bash -c 'docker run --rm -v `pwd`:/cwd astefanutti/decktape /cwd/$$0 /cwd/`dirname $$0`/`basename -s .html $$0`.pdf'
|
||||
CLIENT_COMMIT_WARNING="Please remember to 'make client-production' when finished developing, before a commit!"
|
||||
YARN := $(shell command -v yarn 2> /dev/null)
|
||||
|
||||
all: help
|
||||
@@ -72,9 +71,6 @@ tool-shed-config-convert-dry-run: ## convert old style tool shed ini to yaml (dr
|
||||
tool-shed-config-convert: ## convert old style tool shed ini to yaml
|
||||
$(CONFIG_MANAGE) convert tool_shed
|
||||
|
||||
tool-shed-config-rebuild-sample: ## Rebuild sample tool shed yaml file from schema
|
||||
$(CONFIG_MANAGE) build_sample_yaml tool_shed --add-comments
|
||||
|
||||
reports-config-validate: ## validate reports YAML configuration file
|
||||
$(CONFIG_MANAGE) validate reports
|
||||
|
||||
@@ -84,15 +80,9 @@ reports-config-convert-dry-run: ## convert old style reports ini to yaml (dry ru
|
||||
reports-config-convert: ## convert old style reports ini to yaml
|
||||
$(CONFIG_MANAGE) convert reports
|
||||
|
||||
reports-config-rebuild-sample: ## Rebuild sample reports yaml file from schema
|
||||
$(CONFIG_MANAGE) build_sample_yaml reports --add-comments
|
||||
|
||||
reports-config-lint: ## lint reports YAML configuration file
|
||||
$(CONFIG_MANAGE) lint reports
|
||||
|
||||
reports-config-rebuild-rst: ## Rebuild sample reports RST docs
|
||||
$(CONFIG_MANAGE) build_rst reports > doc/source/admin/reports_options.rst
|
||||
|
||||
config-validate: ## validate galaxy YAML configuration file
|
||||
$(CONFIG_MANAGE) validate galaxy
|
||||
|
||||
@@ -102,15 +92,16 @@ config-convert-dry-run: ## convert old style galaxy ini to yaml (dry run)
|
||||
config-convert: ## convert old style galaxy ini to yaml
|
||||
$(CONFIG_MANAGE) convert galaxy
|
||||
|
||||
config-rebuild-sample: ## Rebuild sample galaxy yaml file from schema
|
||||
config-rebuild: ## Rebuild all sample YAML and RST files from config schema
|
||||
$(CONFIG_MANAGE) build_sample_yaml galaxy --add-comments
|
||||
$(CONFIG_MANAGE) build_rst galaxy > doc/source/admin/galaxy_options.rst
|
||||
$(CONFIG_MANAGE) build_sample_yaml reports --add-comments
|
||||
$(CONFIG_MANAGE) build_rst reports > doc/source/admin/reports_options.rst
|
||||
$(CONFIG_MANAGE) build_sample_yaml tool_shed --add-comments
|
||||
|
||||
config-lint: ## lint galaxy YAML configuration file
|
||||
$(CONFIG_MANAGE) lint galaxy
|
||||
|
||||
config-rebuild-rst: ## Rebuild sample galaxy RST docs
|
||||
$(CONFIG_MANAGE) build_rst galaxy > doc/source/admin/galaxy_options.rst
|
||||
|
||||
release-ensure-upstream: ## Ensure upstream branch for release commands setup
|
||||
ifeq (shell git remote -v | grep $(RELEASE_UPSTREAM), )
|
||||
git remote add $(RELEASE_UPSTREAM) git@github.com:galaxyproject/galaxy.git
|
||||
@@ -156,7 +147,6 @@ endif
|
||||
|
||||
client: node-deps ## Rebuild client-side artifacts for local development.
|
||||
cd client && yarn run build
|
||||
@echo $(CLIENT_COMMIT_WARNING)
|
||||
|
||||
client-production: node-deps ## Rebuild client-side artifacts for a production deployment (or committing to the repository).
|
||||
cd client && yarn run build-production
|
||||
@@ -169,7 +159,6 @@ client-format: node-deps ## Reformat client code
|
||||
|
||||
client-watch: node-deps ## A useful target for parallel development building.
|
||||
cd client && yarn run watch
|
||||
@echo $(CLIENT_COMMIT_WARNING)
|
||||
|
||||
client-test: client ## Run qunit tests via Karma
|
||||
cd client && yarn run test
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import jQuery from "jquery";
|
||||
var $ = jQuery;
|
||||
import * as _ from "underscore";
|
||||
import GalaxyApp from "galaxy";
|
||||
import Router from "layout/router";
|
||||
import ToolPanel from "./panels/tool-panel";
|
||||
@@ -14,6 +15,7 @@ import GridView from "mvc/grid/grid-view";
|
||||
import GridShared from "mvc/grid/grid-shared";
|
||||
import Workflows from "mvc/workflow/workflow";
|
||||
import HistoryImport from "components/HistoryImport.vue";
|
||||
import HistoryView from "components/HistoryView.vue";
|
||||
import HistoryList from "mvc/history/history-list";
|
||||
import ToolFormComposite from "mvc/tool/tool-form-composite";
|
||||
import QueryStringParsing from "utils/query-string-parsing";
|
||||
@@ -21,9 +23,13 @@ import Utils from "utils/utils";
|
||||
import Ui from "mvc/ui/ui-misc";
|
||||
import DatasetError from "mvc/dataset/dataset-error";
|
||||
import DatasetEditAttributes from "mvc/dataset/dataset-edit-attributes";
|
||||
import Visualization from "mvc/visualization/visualization-view";
|
||||
import Citations from "components/Citations.vue";
|
||||
import DisplayStructure from "components/DisplayStructured.vue";
|
||||
import Vue from "vue";
|
||||
|
||||
/* global Galaxy */
|
||||
|
||||
/** define the 'Analyze Data'/analysis/main/home page for Galaxy
|
||||
* * has a masthead
|
||||
* * a left tool menu to allow the user to load tools in the center panel
|
||||
@@ -52,6 +58,8 @@ window.app = function app(options, bootstrapped) {
|
||||
"(/)pages(/)edit(/)": "show_pages_edit",
|
||||
"(/)pages(/)(:action_id)": "show_pages",
|
||||
"(/)visualizations(/)edit(/)": "show_visualizations_edit",
|
||||
"(/)visualizations/show/(:visualization_id)": "show_visualizations_client",
|
||||
"(/)visualizations/dataset_id=(:dataset_id)": "show_visualizations_selector",
|
||||
"(/)visualizations/(:action_id)": "show_visualizations",
|
||||
"(/)workflows/import_workflow": "show_import_workflow",
|
||||
"(/)workflows/run(/)": "show_run",
|
||||
@@ -62,6 +70,8 @@ window.app = function app(options, bootstrapped) {
|
||||
"(/)histories(/)rename(/)": "show_histories_rename",
|
||||
"(/)histories(/)import(/)": "show_histories_import",
|
||||
"(/)histories(/)permissions(/)": "show_histories_permissions",
|
||||
"(/)histories/view": "show_history_view",
|
||||
"(/)histories/show_structure": "show_history_structure",
|
||||
"(/)histories(/)(:action_id)": "show_histories",
|
||||
"(/)datasets(/)list(/)": "show_datasets",
|
||||
"(/)custom_builds": "show_custom_builds",
|
||||
@@ -113,14 +123,37 @@ window.app = function app(options, bootstrapped) {
|
||||
);
|
||||
},
|
||||
|
||||
show_visualizations_selector: function(dataset_id) {
|
||||
this.page.display(
|
||||
new Visualization.View({
|
||||
dataset_id: dataset_id
|
||||
})
|
||||
);
|
||||
},
|
||||
|
||||
show_workflows_published: function() {
|
||||
this.page.display(
|
||||
new GridView({
|
||||
url_base: `${Galaxy.root}workflow/list_published`
|
||||
url_base: `${Galaxy.root}workflow/list_published`,
|
||||
active_tab: "shared"
|
||||
})
|
||||
);
|
||||
},
|
||||
|
||||
show_history_view: function() {
|
||||
var historyInstance = Vue.extend(HistoryView);
|
||||
var vm = document.createElement("div");
|
||||
this.page.display(vm);
|
||||
new historyInstance({ propsData: { id: QueryStringParsing.get("id") } }).$mount(vm);
|
||||
},
|
||||
|
||||
show_history_structure: function() {
|
||||
let displayStructureInstance = Vue.extend(DisplayStructure);
|
||||
let vm = document.createElement("div");
|
||||
this.page.display(vm);
|
||||
new displayStructureInstance({ propsData: { id: QueryStringParsing.get(" id: ") } }).$mount(vm);
|
||||
},
|
||||
|
||||
show_histories: function(action_id) {
|
||||
this.page.display(new HistoryList.View({ action_id: action_id }));
|
||||
},
|
||||
@@ -160,7 +193,8 @@ window.app = function app(options, bootstrapped) {
|
||||
show_openids: function() {
|
||||
this.page.display(
|
||||
new GridView({
|
||||
url_base: `${Galaxy.root}user/openids_list`
|
||||
url_base: `${Galaxy.root}user/openids_list`,
|
||||
active_tab: "user"
|
||||
})
|
||||
);
|
||||
},
|
||||
@@ -168,7 +202,8 @@ window.app = function app(options, bootstrapped) {
|
||||
show_datasets: function() {
|
||||
this.page.display(
|
||||
new GridView({
|
||||
url_base: `${Galaxy.root}dataset/list`
|
||||
url_base: `${Galaxy.root}dataset/list`,
|
||||
active_tab: "user"
|
||||
})
|
||||
);
|
||||
},
|
||||
@@ -302,6 +337,10 @@ window.app = function app(options, bootstrapped) {
|
||||
|
||||
// render and start the router
|
||||
$(() => {
|
||||
options.config = _.extend(options.config, {
|
||||
hide_panels: Galaxy.params.hide_panels,
|
||||
hide_masthead: Galaxy.params.hide_masthead
|
||||
});
|
||||
Galaxy.page = new Page.View(
|
||||
_.extend(options, {
|
||||
Left: ToolPanel,
|
||||
|
||||
@@ -0,0 +1,20 @@
|
||||
import * as _ from "libs/underscore";
|
||||
import Client from "mvc/visualization/chart/chart-client";
|
||||
import Datasets from "mvc/visualization/chart/utilities/datasets";
|
||||
import Series from "mvc/visualization/chart/utilities/series";
|
||||
import Jobs from "mvc/visualization/chart/utilities/jobs";
|
||||
|
||||
window.bundleEntries = window.bundleEntries || {};
|
||||
|
||||
export const bundleEntries = {
|
||||
chart: function(options) {
|
||||
return new Client(options);
|
||||
},
|
||||
chartUtilities: {
|
||||
Datasets: Datasets,
|
||||
Jobs: Jobs,
|
||||
Series: Series
|
||||
}
|
||||
};
|
||||
|
||||
_.extend(window.bundleEntries, bundleEntries);
|
||||
@@ -1,4 +1,3 @@
|
||||
import _l from "utils/localization";
|
||||
import GalaxyApp from "galaxy";
|
||||
import WorkflowView from "mvc/workflow/workflow-view";
|
||||
import Trackster from "viz/trackster";
|
||||
@@ -17,7 +16,6 @@ import StructureView from "mvc/history/history-structure-view";
|
||||
import HistoryContents from "mvc/history/history-contents";
|
||||
import MultiPanel from "mvc/history/multi-panel";
|
||||
import HistoryView from "mvc/history/history-view";
|
||||
import HistoryViewEdit from "mvc/history/history-view-edit";
|
||||
import HistoryViewAnnotated from "mvc/history/history-view-annotated";
|
||||
import HistoryCopyDialog from "mvc/history/copy-dialog";
|
||||
import HDAListItemEdit from "mvc/history/hda-li-edit";
|
||||
@@ -67,89 +65,6 @@ export function libraryEntry(options) {
|
||||
new GalaxyLibrary.GalaxyApp(options);
|
||||
}
|
||||
|
||||
export function historyEntry(options) {
|
||||
$("#toggle-deleted").modeButton({
|
||||
initialMode: options.initialModeDeleted,
|
||||
modes: [
|
||||
{ mode: "showing_deleted", html: _l("Exclude deleted") },
|
||||
{ mode: "not_showing_deleted", html: _l("Include deleted") }
|
||||
]
|
||||
});
|
||||
$("#toggle-hidden").modeButton({
|
||||
initialMode: options.initialModeHidden,
|
||||
modes: [
|
||||
{ mode: "showing_hidden", html: _l("Exclude hidden") },
|
||||
{ mode: "not_showing_hidden", html: _l("Include hidden") }
|
||||
]
|
||||
});
|
||||
$("#switch").click(function() {
|
||||
//##HACK:ity hack hack
|
||||
//##TODO: remove when out of iframe
|
||||
var hview =
|
||||
Galaxy.currHistoryPanel || (window.top.Galaxy && window.top.Galaxy.currHistoryPanel)
|
||||
? window.top.Galaxy.currHistoryPanel
|
||||
: null;
|
||||
if (hview) {
|
||||
hview.switchToHistory("${ history[ 'id' ] }");
|
||||
} else {
|
||||
window.location = "${ switch_to_url }";
|
||||
}
|
||||
});
|
||||
// use_panels effects where the the center_panel() is rendered:
|
||||
// w/o it renders to the body, w/ it renders to #center - we need to adjust a few things for scrolling to work
|
||||
if (options.hasMasthead) {
|
||||
$("#center").addClass("flex-vertical-container");
|
||||
}
|
||||
|
||||
let viewClass = options.userIsOwner ? HistoryViewEdit.HistoryViewEdit : HistoryView.HistoryView;
|
||||
let historyModel = new History.History(options.historyJSON);
|
||||
|
||||
// attach the copy dialog to the import button now that we have a history
|
||||
$("#import").click(function() {
|
||||
HistoryCopyDialog(historyModel, {
|
||||
useImport: true,
|
||||
// use default datasets option to match the toggle-deleted button
|
||||
allDatasets: $("#toggle-deleted").modeButton("getMode").mode === "showing_deleted"
|
||||
}).done(function() {
|
||||
if (window === window.parent) {
|
||||
window.location = Galaxy.root;
|
||||
} else if (Galaxy.currHistoryPanel) {
|
||||
Galaxy.currHistoryPanel.loadCurrentHistory();
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
let historyView = new viewClass({
|
||||
el: $("#history-" + options.historyJSON.id),
|
||||
className: viewClass.prototype.className + " wide",
|
||||
$scrollContainer: options.hasMasthead
|
||||
? function() {
|
||||
return this.$el.parent();
|
||||
}
|
||||
: undefined,
|
||||
model: historyModel,
|
||||
show_deleted: options.showDeletedJson,
|
||||
show_hidden: options.showHiddenJson,
|
||||
purgeAllowed: options.allow_user_dataset_purge
|
||||
});
|
||||
historyView.trigger("loading");
|
||||
historyModel
|
||||
.fetchContents({ silent: true })
|
||||
.fail(function() {
|
||||
alert("Galaxy history failed to load");
|
||||
})
|
||||
.done(function() {
|
||||
historyView.trigger("loading-done");
|
||||
historyView.render();
|
||||
});
|
||||
$("#toggle-deleted").on("click", function() {
|
||||
historyView.toggleShowDeleted();
|
||||
});
|
||||
$("#toggle-hidden").on("click", function() {
|
||||
historyView.toggleShowHidden();
|
||||
});
|
||||
}
|
||||
|
||||
export function multiHistoryEntry(options) {
|
||||
let histories = new History.HistoryCollection([], {
|
||||
includeDeleted: options.includingDeleted,
|
||||
@@ -180,7 +95,7 @@ export const bundleEntries = {
|
||||
phyloviz: Phyloviz.PhylovizView,
|
||||
createTabularDatasetChunkedView: Data.createTabularDatasetChunkedView,
|
||||
multiHistory: multiHistoryEntry,
|
||||
history: historyEntry,
|
||||
history: HistoryView.historyEntry,
|
||||
History: History.History,
|
||||
HistoryContents: HistoryContents.HistoryContents,
|
||||
SweepsterVisualization: Sweepster.SweepsterVisualization,
|
||||
|
||||
@@ -2,6 +2,7 @@ import Tools from "mvc/tool/tools";
|
||||
import Upload from "mvc/upload/upload-view";
|
||||
import _l from "utils/localization";
|
||||
import ToolForm from "mvc/tool/tool-form-composite";
|
||||
import _ from "libs/underscore";
|
||||
|
||||
var ToolPanel = Backbone.View.extend({
|
||||
initialize: function(page, options) {
|
||||
@@ -28,7 +29,8 @@ var ToolPanel = Backbone.View.extend({
|
||||
|
||||
// add upload modal
|
||||
this.upload_button = new Upload({
|
||||
nginx_upload_path: config.nginx_upload_path,
|
||||
upload_path: config.nginx_upload_path || `${Galaxy.root}api/tools`,
|
||||
chunk_upload_size: config.chunk_upload_size,
|
||||
ftp_upload_site: config.ftp_upload_site,
|
||||
default_genome: config.default_genome,
|
||||
default_extension: config.default_extension
|
||||
@@ -74,70 +76,54 @@ var ToolPanel = Backbone.View.extend({
|
||||
|
||||
/** build a link to one tool */
|
||||
_templateTool: function(tool) {
|
||||
return [
|
||||
'<div class="toolTitle">',
|
||||
'<a href="',
|
||||
this.root,
|
||||
tool.href,
|
||||
'" target="galaxy_main">',
|
||||
tool.title,
|
||||
"</a>",
|
||||
"</div>"
|
||||
].join("");
|
||||
return `<div class="toolTitle">
|
||||
<a href="${Galaxy.root}${tool.href}" target="galaxy_main">
|
||||
${tool.title}
|
||||
</a>
|
||||
</div>`;
|
||||
},
|
||||
|
||||
/** build a link to 'All Workflows' */
|
||||
_templateAllWorkflow: function(tool) {
|
||||
return [
|
||||
'<div class="toolTitle">',
|
||||
// global
|
||||
'<a href="',
|
||||
Galaxy.root,
|
||||
tool.href,
|
||||
'">',
|
||||
tool.title,
|
||||
"</a>",
|
||||
"</div>"
|
||||
].join("");
|
||||
return `<div class="toolTitle">
|
||||
<a href="${Galaxy.root}${tool.href}">
|
||||
${tool.title}
|
||||
</a>
|
||||
</div>`;
|
||||
},
|
||||
|
||||
/** build links to workflows in toolpanel */
|
||||
_templateWorkflowLink: function(wf) {
|
||||
return [
|
||||
'<div class="toolTitle">',
|
||||
`<a class="${wf.cls} " href="`,
|
||||
Galaxy.root,
|
||||
wf.href,
|
||||
'">',
|
||||
wf.title,
|
||||
"</a>",
|
||||
"</div>"
|
||||
].join("");
|
||||
return `<div class="toolTitle">
|
||||
<a class="${wf.cls}" href="${Galaxy.root}${wf.href}">
|
||||
${_.escape(wf.title)}
|
||||
</a>
|
||||
</div>`;
|
||||
},
|
||||
|
||||
/** override to include inital menu dom and workflow section */
|
||||
_template: function() {
|
||||
return [
|
||||
'<div class="toolMenuContainer">',
|
||||
'<div class="toolMenu" style="display: none">',
|
||||
'<div id="search-no-results" style="display: none; padding-top: 5px">',
|
||||
"<em><strong>",
|
||||
_l("Search did not match any tools."),
|
||||
"</strong></em>",
|
||||
"</div>",
|
||||
"</div>",
|
||||
'<div class="toolSectionPad"/>',
|
||||
'<div class="toolSectionPad"/>',
|
||||
'<div class="toolSectionTitle" id="title_XXinternalXXworkflow">',
|
||||
"<span>",
|
||||
_l("Workflows"),
|
||||
"</span>",
|
||||
"</div>",
|
||||
'<div id="internal-workflows" class="toolSectionBody">',
|
||||
'<div class="toolSectionBg"/>',
|
||||
"</div>",
|
||||
"</div>"
|
||||
].join("");
|
||||
return `<div class="toolMenuContainer">
|
||||
<div class="toolMenu" style="display: none">
|
||||
<div id="search-no-results" style="display: none; padding-top: 5px">
|
||||
<em>
|
||||
<strong>
|
||||
${_l("Search did not match any tools.")}
|
||||
</strong>
|
||||
</em>
|
||||
</div>
|
||||
</div>
|
||||
<div class="toolSectionPad"/>
|
||||
<div class="toolSectionPad"/>
|
||||
<div class="toolSectionTitle" id="title_XXinternalXXworkflow">
|
||||
<span>
|
||||
${_l("Workflows")}
|
||||
</span>
|
||||
</div>
|
||||
<div id="internal-workflows" class="toolSectionBody">
|
||||
<div class="toolSectionBg"/>
|
||||
</div>
|
||||
</div>`;
|
||||
},
|
||||
|
||||
toString: function() {
|
||||
|
||||
@@ -0,0 +1,205 @@
|
||||
<template>
|
||||
<div>
|
||||
<div v-for="error in errorMessages">
|
||||
<div class="alert alert-danger" role="alert">
|
||||
{{ error }}
|
||||
</div>
|
||||
</div>
|
||||
<div v-html="historyTemplate">
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import axios from "axios";
|
||||
import HDAModel from "mvc/history/hda-model";
|
||||
import HDAListItemEdit from "mvc/history/hda-li-edit";
|
||||
|
||||
export default {
|
||||
props: {
|
||||
id: {
|
||||
type: String,
|
||||
required: false
|
||||
}
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
historyTemplate: "",
|
||||
historyJSON: {},
|
||||
// TODO: Error message standardization -- use bootstrap-vue component or the like.
|
||||
errorMessages: []
|
||||
};
|
||||
},
|
||||
created: function() {
|
||||
let historyId = this.id,
|
||||
url = "";
|
||||
if (historyId !== undefined && historyId !== "" && historyId !== null) {
|
||||
url = Galaxy.root + "history/display_structured?id=" + historyId;
|
||||
} else {
|
||||
url = Galaxy.root + "history/display_structured";
|
||||
}
|
||||
this.ajaxCall(url);
|
||||
},
|
||||
methods: {
|
||||
ajaxCall: function(url) {
|
||||
axios
|
||||
.get(url)
|
||||
.then(response => {
|
||||
this._updateHistoryData(response);
|
||||
})
|
||||
.catch(e => {
|
||||
console.error(e);
|
||||
this.errorMessages.push(
|
||||
"Error fetching history data -- reload the page to retry this request. Please contact an administrator if the problem persists."
|
||||
);
|
||||
});
|
||||
},
|
||||
_updateHistoryData: function(response) {
|
||||
let historyItems = response.data;
|
||||
this.historyTemplate = historyItems.template;
|
||||
this.historyJSON = historyItems.history_json;
|
||||
},
|
||||
makeHistoryView: function(historyDict) {
|
||||
window.hdas = historyDict.map(hda => {
|
||||
return new HDAListItemEdit.HDAListItemEdit({
|
||||
model: new HDAModel.HistoryDatasetAssociation(hda),
|
||||
el: $("#hda-" + hda.id),
|
||||
linkTarget: "galaxy_main",
|
||||
purgeAllowed: Galaxy.config.allow_user_dataset_purge,
|
||||
logger: Galaxy.logger
|
||||
}).render(0);
|
||||
});
|
||||
// toggle the body section of each item in the structure
|
||||
$(function() {
|
||||
$(".workflow, .tool").each((index, element) => {
|
||||
let body = $(element).children(".body");
|
||||
$(element)
|
||||
.children(".header")
|
||||
.click(e => {
|
||||
body.toggle();
|
||||
})
|
||||
.addClass("clickable");
|
||||
});
|
||||
});
|
||||
}
|
||||
},
|
||||
updated: function() {
|
||||
this.makeHistoryView(this.historyJSON);
|
||||
}
|
||||
};
|
||||
</script>
|
||||
|
||||
<style>
|
||||
.bold {
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
.light {
|
||||
font-weight: lighter;
|
||||
color: grey;
|
||||
}
|
||||
.right-aligned {
|
||||
text-align: right;
|
||||
}
|
||||
|
||||
.clickable {
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.workflow {
|
||||
border: solid gray 1px;
|
||||
margin-bottom: 1%;
|
||||
}
|
||||
.workflow > .header {
|
||||
background: lightgray;
|
||||
padding: 5px 10px;
|
||||
}
|
||||
.workflow > .light {
|
||||
color: gray;
|
||||
}
|
||||
.workflow > .body {
|
||||
border-top: solid gray 1px;
|
||||
}
|
||||
.workflow > .body > .toolForm {
|
||||
border: 0px;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
div.toolForm {
|
||||
border-width: 1px;
|
||||
border-radius: 0px;
|
||||
margin-bottom: 1%;
|
||||
}
|
||||
.toolForm > .header {
|
||||
background-color: #ebd9b2;
|
||||
padding: 5px 10px;
|
||||
}
|
||||
.workflow div.toolForm:not(:first-child) .header {
|
||||
border-top: 1px solid #d6b161;
|
||||
}
|
||||
div.toolFormTitle {
|
||||
padding: 0px 0px 4px 0px;
|
||||
margin: 0px 0px 4px 0px;
|
||||
border: 0px;
|
||||
background-color: transparent;
|
||||
border-bottom: 1px solid #d6b161;
|
||||
}
|
||||
/* down from EBD9B2 --> 90743A */
|
||||
.toolFormTitle > .light {
|
||||
color: #90743a;
|
||||
}
|
||||
.toolForm em {
|
||||
color: #90743a;
|
||||
}
|
||||
|
||||
.job-inputs {
|
||||
margin: 0px 6px 0px 6px;
|
||||
text-align: left;
|
||||
}
|
||||
.job-inputs td:nth-child(1) {
|
||||
text-align: right;
|
||||
font-weight: lighter;
|
||||
color: #90743a;
|
||||
}
|
||||
.job-inputs td:nth-child(1):after {
|
||||
content: ":";
|
||||
}
|
||||
.job-inputs td:nth-child(2) {
|
||||
padding-left: 4px;
|
||||
}
|
||||
.job-inputs em {
|
||||
}
|
||||
|
||||
.job-inputs-show {
|
||||
float: right;
|
||||
}
|
||||
|
||||
.copied-from {
|
||||
border: 1px solid lightgrey;
|
||||
border-width: 1px 1px 0px 1px;
|
||||
margin-bottom: 1%;
|
||||
}
|
||||
.copied-from .header {
|
||||
border-bottom: 1px solid lightgrey;
|
||||
padding: 5px;
|
||||
}
|
||||
.copied-from .header .bold,
|
||||
.copied-from .header a {
|
||||
color: #888;
|
||||
}
|
||||
|
||||
.dataset.hda {
|
||||
min-height: 37px;
|
||||
border-width: 0px 0px 1px 0px;
|
||||
}
|
||||
.toolFormBody > .dataset.hda:last-child {
|
||||
border-bottom-width: 0px;
|
||||
}
|
||||
.dataset.hda:first-child {
|
||||
border-top: 1px solid #d6b161;
|
||||
}
|
||||
.dataset.hda .dataset-title-bar {
|
||||
padding-top: 8px;
|
||||
padding-left: 10px;
|
||||
}
|
||||
</style>
|
||||
@@ -0,0 +1,133 @@
|
||||
<template>
|
||||
<div id="structured-history-view">
|
||||
<div id="history-view-controls" class="clear">
|
||||
<div class="pull-left">
|
||||
<span v-if="historyHistory['purged'] == false" >
|
||||
<span v-if="historyData.user_is_owner == false" >
|
||||
<button id="import" class="btn btn-default">Import and start using history</button>
|
||||
</span>
|
||||
<span v-if="historyData.history_is_current == false">
|
||||
<button id="switch-history" class="btn btn-default" v-on:click="switchHistory">Switch to this history</button>
|
||||
</span>
|
||||
<button id="show-structure" class="btn btn-default" v-on:click="showStructure">Show structure</button>
|
||||
</span>
|
||||
</div>
|
||||
<div class="pull-right">
|
||||
<button id="toggle-deleted" class="btn btn-default">Include deleted</button>
|
||||
<button id="toggle-hidden" class="btn btn-default">Include hidden</button>
|
||||
</div>
|
||||
</div>
|
||||
<div v-for="error in errorMessages">
|
||||
<div class="alert alert-danger" role="alert">
|
||||
{{ error }}
|
||||
</div>
|
||||
</div>
|
||||
<div :id="'history-' + historyHistory['id']" class="history-panel unified-panel-body" style="overflow: auto;"></div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import axios from "axios";
|
||||
import Vue from "vue";
|
||||
import DisplayStructure from "components/DisplayStructured.vue";
|
||||
import QueryStringParsing from "utils/query-string-parsing";
|
||||
import HistoryView from "mvc/history/history-view";
|
||||
|
||||
export default {
|
||||
props: {
|
||||
id: {
|
||||
type: String,
|
||||
required: true
|
||||
}
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
historyData: {},
|
||||
historyHistory: {},
|
||||
// TODO: Pick a standard messaging convention and use a child component for it here.
|
||||
errorMessages: []
|
||||
};
|
||||
},
|
||||
created: function() {
|
||||
let url = Galaxy.root + "history/view/" + this.id;
|
||||
this.ajaxCall(url, this.updateHistoryView);
|
||||
},
|
||||
methods: {
|
||||
ajaxCall: function(url, callBack) {
|
||||
axios
|
||||
.get(url)
|
||||
.then(response => {
|
||||
callBack(response);
|
||||
})
|
||||
.catch(e => {
|
||||
this.showError(
|
||||
"Error fetching histories -- reload the page to retry this request. Please contact an administrator if the problem persists.",
|
||||
e
|
||||
);
|
||||
});
|
||||
},
|
||||
updateHistoryView: function(response) {
|
||||
this.historyData = response.data;
|
||||
this.historyHistory = response.data.history;
|
||||
},
|
||||
showError: function(errorMsg, verbose) {
|
||||
console.error(verbose);
|
||||
this.errorMessages.push(errorMsg);
|
||||
},
|
||||
makeHistoryView: function(history) {
|
||||
$(function() {
|
||||
let options = {
|
||||
hasMasthead: history.use_panels ? "true" : "false",
|
||||
userIsOwner: history.user_is_owner ? "true" : "false",
|
||||
isCurrent: history.history_is_current ? "true" : "false",
|
||||
historyJSON: history.history,
|
||||
showDeletedJson: history.show_deleted,
|
||||
showHiddenJson: history.show_hidden,
|
||||
initialModeDeleted: history.show_deleted ? "showing_deleted" : "not_showing_deleted",
|
||||
initialModeHidden: history.show_hidden ? "showing_hidden" : "not_showing_hidden",
|
||||
allowUserDatasetPurge: history.allow_user_dataset_purge ? "true" : "false"
|
||||
};
|
||||
options.viewToUse = options.userIsOwner
|
||||
? { location: "mvc/history/history-view-edit", className: "HistoryViewEdit" }
|
||||
: { location: "mvc/history/history-view", className: "HistoryView" };
|
||||
HistoryView.historyEntry(options);
|
||||
});
|
||||
},
|
||||
showStructure: function() {
|
||||
let displayStructureInstance = Vue.extend(DisplayStructure),
|
||||
mountView = document.createElement("div");
|
||||
Galaxy.page.center.display(mountView);
|
||||
new displayStructureInstance({ propsData: { id: QueryStringParsing.get("id") } }).$mount(mountView);
|
||||
},
|
||||
switchHistory: function() {
|
||||
let url = Galaxy.root + "history/switch_to_history?hist_id=" + this.historyHistory["id"];
|
||||
this.ajaxCall(url, this.reloadPage);
|
||||
},
|
||||
reloadPage: function() {
|
||||
window.location.reload();
|
||||
}
|
||||
},
|
||||
updated: function() {
|
||||
this.makeHistoryView(this.historyData);
|
||||
}
|
||||
};
|
||||
</script>
|
||||
|
||||
<style>
|
||||
#history-view-controls {
|
||||
flex: 0 0 44px;
|
||||
background-color: white;
|
||||
border-bottom: 1px solid #ddd;
|
||||
width: 100%;
|
||||
padding: 8px;
|
||||
}
|
||||
.history-panel > .controls .title {
|
||||
font-size: 120%;
|
||||
}
|
||||
.history-panel > .controls .title input {
|
||||
font-size: 100%;
|
||||
}
|
||||
a.btn {
|
||||
text-decoration: none;
|
||||
}
|
||||
</style>
|
||||
@@ -5,6 +5,9 @@ import userModel from "mvc/user/user-model";
|
||||
import metricsLogger from "utils/metrics-logger";
|
||||
import addLogging from "utils/add-logging";
|
||||
import localize from "utils/localization";
|
||||
|
||||
/* global $ */
|
||||
|
||||
// TODO: move into a singleton pattern and have dependents import Galaxy
|
||||
// ============================================================================
|
||||
/** Base galaxy client-side application.
|
||||
@@ -70,6 +73,9 @@ GalaxyApp.prototype._init = function __init(options, bootstrapped) {
|
||||
self._initUser(options.user || {});
|
||||
self.debug("GalaxyApp.user: ", self.user);
|
||||
|
||||
self._initUserLocale();
|
||||
self.debug("currentLocale: ", sessionStorage.getItem("currentLocale"));
|
||||
|
||||
self._setUpListeners();
|
||||
self.trigger("ready", self);
|
||||
|
||||
@@ -171,6 +177,37 @@ GalaxyApp.prototype._initLocale = function _initLocale(options) {
|
||||
return self;
|
||||
};
|
||||
|
||||
/** add the localize fn to this object and the window namespace (as '_l') */
|
||||
GalaxyApp.prototype._initUserLocale = function _initUserLocale(options) {
|
||||
var self = this;
|
||||
|
||||
// Choose best locale
|
||||
var global_locale = self.config.default_locale ? self.config.default_locale.toLowerCase() : false;
|
||||
|
||||
var extra_user_preferences = {};
|
||||
if (self.user && self.user.attributes.preferences && "extra_user_preferences" in self.user.attributes.preferences) {
|
||||
extra_user_preferences = JSON.parse(self.user.attributes.preferences.extra_user_preferences);
|
||||
}
|
||||
|
||||
var user_locale =
|
||||
"localization|locale" in extra_user_preferences
|
||||
? extra_user_preferences["localization|locale"].toLowerCase()
|
||||
: false;
|
||||
|
||||
if (user_locale == "auto") {
|
||||
user_locale = false;
|
||||
}
|
||||
|
||||
var nav_locale =
|
||||
typeof navigator === "undefined"
|
||||
? "__root"
|
||||
: (navigator.language || navigator.userLanguage || "__root").toLowerCase();
|
||||
|
||||
let locale = user_locale || global_locale || nav_locale;
|
||||
|
||||
sessionStorage.setItem("currentLocale", locale);
|
||||
};
|
||||
|
||||
/** set up the current user as a Backbone model (mvc/user/user-model) */
|
||||
GalaxyApp.prototype._initUser = function _initUser(userJSON) {
|
||||
var self = this;
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import Utils from "utils/utils";
|
||||
import Menu from "layout/menu";
|
||||
import Scratchbook from "layout/scratchbook";
|
||||
import QuotaMeter from "mvc/user/user-quotameter";
|
||||
|
||||
/** Masthead **/
|
||||
var View = Backbone.View.extend({
|
||||
initialize: function(options) {
|
||||
@@ -62,7 +62,9 @@ var View = Backbone.View.extend({
|
||||
var text = "";
|
||||
self.collection.each(model => {
|
||||
var q = model.get("onbeforeunload") && model.get("onbeforeunload")();
|
||||
q && (text += `${q} `);
|
||||
if (q) {
|
||||
text += `${q} `;
|
||||
}
|
||||
});
|
||||
if (text !== "") {
|
||||
return text;
|
||||
|
||||
@@ -105,15 +105,15 @@ var Collection = Backbone.Collection.extend({
|
||||
//
|
||||
// Webhooks
|
||||
//
|
||||
Webhooks.add({
|
||||
url: "api/webhooks/masthead/all",
|
||||
Webhooks.load({
|
||||
type: "masthead",
|
||||
callback: function(webhooks) {
|
||||
$(document).ready(() => {
|
||||
$.each(webhooks.models, (index, model) => {
|
||||
webhooks.each(model => {
|
||||
var webhook = model.toJSON();
|
||||
if (webhook.activate) {
|
||||
var obj = {
|
||||
id: webhook.name,
|
||||
id: webhook.id,
|
||||
icon: webhook.config.icon,
|
||||
url: webhook.config.url,
|
||||
tooltip: webhook.config.tooltip,
|
||||
|
||||
@@ -2,6 +2,7 @@ import Masthead from "layout/masthead";
|
||||
import Panel from "layout/panel";
|
||||
import Modal from "mvc/ui/ui-modal";
|
||||
import Utils from "utils/utils";
|
||||
|
||||
var View = Backbone.View.extend({
|
||||
el: "body",
|
||||
className: "full-content",
|
||||
@@ -14,7 +15,9 @@ var View = Backbone.View.extend({
|
||||
message_box_content: "",
|
||||
message_box_class: "info",
|
||||
show_inactivity_warning: false,
|
||||
inactivity_box_content: ""
|
||||
inactivity_box_content: "",
|
||||
hide_panels: false,
|
||||
hide_masthead: false
|
||||
});
|
||||
|
||||
// attach global objects, build mastheads
|
||||
@@ -27,6 +30,9 @@ var View = Backbone.View.extend({
|
||||
Utils.setWindowTitle();
|
||||
view.allow_title_display = true;
|
||||
}
|
||||
if (view.active_tab) {
|
||||
Utils.setActiveTab(view.active_tab);
|
||||
}
|
||||
self.center.display(view);
|
||||
};
|
||||
Galaxy.router = this.router = options.Router && new options.Router(self, options);
|
||||
@@ -36,44 +42,62 @@ var View = Backbone.View.extend({
|
||||
// build page template
|
||||
this.$el.attr("scroll", "no");
|
||||
this.$el.html(this._template());
|
||||
this.$("#masthead").replaceWith(this.masthead.$el);
|
||||
this.$("#center").append(this.center.$el);
|
||||
this.$el.append(this.masthead.frame.$el);
|
||||
this.$el.append(this.modal.$el);
|
||||
this.$masthead = this.$("#masthead");
|
||||
this.$center = this.$("#center");
|
||||
this.$messagebox = this.$("#messagebox");
|
||||
this.$inactivebox = this.$("#inactivebox");
|
||||
|
||||
// build components
|
||||
if (this.config.hide_masthead) {
|
||||
this.$masthead.remove();
|
||||
this.$center.css("top", 0);
|
||||
} else {
|
||||
this.$masthead.replaceWith(this.masthead.$el);
|
||||
}
|
||||
this.$center.append(this.center.$el);
|
||||
this.$el.append(this.masthead.frame.$el);
|
||||
this.$el.append(this.modal.$el);
|
||||
|
||||
// build panels
|
||||
this.panels = {};
|
||||
_.each(this._panelids, panel_id => {
|
||||
var panel_class_name = panel_id.charAt(0).toUpperCase() + panel_id.slice(1);
|
||||
var panel_class = options[panel_class_name];
|
||||
if (panel_class) {
|
||||
var panel_instance = new panel_class(self, options);
|
||||
self[panel_instance.toString()] = panel_instance;
|
||||
self.panels[panel_id] = new Panel.SidePanel({
|
||||
id: panel_id,
|
||||
el: self.$(`#${panel_id}`),
|
||||
view: panel_instance
|
||||
});
|
||||
}
|
||||
});
|
||||
if (!this.config.hide_panels) {
|
||||
_.each(this._panelids, panel_id => {
|
||||
var panel_class_name = panel_id.charAt(0).toUpperCase() + panel_id.slice(1);
|
||||
var panel_class = options[panel_class_name];
|
||||
if (panel_class) {
|
||||
var panel_instance = new panel_class(self, options);
|
||||
var panel_el = self.$(`#${panel_id}`);
|
||||
self[panel_instance.toString()] = panel_instance;
|
||||
self.panels[panel_id] = new Panel.SidePanel({
|
||||
id: panel_id,
|
||||
el: panel_el,
|
||||
view: panel_instance
|
||||
});
|
||||
if (this.config.hide_masthead) {
|
||||
panel_el.css("top", 0);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
this.render();
|
||||
|
||||
// start the router
|
||||
this.router &&
|
||||
if (this.router) {
|
||||
Backbone.history.start({
|
||||
root: Galaxy.root,
|
||||
pushState: true
|
||||
});
|
||||
}
|
||||
},
|
||||
|
||||
render: function() {
|
||||
// TODO: Remove this line after select2 update
|
||||
$(".select2-hidden-accessible").remove();
|
||||
this.masthead.render();
|
||||
this.renderMessageBox();
|
||||
this.renderInactivityBox();
|
||||
if (!this.config.hide_masthead) {
|
||||
this.masthead.render();
|
||||
this.renderMessageBox();
|
||||
this.renderInactivityBox();
|
||||
}
|
||||
this.renderPanels();
|
||||
this._checkCommunicationServerOnline();
|
||||
return this;
|
||||
@@ -125,7 +149,7 @@ var View = Backbone.View.extend({
|
||||
if (panel) {
|
||||
panel.render();
|
||||
} else {
|
||||
self.$("#center").css(panel_id, 0);
|
||||
self.$center.css(panel_id, 0);
|
||||
self.$(`#${panel_id}`).hide();
|
||||
}
|
||||
});
|
||||
@@ -135,16 +159,16 @@ var View = Backbone.View.extend({
|
||||
/** body template */
|
||||
_template: function() {
|
||||
return [
|
||||
'<div id="everything">',
|
||||
'<div id="background"/>',
|
||||
'<div id="masthead"/>',
|
||||
'<div id="messagebox"/>',
|
||||
'<div id="inactivebox" class="panel-warning-message" />',
|
||||
'<div id="left" />',
|
||||
'<div id="center" />',
|
||||
'<div id="right" />',
|
||||
"</div>",
|
||||
'<div id="dd-helper" />'
|
||||
`<div id="everything">
|
||||
<div id="background"/>
|
||||
<div id="masthead"/>
|
||||
<div id="messagebox"/>
|
||||
<div id="inactivebox" class="panel-warning-message" />
|
||||
<div id="left" />
|
||||
<div id="center" />
|
||||
<div id="right" />
|
||||
</div>
|
||||
<div id="dd-helper" />`
|
||||
].join("");
|
||||
},
|
||||
|
||||
|
||||
@@ -218,13 +218,23 @@ export default Backbone.View.extend({
|
||||
var $galaxy_main = $(window.parent.document).find("#galaxy_main");
|
||||
if (options.target == "galaxy_main" || options.target == "center") {
|
||||
if ($galaxy_main.length === 0) {
|
||||
window.location = `${options.url + (options.url.indexOf("?") == -1 ? "?" : "&")}use_panels=True`;
|
||||
window.location = this._build_url(options.url, { use_panels: true });
|
||||
} else {
|
||||
$galaxy_main.attr("src", options.url);
|
||||
}
|
||||
} else window.location = options.url;
|
||||
} else {
|
||||
options.url = this._build_url(options.url, { hide_panels: true, hide_masthead: true });
|
||||
this.frames.add(options);
|
||||
}
|
||||
},
|
||||
|
||||
/** Url helper */
|
||||
_build_url: function(url, options) {
|
||||
if (url) {
|
||||
url += url.indexOf("?") == -1 ? "?" : "&";
|
||||
url += $.param(options, true);
|
||||
return url;
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import DATASET_MODEL from "mvc/dataset/dataset-model";
|
||||
import BASE_MVC from "mvc/base-mvc";
|
||||
import Utils from "utils/utils";
|
||||
import _l from "utils/localization";
|
||||
|
||||
//==============================================================================
|
||||
@@ -59,10 +60,17 @@ var DatasetCollectionElementMixin = {
|
||||
|
||||
/** merge the attributes of the sub-object 'object' into this model */
|
||||
_mergeObject: function(attributes) {
|
||||
// if we don't preserve and correct ids here, the element id becomes the object id
|
||||
// and collision in backbone's _byId will occur and only
|
||||
// Don't let the dataset ID replace the DCE's ID so record it as the
|
||||
// element_id and when fetching dataset details below use the element_id
|
||||
// instead of this.id.
|
||||
const object = attributes.object;
|
||||
let elementId = this.elementId;
|
||||
if(object) {
|
||||
elementId = attributes.object.id;
|
||||
delete attributes.object.id;
|
||||
}
|
||||
_.extend(attributes, attributes.object, {
|
||||
element_id: attributes.id
|
||||
element_id: elementId
|
||||
});
|
||||
delete attributes.object;
|
||||
return attributes;
|
||||
@@ -122,7 +130,16 @@ var DatasetDCE = DATASET_MODEL.DatasetAssociation.extend(
|
||||
// (a little silly since this api endpoint *also* points at hdas)
|
||||
return `${Galaxy.root}api/datasets`;
|
||||
}
|
||||
return `${Galaxy.root}api/histories/${this.get("history_id")}/contents/${this.get("id")}`;
|
||||
const datasetId = this._getDatasetId();
|
||||
const url = `${Galaxy.root}api/histories/${this.get("history_id")}/contents/${datasetId}`;
|
||||
return url;
|
||||
},
|
||||
|
||||
_getDatasetId: function() {
|
||||
// I'm a DCE acting as dataset, this URL needs to be the dataset URL so
|
||||
// use element_id instead of id. See note above in _mergeObject and
|
||||
// discussion on #3782 for more context.
|
||||
return this.get("element_id");
|
||||
},
|
||||
|
||||
defaults: _.extend(
|
||||
@@ -280,16 +297,19 @@ var DatasetCollection = Backbone.Model.extend(BASE_MVC.LoggableMixin)
|
||||
return parsed;
|
||||
},
|
||||
|
||||
/** save this dataset, _Mark_ing it as deleted (just a flag) */
|
||||
delete: function(options) {
|
||||
/** save this collection, _Mark_ing it as deleted (just a flag) */
|
||||
delete: function(recursive, purge, options) {
|
||||
recursive = recursive || false;
|
||||
purge = purge || false;
|
||||
if (this.get("deleted")) {
|
||||
return jQuery.when();
|
||||
}
|
||||
return this.save({ deleted: true }, options);
|
||||
options = Utils.merge(options, { method: "delete" });
|
||||
return this.save({ deleted: true, recursive: recursive, purge: purge }, options);
|
||||
},
|
||||
/** save this dataset, _Mark_ing it as undeleted */
|
||||
/** save this collection, _Mark_ing it as undeleted */
|
||||
undelete: function(options) {
|
||||
if (!this.get("deleted") || this.get("purged")) {
|
||||
if (!this.get("deleted")) {
|
||||
return jQuery.when();
|
||||
}
|
||||
return this.save({ deleted: false }, options);
|
||||
|
||||
@@ -32,7 +32,7 @@ var CollectionView = _super.extend(
|
||||
initialize: function(attributes) {
|
||||
_super.prototype.initialize.call(this, attributes);
|
||||
this.linkTarget = attributes.linkTarget || "_blank";
|
||||
|
||||
this.dragItems = true;
|
||||
this.hasUser = attributes.hasUser;
|
||||
/** A stack of panels that currently cover or hide this panel */
|
||||
this.panelStack = [];
|
||||
|
||||
@@ -165,7 +165,6 @@ var DatasetListItemEdit = _super.extend(
|
||||
href: "#",
|
||||
faIcon: "fa-question",
|
||||
onclick: function() {
|
||||
var divString = `thdiv-${datasetID}`;
|
||||
if (self.$el.find(".toolhelp").length > 0) {
|
||||
self.$el.find(".toolhelp").toggle();
|
||||
} else {
|
||||
@@ -261,18 +260,22 @@ var DatasetListItemEdit = _super.extend(
|
||||
return null;
|
||||
}
|
||||
|
||||
var $visualizations = $(this.templates.visualizations(visualizations, this));
|
||||
//HACK: need to re-write those directed at galaxy_main with linkTarget
|
||||
$visualizations.find('[target="galaxy_main"]').attr("target", this.linkTarget);
|
||||
// use addBack here to include the root $visualizations elem (for the case of 1 visualization)
|
||||
this._addScratchBookFn($visualizations.find(".visualization-link").addBack(".visualization-link"));
|
||||
return $visualizations;
|
||||
if (visualizations.length >= 1) {
|
||||
var url = Galaxy.root + "visualizations/dataset_id=" + this.model.get("id");
|
||||
return $("<a/>")
|
||||
.addClass("visualization-link icon-btn")
|
||||
.attr("href", url)
|
||||
.append($("<span/>").addClass("fa fa-bar-chart-o"))
|
||||
.on("click", function(e) {
|
||||
Galaxy.frame.add({ url: url, title: "Visualization" });
|
||||
e.preventDefault();
|
||||
});
|
||||
}
|
||||
},
|
||||
|
||||
/** add scratchbook functionality to visualization links */
|
||||
_addScratchBookFn: function($links) {
|
||||
var li = this;
|
||||
$links.click(function(ev) {
|
||||
$links.click(ev => {
|
||||
if (Galaxy.frame && Galaxy.frame.active) {
|
||||
Galaxy.frame.add({
|
||||
title: _l("Visualization"),
|
||||
|
||||
@@ -302,7 +302,7 @@ var DatasetListItemView = _super.extend(
|
||||
)}">
|
||||
<span class="fa fa-floppy-o"></span>
|
||||
</a>
|
||||
<ul class="dropdown-menu" role="menu" aria-labelledby="dLabel">
|
||||
<ul class="dropdown-menu" role="menu">
|
||||
<li>
|
||||
<a href="${urls.download}">
|
||||
${_l("Download dataset")}
|
||||
|
||||
@@ -54,9 +54,13 @@ var DatasetAssociation = Backbone.Model.extend(BASE_MVC.LoggableMixin).extend(
|
||||
this._setUpListeners();
|
||||
},
|
||||
|
||||
_getDatasetId: function() {
|
||||
return this.get("id");
|
||||
},
|
||||
|
||||
/** returns misc. web urls for rendering things like re-run, display, etc. */
|
||||
_generateUrls: function() {
|
||||
var id = this.get("id");
|
||||
const id = this._getDatasetId();
|
||||
if (!id) {
|
||||
return {};
|
||||
}
|
||||
|
||||
@@ -200,7 +200,7 @@ export var matchCase = (input, value) => {
|
||||
* @param{dict} callback - Called with the mapped dictionary object and corresponding model node
|
||||
*/
|
||||
export var visitInputs = (inputs, callback, prefix, context) => {
|
||||
context = $.extend(true, {}, context);
|
||||
context = $.extend({}, context);
|
||||
_.each(inputs, input => {
|
||||
if (input && input.type && input.name) {
|
||||
context[input.name] = input;
|
||||
|
||||
@@ -224,9 +224,7 @@ export default Backbone.Model.extend({
|
||||
var self = this;
|
||||
return new SelectGenomeSpace.View({
|
||||
id: `field-${input_def.id}`,
|
||||
onchange: function() {
|
||||
self.app.trigger("change");
|
||||
}
|
||||
onchange: input_def.onchange
|
||||
});
|
||||
},
|
||||
|
||||
|
||||
@@ -26,9 +26,8 @@ var View = Backbone.View.extend({
|
||||
},
|
||||
|
||||
/** Add a new input element */
|
||||
add: function(input) {
|
||||
var input_def = jQuery.extend(true, {}, input);
|
||||
input_def.id = input.id = Utils.uid();
|
||||
add: function(input_def) {
|
||||
input_def.id = Utils.uid();
|
||||
this.app.input_list[input_def.id] = input_def;
|
||||
switch (input_def.type) {
|
||||
case "conditional":
|
||||
|
||||
@@ -5,8 +5,7 @@ import Portlet from "mvc/ui/ui-portlet";
|
||||
import Ui from "mvc/ui/ui-misc";
|
||||
import FormSection from "mvc/form/form-section";
|
||||
import FormData from "mvc/form/form-data";
|
||||
|
||||
export var View = Backbone.View.extend({
|
||||
export default Backbone.View.extend({
|
||||
initialize: function(options) {
|
||||
this.model = new Backbone.Model({
|
||||
initial_errors: false,
|
||||
@@ -81,7 +80,7 @@ export var View = Backbone.View.extend({
|
||||
.first();
|
||||
$panel.animate(
|
||||
{
|
||||
scrollTop: $panel.scrollTop() + input_element.$el.offset().top - 120
|
||||
scrollTop: $panel.scrollTop() + input_element.$el.offset().top - $panel.position().top - 120
|
||||
},
|
||||
500
|
||||
);
|
||||
@@ -174,5 +173,3 @@ export var View = Backbone.View.extend({
|
||||
Galaxy.emit.debug("form-view::initialize()", "Completed");
|
||||
}
|
||||
});
|
||||
|
||||
export default View;
|
||||
|
||||
@@ -1,9 +1,11 @@
|
||||
/** This class renders the grid list with shared section. */
|
||||
import GridView from "mvc/grid/grid-view";
|
||||
import LoadingIndicator from "ui/loading-indicator";
|
||||
|
||||
var View = Backbone.View.extend({
|
||||
initialize: function(options) {
|
||||
var self = this;
|
||||
this.setElement($("<div/>"));
|
||||
LoadingIndicator.markViewAsLoading(this);
|
||||
this.model = new Backbone.Model(options);
|
||||
this.item = this.model.get("item");
|
||||
this.title = this.model.get("plural");
|
||||
|
||||
@@ -92,8 +92,10 @@ export default {
|
||||
for (let column of options.columns) {
|
||||
if (column.visible) {
|
||||
tmpl += `<th id="${column.key}-header">`;
|
||||
if (column.href) {
|
||||
tmpl += `<a href="${column.href}" class="sort-link" sort_key="${column.key}">${column.label}</a>`;
|
||||
if (column.sortable) {
|
||||
tmpl += `<a href="javascript:void(0)" class="sort-link" sort_key="${column.key}">${
|
||||
column.label
|
||||
}</a>`;
|
||||
} else {
|
||||
tmpl += column.label;
|
||||
}
|
||||
|
||||
@@ -7,6 +7,8 @@ import Utils from "utils/utils";
|
||||
import GridModel from "mvc/grid/grid-model";
|
||||
import Templates from "mvc/grid/grid-template";
|
||||
import PopupMenu from "mvc/ui/popup-menu";
|
||||
import LoadingIndicator from "ui/loading-indicator";
|
||||
|
||||
// grid view
|
||||
export default Backbone.View.extend({
|
||||
// model
|
||||
@@ -16,6 +18,7 @@ export default Backbone.View.extend({
|
||||
initialize: function(grid_config) {
|
||||
this.grid = new GridModel();
|
||||
this.title = grid_config.title;
|
||||
this.active_tab = grid_config.active_tab;
|
||||
var self = this;
|
||||
window.add_tag_to_grid_filter = (tag_name, tag_value) => {
|
||||
// Put tag name and value together.
|
||||
@@ -28,9 +31,8 @@ export default Backbone.View.extend({
|
||||
self.add_filter_condition("tags", tag);
|
||||
};
|
||||
|
||||
// set element
|
||||
this.setElement("<div/>");
|
||||
if (grid_config.url_base && !grid_config.items) {
|
||||
LoadingIndicator.markViewAsLoading(this);
|
||||
var url_data = grid_config.url_data || {};
|
||||
_.each(grid_config.filters, (v, k) => {
|
||||
url_data[`f-${k}`] = v;
|
||||
@@ -44,6 +46,8 @@ export default Backbone.View.extend({
|
||||
}
|
||||
});
|
||||
} else {
|
||||
// set element
|
||||
this.setElement("<div>");
|
||||
this.init_grid(grid_config);
|
||||
}
|
||||
|
||||
|
||||
@@ -11,6 +11,13 @@ var HDCAListItemEdit = _super.extend(
|
||||
/** @lends HDCAListItemEdit.prototype */ {
|
||||
/** logger used to record this.log messages, commonly set to console */
|
||||
//logger : console,
|
||||
/** set up: options */
|
||||
initialize: function(attributes) {
|
||||
_super.prototype.initialize.call(this, attributes);
|
||||
|
||||
/** allow user purge of dataset files? */
|
||||
this.purgeAllowed = attributes.purgeAllowed || false;
|
||||
},
|
||||
|
||||
/** Override to return editable versions of the collection panels */
|
||||
_getFoldoutPanelClass: function() {
|
||||
@@ -25,22 +32,45 @@ var HDCAListItemEdit = _super.extend(
|
||||
return _super.prototype._renderPrimaryActions.call(this).concat([this._renderDeleteButton()]);
|
||||
},
|
||||
|
||||
/** Render icon-button to delete this collection. */
|
||||
_renderDeleteButton: function() {
|
||||
var deleted = this.model.get("deleted");
|
||||
return faIconButton({
|
||||
title: deleted ? _l("Dataset collection is already deleted") : _l("Delete"),
|
||||
classes: "delete-btn",
|
||||
faIcon: "fa-times",
|
||||
disabled: deleted,
|
||||
onclick: () => {
|
||||
// ...bler... tooltips being left behind in DOM (hover out never called on deletion)
|
||||
this.$el.find(".icon-btn.delete-btn").trigger("mouseout");
|
||||
this.model["delete"]();
|
||||
}
|
||||
});
|
||||
return $(`
|
||||
<div class="dropdown">
|
||||
<a class="delete-btn icon-btn" title="${_l("Delete")}" data-toggle="dropdown">
|
||||
<span class="fa fa-times"></span>
|
||||
</a>
|
||||
<ul class="dropdown-menu pull-right" role="menu">
|
||||
<li>
|
||||
<a href="#" class="delete-collection">
|
||||
${_l("Collection Only")}
|
||||
</a>
|
||||
</li>
|
||||
<li>
|
||||
<a href="#" class="delete-collection-and-datasets">
|
||||
${_l("Delete Datasets")}
|
||||
</a>
|
||||
</li>
|
||||
<li style="display: ${this.purgeAllowed ? "inherit" : "none"}">
|
||||
<a href="#" class="delete-collection-and-purge-datasets">
|
||||
${_l("Permanently Delete Datasets")}
|
||||
</a>
|
||||
</li>
|
||||
</ul>
|
||||
</div>`);
|
||||
},
|
||||
|
||||
// ......................................................................... misc
|
||||
events: _.extend(_.clone(_super.prototype.events), {
|
||||
"click .delete-collection": function(ev) {
|
||||
this.model["delete"]();
|
||||
},
|
||||
"click .delete-collection-and-datasets": function(ev) {
|
||||
this.model["delete"](true);
|
||||
},
|
||||
"click .delete-collection-and-purge-datasets": function(ev) {
|
||||
this.model["delete"](true, true);
|
||||
}
|
||||
}),
|
||||
|
||||
// ......................................................................... misc
|
||||
/** string rep */
|
||||
toString: function() {
|
||||
|
||||
@@ -5,11 +5,13 @@ import HISTORY_PREFS from "mvc/history/history-preferences";
|
||||
import JOB_STATES_MODEL from "mvc/history/job-states-model";
|
||||
import BASE_MVC from "mvc/base-mvc";
|
||||
import AJAX_QUEUE from "utils/ajax-queue";
|
||||
import * as _ from "libs/underscore";
|
||||
import * as Backbone from "libs/backbone";
|
||||
|
||||
var limitPerPageDefault = 500;
|
||||
try {
|
||||
limitPerPageDefault = localStorage.getItem("historyContentsLimitPerPageDefault") || limitPerPageDefault;
|
||||
} catch (err) {}
|
||||
/* global Galaxy */
|
||||
/* global jQuery */
|
||||
|
||||
const limitPerPageDefault = window.localStorage.getItem("historyContentsLimitPerPageDefault") || 500;
|
||||
|
||||
//==============================================================================
|
||||
var _super = CONTROLLED_FETCH_COLLECTION.PaginatedCollection;
|
||||
@@ -72,7 +74,7 @@ var HistoryContents = _super.extend(BASE_MVC.LoggableMixin).extend({
|
||||
if (historyContent.attributes.history_content_type === "dataset_collection") {
|
||||
var jobSourceType = historyContent.attributes.job_source_type;
|
||||
var jobSourceId = historyContent.attributes.job_source_id;
|
||||
if (jobSourceType) {
|
||||
if (jobSourceType && this.jobStateSummariesCollection) {
|
||||
this.jobStateSummariesCollection.add({
|
||||
id: jobSourceId,
|
||||
model: jobSourceType,
|
||||
@@ -314,15 +316,14 @@ var HistoryContents = _super.extend(BASE_MVC.LoggableMixin).extend({
|
||||
/** fetch all the visible==false contents of this collection */
|
||||
fetchHidden: function(options) {
|
||||
options = options || {};
|
||||
var self = this;
|
||||
options.filters = _.extend(options.filters, {
|
||||
visible: false
|
||||
});
|
||||
options.remove = false;
|
||||
|
||||
self.trigger("fetching-hidden", self);
|
||||
return self.fetch(options).always(() => {
|
||||
self.trigger("fetching-hidden-done", self);
|
||||
this.trigger("fetching-hidden", this);
|
||||
return this.fetch(options).always(() => {
|
||||
this.trigger("fetching-hidden-done", this);
|
||||
});
|
||||
},
|
||||
|
||||
@@ -338,17 +339,16 @@ var HistoryContents = _super.extend(BASE_MVC.LoggableMixin).extend({
|
||||
// TODO: to batch
|
||||
/** helper that fetches using filterParams then calls save on each fetched using updateWhat as the save params */
|
||||
_filterAndUpdate: function(filterParams, updateWhat) {
|
||||
var self = this;
|
||||
var idAttribute = self.model.prototype.idAttribute;
|
||||
var idAttribute = this.model.prototype.idAttribute;
|
||||
var updateArgs = [updateWhat];
|
||||
|
||||
return self.fetch({ filters: filterParams, remove: false }).then(fetched => {
|
||||
return this.fetch({ filters: filterParams, remove: false }).then(fetched => {
|
||||
// convert filtered json array to model array
|
||||
fetched = fetched.reduce((modelArray, currJson, i) => {
|
||||
var model = self.get(currJson[idAttribute]);
|
||||
var model = this.get(currJson[idAttribute]);
|
||||
return model ? modelArray.concat(model) : modelArray;
|
||||
}, []);
|
||||
return self.ajaxQueue("save", updateArgs, fetched);
|
||||
return this.ajaxQueue("save", updateArgs, fetched);
|
||||
});
|
||||
},
|
||||
|
||||
@@ -366,43 +366,44 @@ var HistoryContents = _super.extend(BASE_MVC.LoggableMixin).extend({
|
||||
).deferred;
|
||||
},
|
||||
|
||||
_recursivelyFetch: function(options, detailKeys, deferred, limit, offset) {
|
||||
offset = offset || 0;
|
||||
var _options = _.extend(_.clone(options), {
|
||||
view: "summary",
|
||||
keys: detailKeys,
|
||||
limit: limit,
|
||||
offset: offset,
|
||||
reset: offset === 0,
|
||||
remove: false
|
||||
});
|
||||
|
||||
_.defer(() => {
|
||||
this.fetch
|
||||
.call(this, _options)
|
||||
.fail(deferred.reject)
|
||||
.done(response => {
|
||||
deferred.notify(response, limit, offset);
|
||||
if (response.length !== limit) {
|
||||
this.allFetched = true;
|
||||
deferred.resolve(response, limit, offset);
|
||||
} else {
|
||||
this._recursivelyFetch(options, detailKeys, deferred, limit, offset + limit);
|
||||
}
|
||||
});
|
||||
});
|
||||
},
|
||||
|
||||
/** fetch contents' details in batches of limitPerCall - note: only get searchable details here */
|
||||
progressivelyFetchDetails: function(options) {
|
||||
options = options || {};
|
||||
var deferred = jQuery.Deferred();
|
||||
var self = this;
|
||||
var limit = options.limitPerCall || self.limitPerProgressiveFetch;
|
||||
// TODO: only fetch tags and annotations if specifically requested
|
||||
var searchAttributes = HDA_MODEL.HistoryDatasetAssociation.prototype.searchAttributes;
|
||||
var detailKeys = searchAttributes.join(",");
|
||||
|
||||
function _recursivelyFetch(offset) {
|
||||
offset = offset || 0;
|
||||
var _options = _.extend(_.clone(options), {
|
||||
view: "summary",
|
||||
keys: detailKeys,
|
||||
limit: limit,
|
||||
offset: offset,
|
||||
reset: offset === 0,
|
||||
remove: false
|
||||
});
|
||||
|
||||
_.defer(() => {
|
||||
self.fetch
|
||||
.call(self, _options)
|
||||
.fail(deferred.reject)
|
||||
.done(response => {
|
||||
deferred.notify(response, limit, offset);
|
||||
if (response.length !== limit) {
|
||||
self.allFetched = true;
|
||||
deferred.resolve(response, limit, offset);
|
||||
} else {
|
||||
_recursivelyFetch(offset + limit);
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
_recursivelyFetch();
|
||||
options = options || {};
|
||||
let deferred = jQuery.Deferred();
|
||||
this._recursivelyFetch(
|
||||
options,
|
||||
HDA_MODEL.HistoryDatasetAssociation.prototype.searchAttributes.join(","),
|
||||
deferred,
|
||||
options.limitPerCall || this.limitPerProgressiveFetch
|
||||
);
|
||||
return deferred;
|
||||
},
|
||||
|
||||
|
||||
@@ -1,11 +1,61 @@
|
||||
import _l from "utils/localization";
|
||||
/** This class renders the grid list. */
|
||||
import AjaxQueue from "utils/ajax-queue";
|
||||
import Utils from "utils/utils";
|
||||
/** This class renders the grid list. */
|
||||
import GridView from "mvc/grid/grid-view";
|
||||
import HistoryModel from "mvc/history/history-model";
|
||||
import historyCopyDialog from "mvc/history/copy-dialog";
|
||||
import LoadingIndicator from "ui/loading-indicator";
|
||||
|
||||
var HistoryGridView = GridView.extend({
|
||||
initialize: function(grid_config) {
|
||||
this.ajaxQueue = new AjaxQueue.AjaxQueue();
|
||||
GridView.prototype.initialize.call(this, grid_config);
|
||||
},
|
||||
|
||||
init_grid_elements: function() {
|
||||
const ajaxQueue = this.ajaxQueue;
|
||||
ajaxQueue.stop();
|
||||
GridView.prototype.init_grid_elements.call(this);
|
||||
const fetchDetails = $.makeArray(
|
||||
this.$el.find(".delayed-value-datasets_by_state").map((i, el) => {
|
||||
return () => {
|
||||
const historyId = $(el).data("history-id");
|
||||
const url = `${
|
||||
Galaxy.root
|
||||
}api/histories/${historyId}?keys=nice_size,contents_active,contents_states`;
|
||||
const options = {};
|
||||
options.url = url;
|
||||
options.type = "GET";
|
||||
options.success = req => {
|
||||
const contentsStates = req["contents_states"];
|
||||
let stateHtml = "";
|
||||
for (let state of ["ok", "running", "queued", "new", "error"]) {
|
||||
const stateCount = contentsStates[state];
|
||||
if (stateCount) {
|
||||
stateHtml += `<div class="count-box state-color-${state}" title="Datasets in ${state} state">${stateCount}</div> `;
|
||||
}
|
||||
}
|
||||
const contentsActive = req["contents_active"];
|
||||
const deleted = contentsActive["deleted"];
|
||||
if (deleted) {
|
||||
stateHtml += `<div class="count-box state-color-deleted" title="Deleted datasets">${deleted}</div> `;
|
||||
}
|
||||
const hidden = contentsActive["hidden"];
|
||||
if (hidden) {
|
||||
stateHtml += `<div class="count-box state-color-hidden" title="Hidden datasets">${hidden}</div> `;
|
||||
}
|
||||
$(`.delayed-value-datasets_by_state[data-history-id='${historyId}']`).html(stateHtml);
|
||||
$(`.delayed-value-disk_size[data-history-id='${historyId}']`).html(req["nice_size"]);
|
||||
};
|
||||
var xhr = jQuery.ajax(options);
|
||||
return xhr;
|
||||
};
|
||||
})
|
||||
);
|
||||
fetchDetails.forEach(fn => ajaxQueue.add(fn));
|
||||
ajaxQueue.start();
|
||||
},
|
||||
_showCopyDialog: function(id) {
|
||||
var history = new HistoryModel.History({ id: id });
|
||||
history
|
||||
@@ -55,7 +105,8 @@ var View = Backbone.View.extend({
|
||||
title: _l("Histories"),
|
||||
initialize: function(options) {
|
||||
var self = this;
|
||||
this.setElement($("<div/>"));
|
||||
LoadingIndicator.markViewAsLoading(this);
|
||||
|
||||
this.model = new Backbone.Model();
|
||||
Utils.get({
|
||||
url: `${Galaxy.root}history/${options.action_id}?${$.param(Galaxy.params)}`,
|
||||
|
||||
@@ -275,7 +275,11 @@ var HistoryViewEdit = _super.extend(
|
||||
func: function() {
|
||||
if (confirm(_l("This will permanently remove the data in your datasets. Are you sure?"))) {
|
||||
var action = HDA_MODEL.HistoryDatasetAssociation.prototype.purge;
|
||||
panel.getSelectedModels().ajaxQueue(action);
|
||||
const historyContents = panel.getSelectedModels();
|
||||
const selectedDatasets = historyContents.filter(
|
||||
c => c.get("history_content_type") == "dataset"
|
||||
);
|
||||
historyContents.ajaxQueue(action, {}, selectedDatasets);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
@@ -1,15 +1,21 @@
|
||||
import * as _ from "underscore";
|
||||
import _l from "utils/localization";
|
||||
import LIST_VIEW from "mvc/list/list-view";
|
||||
import HISTORY_MODEL from "mvc/history/history-model";
|
||||
import HISTORY_CONTENTS from "mvc/history/history-contents";
|
||||
import HISTORY_PREFS from "mvc/history/history-preferences";
|
||||
import History from "mvc/history/history-model";
|
||||
import HDA_LI from "mvc/history/hda-li";
|
||||
import HDCA_LI from "mvc/history/hdca-li";
|
||||
import USER from "mvc/user/user-model";
|
||||
import ERROR_MODAL from "mvc/ui/error-modal";
|
||||
import faIconButton from "ui/fa-icon-button";
|
||||
import BASE_MVC from "mvc/base-mvc";
|
||||
import _l from "utils/localization";
|
||||
import HistoryViewEdit from "mvc/history/history-view-edit";
|
||||
import HistoryCopyDialog from "mvc/history/copy-dialog";
|
||||
import "ui/search-input";
|
||||
import "ui/mode-button";
|
||||
|
||||
/* global $ */
|
||||
/* global Galaxy */
|
||||
|
||||
/* =============================================================================
|
||||
TODO:
|
||||
@@ -413,7 +419,7 @@ var HistoryView = _super.extend(
|
||||
this.$(inputSelector).searchInput("toggle-loading");
|
||||
// set this now so that only results will show during progress
|
||||
this.searchFor = searchFor;
|
||||
var xhr = this.model.contents
|
||||
this.model.contents
|
||||
.progressivelyFetchDetails({ silent: true })
|
||||
.progress((response, limit, offset) => {
|
||||
this.renderItems();
|
||||
@@ -611,7 +617,91 @@ HistoryView.prototype.templates = (() => {
|
||||
});
|
||||
})();
|
||||
|
||||
export function historyEntry(options) {
|
||||
$("#toggle-deleted").modeButton({
|
||||
initialMode: options.initialModeDeleted,
|
||||
modes: [
|
||||
{ mode: "showing_deleted", html: _l("Exclude deleted") },
|
||||
{ mode: "not_showing_deleted", html: _l("Include deleted") }
|
||||
]
|
||||
});
|
||||
$("#toggle-hidden").modeButton({
|
||||
initialMode: options.initialModeHidden,
|
||||
modes: [
|
||||
{ mode: "showing_hidden", html: _l("Exclude hidden") },
|
||||
{ mode: "not_showing_hidden", html: _l("Include hidden") }
|
||||
]
|
||||
});
|
||||
$("#switch").click(function() {
|
||||
//##HACK:ity hack hack
|
||||
//##TODO: remove when out of iframe
|
||||
var hview =
|
||||
Galaxy.currHistoryPanel || (window.top.Galaxy && window.top.Galaxy.currHistoryPanel)
|
||||
? window.top.Galaxy.currHistoryPanel
|
||||
: null;
|
||||
if (hview) {
|
||||
hview.switchToHistory("${ history[ 'id' ] }");
|
||||
} else {
|
||||
window.location = "${ switch_to_url }";
|
||||
}
|
||||
});
|
||||
// use_panels effects where the the center_panel() is rendered:
|
||||
// w/o it renders to the body, w/ it renders to #center - we need to adjust a few things for scrolling to work
|
||||
if (options.hasMasthead) {
|
||||
$("#center").addClass("flex-vertical-container");
|
||||
}
|
||||
|
||||
let viewClass = options.userIsOwner ? HistoryViewEdit.HistoryViewEdit : HistoryView.HistoryView;
|
||||
let historyModel = new History.History(options.historyJSON);
|
||||
|
||||
// attach the copy dialog to the import button now that we have a history
|
||||
$("#import").click(function() {
|
||||
HistoryCopyDialog(historyModel, {
|
||||
useImport: true,
|
||||
// use default datasets option to match the toggle-deleted button
|
||||
allDatasets: $("#toggle-deleted").modeButton("getMode").mode === "showing_deleted"
|
||||
}).done(function() {
|
||||
if (window === window.parent) {
|
||||
window.location = Galaxy.root;
|
||||
} else if (Galaxy.currHistoryPanel) {
|
||||
Galaxy.currHistoryPanel.loadCurrentHistory();
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
let historyView = new viewClass({
|
||||
el: $("#history-" + options.historyJSON.id),
|
||||
className: viewClass.prototype.className + " wide",
|
||||
$scrollContainer: options.hasMasthead
|
||||
? function() {
|
||||
return this.$el.parent();
|
||||
}
|
||||
: undefined,
|
||||
model: historyModel,
|
||||
show_deleted: options.showDeletedJson,
|
||||
show_hidden: options.showHiddenJson,
|
||||
purgeAllowed: options.allow_user_dataset_purge
|
||||
});
|
||||
historyView.trigger("loading");
|
||||
historyModel
|
||||
.fetchContents({ silent: true })
|
||||
.fail(function() {
|
||||
alert("Galaxy history failed to load");
|
||||
})
|
||||
.done(function() {
|
||||
historyView.trigger("loading-done");
|
||||
historyView.render();
|
||||
});
|
||||
$("#toggle-deleted").on("click", function() {
|
||||
historyView.toggleShowDeleted();
|
||||
});
|
||||
$("#toggle-hidden").on("click", function() {
|
||||
historyView.toggleShowHidden();
|
||||
});
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
export default {
|
||||
HistoryView: HistoryView
|
||||
HistoryView: HistoryView,
|
||||
historyEntry: historyEntry
|
||||
};
|
||||
|
||||
@@ -8,7 +8,6 @@ import historyCopyDialog from "mvc/history/copy-dialog";
|
||||
import ERROR_MODAL from "mvc/ui/error-modal";
|
||||
import baseMVC from "mvc/base-mvc";
|
||||
import ajaxQueue from "utils/ajax-queue";
|
||||
import "ui/mode-button";
|
||||
import "ui/search-input";
|
||||
|
||||
/* global $ */
|
||||
|
||||
@@ -1,9 +1,12 @@
|
||||
import * as _ from "underscore";
|
||||
import _l from "utils/localization";
|
||||
import PopupMenu from "mvc/ui/popup-menu";
|
||||
import historyCopyDialog from "mvc/history/copy-dialog";
|
||||
import BASE_MVC from "mvc/base-mvc";
|
||||
import _l from "utils/localization";
|
||||
import Webhooks from "mvc/webhooks";
|
||||
|
||||
/* global $ */
|
||||
/* global Galaxy */
|
||||
|
||||
// ============================================================================
|
||||
var menu = [
|
||||
{
|
||||
@@ -47,8 +50,12 @@ var menu = [
|
||||
},
|
||||
{
|
||||
html: _l("Show Structure"),
|
||||
href: "history/display_structured",
|
||||
anon: true
|
||||
anon: true,
|
||||
func: function() {
|
||||
if (Galaxy && Galaxy.currHistoryPanel && Galaxy.router) {
|
||||
Galaxy.router.push(`/histories/show_structure`);
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
html: _l("Extract Workflow"),
|
||||
@@ -175,13 +182,13 @@ var menu = [
|
||||
];
|
||||
|
||||
// Webhooks
|
||||
Webhooks.add({
|
||||
url: "api/webhooks/history-menu/all",
|
||||
Webhooks.load({
|
||||
type: "history-menu",
|
||||
async: false, // (hypothetically) slows down the performance
|
||||
callback: function(webhooks) {
|
||||
var webhooks_menu = [];
|
||||
|
||||
$.each(webhooks.models, (index, model) => {
|
||||
webhooks.each(model => {
|
||||
var webhook = model.toJSON();
|
||||
if (webhook.activate) {
|
||||
webhooks_menu.push({
|
||||
@@ -220,6 +227,7 @@ function buildMenu(isAnon, purgeAllowed, urlRoot) {
|
||||
if (menuOption.confirm) {
|
||||
menuOption.func = () => {
|
||||
if (confirm(menuOption.confirm)) {
|
||||
/* galaxy_main is a global here: TODO: Fix it! */
|
||||
galaxy_main.location = menuOption.href;
|
||||
}
|
||||
};
|
||||
|
||||
@@ -791,7 +791,7 @@ var ListPanel = Backbone.View.extend(BASE_MVC.LoggableMixin).extend(
|
||||
this.debug("_showLoadingIndicator", this.indicator, msg, speed, callback);
|
||||
speed = speed !== undefined ? speed : this.fxSpeed;
|
||||
if (!this.indicator) {
|
||||
this.indicator = new LoadingIndicator(this.$el);
|
||||
this.indicator = new LoadingIndicator.LoadingIndicator(this.$el);
|
||||
this.debug("\t created", this.indicator);
|
||||
}
|
||||
if (!this.$el.is(":visible")) {
|
||||
|
||||
@@ -6,6 +6,7 @@ import Utils from "utils/utils";
|
||||
import Deferred from "utils/deferred";
|
||||
import Ui from "mvc/ui/ui-misc";
|
||||
import FormBase from "mvc/form/form-view";
|
||||
import Webhooks from "mvc/webhooks";
|
||||
import Citations from "components/Citations.vue";
|
||||
import Vue from "vue";
|
||||
export default FormBase.extend({
|
||||
@@ -200,19 +201,23 @@ export default FormBase.extend({
|
||||
}
|
||||
|
||||
// add tool menu webhooks
|
||||
$.getJSON("/api/webhooks/tool-menu/all", webhooks => {
|
||||
_.each(webhooks, webhook => {
|
||||
if (webhook.activate && webhook.config.function) {
|
||||
menu_button.addMenu({
|
||||
icon: webhook.config.icon,
|
||||
title: webhook.config.title,
|
||||
onclick: function() {
|
||||
var func = new Function("options", webhook.config.function);
|
||||
func(options);
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
Webhooks.load({
|
||||
type: "tool-menu",
|
||||
callback: function(webhooks) {
|
||||
webhooks.each(model => {
|
||||
var webhook = model.toJSON();
|
||||
if (webhook.activate && webhook.config.function) {
|
||||
menu_button.addMenu({
|
||||
icon: webhook.config.icon,
|
||||
title: webhook.config.title,
|
||||
onclick: function() {
|
||||
var func = new Function("options", webhook.config.function);
|
||||
func(options);
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
return {
|
||||
|
||||
@@ -25,18 +25,6 @@ var View = Backbone.View.extend({
|
||||
$("body").append(this.$el);
|
||||
this._configure();
|
||||
this.render();
|
||||
$(window).resize(() => {
|
||||
self._refresh();
|
||||
});
|
||||
},
|
||||
|
||||
/** Refresh height of scrollable div below header, handle scrolling by lazy loading steps */
|
||||
_refresh: function(step_index) {
|
||||
var margin =
|
||||
_.reduce(this.$el.children(), (memo, child) => memo + $(child).outerHeight(), 0) -
|
||||
this.$steps.height() +
|
||||
90;
|
||||
this.$steps.css("height", $(window).height() - margin);
|
||||
},
|
||||
|
||||
/** Configures form/step options for each workflow step */
|
||||
@@ -432,7 +420,6 @@ var View = Backbone.View.extend({
|
||||
}
|
||||
self.forms[step.index] = form;
|
||||
self._append(self.$steps, form.$el);
|
||||
self._refresh();
|
||||
step.needs_refresh && self._refreshStep(step);
|
||||
form.portlet[!self.show_progress ? "enable" : "disable"]();
|
||||
self.show_progress &&
|
||||
@@ -598,7 +585,7 @@ var View = Backbone.View.extend({
|
||||
if ($.isArray(response) && response.length > 0) {
|
||||
self.$el.append($("<div/>", { id: "webhook-view" }));
|
||||
var WebhookApp = new Webhooks.WebhookView({
|
||||
urlRoot: `${Galaxy.root}api/webhooks/workflow`,
|
||||
type: "workflow",
|
||||
toolId: job_def.tool_id,
|
||||
toolVersion: job_def.tool_version
|
||||
});
|
||||
|
||||
@@ -227,7 +227,7 @@ var View = Backbone.View.extend({
|
||||
if (response.jobs && response.jobs.length > 0) {
|
||||
self.$el.append($("<div/>", { id: "webhook-view" }));
|
||||
var WebhookApp = new Webhooks.WebhookView({
|
||||
urlRoot: `${Galaxy.root}api/webhooks/tool`,
|
||||
type: "tool",
|
||||
toolId: job_def.tool_id
|
||||
});
|
||||
}
|
||||
|
||||
@@ -8,11 +8,12 @@ export default {
|
||||
|
||||
var newWin = window.open(GS_UPLOAD_URL, "GenomeSpace File Browser", "height=360px,width=600px");
|
||||
|
||||
successCalBack = options["successCallback"];
|
||||
window.addEventListener(
|
||||
"message",
|
||||
e => {
|
||||
successCalBack(e.data);
|
||||
if (options.successCallback && e.data.destination) {
|
||||
options.successCallback(e.data);
|
||||
}
|
||||
},
|
||||
false
|
||||
);
|
||||
|
||||
@@ -414,9 +414,20 @@ var View = Backbone.View.extend({
|
||||
var field = this.fields[current];
|
||||
var drop_data = JSON.parse(ev.originalEvent.dataTransfer.getData("text"))[0];
|
||||
var new_id = drop_data.id;
|
||||
var new_src = drop_data.history_content_type == "dataset" ? "hda" : "hdca";
|
||||
var new_src = drop_data.history_content_type == "dataset_collection" ? "hdca" : "hda";
|
||||
var new_value = { id: new_id, src: new_src };
|
||||
if (data && _.findWhere(data[new_src], new_value)) {
|
||||
if (data && drop_data.history_id) {
|
||||
if (!_.findWhere(data[new_src], new_value)) {
|
||||
data[new_src].push({
|
||||
id: new_id,
|
||||
src: new_src,
|
||||
hid: drop_data.hid || "Dropped",
|
||||
name: drop_data.hid ? drop_data.name : new_id,
|
||||
keep: true,
|
||||
tags: []
|
||||
});
|
||||
this._changeData();
|
||||
}
|
||||
if (config.src == new_src) {
|
||||
var current_value = field.value();
|
||||
if (current_value && config.multiple) {
|
||||
|
||||
@@ -11,6 +11,7 @@ var View = Backbone.View.extend({
|
||||
initialize: function(options) {
|
||||
// link this
|
||||
var self = this;
|
||||
this.options = options;
|
||||
|
||||
// create insert new list element button
|
||||
this.browse_button = new Ui.ButtonIcon({
|
||||
@@ -18,23 +19,17 @@ var View = Backbone.View.extend({
|
||||
icon: "fa fa-sign-in",
|
||||
tooltip: _l("Browse GenomeSpace"),
|
||||
onclick: function() {
|
||||
self.browseGenomeSpace();
|
||||
self.browseGenomeSpace(options);
|
||||
}
|
||||
});
|
||||
|
||||
// create genomespace filepath textbox
|
||||
this.filename_textbox = new Ui.Input();
|
||||
|
||||
// create genomespace token textbox
|
||||
this.token_textbox = new Ui.Input({
|
||||
type: "password"
|
||||
});
|
||||
|
||||
// create elements
|
||||
this.setElement(this._template(options));
|
||||
this.$(".ui-gs-browse-button").append(this.browse_button.$el);
|
||||
this.$(".ui-gs-filename-textbox").append(this.filename_textbox.$el);
|
||||
this.$(".ui-gs-token-textbox").append(this.token_textbox.$el);
|
||||
},
|
||||
|
||||
/** Browse GenomeSpace */
|
||||
@@ -42,7 +37,7 @@ var View = Backbone.View.extend({
|
||||
var self = this;
|
||||
GenomespaceBrowser.openFileBrowser({
|
||||
successCallback: function(data) {
|
||||
self.value(`${data.destination}^${data.token}`);
|
||||
self.value(data.destination);
|
||||
}
|
||||
});
|
||||
},
|
||||
@@ -55,15 +50,11 @@ var View = Backbone.View.extend({
|
||||
'<span class="ui-gs-browse-button" />' +
|
||||
'<span class="ui-gs-filename-textbox" />' +
|
||||
"</div>" +
|
||||
'<div class="ui-gs-token-field">' +
|
||||
'<span class=ui-gs-label"><div class="ui-gs-token-label">Token</div></span>' +
|
||||
'<span class="ui-gs-token-textbox" />' +
|
||||
"</div>" +
|
||||
"</div>"
|
||||
);
|
||||
},
|
||||
|
||||
/** Return/Set currently selected genomespace filename/token */
|
||||
/** Return/Set currently selected genomespace filename */
|
||||
value: function(new_value) {
|
||||
// check if new_value is defined
|
||||
if (new_value !== undefined) {
|
||||
@@ -75,16 +66,15 @@ var View = Backbone.View.extend({
|
||||
|
||||
// get value
|
||||
_getValue: function() {
|
||||
return `${this.filename_textbox.value()}^${this.token_textbox.value()}`;
|
||||
return this.filename_textbox.value();
|
||||
},
|
||||
|
||||
// set value
|
||||
_setValue: function(new_value) {
|
||||
if (new_value) {
|
||||
values = new_value.split("^");
|
||||
this.filename_textbox.value(values[0]);
|
||||
this.token_textbox.value(values[1]);
|
||||
this.filename_textbox.value(new_value);
|
||||
}
|
||||
this.options.onchange && this.options.onchange(new_value);
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
@@ -1,8 +1,7 @@
|
||||
/** This class renders the selection grid. */
|
||||
import Utils from "utils/utils";
|
||||
import Tabs from "mvc/ui/ui-tabs";
|
||||
|
||||
export var View = Backbone.View.extend({
|
||||
var View = Backbone.View.extend({
|
||||
events: {
|
||||
"click .ui-thumbnails-item": "_onclick",
|
||||
"dblclick .ui-thumbnails-item": "_ondblclick"
|
||||
@@ -21,15 +20,15 @@ export var View = Backbone.View.extend({
|
||||
render: function() {
|
||||
this.first = null;
|
||||
this.tabs.delAll();
|
||||
this._renderDefault();
|
||||
this._renderRegular();
|
||||
this._renderList();
|
||||
},
|
||||
|
||||
_renderDefault: function() {
|
||||
_renderRegular: function() {
|
||||
var title_length = 20;
|
||||
var $el = $("<div/>").addClass("ui-thumbnails-grid");
|
||||
this.collection.each(model => {
|
||||
if (model.get("keywords").indexOf("default") !== -1) {
|
||||
if (model.get("regular")) {
|
||||
var title = model.get("title");
|
||||
$el.append(
|
||||
$(
|
||||
@@ -94,6 +93,9 @@ export var View = Backbone.View.extend({
|
||||
.closest(".ui-thumbnails-item")
|
||||
.attr("value")
|
||||
);
|
||||
if (this.model.get("onclick")) {
|
||||
this.model.get("onclick")(this.value());
|
||||
}
|
||||
},
|
||||
|
||||
/** Add double click handler */
|
||||
@@ -103,27 +105,30 @@ export var View = Backbone.View.extend({
|
||||
}
|
||||
},
|
||||
|
||||
/** Image/Icon template */
|
||||
_templateImage: function(options) {
|
||||
if (options.image_src) {
|
||||
return `<img class="ui-thumbnails-image" src="${options.image_src}"/>`;
|
||||
}
|
||||
return `<div class="ui-thumbnails-icon fa fa-eye"/>`;
|
||||
},
|
||||
|
||||
/* Thumbnail template with image */
|
||||
_templateThumbnailItem: function(options) {
|
||||
return `<div class="ui-thumbnails-item ui-thumbnails-item-float" value="${
|
||||
options.id
|
||||
}"><img class="ui-thumbnails-image" src="${
|
||||
options.image_src
|
||||
}"><div class="ui-thumbnails-title ui-form-info"><span class="fa ${options.title_icon}"/>${
|
||||
return `<div class="ui-thumbnails-item ui-thumbnails-item-float" value="${options.id}">${this._templateImage(
|
||||
options
|
||||
)}<div class="ui-thumbnails-title ui-form-info"><span class="fa ${options.title_icon}"/>${
|
||||
options.title
|
||||
}</div><div>`;
|
||||
},
|
||||
|
||||
/* Thumbnail template with image and description */
|
||||
_templateRegularItem: function(options) {
|
||||
return `<div class="ui-thumbnails-item" value="${
|
||||
options.id
|
||||
}"><table><tr><td><img class="ui-thumbnails-image" src="${
|
||||
options.image_src
|
||||
}"></td><td><div class="ui-thumbnails-description-title ui-form-info">${
|
||||
return `<div class="ui-thumbnails-item" value="${options.id}"><table><tr><td>${this._templateImage(
|
||||
options
|
||||
)}</td><td><div class="ui-thumbnails-description-title ui-form-info">${
|
||||
options.title
|
||||
}</div><div class="ui-thumbnails-description-text ui-form-info">${options.description}</div></td></tr><div>`;
|
||||
}
|
||||
});
|
||||
|
||||
export default { View: View };
|
||||
|
||||
@@ -116,7 +116,7 @@ export default Backbone.View.extend({
|
||||
|
||||
// file upload
|
||||
this.uploadbox = this.$(".upload-box").uploadbox({
|
||||
url: this.app.options.nginx_upload_path,
|
||||
url: this.app.options.upload_path,
|
||||
announce: function(index, file) {
|
||||
self._eventAnnounce(index, file);
|
||||
},
|
||||
|
||||
@@ -133,7 +133,7 @@ export default Backbone.View.extend({
|
||||
});
|
||||
});
|
||||
$.uploadpost({
|
||||
url: this.app.options.nginx_upload_path,
|
||||
url: this.app.options.upload_path,
|
||||
data: this.app.toData(this.collection.filter()),
|
||||
success: function(message) {
|
||||
self._eventSuccess(message);
|
||||
|
||||
@@ -11,6 +11,7 @@ export default Backbone.View.extend({
|
||||
init: "upload-icon-button fa fa-trash-o",
|
||||
queued: "upload-icon fa fa-spinner fa-spin",
|
||||
running: "upload-icon fa fa-spinner fa-spin",
|
||||
warning: "upload-icon fa fa-spinner fa-spin",
|
||||
success: "upload-icon-button fa fa-check",
|
||||
error: "upload-icon-button fa fa-exclamation-triangle"
|
||||
},
|
||||
@@ -18,6 +19,7 @@ export default Backbone.View.extend({
|
||||
initialize: function(app, options) {
|
||||
var self = this;
|
||||
this.app = app;
|
||||
this.list_extensions = app.list_extensions;
|
||||
this.model = options.model;
|
||||
this.setElement(this._template(options.model));
|
||||
this.$mode = this.$(".upload-mode");
|
||||
@@ -57,7 +59,7 @@ export default Backbone.View.extend({
|
||||
// create select extension
|
||||
this.select_extension = new Select.View({
|
||||
css: "upload-extension",
|
||||
data: self.app.list_extensions,
|
||||
data: _.filter(this.list_extensions, ext => !ext.composite_files),
|
||||
container: this.$(".upload-extension"),
|
||||
value: default_extension,
|
||||
onchange: function(extension) {
|
||||
@@ -183,7 +185,7 @@ export default Backbone.View.extend({
|
||||
_refreshInfo: function() {
|
||||
var info = this.model.get("info");
|
||||
if (info) {
|
||||
this.$info_text.html(`<strong>Failed: </strong>${info}`).show();
|
||||
this.$info_text.html(`<strong>Warning: </strong>${info}`).show();
|
||||
} else {
|
||||
this.$info_text.hide();
|
||||
}
|
||||
@@ -213,13 +215,17 @@ export default Backbone.View.extend({
|
||||
this.select_genome.disable();
|
||||
this.select_extension.disable();
|
||||
}
|
||||
this.$info_progress.show();
|
||||
this.$el.removeClass().addClass("upload-row");
|
||||
if (status == "success") {
|
||||
this.$el.addClass("success");
|
||||
this.$percentage.html("100%");
|
||||
}
|
||||
if (status == "error") {
|
||||
} else if (status == "error") {
|
||||
this.$el.addClass("danger");
|
||||
this.$info_progress.hide();
|
||||
} else if (status == "warning") {
|
||||
this.$el.addClass("warning");
|
||||
this.$info_progress.hide();
|
||||
}
|
||||
},
|
||||
|
||||
|
||||
@@ -104,7 +104,7 @@ export default Backbone.View.extend({
|
||||
|
||||
// file upload
|
||||
this.uploadbox = this.$uploadbox.uploadbox({
|
||||
url: this.app.options.nginx_upload_path,
|
||||
url: this.app.options.upload_path,
|
||||
announce: function(index, file) {
|
||||
self._eventAnnounce(index, file);
|
||||
},
|
||||
@@ -120,6 +120,9 @@ export default Backbone.View.extend({
|
||||
error: function(index, message) {
|
||||
self._eventError(index, message);
|
||||
},
|
||||
warning: function(index, message) {
|
||||
self._eventWarning(index, message);
|
||||
},
|
||||
complete: function() {
|
||||
self._eventComplete();
|
||||
},
|
||||
@@ -247,7 +250,7 @@ export default Backbone.View.extend({
|
||||
/** Progress */
|
||||
_eventProgress: function(index, percentage) {
|
||||
var it = this.collection.get(index);
|
||||
it.set("percentage", percentage);
|
||||
it.set({ percentage: percentage, status: "running", info: "" });
|
||||
this.ui_button.model.set("percentage", this._uploadPercentage(percentage, it.get("file_size")));
|
||||
},
|
||||
|
||||
@@ -263,6 +266,12 @@ export default Backbone.View.extend({
|
||||
Galaxy.currHistoryPanel.refreshContents();
|
||||
},
|
||||
|
||||
/** Warning */
|
||||
_eventWarning: function(index, message) {
|
||||
var it = this.collection.get(index);
|
||||
it.set({ status: "warning", info: message });
|
||||
},
|
||||
|
||||
/** Error */
|
||||
_eventError: function(index, message) {
|
||||
var it = this.collection.get(index);
|
||||
@@ -363,7 +372,10 @@ export default Backbone.View.extend({
|
||||
this._uploadFtp();
|
||||
|
||||
// queue remaining files
|
||||
this.uploadbox.start();
|
||||
this.uploadbox.start({
|
||||
id: Galaxy.user.id,
|
||||
chunk_upload_size: this.app.options.chunk_upload_size
|
||||
});
|
||||
this.render();
|
||||
}
|
||||
},
|
||||
@@ -430,7 +442,7 @@ export default Backbone.View.extend({
|
||||
if (list.length > 0) {
|
||||
$.uploadpost({
|
||||
data: this.app.toData(list),
|
||||
url: this.app.options.nginx_upload_path,
|
||||
url: this.app.options.upload_path,
|
||||
success: function(message) {
|
||||
_.each(list, model => {
|
||||
self._eventSuccess(model.id);
|
||||
@@ -452,37 +464,37 @@ export default Backbone.View.extend({
|
||||
|
||||
/** Template */
|
||||
_template: function() {
|
||||
return (
|
||||
'<div class="upload-view-default">' +
|
||||
'<div class="upload-top">' +
|
||||
'<h6 class="upload-top-info"/>' +
|
||||
"</div>" +
|
||||
'<div class="upload-box">' +
|
||||
'<div class="upload-helper"><i class="fa fa-files-o"/>Drop files here</div>' +
|
||||
'<table class="upload-table ui-table-striped" style="display: none;">' +
|
||||
"<thead>" +
|
||||
"<tr>" +
|
||||
"<th>Name</th>" +
|
||||
"<th>Size</th>" +
|
||||
"<th>Type</th>" +
|
||||
"<th>Genome</th>" +
|
||||
"<th>Settings</th>" +
|
||||
"<th>Status</th>" +
|
||||
"<th/>" +
|
||||
"</tr>" +
|
||||
"</thead>" +
|
||||
"<tbody/>" +
|
||||
"</table>" +
|
||||
"</div>" +
|
||||
'<div class="upload-footer">' +
|
||||
'<span class="upload-footer-title">Type (set all):</span>' +
|
||||
'<span class="upload-footer-extension"/>' +
|
||||
'<span class="upload-footer-extension-info upload-icon-button fa fa-search"/> ' +
|
||||
'<span class="upload-footer-title">Genome (set all):</span>' +
|
||||
'<span class="upload-footer-genome"/>' +
|
||||
"</div>" +
|
||||
'<div class="upload-buttons"/>' +
|
||||
"</div>"
|
||||
);
|
||||
return `<div class="upload-view-default">
|
||||
<div class="upload-top">
|
||||
<h6 class="upload-top-info"/>
|
||||
</div>
|
||||
<div class="upload-box">
|
||||
<div class="upload-helper">
|
||||
<i class="fa fa-files-o"/>Drop files here
|
||||
</div>
|
||||
<table class="upload-table ui-table-striped" style="display: none;">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Name</th>
|
||||
<th>Size</th>
|
||||
<th>Type</th>
|
||||
<th>Genome</th>
|
||||
<th>Settings</th>
|
||||
<th>Status</th>
|
||||
<th/>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody/>
|
||||
</table>
|
||||
</div>
|
||||
<div class="upload-footer">
|
||||
<span class="upload-footer-title">Type (set all):</span>
|
||||
<span class="upload-footer-extension"/>
|
||||
<span class="upload-footer-extension-info upload-icon-button fa fa-search"/>
|
||||
<span class="upload-footer-title">Genome (set all):</span>
|
||||
<span class="upload-footer-genome"/>
|
||||
</div>
|
||||
<div class="upload-buttons"/>
|
||||
</div>`;
|
||||
}
|
||||
});
|
||||
|
||||
@@ -9,7 +9,6 @@ import UploadViewComposite from "mvc/upload/composite/composite-view";
|
||||
import UploadViewCollection from "mvc/upload/collection/collection-view";
|
||||
export default Backbone.View.extend({
|
||||
options: {
|
||||
nginx_upload_path: "",
|
||||
ftp_upload_site: "n/a",
|
||||
default_genome: "?",
|
||||
default_extension: "auto",
|
||||
|
||||
@@ -0,0 +1,51 @@
|
||||
import Deferred from "utils/deferred";
|
||||
import Modal from "mvc/ui/ui-modal";
|
||||
import Ui from "mvc/ui/ui-misc";
|
||||
import Chart from "mvc/visualization/chart/components/model";
|
||||
import Editor from "mvc/visualization/chart/views/editor";
|
||||
import Viewer from "mvc/visualization/chart/views/viewer";
|
||||
import Menu from "mvc/visualization/chart/views/menu";
|
||||
import * as Backbone from "backbone";
|
||||
|
||||
/* global $ */
|
||||
/* global Galaxy */
|
||||
|
||||
export default Backbone.View.extend({
|
||||
initialize: function(options) {
|
||||
this.modal = (window.parent.Galaxy && window.parent.Galaxy.modal) || new Modal.View();
|
||||
this.setElement(
|
||||
$("<div/>")
|
||||
.addClass("charts-client")
|
||||
.append($("<div/>").addClass("charts-buttons"))
|
||||
.append($("<div/>").addClass("charts-center"))
|
||||
.append($("<div/>").addClass("charts-right"))
|
||||
);
|
||||
this.$center = this.$(".charts-center");
|
||||
this.$right = this.$(".charts-right");
|
||||
this.$buttons = this.$(".charts-buttons");
|
||||
this.chart = new Chart({}, options);
|
||||
this.chart.plugin = options.visualization_plugin;
|
||||
this.chart.plugin.specs = this.chart.plugin.specs || {};
|
||||
this.chart_load = options.chart_load;
|
||||
this.message = new Ui.Message();
|
||||
this.deferred = new Deferred();
|
||||
this.viewer = new Viewer(this);
|
||||
this.editor = new Editor(this);
|
||||
this.menu = new Menu(this);
|
||||
this.$center.append(this.viewer.$el);
|
||||
this.$right.append(this.message.$el).append(this.editor.$el);
|
||||
this.$buttons.append(this.menu.$el);
|
||||
$.ajax({
|
||||
url: `${Galaxy.root}api/datasets/${options.dataset_id}`
|
||||
})
|
||||
.done(dataset => {
|
||||
this.dataset = dataset;
|
||||
this.chart.load();
|
||||
this.chart.trigger("redraw");
|
||||
})
|
||||
.fail(response => {
|
||||
let message = response.responseJSON && response.responseJSON.err_msg;
|
||||
this.errormessage = message || "Import failed for unkown reason.";
|
||||
});
|
||||
}
|
||||
});
|
||||
@@ -0,0 +1,117 @@
|
||||
import * as Backbone from "backbone";
|
||||
import Utils from "utils/utils";
|
||||
import { Visualization } from "mvc/visualization/visualization-model";
|
||||
export default Backbone.Model.extend({
|
||||
defaults: {
|
||||
title: "",
|
||||
type: "",
|
||||
date: null,
|
||||
state: "",
|
||||
state_info: "",
|
||||
modified: false,
|
||||
dataset_id: "",
|
||||
dataset_id_job: ""
|
||||
},
|
||||
|
||||
initialize: function(options, viz_options) {
|
||||
this.groups = new Backbone.Collection();
|
||||
this.settings = new Backbone.Model();
|
||||
this.visualization_id = viz_options.visualization_id;
|
||||
this.visualization_name = viz_options.visualization_name;
|
||||
this.dataset_id = viz_options.dataset_id;
|
||||
this.chart_dict = viz_options.chart_dict;
|
||||
console.debug("model::initialize() - Initialized with configuration:");
|
||||
console.debug(viz_options);
|
||||
},
|
||||
|
||||
reset: function() {
|
||||
this.clear().set({
|
||||
title: "New Chart",
|
||||
dataset_id: this.dataset_id
|
||||
});
|
||||
this.settings.clear();
|
||||
this.groups.reset();
|
||||
this.groups.add({ id: Utils.uid() });
|
||||
},
|
||||
|
||||
state: function(value, info) {
|
||||
this.set({ state: value, state_info: info });
|
||||
this.trigger("set:state");
|
||||
console.debug("model::state() - " + info + " (" + value + ")");
|
||||
},
|
||||
|
||||
/** Create chart dictionary */
|
||||
serialize: function() {
|
||||
var d = {
|
||||
attributes: this.attributes,
|
||||
settings: this.settings.attributes,
|
||||
groups: []
|
||||
};
|
||||
this.groups.each(function(group) {
|
||||
d.groups.push(group.attributes);
|
||||
});
|
||||
return d;
|
||||
},
|
||||
|
||||
/** Pack and save nested chart model */
|
||||
save: function(options) {
|
||||
var self = this;
|
||||
options = options || {};
|
||||
this.chart_dict = this.serialize();
|
||||
var viz = new Visualization({
|
||||
id: this.visualization_id || undefined,
|
||||
type: this.visualization_name,
|
||||
title: this.get("title") || "",
|
||||
config: {
|
||||
dataset_id: this.dataset_id,
|
||||
chart_dict: this.chart_dict
|
||||
}
|
||||
});
|
||||
viz
|
||||
.save()
|
||||
.then(function(response) {
|
||||
if (response && response.id) {
|
||||
self.visualization_id = response.id;
|
||||
if (options.success) {
|
||||
options.success();
|
||||
}
|
||||
console.debug("model::save() - Received visualization id: " + response.id);
|
||||
} else {
|
||||
if (options.error) {
|
||||
options.error();
|
||||
}
|
||||
console.debug("model::save() - Unrecognized response. Saving may have failed.");
|
||||
}
|
||||
})
|
||||
.fail(function(response) {
|
||||
if (options.error) {
|
||||
options.error();
|
||||
}
|
||||
console.debug("model::save() - Saving failed.");
|
||||
});
|
||||
console.debug("model::save() - Saved with configuration:");
|
||||
console.debug(this.chart_last);
|
||||
},
|
||||
|
||||
/** Load nested models/collections from packed dictionary */
|
||||
load: function(chart_parsed) {
|
||||
var d = chart_parsed || this.chart_dict;
|
||||
console.debug("model::load() - Attempting to load with configuration:");
|
||||
console.debug(d);
|
||||
this.reset();
|
||||
if (d && d.attributes) {
|
||||
this.set(d.attributes);
|
||||
this.state("ok", "Loading saved visualization...");
|
||||
this.settings.set(d.settings);
|
||||
this.groups.reset();
|
||||
this.groups.add(d.groups);
|
||||
this.set("modified", false);
|
||||
this.trigger("load");
|
||||
console.debug("model::load() - Loading chart model " + d.attributes.type + ".");
|
||||
return true;
|
||||
}
|
||||
this.trigger("load");
|
||||
console.debug("model::load() - Visualization attributes unavailable.");
|
||||
return false;
|
||||
}
|
||||
});
|
||||
@@ -0,0 +1,186 @@
|
||||
/** This class enables users to export/download a chart as PNG, SVG or PDF. */
|
||||
/** PNG export */
|
||||
function createPNG(options) {
|
||||
if (options.$el.find("svg").length > 0) {
|
||||
_svg2png(options);
|
||||
} else {
|
||||
_canvas2png(options.$el.find(".charts-viewer-canvas"));
|
||||
}
|
||||
}
|
||||
|
||||
/** Convert canvas to png */
|
||||
function _canvas2png($canvas) {
|
||||
try {
|
||||
if ($canvas.width() !== 0 && $canvas.height() !== 0) {
|
||||
var newCanvas = document.createElement("canvas");
|
||||
newCanvas.width = $canvas.outerWidth(true);
|
||||
newCanvas.height = $canvas.outerHeight(true);
|
||||
var newContext = newCanvas.getContext("2d");
|
||||
newContext.save();
|
||||
newContext.fillStyle = "rgb(255,255,255)";
|
||||
newContext.fillRect(0, 0, newCanvas.width, newCanvas.height);
|
||||
newContext.restore();
|
||||
newContext.translate(0, 0);
|
||||
newContext.textAlign = "left";
|
||||
newContext.textBaseline = "top";
|
||||
function _toImage($el, x_offset, y_offset) {
|
||||
var tagname = $el.prop("tagName").toLowerCase();
|
||||
var p = $el.position();
|
||||
var left =
|
||||
x_offset +
|
||||
p.left +
|
||||
parseInt($el.css("marginLeft"), 10) +
|
||||
parseInt($el.css("borderLeftWidth"), 10) +
|
||||
parseInt($el.css("paddingLeft"), 10);
|
||||
var top =
|
||||
y_offset +
|
||||
p.top +
|
||||
parseInt($el.css("marginTop"), 10) +
|
||||
parseInt($el.css("borderTopWidth"), 10) +
|
||||
parseInt($el.css("paddingTop"), 10);
|
||||
var w = newCanvas.width;
|
||||
if (tagname == "div" || tagname == "span") {
|
||||
$el.children().each(function() {
|
||||
_toImage($(this), left, top);
|
||||
});
|
||||
} else if (tagname == "canvas") {
|
||||
newContext.drawImage($el[0], left, top);
|
||||
}
|
||||
}
|
||||
$canvas.children().each(function() {
|
||||
_toImage($(this), 0, 0);
|
||||
});
|
||||
var imgData = newCanvas.toDataURL("image/png");
|
||||
if (imgData) {
|
||||
window.location.href = imgData.replace("image/png", "image/octet-stream");
|
||||
}
|
||||
}
|
||||
} catch (err) {
|
||||
console.debug("FAILED - screenshot::_canvas2png() - " + err);
|
||||
if (options.error) {
|
||||
options.error("Please reduce your visualization to a single panel and try again.");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/** Convert svg to png */
|
||||
function _svg2png(options) {
|
||||
var scale = 5;
|
||||
var xml = toXML(options);
|
||||
var canvas = document.createElement("canvas");
|
||||
var context = canvas.getContext("2d");
|
||||
var source = new Image();
|
||||
var $container = $('<div style="display:none;"/>').append($(canvas));
|
||||
$("body").append($container);
|
||||
canvas.width = xml.width * scale;
|
||||
canvas.height = xml.height * scale;
|
||||
source.src = "data:image/svg+xml;base64," + btoa(xml.string);
|
||||
source.onload = function() {
|
||||
context.drawImage(source, 0, 0, canvas.width, canvas.height);
|
||||
window.location.href = canvas.toDataURL("image/png").replace("image/png", "image/octet-stream");
|
||||
$container.remove();
|
||||
};
|
||||
}
|
||||
|
||||
/** SVG export */
|
||||
function createSVG(options) {
|
||||
window.location.href = "data:none/none;base64," + btoa(toXML(options).string);
|
||||
}
|
||||
|
||||
/** PDF export */
|
||||
function createPDF(options) {
|
||||
var xml = toXML(options);
|
||||
var data = {
|
||||
filename: "visualization",
|
||||
type: "application/pdf",
|
||||
height: xml.height,
|
||||
width: xml.width,
|
||||
scale: 2,
|
||||
svg: xml.string
|
||||
};
|
||||
var $el = $("body");
|
||||
var form = $el.find("#viewer-form");
|
||||
if (form.length === 0) {
|
||||
form = $("<form>", {
|
||||
id: "viewer-form",
|
||||
method: "post",
|
||||
action: "http://export.highcharts.com/",
|
||||
display: "none"
|
||||
});
|
||||
$el.append(form);
|
||||
}
|
||||
form.empty();
|
||||
for (var name in data) {
|
||||
var input = $("<input/>", {
|
||||
type: "hidden",
|
||||
name: name,
|
||||
value: data[name]
|
||||
});
|
||||
form.append(input);
|
||||
}
|
||||
try {
|
||||
form.submit();
|
||||
} catch (err) {
|
||||
console.log(err);
|
||||
}
|
||||
}
|
||||
|
||||
/** XML export */
|
||||
function toXML(options) {
|
||||
var $svg = options.$el.find("svg");
|
||||
if ($svg.length == 0) {
|
||||
if (options.error) {
|
||||
options.error("No SVG found. This visualization type does not support SVG/PDF export.");
|
||||
return;
|
||||
}
|
||||
}
|
||||
var $el = options.$el;
|
||||
var nsvgs = $svg.length;
|
||||
var height = parseInt($svg.first().css("height"));
|
||||
var width = parseInt($svg.first().css("width"));
|
||||
var serializer = new XMLSerializer();
|
||||
var $composite = $("<svg/>").attr({
|
||||
version: "1.1",
|
||||
xmlns: "http://www.w3.org/2000/svg",
|
||||
width: width * nsvgs,
|
||||
height: height
|
||||
});
|
||||
var xmlString = "";
|
||||
var offsetX = 0;
|
||||
$svg.each(function() {
|
||||
var $svg = $(this).clone();
|
||||
_inline($svg);
|
||||
var $g = $('<g transform="translate(' + offsetX + ', 0)">').attr("xmlns", "http://www.w3.org/2000/svg");
|
||||
$g.append($svg.find("g").first());
|
||||
$composite.append($g);
|
||||
offsetX += width;
|
||||
});
|
||||
return {
|
||||
string: serializer.serializeToString($composite[0]),
|
||||
height: height,
|
||||
width: width
|
||||
};
|
||||
}
|
||||
|
||||
/** inlines CSS code */
|
||||
function _inline($target) {
|
||||
for (var sheet_id in document.styleSheets) {
|
||||
var sheet = document.styleSheets[sheet_id];
|
||||
var rules = sheet.cssRules;
|
||||
if (rules) {
|
||||
for (var idx = 0, len = rules.length; idx < len; idx++) {
|
||||
try {
|
||||
$target.find(rules[idx].selectorText).each(function(i, elem) {
|
||||
elem.style.cssText += rules[idx].style.cssText;
|
||||
});
|
||||
} catch (err) {}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export default {
|
||||
createPNG: createPNG,
|
||||
createSVG: createSVG,
|
||||
createPDF: createPDF
|
||||
};
|
||||
@@ -0,0 +1,174 @@
|
||||
/** This class handles, formats and caches datasets. */
|
||||
import Utils from "utils/utils";
|
||||
import * as _ from "underscore";
|
||||
|
||||
/* global Galaxy */
|
||||
|
||||
/** Assists in assigning the viewport panels */
|
||||
var requestPanels = function(options) {
|
||||
var process = options.process;
|
||||
var chart = options.chart;
|
||||
var render = options.render;
|
||||
var targets = options.targets;
|
||||
var dataset_id = options.dataset_id || options.chart.get("dataset_id");
|
||||
var dataset_groups = options.dataset_groups || options.chart.groups;
|
||||
request({
|
||||
chart: chart,
|
||||
dataset_id: dataset_id,
|
||||
dataset_groups: dataset_groups,
|
||||
success: function(result) {
|
||||
try {
|
||||
if (targets.length == result.length) {
|
||||
var valid = true;
|
||||
for (var group_index in result) {
|
||||
var group = result[group_index];
|
||||
if (!render(targets[group_index], [group])) {
|
||||
valid = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (valid) {
|
||||
chart.state("ok", "Multi-panel chart drawn.");
|
||||
}
|
||||
} else if (targets.length == 1) {
|
||||
if (render(targets[0], result)) {
|
||||
chart.state("ok", "Chart drawn.");
|
||||
}
|
||||
} else {
|
||||
chart.state("failed", "Invalid panel count.");
|
||||
}
|
||||
process.resolve();
|
||||
} catch (err) {
|
||||
console.debug("FAILED: tabular-utilities::panelHelper() - " + err);
|
||||
chart.state("failed", err);
|
||||
process.reject();
|
||||
}
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
/** Fills request dictionary with data from cache/response */
|
||||
var _cache = {};
|
||||
var request = function(options) {
|
||||
var groups = options.dataset_groups;
|
||||
var dataset_id = options.dataset_id;
|
||||
// identify columns needed to fulfill request
|
||||
var column_list = [];
|
||||
groups.each(function(group) {
|
||||
_.each(group.get("__data_columns"), function(column_def, column_name) {
|
||||
var column = group.get(column_name);
|
||||
var block_id = _block_id(dataset_id, column);
|
||||
if (
|
||||
column_list.indexOf(column) === -1 &&
|
||||
!_cache[block_id] &&
|
||||
column != "auto" &&
|
||||
column != "zero" &&
|
||||
column !== undefined
|
||||
) {
|
||||
column_list.push(column);
|
||||
}
|
||||
});
|
||||
});
|
||||
if (column_list.length === 0) {
|
||||
_fillFromCache(options);
|
||||
return;
|
||||
}
|
||||
// Fetch data columns into dataset object
|
||||
Utils.get({
|
||||
url: Galaxy.root + "api/datasets/" + dataset_id,
|
||||
data: {
|
||||
data_type: "raw_data",
|
||||
provider: "dataset-column",
|
||||
indeces: column_list.toString()
|
||||
},
|
||||
success: function(response) {
|
||||
var column_length = column_list.length;
|
||||
var results = new Array(column_length);
|
||||
for (let i = 0; i < results.length; i++) {
|
||||
results[i] = [];
|
||||
}
|
||||
for (let i in response.data) {
|
||||
var row = response.data[i];
|
||||
for (let j in row) {
|
||||
var v = row[j];
|
||||
if (v !== undefined && v != 2147483647 && j < column_length) {
|
||||
results[j].push(v);
|
||||
}
|
||||
}
|
||||
}
|
||||
console.debug("tabular-datasets::_fetch() - Fetching complete.");
|
||||
for (let i in results) {
|
||||
var column = column_list[i];
|
||||
var block_id = _block_id(dataset_id, column);
|
||||
_cache[block_id] = results[i];
|
||||
}
|
||||
_fillFromCache(options);
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
/** Fill data from cache */
|
||||
var _fillFromCache = function(options) {
|
||||
var groups = options.dataset_groups;
|
||||
var dataset_id = options.dataset_id;
|
||||
console.debug("tabular-datasets::_fillFromCache() - Filling request from cache.");
|
||||
var limit = 0;
|
||||
groups.each(function(group) {
|
||||
_.each(group.get("__data_columns"), function(column_def, column_name) {
|
||||
var column = group.get(column_name);
|
||||
var block_id = _block_id(dataset_id, column);
|
||||
var column_data = _cache[block_id];
|
||||
if (column_data) {
|
||||
limit = Math.max(limit, column_data.length);
|
||||
}
|
||||
});
|
||||
});
|
||||
if (limit === 0) {
|
||||
console.debug("tabular-datasets::_fillFromCache() - No data available.");
|
||||
}
|
||||
var results = [];
|
||||
groups.each(function(group, group_index) {
|
||||
var dict = Utils.merge({ key: group_index + ":" + group.get("key"), values: [] }, group.attributes);
|
||||
for (let j = 0; j < limit; j++) {
|
||||
dict.values[j] = { x: parseInt(j) };
|
||||
}
|
||||
results.push(dict);
|
||||
});
|
||||
groups.each(function(group, group_index) {
|
||||
var values = results[group_index].values;
|
||||
_.each(group.get("__data_columns"), function(column_def, column_name) {
|
||||
var column = group.get(column_name);
|
||||
switch (column) {
|
||||
case "auto":
|
||||
for (let j = 0; j < limit; j++) {
|
||||
values[j][column_name] = parseInt(j);
|
||||
}
|
||||
break;
|
||||
case "zero":
|
||||
for (let j = 0; j < limit; j++) {
|
||||
values[j][column_name] = 0;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
var block_id = _block_id(dataset_id, column);
|
||||
var column_data = _cache[block_id];
|
||||
for (let j = 0; j < limit; j++) {
|
||||
var value = values[j];
|
||||
var v = column_data[j];
|
||||
if (isNaN(v) && !column_def.is_label) {
|
||||
v = 0;
|
||||
}
|
||||
value[column_name] = v;
|
||||
}
|
||||
}
|
||||
});
|
||||
});
|
||||
options.success(results);
|
||||
};
|
||||
|
||||
/** Get block id */
|
||||
var _block_id = function(dataset_id, column) {
|
||||
return dataset_id + "_" + "_" + column;
|
||||
};
|
||||
|
||||
export default { request: request, requestPanels: requestPanels };
|
||||
@@ -0,0 +1,159 @@
|
||||
/** This class handles job submissions to the Galaxy API. */
|
||||
import * as _ from "underscore";
|
||||
import Utils from "utils/utils";
|
||||
|
||||
/* global Galaxy */
|
||||
|
||||
/** Time to wait before refreshing to check if job has completed */
|
||||
const WAITTIME = 1000;
|
||||
|
||||
/** build job dictionary */
|
||||
var requestCharts = function(chart, module) {
|
||||
var settings_string = "";
|
||||
var columns_string = "";
|
||||
var group_index = 0;
|
||||
for (var key in chart.settings.attributes) {
|
||||
var settings_value = chart.settings.get(key);
|
||||
_.each([[" ", " "], [",", ","], [":", ":"]], function(pair) {
|
||||
settings_value = settings_value.replace(new RegExp(pair[0], "g"), pair[1]);
|
||||
});
|
||||
settings_string += key + ":" + settings_value + ", ";
|
||||
}
|
||||
settings_string = settings_string.substring(0, settings_string.length - 2);
|
||||
chart.groups.each(function(group) {
|
||||
group_index++;
|
||||
_.each(group.get("__data_columns"), function(data_columns, name) {
|
||||
columns_string += name + "_" + group_index + ":" + (parseInt(group.get(name)) + 1) + ", ";
|
||||
});
|
||||
});
|
||||
columns_string = columns_string.substring(0, columns_string.length - 2);
|
||||
return {
|
||||
tool_id: "charts",
|
||||
inputs: {
|
||||
input: {
|
||||
id: chart.get("dataset_id"),
|
||||
src: "hda"
|
||||
},
|
||||
module: module,
|
||||
columns: columns_string,
|
||||
settings: settings_string
|
||||
}
|
||||
};
|
||||
};
|
||||
|
||||
/** Submit job request to charts tool */
|
||||
var request = function(chart, parameters, success, error) {
|
||||
chart.state("wait", "Requesting job results...");
|
||||
if (chart.get("modified")) {
|
||||
cleanup(chart);
|
||||
chart.set("modified", false);
|
||||
}
|
||||
var dataset_id_job = chart.get("dataset_id_job");
|
||||
if (dataset_id_job !== "") {
|
||||
wait(chart, success, error);
|
||||
} else {
|
||||
chart.state("wait", "Sending job request...");
|
||||
Utils.request({
|
||||
type: "POST",
|
||||
url: Galaxy.root + "api/tools",
|
||||
data: parameters,
|
||||
success: function(response) {
|
||||
if (!response.outputs || response.outputs.length === 0) {
|
||||
chart.state("failed", "Job submission failed. No response.");
|
||||
if (error) {
|
||||
error();
|
||||
}
|
||||
} else {
|
||||
refreshHdas();
|
||||
var job = response.outputs[0];
|
||||
chart.state(
|
||||
"wait",
|
||||
"Your job has been queued. You may close the browser window. The job will run in the background."
|
||||
);
|
||||
chart.set("dataset_id_job", job.id);
|
||||
chart.save();
|
||||
wait(chart, success, error);
|
||||
}
|
||||
},
|
||||
error: function(response) {
|
||||
var message = "";
|
||||
if (response && response.message && response.message.data && response.message.data.input) {
|
||||
message = response.message.data.input + ".";
|
||||
}
|
||||
chart.state(
|
||||
"failed",
|
||||
"This visualization requires the '" +
|
||||
parameters.tool_id +
|
||||
"' tool. Please make sure it is installed. " +
|
||||
message
|
||||
);
|
||||
if (error) {
|
||||
error();
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
/* Remove previous data when re-running jobs */
|
||||
var cleanup = function(chart) {
|
||||
var previous = chart.get("dataset_id_job");
|
||||
if (previous !== "") {
|
||||
Utils.request({
|
||||
type: "PUT",
|
||||
url: Galaxy.root + "api/histories/none/contents/" + previous,
|
||||
data: { deleted: true },
|
||||
success: function() {
|
||||
refreshHdas();
|
||||
}
|
||||
});
|
||||
chart.set("dataset_id_job", "");
|
||||
}
|
||||
};
|
||||
|
||||
/** Request job details */
|
||||
var wait = function(chart, success, error) {
|
||||
Utils.request({
|
||||
type: "GET",
|
||||
url: Galaxy.root + "api/datasets/" + chart.get("dataset_id_job"),
|
||||
data: {},
|
||||
success: function(dataset) {
|
||||
var ready = false;
|
||||
switch (dataset.state) {
|
||||
case "ok":
|
||||
chart.state("wait", "Job completed successfully...");
|
||||
if (success) {
|
||||
success(dataset);
|
||||
}
|
||||
ready = true;
|
||||
break;
|
||||
case "error":
|
||||
chart.state("failed", "Job has failed. Please check the history for details.");
|
||||
if (error) {
|
||||
error(dataset);
|
||||
}
|
||||
ready = true;
|
||||
break;
|
||||
case "running":
|
||||
chart.state(
|
||||
"wait",
|
||||
"Your job is running. You may close the browser window. The job will continue in the background."
|
||||
);
|
||||
}
|
||||
if (!ready) {
|
||||
window.setTimeout(function() {
|
||||
wait(chart, success, error);
|
||||
}, WAITTIME);
|
||||
}
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
/** Refresh history panel */
|
||||
var refreshHdas = function() {
|
||||
if (Galaxy && Galaxy.currHistoryPanel) {
|
||||
Galaxy.currHistoryPanel.refreshContents();
|
||||
}
|
||||
};
|
||||
|
||||
export default { request: request, requestCharts: requestCharts };
|
||||
@@ -0,0 +1,233 @@
|
||||
import * as _ from "underscore";
|
||||
|
||||
// TODO: This d3 must(?) just be global because various charts use different
|
||||
// versions, and we want to use whichever one that is. I think. I hope.
|
||||
/* global d3 */
|
||||
|
||||
/** Get domain boundaries value */
|
||||
function getDomains(groups, keys) {
|
||||
function _apply(operator, key) {
|
||||
let value;
|
||||
for (let group_index in groups) {
|
||||
let value_sub = d3[operator](groups[group_index].values, function(d) {
|
||||
return d[key];
|
||||
});
|
||||
value = value === undefined ? value_sub : Math[operator](value, value_sub);
|
||||
}
|
||||
return value;
|
||||
}
|
||||
var result = {};
|
||||
for (var index in keys) {
|
||||
var key = keys[index];
|
||||
result[key] = {
|
||||
min: _apply("min", key),
|
||||
max: _apply("max", key)
|
||||
};
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
/** Default series maker */
|
||||
function makeSeries(groups, keys) {
|
||||
var plot_data = [];
|
||||
for (var group_index in groups) {
|
||||
var group = groups[group_index];
|
||||
var data = [];
|
||||
for (var value_index in group.values) {
|
||||
var point = [];
|
||||
if (keys) {
|
||||
for (var key_index in keys) {
|
||||
let column_index = keys[key_index];
|
||||
point.push(group.values[value_index][column_index]);
|
||||
}
|
||||
} else {
|
||||
for (let column_index in group.values[value_index]) {
|
||||
point.push(group.values[value_index][column_index]);
|
||||
}
|
||||
}
|
||||
data.push(point);
|
||||
}
|
||||
plot_data.push(data);
|
||||
}
|
||||
return plot_data;
|
||||
}
|
||||
|
||||
/** Default category maker */
|
||||
function makeCategories(groups, column_keys) {
|
||||
var array = {};
|
||||
var data_columns = groups[0].__data_columns;
|
||||
_.each(column_keys, function(key) {
|
||||
if (data_columns[key].is_label) {
|
||||
array[key] = [];
|
||||
}
|
||||
});
|
||||
if (groups && groups[0]) {
|
||||
_.each(groups[0].values, function(value_dict) {
|
||||
for (var key in array) {
|
||||
array[key].push(String(value_dict[key]));
|
||||
}
|
||||
});
|
||||
}
|
||||
mapCategories(array, groups);
|
||||
return { array: array };
|
||||
}
|
||||
|
||||
/** Apply default mapping index all values contained in label columns (for all groups) */
|
||||
function mapCategories(array, groups) {
|
||||
_.each(groups, function(group) {
|
||||
_.each(group.values, function(value_dict, i) {
|
||||
for (var key in array) {
|
||||
value_dict[key] = parseInt(i);
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
/** Category make for unique category labels */
|
||||
function makeUniqueCategories(groups, with_index) {
|
||||
var categories = {};
|
||||
var array = {};
|
||||
var counter = {};
|
||||
var data_columns = groups[0].__data_columns;
|
||||
_.each(data_columns, function(column_def, key) {
|
||||
if (column_def.is_label) {
|
||||
categories[key] = {};
|
||||
array[key] = [];
|
||||
counter[key] = 0;
|
||||
}
|
||||
});
|
||||
// index all values contained in label columns (for all groups)
|
||||
for (var i in groups) {
|
||||
var group = groups[i];
|
||||
for (var j in group.values) {
|
||||
var value_dict = group.values[j];
|
||||
for (var key in categories) {
|
||||
var value = String(value_dict[key]);
|
||||
if (categories[key][value] === undefined) {
|
||||
categories[key][value] = counter[key];
|
||||
array[key].push(with_index ? [counter[key], value] : value);
|
||||
counter[key]++;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
// convert group values into category indeces
|
||||
for (let i in groups) {
|
||||
let group = groups[i];
|
||||
for (let j in group.values) {
|
||||
let value_dict = group.values[j];
|
||||
for (let key in categories) {
|
||||
let value = String(value_dict[key]);
|
||||
value_dict[key] = categories[key][value];
|
||||
}
|
||||
}
|
||||
}
|
||||
return {
|
||||
categories: categories,
|
||||
array: array,
|
||||
counter: counter
|
||||
};
|
||||
}
|
||||
|
||||
/** Make axis */
|
||||
function makeTickFormat(options) {
|
||||
var type = options.type;
|
||||
var precision = options.precision;
|
||||
var categories = options.categories;
|
||||
var formatter = options.formatter;
|
||||
if (type == "hide") {
|
||||
formatter(function() {
|
||||
return "";
|
||||
});
|
||||
} else if (type == "auto") {
|
||||
if (categories) {
|
||||
formatter(function(value) {
|
||||
return categories[value] || "";
|
||||
});
|
||||
}
|
||||
} else {
|
||||
var d3format = function(d) {
|
||||
switch (type) {
|
||||
case "s":
|
||||
var prefix = d3.formatPrefix(d);
|
||||
return prefix.scale(d).toFixed() + prefix.symbol;
|
||||
default:
|
||||
return d3.format("." + precision + type)(d);
|
||||
}
|
||||
};
|
||||
if (categories) {
|
||||
formatter(function(value) {
|
||||
var label = categories[value];
|
||||
if (label) {
|
||||
if (isNaN(label)) {
|
||||
return label;
|
||||
} else {
|
||||
try {
|
||||
return d3format(label);
|
||||
} catch (err) {
|
||||
return label;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
return "";
|
||||
}
|
||||
});
|
||||
} else {
|
||||
formatter(function(value) {
|
||||
return d3format(value);
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/** Add zoom handler */
|
||||
function addZoom(options) {
|
||||
var scaleExtent = 100;
|
||||
var yAxis = options.yAxis;
|
||||
var xAxis = options.xAxis;
|
||||
var xDomain = options.xDomain || xAxis.scale().domain;
|
||||
var yDomain = options.yDomain || yAxis.scale().domain;
|
||||
var redraw = options.redraw;
|
||||
var svg = options.svg;
|
||||
var xScale = xAxis.scale();
|
||||
var yScale = yAxis.scale();
|
||||
var x_boundary = xScale.domain().slice();
|
||||
var y_boundary = yScale.domain().slice();
|
||||
var d3zoom = d3.behavior.zoom();
|
||||
xScale.nice();
|
||||
yScale.nice();
|
||||
function fixDomain(domain, boundary) {
|
||||
domain[0] = Math.min(Math.max(domain[0], boundary[0]), boundary[1] - boundary[1] / scaleExtent);
|
||||
domain[1] = Math.max(boundary[0] + boundary[1] / scaleExtent, Math.min(domain[1], boundary[1]));
|
||||
return domain;
|
||||
}
|
||||
function zoomed() {
|
||||
yDomain(fixDomain(yScale.domain(), y_boundary));
|
||||
xDomain(fixDomain(xScale.domain(), x_boundary));
|
||||
redraw();
|
||||
}
|
||||
function unzoomed() {
|
||||
xDomain(x_boundary);
|
||||
yDomain(y_boundary);
|
||||
redraw();
|
||||
d3zoom.scale(1);
|
||||
d3zoom.translate([0, 0]);
|
||||
}
|
||||
d3zoom
|
||||
.x(xScale)
|
||||
.y(yScale)
|
||||
.scaleExtent([1, scaleExtent])
|
||||
.on("zoom", zoomed);
|
||||
svg.call(d3zoom).on("dblclick.zoom", unzoomed);
|
||||
return d3zoom;
|
||||
}
|
||||
|
||||
export default {
|
||||
makeCategories: makeCategories,
|
||||
makeUniqueCategories: makeUniqueCategories,
|
||||
makeSeries: makeSeries,
|
||||
getDomains: getDomains,
|
||||
mapCategories: mapCategories,
|
||||
makeTickFormat: makeTickFormat,
|
||||
addZoom: addZoom
|
||||
};
|
||||
@@ -0,0 +1,37 @@
|
||||
/** Renders the visualization header with title, logo and description. */
|
||||
import Utils from "utils/utils";
|
||||
export default Backbone.View.extend({
|
||||
initialize: function(app) {
|
||||
this.plugin = app.chart.plugin;
|
||||
this.setElement(this._template());
|
||||
this.$title = this.$(".charts-description-title");
|
||||
this.$image = this.$(".charts-description-image");
|
||||
this.$text = this.$(".charts-description-text");
|
||||
this.render();
|
||||
},
|
||||
render: function() {
|
||||
if (this.plugin.logo) {
|
||||
this.$image.attr("src", Galaxy.root + this.plugin.logo);
|
||||
this.$title.html(this.plugin.html || "Unavailable");
|
||||
this.$text.html(Utils.linkify(this.plugin.description || ""));
|
||||
this.$el.show();
|
||||
} else {
|
||||
this.$el.hide();
|
||||
}
|
||||
},
|
||||
_template: function() {
|
||||
return `<div class="charts-description">
|
||||
<table>
|
||||
<tr>
|
||||
<td class="charts-description-image-td">
|
||||
<img class="charts-description-image"/>
|
||||
</td>
|
||||
<td>
|
||||
<div class="charts-description-title ui-form-info"/>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
<div class="charts-description-text ui-form-info"/>
|
||||
</div>`;
|
||||
}
|
||||
});
|
||||
@@ -0,0 +1,52 @@
|
||||
/**
|
||||
* The charts editor holds the tabs for selecting chart types, chart configuration
|
||||
* and data group selections.
|
||||
*/
|
||||
import Ui from "mvc/ui/ui-misc";
|
||||
import Utils from "utils/utils";
|
||||
import Tabs from "mvc/ui/ui-tabs";
|
||||
import Groups from "mvc/visualization/chart/views/groups";
|
||||
import Settings from "mvc/visualization/chart/views/settings";
|
||||
import Description from "mvc/visualization/chart/views/description";
|
||||
|
||||
export default Backbone.View.extend({
|
||||
initialize: function(app, options) {
|
||||
this.app = app;
|
||||
this.chart = this.app.chart;
|
||||
this.description = new Description(this.app);
|
||||
this.title = new Ui.Input({
|
||||
onchange: () => {
|
||||
this.chart.set("title", this.title.value());
|
||||
}
|
||||
});
|
||||
this.tabs = new Tabs.View({});
|
||||
this.tabs.add({
|
||||
id: "settings",
|
||||
icon: "fa fa-gear",
|
||||
tooltip: "Change settings.",
|
||||
$el: $("<div/>")
|
||||
.append(new Ui.Label({ title: "Provide a title:" }).$el)
|
||||
.append(this.title.$el)
|
||||
.append(
|
||||
$("<div/>")
|
||||
.addClass("ui-form-info ui-margin-bottom")
|
||||
.html("This title will appear in the list of 'Saved Visualizations'.")
|
||||
)
|
||||
.append(new Settings(this.app).$el)
|
||||
});
|
||||
if (this.chart.plugin.groups) {
|
||||
this.tabs.add({
|
||||
id: "groups",
|
||||
icon: "fa-database",
|
||||
tooltip: "Select data.",
|
||||
$el: new Groups(this.app).$el
|
||||
});
|
||||
}
|
||||
this.setElement("<div class='charts-editor'/>");
|
||||
this.$el.append(this.description.$el);
|
||||
this.$el.append(this.tabs.$el);
|
||||
this.listenTo(this.chart, "load", () => {
|
||||
this.title.value(this.chart.get("title"));
|
||||
});
|
||||
}
|
||||
});
|
||||
@@ -0,0 +1,122 @@
|
||||
/** This class renders the chart data selection form with repeats. */
|
||||
import Utils from "utils/utils";
|
||||
import Ui from "mvc/ui/ui-misc";
|
||||
import Form from "mvc/form/form-view";
|
||||
import Repeat from "mvc/form/form-repeat";
|
||||
import FormData from "mvc/form/form-data";
|
||||
var GroupView = Backbone.View.extend({
|
||||
initialize: function(app, options) {
|
||||
var self = this;
|
||||
this.deferred = app.deferred;
|
||||
this.chart = app.chart;
|
||||
this.group = options.group;
|
||||
this.setElement($("<div/>"));
|
||||
this.listenTo(this.chart, "change:dataset_id", function() {
|
||||
self.render();
|
||||
});
|
||||
this.render();
|
||||
},
|
||||
render: function() {
|
||||
var self = this;
|
||||
var inputs = Utils.clone(this.chart.plugin.groups) || [];
|
||||
var dataset_id = this.chart.get("dataset_id");
|
||||
if (dataset_id) {
|
||||
this.chart.state("wait", "Loading metadata...");
|
||||
this.deferred.execute(function(process) {
|
||||
Utils.get({
|
||||
url: Galaxy.root + "api/datasets/" + dataset_id,
|
||||
cache: true,
|
||||
success: function(dataset) {
|
||||
var data_columns = {};
|
||||
FormData.visitInputs(inputs, function(input, prefixed) {
|
||||
if (input.type == "data_column") {
|
||||
data_columns[prefixed] = Utils.clone(input);
|
||||
var columns = [];
|
||||
if (input.is_auto) {
|
||||
columns.push({ label: "Column: Row Number", value: "auto" });
|
||||
}
|
||||
if (input.is_zero) {
|
||||
columns.push({ label: "Column: None", value: "zero" });
|
||||
}
|
||||
var meta = dataset.metadata_column_types;
|
||||
for (var key in meta) {
|
||||
if (
|
||||
(["int", "float"].indexOf(meta[key]) != -1 && input.is_numeric) ||
|
||||
input.is_label
|
||||
) {
|
||||
columns.push({ label: "Column: " + (parseInt(key) + 1), value: key });
|
||||
}
|
||||
}
|
||||
input.data = columns;
|
||||
}
|
||||
var model_value = self.group.get(prefixed);
|
||||
if (model_value !== undefined && !input.hidden) {
|
||||
input.value = model_value;
|
||||
}
|
||||
});
|
||||
inputs.push({
|
||||
name: "__data_columns",
|
||||
type: "hidden",
|
||||
hidden: true,
|
||||
value: data_columns
|
||||
});
|
||||
self.chart.state("ok", "Metadata initialized...");
|
||||
self.form = new Form({
|
||||
inputs: inputs,
|
||||
cls: "ui-portlet-plain",
|
||||
onchange: function() {
|
||||
self.group.set(self.form.data.create());
|
||||
self.chart.trigger("redraw");
|
||||
}
|
||||
});
|
||||
self.group.set(self.form.data.create());
|
||||
self.$el.empty().append(self.form.$el);
|
||||
process.resolve();
|
||||
self.chart.trigger("redraw");
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
export default Backbone.View.extend({
|
||||
initialize: function(app) {
|
||||
var self = this;
|
||||
this.app = app;
|
||||
this.chart = app.chart;
|
||||
this.repeat = new Repeat.View({
|
||||
title: "Data series",
|
||||
title_new: "Data series",
|
||||
min: 1,
|
||||
onnew: function() {
|
||||
self.chart.groups.add({ id: Utils.uid() });
|
||||
}
|
||||
});
|
||||
this.setElement($("<div/>").append(this.repeat.$el));
|
||||
this.listenTo(this.chart.groups, "remove", function(group) {
|
||||
self.repeat.del(group.id);
|
||||
self.chart.trigger("redraw");
|
||||
});
|
||||
this.listenTo(this.chart.groups, "reset", function() {
|
||||
self.repeat.delAll();
|
||||
});
|
||||
this.listenTo(this.chart.groups, "add", function(group) {
|
||||
self.repeat.add({
|
||||
id: group.id,
|
||||
cls: "ui-portlet-panel",
|
||||
$el: new GroupView(self.app, { group: group }).$el,
|
||||
ondel: function() {
|
||||
self.chart.groups.remove(group);
|
||||
}
|
||||
});
|
||||
});
|
||||
},
|
||||
render: function() {
|
||||
if (_.size(this.chart.plugin.groups) > 0) {
|
||||
this.repeat.$el.show();
|
||||
} else {
|
||||
this.repeat.$el.hide();
|
||||
}
|
||||
}
|
||||
});
|
||||
@@ -0,0 +1,149 @@
|
||||
/** This class renders the chart menu options. */
|
||||
import Ui from "mvc/ui/ui-misc";
|
||||
import Screenshot from "mvc/visualization/chart/components/screenshot";
|
||||
import Utils from "utils/utils";
|
||||
|
||||
export default Backbone.View.extend({
|
||||
initialize: function(app) {
|
||||
this.app = app;
|
||||
this.model = new Backbone.Model({ visible: true });
|
||||
this.export_button = new Ui.ButtonMenu({
|
||||
icon: "fa-camera",
|
||||
tooltip: "Export"
|
||||
});
|
||||
this.export_button.addMenu({
|
||||
key: "png",
|
||||
title: "Save as PNG",
|
||||
icon: "fa-file",
|
||||
onclick: () => {
|
||||
this._wait(app.chart, () => {
|
||||
Screenshot.createPNG({
|
||||
$el: app.viewer.$el,
|
||||
title: app.chart.get("title"),
|
||||
error: err => {
|
||||
app.message.update({ message: err, status: "danger" });
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
});
|
||||
this.export_button.addMenu({
|
||||
key: "svg",
|
||||
title: "Save as SVG",
|
||||
icon: "fa-file-text-o",
|
||||
onclick: () => {
|
||||
this._wait(app.chart, () => {
|
||||
Screenshot.createSVG({
|
||||
$el: app.viewer.$el,
|
||||
title: app.chart.get("title"),
|
||||
error: err => {
|
||||
app.message.update({ message: err, status: "danger" });
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
});
|
||||
this.export_button.addMenu({
|
||||
key: "pdf",
|
||||
title: "Save as PDF",
|
||||
icon: "fa-file-o",
|
||||
onclick: () => {
|
||||
app.modal.show({
|
||||
title: "Send visualization data for PDF creation",
|
||||
body:
|
||||
"Galaxy does not provide integrated PDF export scripts. You may click 'Continue' to create the PDF by using a 3rd party service (https://export.highcharts.com).",
|
||||
buttons: {
|
||||
Cancel: () => {
|
||||
app.modal.hide();
|
||||
},
|
||||
Continue: () => {
|
||||
app.modal.hide();
|
||||
this._wait(app.chart, () => {
|
||||
Screenshot.createPDF({
|
||||
$el: app.viewer.$el,
|
||||
title: app.chart.get("title"),
|
||||
error: err => {
|
||||
app.message.update({ message: err, status: "danger" });
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
this.buttons = [
|
||||
new Ui.ButtonIcon({
|
||||
icon: "fa-angle-double-left",
|
||||
tooltip: "Show",
|
||||
cls: "ui-button-icon charts-fullscreen-button",
|
||||
onclick: () => {
|
||||
this.model.set("visible", true);
|
||||
window.dispatchEvent(new Event("resize"));
|
||||
}
|
||||
}),
|
||||
new Ui.ButtonIcon({
|
||||
icon: "fa-angle-double-right",
|
||||
tooltip: "Hide",
|
||||
onclick: () => {
|
||||
this.model.set("visible", false);
|
||||
window.dispatchEvent(new Event("resize"));
|
||||
}
|
||||
}),
|
||||
this.export_button,
|
||||
new Ui.ButtonIcon({
|
||||
icon: "fa-save",
|
||||
tooltip: "Save",
|
||||
onclick: () => {
|
||||
if (app.chart.get("title")) {
|
||||
app.message.update({
|
||||
message: `Saving '${app.chart.get(
|
||||
"title"
|
||||
)}'. It will appear in the list of 'Saved Visualizations'.`,
|
||||
status: "success"
|
||||
});
|
||||
app.chart.save({
|
||||
error: () => {
|
||||
app.message.update({
|
||||
message: "Could not save visualization.",
|
||||
status: "danger"
|
||||
});
|
||||
}
|
||||
});
|
||||
} else {
|
||||
app.message.update({
|
||||
message: "Please provide a name.",
|
||||
status: "danger"
|
||||
});
|
||||
}
|
||||
}
|
||||
})
|
||||
];
|
||||
this.setElement("<div/>");
|
||||
for (let b of this.buttons) {
|
||||
this.$el.append(b.$el);
|
||||
}
|
||||
this.listenTo(this.model, "change", () => this.render());
|
||||
this.render();
|
||||
},
|
||||
|
||||
render: function() {
|
||||
var visible = this.model.get("visible");
|
||||
this.app.$el[visible ? "removeClass" : "addClass"]("charts-fullscreen");
|
||||
this.export_button.model.set("visible", visible);
|
||||
var exports = this.app.chart.plugin.specs.exports || [];
|
||||
this.export_button.collection.each(model => {
|
||||
model.set("visible", exports.indexOf(model.get("key")) !== -1);
|
||||
});
|
||||
},
|
||||
|
||||
_wait: function(chart, callback) {
|
||||
if (this.app.deferred.ready()) {
|
||||
callback();
|
||||
} else {
|
||||
this.app.message.update({
|
||||
message: "Your visualization is currently being processed. Please wait and try again."
|
||||
});
|
||||
}
|
||||
}
|
||||
});
|
||||
@@ -0,0 +1,49 @@
|
||||
/** This class renders the chart configuration form. */
|
||||
import Utils from "utils/utils";
|
||||
import Form from "mvc/form/form-view";
|
||||
import FormData from "mvc/form/form-data";
|
||||
|
||||
export default Backbone.View.extend({
|
||||
initialize: function(app, options) {
|
||||
var self = this;
|
||||
this.chart = app.chart;
|
||||
this.setElement("<div/>");
|
||||
this.listenTo(this.chart, "load", function() {
|
||||
self.render();
|
||||
});
|
||||
},
|
||||
render: function() {
|
||||
var self = this;
|
||||
var inputs = Utils.clone(this.chart.plugin.settings) || [];
|
||||
var panel_option = this.chart.plugin.specs.use_panels;
|
||||
if (panel_option == "optional") {
|
||||
inputs.push({
|
||||
name: "__use_panels",
|
||||
type: "boolean",
|
||||
label: "Use multi-panels",
|
||||
help: "Would you like to separate your data into individual panels?"
|
||||
});
|
||||
} else {
|
||||
this.chart.settings.set("__use_panels", panel_option == "yes" ? "true" : "false");
|
||||
}
|
||||
this.$el.empty().addClass("ui-margin-bottom");
|
||||
if (_.size(inputs) > 0) {
|
||||
FormData.visitInputs(inputs, function(input, name) {
|
||||
var model_value = self.chart.settings.get(name);
|
||||
if (model_value !== undefined && !input.hidden) {
|
||||
input.value = model_value;
|
||||
}
|
||||
});
|
||||
this.form = new Form({
|
||||
inputs: inputs,
|
||||
cls: "ui-portlet-plain",
|
||||
onchange: function() {
|
||||
self.chart.settings.set(self.form.data.create());
|
||||
self.chart.trigger("redraw");
|
||||
}
|
||||
});
|
||||
this.chart.settings.set(this.form.data.create());
|
||||
this.$el.append(this.form.$el);
|
||||
}
|
||||
}
|
||||
});
|
||||
@@ -0,0 +1,92 @@
|
||||
/**
|
||||
* The viewer creates and manages the dom elements used by the visualization plugins to draw the chart.
|
||||
* This is the last class of the charts core classes before handing control over to the visualization plugins.
|
||||
*/
|
||||
import Utils from "utils/utils";
|
||||
|
||||
export default Backbone.View.extend({
|
||||
initialize: function(app, options) {
|
||||
var self = this;
|
||||
this.app = app;
|
||||
this.chart = this.app.chart;
|
||||
this.options = options;
|
||||
this.setElement(
|
||||
$("<div/>")
|
||||
.addClass("charts-viewer")
|
||||
.append(
|
||||
$("<div/>")
|
||||
.addClass("info")
|
||||
.append($("<span/>").addClass("icon"))
|
||||
.append($("<span/>").addClass("text"))
|
||||
)
|
||||
);
|
||||
this.$info = this.$(".info");
|
||||
this.$icon = this.$(".icon");
|
||||
this.$text = this.$(".text");
|
||||
this._fullscreen(this.$el, 20);
|
||||
this._createContainer("div");
|
||||
this.chart.on("redraw", function() {
|
||||
self.app.deferred.execute(function(process) {
|
||||
console.debug("viewer:redraw() - Redrawing...");
|
||||
self._draw(process, self.chart);
|
||||
});
|
||||
});
|
||||
this.chart.on("set:state", function() {
|
||||
var $container = self.$(".charts-viewer-container");
|
||||
var $info = self.$info;
|
||||
var $icon = self.$icon;
|
||||
var $text = self.$text;
|
||||
$icon.removeClass();
|
||||
$info.show();
|
||||
$text.html(self.chart.get("state_info"));
|
||||
var state = self.chart.get("state");
|
||||
switch (state) {
|
||||
case "ok":
|
||||
$info.hide();
|
||||
$container.show();
|
||||
break;
|
||||
case "failed":
|
||||
$icon.addClass("icon fa fa-warning");
|
||||
$container.hide();
|
||||
break;
|
||||
default:
|
||||
$icon.addClass("icon fa fa-spinner fa-spin");
|
||||
$container.show();
|
||||
}
|
||||
});
|
||||
},
|
||||
|
||||
/** Force resize to fullscreen */
|
||||
_fullscreen: function($el, margin) {
|
||||
$el.css("height", $(window).height() - margin);
|
||||
$(window).resize(function() {
|
||||
$el.css("height", $(window).height() - margin);
|
||||
});
|
||||
},
|
||||
|
||||
/** A chart may contain multiple sub charts/containers which are created here */
|
||||
_createContainer: function(tag, n) {
|
||||
tag = tag || "div";
|
||||
n = n || 1;
|
||||
this.$(".charts-viewer-container").remove();
|
||||
this.targets = [];
|
||||
for (var i = 0; i < n; i++) {
|
||||
var container_id = Utils.uid();
|
||||
var container_el = $("<div/>")
|
||||
.addClass("charts-viewer-container")
|
||||
.width(parseInt(100 / n) + "%")
|
||||
.append($("<" + tag + " class='charts-viewer-canvas' />").attr("id", container_id));
|
||||
this.$el.append(container_el);
|
||||
this.targets.push(container_id);
|
||||
}
|
||||
},
|
||||
|
||||
/** Draws a new chart by loading and executing the corresponding chart wrapper */
|
||||
_draw: function(process, chart) {
|
||||
var n_panels = chart.settings.get("__use_panels") == "true" ? chart.groups.length : 1;
|
||||
this._createContainer(chart.plugin.specs.tag, n_panels);
|
||||
chart.set("date", Utils.time());
|
||||
chart.state("wait", "Please wait...");
|
||||
this.app.chart_load({ process: process, chart: chart, dataset: this.app.dataset, targets: this.targets });
|
||||
}
|
||||
});
|
||||
@@ -0,0 +1,66 @@
|
||||
/**
|
||||
* Visualization selection grid
|
||||
*/
|
||||
import Thumbnails from "mvc/ui/ui-thumbnails";
|
||||
var View = Backbone.View.extend({
|
||||
initialize: function(options) {
|
||||
var self = this;
|
||||
this.setElement($("<div/>"));
|
||||
this.model = new Backbone.Model();
|
||||
$.ajax({
|
||||
url: `${Galaxy.root}api/datasets/${options.dataset_id}`
|
||||
}).done(dataset => {
|
||||
if (dataset.visualizations.length === 0) {
|
||||
self.$el.append(
|
||||
$("<div/>")
|
||||
.addClass("errormessagelarge")
|
||||
.append(
|
||||
$("<p/>").text(
|
||||
"Unfortunately we could not identify a suitable plugin. Feel free to contact us if you are aware of visualizations for this datatype."
|
||||
)
|
||||
)
|
||||
);
|
||||
} else {
|
||||
self.model.set({ dataset: dataset });
|
||||
self.render();
|
||||
}
|
||||
});
|
||||
},
|
||||
|
||||
render: function() {
|
||||
var self = this;
|
||||
var dataset = this.model.get("dataset");
|
||||
this.vis_index = {};
|
||||
this.vis_array = [];
|
||||
_.each(
|
||||
dataset.visualizations.sort(function(a, b) {
|
||||
return a.html < b.html ? -1 : 1;
|
||||
}),
|
||||
function(v, id) {
|
||||
var dict = {
|
||||
id: id,
|
||||
name: v.name,
|
||||
keywords: v.keywords || [],
|
||||
title: v.html,
|
||||
image_src: v.logo ? Galaxy.root + v.logo : null,
|
||||
description: v.description || "No description available.",
|
||||
regular: v.regular,
|
||||
visualization: v
|
||||
};
|
||||
self.vis_index[dict.id] = dict;
|
||||
self.vis_array.push(dict);
|
||||
}
|
||||
);
|
||||
this.types = new Thumbnails.View({
|
||||
title_default: "Suggested plugins",
|
||||
title_list: "List of available plugins",
|
||||
collection: this.vis_array,
|
||||
onclick: function(id) {
|
||||
var vis_type = self.vis_index[id];
|
||||
$("#galaxy_main").attr("src", vis_type.visualization.href);
|
||||
}
|
||||
});
|
||||
this.$el.empty().append(this.types.$el);
|
||||
}
|
||||
});
|
||||
export default { View: View };
|
||||
@@ -1,66 +1,86 @@
|
||||
/**
|
||||
Webhooks
|
||||
**/
|
||||
import Utils from "utils/utils";
|
||||
|
||||
var WebhookModel = Backbone.Model.extend({
|
||||
defaults: {
|
||||
activate: false
|
||||
const Webhooks = Backbone.Collection.extend({
|
||||
url: function() {
|
||||
return `${Galaxy.root}api/webhooks`;
|
||||
}
|
||||
});
|
||||
|
||||
var Webhooks = Backbone.Collection.extend({
|
||||
model: WebhookModel
|
||||
});
|
||||
|
||||
var WebhookView = Backbone.View.extend({
|
||||
const WebhookView = Backbone.View.extend({
|
||||
el: "#webhook-view",
|
||||
|
||||
initialize: function(options) {
|
||||
var me = this;
|
||||
var toolId = options.toolId || "";
|
||||
var toolVersion = options.toolVersion || "";
|
||||
const toolId = options.toolId || "";
|
||||
const toolVersion = options.toolVersion || "";
|
||||
|
||||
this.$el.attr("tool_id", toolId);
|
||||
this.$el.attr("tool_version", toolVersion);
|
||||
|
||||
this.model = new WebhookModel();
|
||||
this.model.urlRoot = options.urlRoot;
|
||||
this.model.fetch({
|
||||
success: function() {
|
||||
me.render();
|
||||
const webhooks = new Webhooks();
|
||||
webhooks.fetch({
|
||||
success: data => {
|
||||
if (options.type) {
|
||||
data.reset(filterType(data, options.type));
|
||||
}
|
||||
if (data.length > 0) {
|
||||
this.render(weightedRandomPick(data));
|
||||
}
|
||||
}
|
||||
});
|
||||
},
|
||||
|
||||
render: function() {
|
||||
var webhook = this.model.toJSON();
|
||||
|
||||
this.$el.html(`<div id="${webhook.name}"></div>`);
|
||||
if (webhook.styles)
|
||||
$("<style/>", { type: "text/css" })
|
||||
.text(webhook.styles)
|
||||
.appendTo("head");
|
||||
if (webhook.script)
|
||||
$("<script/>", { type: "text/javascript" })
|
||||
.text(webhook.script)
|
||||
.appendTo("head");
|
||||
|
||||
render: function(model) {
|
||||
const webhook = model.toJSON();
|
||||
this.$el.html(`<div id="${webhook.id}"></div>`);
|
||||
Utils.appendScriptStyle(webhook);
|
||||
return this;
|
||||
}
|
||||
});
|
||||
|
||||
var add = options => {
|
||||
var webhooks = new Webhooks();
|
||||
|
||||
webhooks.url = Galaxy.root + options.url;
|
||||
const load = options => {
|
||||
const webhooks = new Webhooks();
|
||||
webhooks.fetch({
|
||||
async: options.async ? options.async : true,
|
||||
success: options.callback
|
||||
async: options.async !== undefined ? options.async : true,
|
||||
success: data => {
|
||||
if (options.type) {
|
||||
data.reset(filterType(data, options.type));
|
||||
}
|
||||
options.callback(data);
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
function filterType(data, type) {
|
||||
return data.models.filter(item => {
|
||||
let itype = item.get("type");
|
||||
if (itype) {
|
||||
return itype.indexOf(type) !== -1;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function weightedRandomPick(data) {
|
||||
const weights = data.pluck("weight");
|
||||
const sum = weights.reduce((a, b) => a + b);
|
||||
|
||||
const normalizedWeightsMap = new Map();
|
||||
weights.forEach((weight, index) => {
|
||||
normalizedWeightsMap.set(index, parseFloat((weight / sum).toFixed(2)));
|
||||
});
|
||||
|
||||
const table = [];
|
||||
for (const [index, weight] of normalizedWeightsMap) {
|
||||
for (let i = 0; i < weight * 100; i++) {
|
||||
table.push(index);
|
||||
}
|
||||
}
|
||||
|
||||
return data.at(table[Math.floor(Math.random() * table.length)]);
|
||||
}
|
||||
|
||||
export default {
|
||||
Webhooks: Webhooks,
|
||||
WebhookView: WebhookView,
|
||||
add: add
|
||||
load: load
|
||||
};
|
||||
|
||||
@@ -180,7 +180,7 @@ var Node = Backbone.Model.extend({
|
||||
$(element).removeClass("toolForm-active");
|
||||
},
|
||||
set_tool_version: function() {
|
||||
if (this.config_form) {
|
||||
if (this.type === "tool" && this.config_form) {
|
||||
this.tool_version = this.config_form.version;
|
||||
this.content_id = this.config_form.id;
|
||||
}
|
||||
|
||||
@@ -4,6 +4,8 @@ import TAGS from "mvc/tag";
|
||||
import WORKFLOWS from "mvc/workflow/workflow-model";
|
||||
import QueryStringParsing from "utils/query-string-parsing";
|
||||
import _l from "utils/localization";
|
||||
import LoadingIndicator from "ui/loading-indicator";
|
||||
|
||||
/** View of the individual workflows */
|
||||
const WorkflowItemView = Backbone.View.extend({
|
||||
tagName: "tr", // name of (orphan) root tag in this.el
|
||||
@@ -156,8 +158,9 @@ const WorkflowItemView = Backbone.View.extend({
|
||||
/** View of the main workflow list page */
|
||||
const WorkflowListView = Backbone.View.extend({
|
||||
title: _l("Workflows"),
|
||||
active_tab: "workflow",
|
||||
initialize: function() {
|
||||
this.setElement("<div/>");
|
||||
LoadingIndicator.markViewAsLoading(this);
|
||||
_.bindAll(this, "adjustActiondropdown");
|
||||
this.collection = new WORKFLOWS.WorkflowCollection();
|
||||
this.collection.fetch().done(this.render());
|
||||
|
||||
@@ -25,6 +25,8 @@ window.make_popup_menus = Popupmenu.make_popup_menus;
|
||||
import init_tag_click_function from "ui/autocom_tagging";
|
||||
window.init_tag_click_function = init_tag_click_function;
|
||||
import Tours from "mvc/tours";
|
||||
import Webhooks from "mvc/webhooks";
|
||||
import Utils from "utils/utils";
|
||||
// console.debug( 'galaxy globals loaded' );
|
||||
|
||||
// ============================================================================
|
||||
@@ -176,21 +178,19 @@ $(document).ready(() => {
|
||||
Tours.activeGalaxyTourRunner();
|
||||
|
||||
function onloadWebhooks() {
|
||||
// Wait until Galaxy.config is loaded.
|
||||
if (Galaxy.config) {
|
||||
if (Galaxy.root !== undefined) {
|
||||
if (Galaxy.config.enable_webhooks) {
|
||||
// Load all webhooks with the type 'onload'
|
||||
$.getJSON(`${Galaxy.root}api/webhooks/onload/all`, webhooks => {
|
||||
_.each(webhooks, webhook => {
|
||||
if (webhook.activate && webhook.script) {
|
||||
$("<script/>", { type: "text/javascript" })
|
||||
.text(webhook.script)
|
||||
.appendTo("head");
|
||||
$("<style/>", { type: "text/css" })
|
||||
.text(webhook.styles)
|
||||
.appendTo("head");
|
||||
}
|
||||
});
|
||||
Webhooks.load({
|
||||
type: "onload",
|
||||
callback: function(webhooks) {
|
||||
webhooks.each(model => {
|
||||
var webhook = model.toJSON();
|
||||
if (webhook.activate && webhook.script) {
|
||||
Utils.appendScriptStyle(webhook);
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
} else {
|
||||
|
||||
@@ -1,4 +1,6 @@
|
||||
/** Creates a generic/global Galaxy environment, loads shared libraries and a fake server */
|
||||
/* global define */
|
||||
|
||||
define(
|
||||
[
|
||||
"jquery",
|
||||
|
||||
@@ -1,8 +1,7 @@
|
||||
/* global define */
|
||||
/* global QUnit */
|
||||
|
||||
import testApp from "qunit/test-app";
|
||||
import Page from "layout/page";
|
||||
import Panel from "layout/panel";
|
||||
|
||||
QUnit.module("Page test", {
|
||||
beforeEach: function() {
|
||||
|
||||
@@ -68,15 +68,12 @@ QUnit.test("thumbnails", function(assert) {
|
||||
assert.ok(thumb.$(".tab-pane").length == options.ntabs, "Two tabs found.");
|
||||
assert.ok(thumb.$(".ui-thumbnails-item").length == options.nitems, "Thumbnail item.");
|
||||
assert.ok(
|
||||
$(thumb.$(".ui-thumbnails-image")[options.index || 0]).attr("src") == options.image_src,
|
||||
$(thumb.$(".ui-thumbnails-image")[options.index]).attr("src") == options.image_src,
|
||||
"Correct image source"
|
||||
);
|
||||
assert.ok($(thumb.$(".ui-thumbnails-title")[options.index]).html() == options.title, "Correct title with icon");
|
||||
assert.ok(
|
||||
$(thumb.$(".ui-thumbnails-title")[options.index || 0]).html() == options.title,
|
||||
"Correct title with icon"
|
||||
);
|
||||
assert.ok(
|
||||
$(thumb.$(".ui-thumbnails-description-text")[options.index || 0]).html() == options.description,
|
||||
$(thumb.$(".ui-thumbnails-description-text")[options.index]).html() == options.description,
|
||||
"Correct description"
|
||||
);
|
||||
};
|
||||
@@ -86,7 +83,7 @@ QUnit.test("thumbnails", function(assert) {
|
||||
collection: [
|
||||
{
|
||||
id: "id",
|
||||
keywords: "default",
|
||||
regular: true,
|
||||
title: "title",
|
||||
title_icon: "title_icon",
|
||||
image_src: "image_src",
|
||||
@@ -97,6 +94,7 @@ QUnit.test("thumbnails", function(assert) {
|
||||
var model = thumb.model;
|
||||
$("body").prepend(thumb.$el);
|
||||
_test({
|
||||
index: 0,
|
||||
ntabs: 2,
|
||||
nitems: 2,
|
||||
image_src: "image_src",
|
||||
@@ -105,7 +103,7 @@ QUnit.test("thumbnails", function(assert) {
|
||||
});
|
||||
thumb.collection.add({
|
||||
id: "id_a",
|
||||
keywords: "default_a",
|
||||
regular: true,
|
||||
title: "title_a",
|
||||
title_icon: "title_icon_a",
|
||||
image_src: "image_src_a",
|
||||
|
||||
@@ -99,5 +99,13 @@ function LoadingIndicator($where, options) {
|
||||
return self;
|
||||
}
|
||||
|
||||
const markViewAsLoading = function(view) {
|
||||
view.setElement($('<div><div class="loading"></div></div>'));
|
||||
new LoadingIndicator(view.$(".loading")).show();
|
||||
};
|
||||
|
||||
//============================================================================
|
||||
export default LoadingIndicator;
|
||||
export default {
|
||||
LoadingIndicator: LoadingIndicator,
|
||||
markViewAsLoading: markViewAsLoading
|
||||
};
|
||||
|
||||
@@ -1,6 +1,10 @@
|
||||
import * as _ from "libs/underscore";
|
||||
import * as Backbone from "libs/backbone";
|
||||
import util_mod from "viz/trackster/util";
|
||||
import config_mod from "utils/config";
|
||||
|
||||
/* global $ */
|
||||
/* global Galaxy */
|
||||
|
||||
/**
|
||||
* A configuration setting. Currently key is used as id.
|
||||
*/
|
||||
@@ -307,23 +311,22 @@ var ConfigSettingCollectionView = Backbone.View.extend({
|
||||
// No propagation to avoid triggering document click (and tip hiding) above.
|
||||
e.stopPropagation();
|
||||
});
|
||||
|
||||
var // Icon for setting a new random color; behavior set below.
|
||||
new_color_icon = $("<a href='javascript:void(0)'/>")
|
||||
// Icon for setting a new random color; behavior set below.
|
||||
var new_color_icon = $("<a href='javascript:void(0)'/>")
|
||||
.addClass("icon-button arrow-circle")
|
||||
.appendTo(container_div)
|
||||
.attr("title", "Set new random color")
|
||||
.tooltip();
|
||||
|
||||
var // Color picker in tool tip style.
|
||||
tip = $("<div class='tooltip right' style='position: absolute;' />")
|
||||
// Color picker in tool tip style.
|
||||
var tip = $("<div class='tooltip right' style='position: absolute;' />")
|
||||
.appendTo(container_div)
|
||||
.hide();
|
||||
|
||||
var // Inner div for padding purposes
|
||||
tip_inner = $("<div class='tooltip-inner' style='text-align: inherit'></div>").appendTo(tip);
|
||||
// Inner div for padding purposes
|
||||
var tip_inner = $("<div class='tooltip-inner' style='text-align: inherit'></div>").appendTo(tip);
|
||||
|
||||
var tip_arrow = $("<div class='tooltip-arrow'></div>").appendTo(tip);
|
||||
$("<div class='tooltip-arrow'></div>").appendTo(tip);
|
||||
|
||||
var farb_obj = $.farbtastic(tip_inner, {
|
||||
width: 100,
|
||||
|
||||
@@ -2,16 +2,15 @@ define(["i18n!nls/locale"], function(localeStrings) {
|
||||
// =============================================================================
|
||||
/** Simple string replacement localization. Language data from galaxy/scripts/nls */
|
||||
|
||||
// support both requirejs and webpack from the same file
|
||||
// if loaded via webpack, it will be a different format than via requirejs - convert
|
||||
if (localeStrings.hasOwnProperty("__root")) {
|
||||
//console.debug( 'amdi18n+webpack localization for ' + locale + ' loaded' );
|
||||
var locale =
|
||||
typeof navigator === "undefined"
|
||||
? "__root"
|
||||
: (navigator.language || navigator.userLanguage || "__root").toLowerCase();
|
||||
localeStrings =
|
||||
localeStrings["__" + locale] || localeStrings["__" + locale.split("-")[0]] || localeStrings.__root;
|
||||
|
||||
locale = sessionStorage.getItem("currentLocale");
|
||||
|
||||
if (locale) {
|
||||
localeStrings =
|
||||
localeStrings["__" + locale] || localeStrings["__" + locale.split("-")[0]] || localeStrings.__root;
|
||||
}
|
||||
|
||||
// } else {
|
||||
// console.debug( 'i18n+requirejs localization for ' + locale + ' loaded' );
|
||||
|
||||
@@ -6,81 +6,29 @@
|
||||
jQuery.event.props.push("dataTransfer");
|
||||
|
||||
/**
|
||||
Posts file data to the API
|
||||
xhr request helper
|
||||
*/
|
||||
$.uploadpost = config => {
|
||||
// parse options
|
||||
var _uploadrequest = config => {
|
||||
var cnf = $.extend(
|
||||
{},
|
||||
{
|
||||
data: {},
|
||||
success: function() {},
|
||||
error: function() {},
|
||||
progress: function() {},
|
||||
url: null,
|
||||
maxfilesize: 2048,
|
||||
error_filesize: "File exceeds 2GB. Please use a FTP client.",
|
||||
error_default: "Please make sure the file is available.",
|
||||
error_server: "Upload request failed.",
|
||||
error_login: "Uploads require you to log in."
|
||||
error_login: "Uploads require you to log in.",
|
||||
error_retry: "Waiting for server to resume..."
|
||||
},
|
||||
config
|
||||
);
|
||||
|
||||
// link data
|
||||
var data = cnf.data;
|
||||
|
||||
// check errors
|
||||
if (data.error_message) {
|
||||
cnf.error(data.error_message);
|
||||
return;
|
||||
}
|
||||
|
||||
// construct form data
|
||||
var form = new FormData();
|
||||
for (var key in data.payload) {
|
||||
form.append(key, data.payload[key]);
|
||||
}
|
||||
|
||||
// add files to submission
|
||||
var sizes = 0;
|
||||
for (var key in data.files) {
|
||||
var d = data.files[key];
|
||||
form.append(d.name, d.file, d.file.name);
|
||||
sizes += d.file.size;
|
||||
}
|
||||
|
||||
// check file size, unless it's an ftp file
|
||||
if (sizes > 1048576 * cnf.maxfilesize) {
|
||||
cnf.error(cnf.error_filesize);
|
||||
return;
|
||||
}
|
||||
|
||||
// prepare request
|
||||
console.debug(cnf);
|
||||
var xhr = new XMLHttpRequest();
|
||||
xhr.open("POST", cnf.url, true);
|
||||
xhr.setRequestHeader("Accept", "application/json");
|
||||
xhr.setRequestHeader("Cache-Control", "no-cache");
|
||||
xhr.setRequestHeader("X-Requested-With", "XMLHttpRequest");
|
||||
|
||||
// captures state changes
|
||||
xhr.setRequestHeader("Accept", "application/json");
|
||||
xhr.onreadystatechange = () => {
|
||||
// check for request completed, server connection closed
|
||||
if (xhr.readyState == xhr.DONE) {
|
||||
// parse response
|
||||
var response = null;
|
||||
var extra_info = "";
|
||||
if (xhr.responseText) {
|
||||
try {
|
||||
response = jQuery.parseJSON(xhr.responseText);
|
||||
extra_info = response.err_msg;
|
||||
} catch (e) {
|
||||
response = xhr.responseText;
|
||||
extra_info = response;
|
||||
}
|
||||
}
|
||||
// pass any error to the error option
|
||||
if (xhr.status < 200 || xhr.status > 299) {
|
||||
if ([502, 0].indexOf(xhr.status) !== -1 && cnf.warning) {
|
||||
cnf.warning(cnf.error_retry);
|
||||
} else if (xhr.status < 200 || xhr.status > 299) {
|
||||
var text = xhr.statusText;
|
||||
if (xhr.status == 403) {
|
||||
text = cnf.error_login;
|
||||
@@ -89,27 +37,191 @@
|
||||
} else if (!text) {
|
||||
text = cnf.error_default;
|
||||
}
|
||||
cnf.error(`${text} (${xhr.status}). ${extra_info}`);
|
||||
cnf.error(`${text} (${xhr.status})`);
|
||||
} else {
|
||||
var response = null;
|
||||
if (xhr.responseText) {
|
||||
try {
|
||||
response = jQuery.parseJSON(xhr.responseText);
|
||||
} catch (e) {
|
||||
response = xhr.responseText;
|
||||
}
|
||||
}
|
||||
cnf.success(response);
|
||||
}
|
||||
}
|
||||
};
|
||||
xhr.upload.addEventListener("progress", cnf.progress, false);
|
||||
xhr.send(cnf.data);
|
||||
};
|
||||
|
||||
// prepare upload progress
|
||||
xhr.upload.addEventListener(
|
||||
"progress",
|
||||
e => {
|
||||
/**
|
||||
Posts chunked files to the API.
|
||||
*/
|
||||
$.uploadchunk = function(config) {
|
||||
// parse options
|
||||
var cnf = $.extend(
|
||||
{},
|
||||
{
|
||||
data: {},
|
||||
success: () => {},
|
||||
error: () => {},
|
||||
warning: () => {},
|
||||
progress: () => {},
|
||||
attempts: 70000,
|
||||
timeout: 5000,
|
||||
url: null,
|
||||
error_file: "File not provided.",
|
||||
error_attempt: "Maximum number of attempts reached.",
|
||||
error_tool: "Tool submission failed."
|
||||
},
|
||||
config
|
||||
);
|
||||
|
||||
// initial validation
|
||||
var data = cnf.data;
|
||||
if (data.error_message) {
|
||||
cnf.error(data.error_message);
|
||||
return;
|
||||
}
|
||||
var file_data = data.files && data.files[0];
|
||||
if (!file_data) {
|
||||
cnf.error(cnf.error_file);
|
||||
return;
|
||||
}
|
||||
var file = file_data.file;
|
||||
var attempts = cnf.attempts;
|
||||
var session_id = `${cnf.session.id}-${new Date().valueOf()}-${file.size}`;
|
||||
var chunk_size = cnf.session.chunk_upload_size;
|
||||
console.debug(`Starting chunked uploads [size=${chunk_size}].`);
|
||||
|
||||
// chunk processing helper
|
||||
function process(start) {
|
||||
start = start || 0;
|
||||
var slicer = file.mozSlice || file.webkitSlice || file.slice;
|
||||
if (!slicer) {
|
||||
cnf.error("Browser does not support chunked uploads.");
|
||||
return;
|
||||
}
|
||||
var end = Math.min(start + chunk_size, file.size);
|
||||
var size = file.size;
|
||||
console.debug(`Submitting chunk at ${start} bytes...`);
|
||||
var form = new FormData();
|
||||
form.append("session_id", session_id);
|
||||
form.append("session_start", start);
|
||||
form.append("session_chunk", slicer.bind(file)(start, end));
|
||||
_uploadrequest({
|
||||
url: `${Galaxy.root}api/uploads`,
|
||||
data: form,
|
||||
success: upload_response => {
|
||||
var new_start = start + chunk_size;
|
||||
if (new_start < size) {
|
||||
attempts = cnf.attempts;
|
||||
process(new_start);
|
||||
} else {
|
||||
console.debug("Upload completed.");
|
||||
data.payload.inputs = JSON.parse(data.payload.inputs);
|
||||
data.payload.inputs["files_0|file_data"] = {
|
||||
session_id: session_id,
|
||||
name: file.name
|
||||
};
|
||||
data.payload.inputs = JSON.stringify(data.payload.inputs);
|
||||
$.ajax({
|
||||
url: `${Galaxy.root}api/tools`,
|
||||
method: "POST",
|
||||
data: data.payload,
|
||||
success: tool_response => {
|
||||
cnf.success(tool_response);
|
||||
},
|
||||
error: tool_response => {
|
||||
var err_msg =
|
||||
tool_response && tool_response.responseJSON && tool_response.responseJSON.err_msg;
|
||||
cnf.error(err_msg || cnf.error_tool);
|
||||
}
|
||||
});
|
||||
}
|
||||
},
|
||||
warning: upload_response => {
|
||||
if (--attempts > 0) {
|
||||
console.debug("Retrying last chunk...");
|
||||
cnf.warning(upload_response);
|
||||
setTimeout(() => process(start), cnf.timeout);
|
||||
} else {
|
||||
console.debug(cnf.error_attempt);
|
||||
cnf.error(cnf.error_attempt);
|
||||
}
|
||||
},
|
||||
error: upload_response => {
|
||||
console.debug(upload_response);
|
||||
cnf.error(upload_response);
|
||||
},
|
||||
progress: e => {
|
||||
if (e.lengthComputable) {
|
||||
cnf.progress(Math.min(Math.round((start + e.loaded) * 100 / file.size), 100));
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// initiate processing queue for chunks
|
||||
process();
|
||||
};
|
||||
|
||||
/**
|
||||
Posts multiple files without chunking to the API.
|
||||
*/
|
||||
$.uploadpost = function(config) {
|
||||
var cnf = $.extend(
|
||||
{},
|
||||
{
|
||||
data: {},
|
||||
success: () => {},
|
||||
error: () => {},
|
||||
progress: () => {},
|
||||
url: null,
|
||||
maxfilesize: 1048576 * 2048,
|
||||
error_filesize: "File exceeds 2GB. Please use a FTP client."
|
||||
},
|
||||
config
|
||||
);
|
||||
var data = cnf.data;
|
||||
if (data.error_message) {
|
||||
cnf.error(data.error_message);
|
||||
return;
|
||||
}
|
||||
|
||||
// construct form data
|
||||
var form = new FormData();
|
||||
for (let key in data.payload) {
|
||||
form.append(key, data.payload[key]);
|
||||
}
|
||||
|
||||
// add files to submission
|
||||
var sizes = 0;
|
||||
for (let key in data.files) {
|
||||
var d = data.files[key];
|
||||
form.append(d.name, d.file, d.file.name);
|
||||
sizes += d.file.size;
|
||||
}
|
||||
|
||||
// check file size, unless it's an ftp file
|
||||
if (sizes > cnf.maxfilesize) {
|
||||
cnf.error(cnf.error_filesize);
|
||||
return;
|
||||
}
|
||||
|
||||
// submit request
|
||||
_uploadrequest({
|
||||
url: cnf.url,
|
||||
data: form,
|
||||
success: cnf.success,
|
||||
error: cnf.error,
|
||||
progress: e => {
|
||||
if (e.lengthComputable) {
|
||||
cnf.progress(Math.round(e.loaded * 100 / e.total));
|
||||
}
|
||||
},
|
||||
false
|
||||
);
|
||||
|
||||
// send request
|
||||
Galaxy.emit.debug("uploadbox::uploadpost()", "Posting following data.", cnf);
|
||||
xhr.send(form);
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
@@ -121,9 +233,9 @@
|
||||
var opts = $.extend(
|
||||
{},
|
||||
{
|
||||
ondragover: function() {},
|
||||
ondragleave: function() {},
|
||||
onchange: function() {},
|
||||
ondragover: () => {},
|
||||
ondragleave: () => {},
|
||||
onchange: () => {},
|
||||
multiple: false
|
||||
},
|
||||
options
|
||||
@@ -132,7 +244,7 @@
|
||||
// append hidden upload field
|
||||
var $input = $(`<input type="file" style="display: none" ${(opts.multiple && "multiple") || ""}/>`);
|
||||
el.append(
|
||||
$input.change(function(e) {
|
||||
$input.change(e => {
|
||||
opts.onchange(e.target.files);
|
||||
$(this).val("");
|
||||
})
|
||||
@@ -157,7 +269,7 @@
|
||||
|
||||
// exports
|
||||
return {
|
||||
dialog: function() {
|
||||
dialog: () => {
|
||||
$input.trigger("click");
|
||||
}
|
||||
};
|
||||
@@ -171,16 +283,17 @@
|
||||
var opts = $.extend(
|
||||
{},
|
||||
{
|
||||
dragover: function() {},
|
||||
dragleave: function() {},
|
||||
announce: function(d) {},
|
||||
initialize: function(d) {},
|
||||
progress: function(d, m) {},
|
||||
success: function(d, m) {},
|
||||
error: function(d, m) {
|
||||
dragover: () => {},
|
||||
dragleave: () => {},
|
||||
announce: d => {},
|
||||
initialize: d => {},
|
||||
progress: (d, m) => {},
|
||||
success: (d, m) => {},
|
||||
warning: (d, m) => {},
|
||||
error: (d, m) => {
|
||||
alert(m);
|
||||
},
|
||||
complete: function() {}
|
||||
complete: () => {}
|
||||
},
|
||||
options
|
||||
);
|
||||
@@ -188,6 +301,9 @@
|
||||
// file queue
|
||||
var queue = {};
|
||||
|
||||
// session options
|
||||
var session = null;
|
||||
|
||||
// queue index/length counter
|
||||
var queue_index = 0;
|
||||
var queue_length = 0;
|
||||
@@ -199,7 +315,10 @@
|
||||
// element
|
||||
var uploadinput = $(this).uploadinput({
|
||||
multiple: true,
|
||||
onchange: function(files) {
|
||||
onchange: files => {
|
||||
_.each(files, file => {
|
||||
file.chunk_mode = true;
|
||||
});
|
||||
add(files);
|
||||
},
|
||||
ondragover: options.ondragover,
|
||||
@@ -252,7 +371,7 @@
|
||||
|
||||
// get an identifier from the queue
|
||||
var index = -1;
|
||||
for (var key in queue) {
|
||||
for (let key in queue) {
|
||||
index = key;
|
||||
break;
|
||||
}
|
||||
@@ -264,18 +383,32 @@
|
||||
remove(index);
|
||||
|
||||
// create and submit data
|
||||
$.uploadpost({
|
||||
var submitter = $.uploadpost;
|
||||
if (
|
||||
file.chunk_mode &&
|
||||
session &&
|
||||
session.id &&
|
||||
session.chunk_upload_size &&
|
||||
session.chunk_upload_size > 0
|
||||
) {
|
||||
submitter = $.uploadchunk;
|
||||
}
|
||||
submitter({
|
||||
url: opts.url,
|
||||
data: opts.initialize(index),
|
||||
success: function(message) {
|
||||
session: session,
|
||||
success: message => {
|
||||
opts.success(index, message);
|
||||
process();
|
||||
},
|
||||
error: function(message) {
|
||||
warning: message => {
|
||||
opts.warning(index, message);
|
||||
},
|
||||
error: message => {
|
||||
opts.error(index, message);
|
||||
process();
|
||||
},
|
||||
progress: function(percentage) {
|
||||
progress: percentage => {
|
||||
opts.progress(index, percentage);
|
||||
}
|
||||
});
|
||||
@@ -298,7 +431,8 @@
|
||||
}
|
||||
|
||||
// initiate upload process
|
||||
function start() {
|
||||
function start(_session) {
|
||||
session = _session;
|
||||
if (!queue_running) {
|
||||
queue_running = true;
|
||||
process();
|
||||
|
||||
@@ -3,6 +3,11 @@
|
||||
* do not require their own classes/files
|
||||
*/
|
||||
import _l from "utils/localization";
|
||||
import * as _ from "underscore";
|
||||
|
||||
/* global $ */
|
||||
/* global Galaxy */
|
||||
|
||||
/** Builds a basic iframe */
|
||||
export function iframe(src) {
|
||||
return `<iframe src="${src}" frameborder="0" style="width: 100%; height: 100%;"/>`;
|
||||
@@ -14,7 +19,9 @@ export function deepeach(dict, callback) {
|
||||
var d = dict[i];
|
||||
if (_.isObject(d)) {
|
||||
var new_dict = callback(d);
|
||||
new_dict && (dict[i] = new_dict);
|
||||
if (new_dict) {
|
||||
dict[i] = new_dict;
|
||||
}
|
||||
deepeach(d, callback);
|
||||
}
|
||||
}
|
||||
@@ -117,7 +124,9 @@ export function get(options) {
|
||||
top.__utils__get__ = top.__utils__get__ || {};
|
||||
var cache_key = JSON.stringify(options);
|
||||
if (options.cache && top.__utils__get__[cache_key]) {
|
||||
options.success && options.success(top.__utils__get__[cache_key]);
|
||||
if (options.success) {
|
||||
options.success(top.__utils__get__[cache_key]);
|
||||
}
|
||||
window.console.debug(`utils.js::get() - Fetching from cache [${options.url}].`);
|
||||
} else {
|
||||
request({
|
||||
@@ -125,10 +134,14 @@ export function get(options) {
|
||||
data: options.data,
|
||||
success: function(response) {
|
||||
top.__utils__get__[cache_key] = response;
|
||||
options.success && options.success(response);
|
||||
if (options.success) {
|
||||
options.success(response);
|
||||
}
|
||||
},
|
||||
error: function(response, status) {
|
||||
options.error && options.error(response, status);
|
||||
if (options.error) {
|
||||
options.error(response, status);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
@@ -169,24 +182,30 @@ export function request(options) {
|
||||
if (typeof response === "string") {
|
||||
try {
|
||||
response = response.replace("Infinity,", '"Infinity",');
|
||||
response = jQuery.parseJSON(response);
|
||||
response = $.parseJSON(response);
|
||||
} catch (e) {
|
||||
console.debug(e);
|
||||
}
|
||||
}
|
||||
options.success && options.success(response);
|
||||
if (options.success) {
|
||||
options.success(response);
|
||||
}
|
||||
})
|
||||
.fail(response => {
|
||||
var response_text = null;
|
||||
try {
|
||||
response_text = jQuery.parseJSON(response.responseText);
|
||||
response_text = $.parseJSON(response.responseText);
|
||||
} catch (e) {
|
||||
response_text = response.responseText;
|
||||
}
|
||||
options.error && options.error(response_text, response.status);
|
||||
if (options.error) {
|
||||
options.error(response_text, response.status);
|
||||
}
|
||||
})
|
||||
.always(() => {
|
||||
options.complete && options.complete();
|
||||
if (options.complete) {
|
||||
options.complete();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@@ -330,6 +349,11 @@ export function setWindowTitle(title) {
|
||||
}
|
||||
}
|
||||
|
||||
export function setActiveTab(active_tab) {
|
||||
$("#masthead .dropdown.active").removeClass("active");
|
||||
$("#" + active_tab + ">.dropdown").addClass("active");
|
||||
}
|
||||
|
||||
export default {
|
||||
cssLoadFile: cssLoadFile,
|
||||
cssGetAttribute: cssGetAttribute,
|
||||
@@ -349,5 +373,6 @@ export default {
|
||||
linkify: linkify,
|
||||
appendScriptStyle: appendScriptStyle,
|
||||
getQueryString: getQueryString,
|
||||
setWindowTitle: setWindowTitle
|
||||
setWindowTitle: setWindowTitle,
|
||||
setActiveTab: setActiveTab
|
||||
};
|
||||
|
||||
@@ -1,15 +1,15 @@
|
||||
import _l from "utils/localization";
|
||||
/**
|
||||
* Top-level trackster code, used for creating/loading visualizations and user interface elements.
|
||||
*/
|
||||
//import * as $ from 'jquery';
|
||||
import _l from "utils/localization";
|
||||
import * as _ from "libs/underscore";
|
||||
import * as Backbone from "libs/backbone";
|
||||
import tracks from "viz/trackster/tracks";
|
||||
import visualization from "viz/visualization";
|
||||
import mod_icon_btn from "mvc/ui/icon-button";
|
||||
import IconButton from "mvc/ui/icon-button";
|
||||
import query_string from "utils/query-string-parsing";
|
||||
import GridView from "mvc/grid/grid-view";
|
||||
import mod_utils from "utils/utils";
|
||||
import Utils from "utils/utils";
|
||||
import "libs/jquery/jquery.event.drag";
|
||||
import "libs/jquery/jquery.event.hover";
|
||||
import "libs/jquery/jquery.mousewheel";
|
||||
@@ -20,44 +20,36 @@ import "libs/jquery/jquery.form";
|
||||
import "libs/jquery/jquery.rating";
|
||||
import "ui/editable-text";
|
||||
|
||||
// trackster global variables
|
||||
var ui = null;
|
||||
var view = null;
|
||||
var browser_router = null;
|
||||
|
||||
/**
|
||||
* Base Object/Model for inhertiance.
|
||||
*/
|
||||
var Base = function() {
|
||||
if (this.initialize) {
|
||||
this.initialize.apply(this, arguments);
|
||||
}
|
||||
};
|
||||
Base.extend = Backbone.Model.extend;
|
||||
/* global Galaxy */
|
||||
/* global $ */
|
||||
|
||||
/**
|
||||
* User interface controls for trackster
|
||||
*/
|
||||
var TracksterUI = Base.extend({
|
||||
initialize: function(baseURL) {
|
||||
mod_utils.cssLoadFile("static/style/jquery.rating.css");
|
||||
mod_utils.cssLoadFile("static/style/autocomplete_tagging.css");
|
||||
mod_utils.cssLoadFile("static/style/jquery-ui/smoothness/jquery-ui.css");
|
||||
mod_utils.cssLoadFile("static/style/library.css");
|
||||
mod_utils.cssLoadFile("static/style/trackster.css");
|
||||
class TracksterUI extends Backbone.Model {
|
||||
constructor(options) {
|
||||
super(options);
|
||||
}
|
||||
|
||||
initialize(baseURL) {
|
||||
this.baseURL = baseURL;
|
||||
},
|
||||
Utils.cssLoadFile("static/style/jquery.rating.css");
|
||||
Utils.cssLoadFile("static/style/autocomplete_tagging.css");
|
||||
Utils.cssLoadFile("static/style/jquery-ui/smoothness/jquery-ui.css");
|
||||
Utils.cssLoadFile("static/style/library.css");
|
||||
Utils.cssLoadFile("static/style/trackster.css");
|
||||
}
|
||||
|
||||
/**
|
||||
* Save visualization, returning a Deferred object for the remote call to save.
|
||||
*/
|
||||
save_viz: function() {
|
||||
save_viz() {
|
||||
// show dialog
|
||||
Galaxy.modal.show({ title: "Saving...", body: "progress" });
|
||||
|
||||
// Save bookmarks.
|
||||
var bookmarks = [];
|
||||
$(".bookmark").each(function() {
|
||||
$(".bookmark").each(() => {
|
||||
bookmarks.push({
|
||||
position: $(this)
|
||||
.children(".position")
|
||||
@@ -69,14 +61,16 @@ var TracksterUI = Base.extend({
|
||||
});
|
||||
|
||||
// FIXME: give unique IDs to Drawables and save overview as ID.
|
||||
var overview_track_name = view.overview_drawable ? view.overview_drawable.config.get_value("name") : null;
|
||||
var overview_track_name = this.view.overview_drawable
|
||||
? this.view.overview_drawable.config.get_value("name")
|
||||
: null;
|
||||
|
||||
var viz_config = {
|
||||
view: view.to_dict(),
|
||||
view: this.view.to_dict(),
|
||||
viewport: {
|
||||
chrom: view.chrom,
|
||||
start: view.low,
|
||||
end: view.high,
|
||||
chrom: this.view.chrom,
|
||||
start: this.view.low,
|
||||
end: this.view.high,
|
||||
overview: overview_track_name
|
||||
},
|
||||
bookmarks: bookmarks
|
||||
@@ -88,17 +82,17 @@ var TracksterUI = Base.extend({
|
||||
type: "POST",
|
||||
dataType: "json",
|
||||
data: {
|
||||
id: view.vis_id,
|
||||
title: view.config.get_value("name"),
|
||||
dbkey: view.dbkey,
|
||||
id: this.view.vis_id,
|
||||
title: this.view.config.get_value("name"),
|
||||
dbkey: this.view.dbkey,
|
||||
type: "trackster",
|
||||
vis_json: JSON.stringify(viz_config)
|
||||
}
|
||||
})
|
||||
.success(vis_info => {
|
||||
Galaxy.modal.hide();
|
||||
view.vis_id = vis_info.vis_id;
|
||||
view.has_changes = false;
|
||||
this.view.vis_id = vis_info.vis_id;
|
||||
this.view.has_changes = false;
|
||||
|
||||
// Needed to set URL when first saving a visualization.
|
||||
window.history.pushState({}, "", vis_info.url + window.location.hash);
|
||||
@@ -109,29 +103,27 @@ var TracksterUI = Base.extend({
|
||||
title: _l("Could Not Save"),
|
||||
body: "Could not save visualization. Please try again later.",
|
||||
buttons: {
|
||||
Cancel: function() {
|
||||
Cancel: () => {
|
||||
Galaxy.modal.hide();
|
||||
}
|
||||
}
|
||||
});
|
||||
});
|
||||
},
|
||||
}
|
||||
|
||||
/**
|
||||
* Create button menu
|
||||
*/
|
||||
createButtonMenu: function() {
|
||||
var self = this;
|
||||
|
||||
var menu = mod_icon_btn.create_icon_buttons_menu(
|
||||
createButtonMenu() {
|
||||
var menu = IconButton.create_icon_buttons_menu(
|
||||
[
|
||||
{
|
||||
icon_class: "plus-button",
|
||||
title: _l("Add tracks"),
|
||||
on_click: function() {
|
||||
visualization.select_datasets({ dbkey: view.dbkey }, new_tracks => {
|
||||
on_click: () => {
|
||||
visualization.select_datasets({ dbkey: this.view.dbkey }, new_tracks => {
|
||||
_.each(new_tracks, track => {
|
||||
view.add_drawable(tracks.object_from_template(track, view, view));
|
||||
this.view.add_drawable(tracks.object_from_template(track, this.view, this.view));
|
||||
});
|
||||
});
|
||||
}
|
||||
@@ -139,9 +131,9 @@ var TracksterUI = Base.extend({
|
||||
{
|
||||
icon_class: "block--plus",
|
||||
title: _l("Add group"),
|
||||
on_click: function() {
|
||||
view.add_drawable(
|
||||
new tracks.DrawableGroup(view, view, {
|
||||
on_click: () => {
|
||||
this.view.add_drawable(
|
||||
new tracks.DrawableGroup(this.view, this.view, {
|
||||
name: "New Group"
|
||||
})
|
||||
);
|
||||
@@ -150,7 +142,7 @@ var TracksterUI = Base.extend({
|
||||
{
|
||||
icon_class: "bookmarks",
|
||||
title: _l("Bookmarks"),
|
||||
on_click: function() {
|
||||
on_click: () => {
|
||||
// HACK -- use style to determine if panel is hidden and hide/show accordingly.
|
||||
window.force_right_panel($("div#right").css("right") == "0px" ? "hide" : "show");
|
||||
}
|
||||
@@ -158,22 +150,22 @@ var TracksterUI = Base.extend({
|
||||
{
|
||||
icon_class: "globe",
|
||||
title: _l("Circster"),
|
||||
on_click: function() {
|
||||
window.location = `${self.baseURL}visualization/circster?id=${view.vis_id}`;
|
||||
on_click: () => {
|
||||
window.location = `${this.baseURL}visualization/circster?id=${this.view.vis_id}`;
|
||||
}
|
||||
},
|
||||
{
|
||||
icon_class: "disk--arrow",
|
||||
title: _l("Save"),
|
||||
on_click: function() {
|
||||
self.save_viz();
|
||||
on_click: () => {
|
||||
this.save_viz();
|
||||
}
|
||||
},
|
||||
{
|
||||
icon_class: "cross-circle",
|
||||
title: _l("Close"),
|
||||
on_click: function() {
|
||||
self.handle_unsaved_changes(view);
|
||||
on_click: () => {
|
||||
this.handle_unsaved_changes(this.view);
|
||||
}
|
||||
}
|
||||
],
|
||||
@@ -184,12 +176,12 @@ var TracksterUI = Base.extend({
|
||||
|
||||
this.buttonMenu = menu;
|
||||
return menu;
|
||||
},
|
||||
}
|
||||
|
||||
/**
|
||||
* Add bookmark.
|
||||
*/
|
||||
add_bookmark: function(position, annotation, editable) {
|
||||
add_bookmark(position, annotation, editable) {
|
||||
// Create HTML.
|
||||
var bookmarks_container = $("#right .unified-panel-body");
|
||||
|
||||
@@ -201,11 +193,12 @@ var TracksterUI = Base.extend({
|
||||
.addClass("position")
|
||||
.appendTo(new_bookmark);
|
||||
|
||||
var position_link = $("<a href=''/>")
|
||||
//position_link
|
||||
$("<a href=''/>")
|
||||
.text(position)
|
||||
.appendTo(position_div)
|
||||
.click(() => {
|
||||
view.go_to(position);
|
||||
this.view.go_to(position);
|
||||
return false;
|
||||
});
|
||||
|
||||
@@ -222,11 +215,12 @@ var TracksterUI = Base.extend({
|
||||
// Remove bookmark.
|
||||
new_bookmark.slideUp("fast");
|
||||
new_bookmark.remove();
|
||||
view.has_changes = true;
|
||||
this.view.has_changes = true;
|
||||
return false;
|
||||
});
|
||||
|
||||
var delete_icon = $("<a href=''/>")
|
||||
// delete_icon
|
||||
$("<a href=''/>")
|
||||
.addClass("icon-button delete")
|
||||
.appendTo(delete_icon_container);
|
||||
|
||||
@@ -239,56 +233,50 @@ var TracksterUI = Base.extend({
|
||||
.addClass("annotation");
|
||||
}
|
||||
|
||||
view.has_changes = true;
|
||||
this.view.has_changes = true;
|
||||
return new_bookmark;
|
||||
},
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a complete Trackster visualization. Returns view.
|
||||
*/
|
||||
create_visualization: function(view_config, viewport_config, drawables_config, bookmarks_config, editable) {
|
||||
create_visualization(view_config, viewport_config, drawables_config, bookmarks_config, editable) {
|
||||
// Create view.
|
||||
var self = this;
|
||||
this.view = new tracks.TracksterView(_.extend(view_config, { header: false }));
|
||||
this.view.editor = true;
|
||||
|
||||
view = new tracks.TracksterView(_.extend(view_config, { header: false }));
|
||||
|
||||
view.editor = true;
|
||||
$.when(view.load_chroms_deferred).then(chrom_info => {
|
||||
$.when(this.view.load_chroms_deferred).then(chrom_info => {
|
||||
var overview_drawable_name = null;
|
||||
// Viewport config.
|
||||
if (viewport_config) {
|
||||
var chrom = viewport_config.chrom;
|
||||
var start = viewport_config.start;
|
||||
var end = viewport_config.end;
|
||||
var overview_drawable_name = viewport_config.overview;
|
||||
overview_drawable_name = viewport_config.overview;
|
||||
|
||||
if (chrom && start !== undefined && end) {
|
||||
view.change_chrom(chrom, start, end);
|
||||
this.view.change_chrom(chrom, start, end);
|
||||
} else {
|
||||
// No valid viewport, so use first chromosome.
|
||||
view.change_chrom(chrom_info[0].chrom);
|
||||
this.view.change_chrom(chrom_info[0].chrom);
|
||||
}
|
||||
} else {
|
||||
// No viewport, so use first chromosome.
|
||||
view.change_chrom(chrom_info[0].chrom);
|
||||
this.view.change_chrom(chrom_info[0].chrom);
|
||||
}
|
||||
|
||||
// Add drawables to view.
|
||||
if (drawables_config) {
|
||||
// FIXME: can from_dict() be used to create view and add drawables?
|
||||
var drawable_config;
|
||||
|
||||
var drawable_type;
|
||||
var drawable;
|
||||
for (var i = 0; i < drawables_config.length; i++) {
|
||||
view.add_drawable(tracks.object_from_template(drawables_config[i], view, view));
|
||||
for (let i = 0; i < drawables_config.length; i++) {
|
||||
this.view.add_drawable(tracks.object_from_template(drawables_config[i], this.view, this.view));
|
||||
}
|
||||
}
|
||||
|
||||
// Set overview.
|
||||
var overview_drawable;
|
||||
for (var i = 0; i < view.drawables.length; i++) {
|
||||
if (view.drawables[i].config.get_value("name") === overview_drawable_name) {
|
||||
view.set_overview(view.drawables[i]);
|
||||
for (let i = 0; i < this.view.drawables.length; i++) {
|
||||
if (this.view.drawables[i].config.get_value("name") === overview_drawable_name) {
|
||||
this.view.set_overview(this.view.drawables[i]);
|
||||
break;
|
||||
}
|
||||
}
|
||||
@@ -296,34 +284,35 @@ var TracksterUI = Base.extend({
|
||||
// Load bookmarks.
|
||||
if (bookmarks_config) {
|
||||
var bookmark;
|
||||
for (var i = 0; i < bookmarks_config.length; i++) {
|
||||
for (let i = 0; i < bookmarks_config.length; i++) {
|
||||
bookmark = bookmarks_config[i];
|
||||
self.add_bookmark(bookmark["position"], bookmark["annotation"], editable);
|
||||
this.add_bookmark(bookmark.position, bookmark.annotation, editable);
|
||||
}
|
||||
}
|
||||
|
||||
// View has no changes as of yet.
|
||||
view.has_changes = false;
|
||||
this.view.has_changes = false;
|
||||
});
|
||||
|
||||
// Final initialization.
|
||||
this.set_up_router({ view: view });
|
||||
this.set_up_router({ view: this.view });
|
||||
|
||||
return view;
|
||||
},
|
||||
// TODO: This is hopefully not necessary anymore, since we're using the instance view. Do it for compatibility for now.
|
||||
return this.view;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set up location router to use hashes as track browser locations.
|
||||
*/
|
||||
set_up_router: function(options) {
|
||||
set_up_router(options) {
|
||||
new visualization.TrackBrowserRouter(options);
|
||||
Backbone.history.start();
|
||||
},
|
||||
}
|
||||
|
||||
/**
|
||||
* Set up keyboard navigation for a visualization.
|
||||
*/
|
||||
init_keyboard_nav: function(view) {
|
||||
init_keyboard_nav(view) {
|
||||
// Keyboard navigation. Scroll ~7% of height when scrolling up/down.
|
||||
$(document).keyup(e => {
|
||||
// Do not navigate if arrow keys used in input element.
|
||||
@@ -337,39 +326,38 @@ var TracksterUI = Base.extend({
|
||||
view.move_fraction(0.25);
|
||||
break;
|
||||
case 38:
|
||||
var change = Math.round(view.viewport_container.height() / 15.0);
|
||||
// var change = Math.round(view.viewport_container.height() / 15.0);
|
||||
view.viewport_container.scrollTop(view.viewport_container.scrollTop() - 20);
|
||||
break;
|
||||
case 39:
|
||||
view.move_fraction(-0.25);
|
||||
break;
|
||||
case 40:
|
||||
var change = Math.round(view.viewport_container.height() / 15.0);
|
||||
// var change = Math.round(view.viewport_container.height() / 15.0);
|
||||
view.viewport_container.scrollTop(view.viewport_container.scrollTop() + 20);
|
||||
break;
|
||||
}
|
||||
});
|
||||
},
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle unsaved changes in visualization.
|
||||
*/
|
||||
handle_unsaved_changes: function(view) {
|
||||
handle_unsaved_changes(view) {
|
||||
if (view.has_changes) {
|
||||
var self = this;
|
||||
Galaxy.modal.show({
|
||||
title: _l("Close visualization"),
|
||||
body: "There are unsaved changes to your visualization which will be lost if you do not save them.",
|
||||
buttons: {
|
||||
Cancel: function() {
|
||||
Cancel: () => {
|
||||
Galaxy.modal.hide();
|
||||
},
|
||||
"Leave without Saving": function() {
|
||||
"Leave without Saving": () => {
|
||||
$(window).off("beforeunload");
|
||||
window.location = `${Galaxy.root}visualization`;
|
||||
},
|
||||
Save: function() {
|
||||
$.when(self.save_viz()).then(() => {
|
||||
Save: () => {
|
||||
$.when(this.save_viz()).then(() => {
|
||||
window.location = `${Galaxy.root}visualization`;
|
||||
});
|
||||
}
|
||||
@@ -379,22 +367,25 @@ var TracksterUI = Base.extend({
|
||||
window.location = `${Galaxy.root}visualization`;
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
var TracksterView = Backbone.View.extend({
|
||||
class TracksterUIView extends Backbone.View {
|
||||
constructor(options) {
|
||||
super(options);
|
||||
}
|
||||
// initalize trackster
|
||||
initialize: function() {
|
||||
initialize() {
|
||||
// load ui
|
||||
ui = new TracksterUI(Galaxy.root);
|
||||
this.ui = new TracksterUI(Galaxy.root);
|
||||
|
||||
// create button menu
|
||||
ui.createButtonMenu();
|
||||
this.ui.createButtonMenu();
|
||||
|
||||
// attach the button menu to the panel header and float it left
|
||||
ui.buttonMenu.$el.attr("style", "float: right");
|
||||
this.ui.buttonMenu.$el.attr("style", "float: right");
|
||||
|
||||
// add to center panel
|
||||
$("#center .unified-panel-header-inner").append(ui.buttonMenu.$el);
|
||||
$("#center .unified-panel-header-inner").append(this.ui.buttonMenu.$el);
|
||||
|
||||
// configure right panel
|
||||
$("#right .unified-panel-title").append("Bookmarks");
|
||||
@@ -404,7 +395,7 @@ var TracksterView = Backbone.View.extend({
|
||||
|
||||
// resize view when showing/hiding right panel (bookmarks for now).
|
||||
$("#right-border").click(() => {
|
||||
view.resize_window();
|
||||
this.ui.view.resize_window();
|
||||
});
|
||||
|
||||
// hide right panel
|
||||
@@ -418,10 +409,9 @@ var TracksterView = Backbone.View.extend({
|
||||
} else {
|
||||
this.view_new();
|
||||
}
|
||||
},
|
||||
}
|
||||
|
||||
choose_existing_or_new: function() {
|
||||
var self = this;
|
||||
choose_existing_or_new() {
|
||||
var dbkey = query_string.get("dbkey");
|
||||
var listTracksParams = {};
|
||||
|
||||
@@ -442,21 +432,21 @@ var TracksterView = Backbone.View.extend({
|
||||
body: `<p><ul style='list-style: disc inside none'>You can add this dataset as:<li>a new track to one of your existing, saved Trackster sessions if they share the genome build: <b>${dbkey ||
|
||||
"Not available."}</b></li><li>or create a new session with this dataset as the only track</li></ul></p>`,
|
||||
buttons: {
|
||||
Cancel: function() {
|
||||
Cancel: () => {
|
||||
window.location = `${Galaxy.root}visualizations/list`;
|
||||
},
|
||||
"View in saved visualization": function() {
|
||||
self.view_in_saved(dataset_params);
|
||||
"View in saved visualization": () => {
|
||||
this.view_in_saved(dataset_params);
|
||||
},
|
||||
"View in new visualization": function() {
|
||||
self.view_new();
|
||||
"View in new visualization": () => {
|
||||
this.view_new();
|
||||
}
|
||||
}
|
||||
});
|
||||
},
|
||||
}
|
||||
|
||||
// view
|
||||
view_in_saved: function(dataset_params) {
|
||||
view_in_saved(dataset_params) {
|
||||
var tracks_grid = new GridView({
|
||||
url_base: `${Galaxy.root}visualization/list_tracks`,
|
||||
embedded: true
|
||||
@@ -465,28 +455,28 @@ var TracksterView = Backbone.View.extend({
|
||||
title: _l("Add Data to Saved Visualization"),
|
||||
body: tracks_grid.$el,
|
||||
buttons: {
|
||||
Cancel: function() {
|
||||
Cancel: () => {
|
||||
window.location = `${Galaxy.root}visualizations/list`;
|
||||
},
|
||||
"Add to visualization": function() {
|
||||
$(parent.document)
|
||||
"Add to visualization": () => {
|
||||
$(window.parent.document)
|
||||
.find("input[name=id]:checked")
|
||||
.each(function() {
|
||||
.each(() => {
|
||||
dataset_params.id = $(this).val();
|
||||
window.location = `${Galaxy.root}visualization/trackster?${$.param(dataset_params)}`;
|
||||
});
|
||||
}
|
||||
}
|
||||
});
|
||||
},
|
||||
}
|
||||
|
||||
// view
|
||||
view_existing: function() {
|
||||
view_existing() {
|
||||
// get config
|
||||
var viz_config = window.galaxy_config.app.viz_config;
|
||||
|
||||
// view
|
||||
view = ui.create_visualization(
|
||||
this.ui.create_visualization(
|
||||
{
|
||||
container: $("#center .unified-panel-body"),
|
||||
name: viz_config.title,
|
||||
@@ -501,31 +491,28 @@ var TracksterView = Backbone.View.extend({
|
||||
|
||||
// initialize editor
|
||||
this.init_editor();
|
||||
},
|
||||
}
|
||||
|
||||
// view
|
||||
view_new: function() {
|
||||
// reference this
|
||||
var self = this;
|
||||
|
||||
view_new() {
|
||||
// ajax
|
||||
$.ajax({
|
||||
url: `${Galaxy.root}api/genomes?chrom_info=True`,
|
||||
data: {},
|
||||
error: function() {
|
||||
error: () => {
|
||||
alert("Couldn't create new browser.");
|
||||
},
|
||||
success: function(response) {
|
||||
success: response => {
|
||||
// show dialog
|
||||
Galaxy.modal.show({
|
||||
title: _l("New Visualization"),
|
||||
body: self.template_view_new(response),
|
||||
body: this.template_view_new(response),
|
||||
buttons: {
|
||||
Cancel: function() {
|
||||
Cancel: () => {
|
||||
window.location = `${Galaxy.root}visualizations/list`;
|
||||
},
|
||||
Create: function() {
|
||||
self.create_browser($("#new-title").val(), $("#new-dbkey").val());
|
||||
Create: () => {
|
||||
this.create_browser($("#new-title").val(), $("#new-dbkey").val());
|
||||
Galaxy.modal.hide();
|
||||
}
|
||||
}
|
||||
@@ -548,10 +535,10 @@ var TracksterView = Backbone.View.extend({
|
||||
$("#overlay").css("overflow", "auto");
|
||||
}
|
||||
});
|
||||
},
|
||||
}
|
||||
|
||||
// new browser form
|
||||
template_view_new: function(response) {
|
||||
template_view_new(response) {
|
||||
// start template
|
||||
var html =
|
||||
'<form id="new-browser-form" action="javascript:void(0);" method="post" onsubmit="return false;">' +
|
||||
@@ -568,7 +555,7 @@ var TracksterView = Backbone.View.extend({
|
||||
'<select name="dbkey" id="new-dbkey">';
|
||||
|
||||
// add dbkeys
|
||||
for (var i = 0; i < response.length; i++) {
|
||||
for (let i = 0; i < response.length; i++) {
|
||||
html += `<option value="${response[i][1]}">${response[i][0]}</option>`;
|
||||
}
|
||||
|
||||
@@ -579,13 +566,48 @@ var TracksterView = Backbone.View.extend({
|
||||
|
||||
// return
|
||||
return html;
|
||||
},
|
||||
}
|
||||
|
||||
// initialization for editor-specific functions.
|
||||
init_editor() {
|
||||
// set title
|
||||
$("#center .unified-panel-title").text(`${this.ui.view.config.get_value("name")} (${this.ui.view.dbkey})`);
|
||||
|
||||
// add dataset
|
||||
if (window.galaxy_config.app.add_dataset)
|
||||
$.ajax({
|
||||
url: `${Galaxy.root}api/datasets/${window.galaxy_config.app.add_dataset}`,
|
||||
data: { hda_ldda: "hda", data_type: "track_config" },
|
||||
dataType: "json",
|
||||
success: track_data => {
|
||||
this.ui.view.add_drawable(tracks.object_from_template(track_data, this.ui.view, this.ui.view));
|
||||
}
|
||||
});
|
||||
|
||||
// initialize icons
|
||||
$("#add-bookmark-button").click(() => {
|
||||
// add new bookmark.
|
||||
var position = `${this.ui.view.chrom}:${this.ui.view.low}-${this.ui.view.high}`;
|
||||
|
||||
var annotation = "Bookmark description";
|
||||
return this.ui.add_bookmark(position, annotation, true);
|
||||
});
|
||||
|
||||
// initialize keyboard
|
||||
this.ui.init_keyboard_nav(this.ui.view);
|
||||
|
||||
$(window).on("beforeunload", () => {
|
||||
if (this.ui.view.has_changes) {
|
||||
return "There are unsaved changes to your visualization that will be lost if you leave this page.";
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// create
|
||||
create_browser: function(name, dbkey) {
|
||||
create_browser(name, dbkey) {
|
||||
$(document).trigger("convert_to_values");
|
||||
|
||||
view = ui.create_visualization(
|
||||
this.ui.create_visualization(
|
||||
{
|
||||
container: $("#center .unified-panel-body"),
|
||||
name: name,
|
||||
@@ -598,46 +620,11 @@ var TracksterView = Backbone.View.extend({
|
||||
this.init_editor();
|
||||
|
||||
// modify view setting
|
||||
view.editor = true;
|
||||
},
|
||||
|
||||
// initialization for editor-specific functions.
|
||||
init_editor: function() {
|
||||
// set title
|
||||
$("#center .unified-panel-title").text(`${view.config.get_value("name")} (${view.dbkey})`);
|
||||
|
||||
// add dataset
|
||||
if (window.galaxy_config.app.add_dataset)
|
||||
$.ajax({
|
||||
url: `${Galaxy.root}api/datasets/${window.galaxy_config.app.add_dataset}`,
|
||||
data: { hda_ldda: "hda", data_type: "track_config" },
|
||||
dataType: "json",
|
||||
success: function(track_data) {
|
||||
view.add_drawable(tracks.object_from_template(track_data, view, view));
|
||||
}
|
||||
});
|
||||
|
||||
// initialize icons
|
||||
$("#add-bookmark-button").click(() => {
|
||||
// add new bookmark.
|
||||
var position = `${view.chrom}:${view.low}-${view.high}`;
|
||||
|
||||
var annotation = "Bookmark description";
|
||||
return ui.add_bookmark(position, annotation, true);
|
||||
});
|
||||
|
||||
// initialize keyboard
|
||||
ui.init_keyboard_nav(view);
|
||||
|
||||
$(window).on("beforeunload", () => {
|
||||
if (view.has_changes) {
|
||||
return "There are unsaved changes to your visualization that will be lost if you leave this page.";
|
||||
}
|
||||
});
|
||||
this.ui.view.editor = true;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
export default {
|
||||
TracksterUI: TracksterUI,
|
||||
GalaxyApp: TracksterView
|
||||
GalaxyApp: TracksterUIView
|
||||
};
|
||||
|
||||
@@ -151,7 +151,7 @@ var NumberFilter = function(obj_dict) {
|
||||
.addClass("elt-label")
|
||||
.appendTo(filter.parent_div);
|
||||
|
||||
var name_span = $("<span/>")
|
||||
$("<span/>")
|
||||
.addClass("slider-name")
|
||||
.text(`${filter.name} `)
|
||||
.appendTo(filter_label);
|
||||
@@ -341,9 +341,6 @@ extend(NumberFilter.prototype, {
|
||||
return true;
|
||||
}
|
||||
|
||||
// Keep value function.
|
||||
var filter = this;
|
||||
|
||||
// Do filtering.
|
||||
var to_filter = element[this.index];
|
||||
if (to_filter instanceof Array) {
|
||||
@@ -482,7 +479,7 @@ var FiltersManager = function(track, obj_dict) {
|
||||
filter.height_icon.addClass("active").show();
|
||||
}
|
||||
} else {
|
||||
console.log("ERROR: unsupported filter: ", name, type);
|
||||
console.log("ERROR: unsupported filter: ", filters_dict[i]);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -656,6 +653,7 @@ extend(FiltersManager.prototype, {
|
||||
// Remove current filter.
|
||||
filters = filters.slice(1);
|
||||
|
||||
// DBTODO: This will never work, run_tool_url doesn't exist?
|
||||
$.getJSON(run_tool_url, url_params, response => {
|
||||
if (response.error) {
|
||||
// General error.
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -45,7 +45,7 @@ extend(FeatureSlotter.prototype, {
|
||||
|
||||
var draw_end = Math.ceil(feature[2] * this.w_scale);
|
||||
var f_name = feature[3];
|
||||
var text_align;
|
||||
//var text_align;
|
||||
|
||||
// Update start, end drawing locations to include feature name.
|
||||
// Try to put the name on the left, if not, put on right.
|
||||
@@ -55,10 +55,10 @@ extend(FeatureSlotter.prototype, {
|
||||
var text_len = this.measureText(f_name).width + (LABEL_SPACING + PACK_SPACING);
|
||||
if (draw_start - text_len >= 0) {
|
||||
draw_start -= text_len;
|
||||
text_align = "left";
|
||||
//text_align = "left";
|
||||
} else {
|
||||
draw_end += text_len;
|
||||
text_align = "right";
|
||||
//text_align = "right";
|
||||
}
|
||||
}
|
||||
|
||||
@@ -130,7 +130,7 @@ extend(FeatureSlotter.prototype, {
|
||||
// Loop through features to (a) find those that are not yet slotted and (b) update
|
||||
// those that are slotted if new information is availabe. For (a), features already
|
||||
// slotted (based on slotting from other tiles) will retain their current slot.
|
||||
for (var i = 0, len = features.length; i < len; i++) {
|
||||
for (let i = 0, len = features.length; i < len; i++) {
|
||||
feature = features[i];
|
||||
feature_uid = feature[0];
|
||||
var slotted_info = this.slots[feature_uid];
|
||||
@@ -163,7 +163,7 @@ extend(FeatureSlotter.prototype, {
|
||||
// Slot unslotted features.
|
||||
|
||||
// Do slotting.
|
||||
for (var i = 0, len = undone.length; i < len; i++) {
|
||||
for (let i = 0, len = undone.length; i < len; i++) {
|
||||
feature = features[undone[i]];
|
||||
feature_uid = feature[0];
|
||||
var draw_coords = this._get_draw_coords(feature);
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import _l from "utils/localization";
|
||||
import * as _ from "libs/underscore";
|
||||
import * as Backbone from "libs/backbone";
|
||||
import visualization from "viz/visualization";
|
||||
import viz_views from "viz/viz_views";
|
||||
import util from "viz/trackster/util";
|
||||
@@ -13,6 +14,9 @@ import bbi from "viz/bbi-data-manager";
|
||||
import "ui/editable-text";
|
||||
var extend = _.extend;
|
||||
|
||||
/* global $ */
|
||||
/* global Galaxy */
|
||||
|
||||
// ---- Web UI specific utilities ----
|
||||
|
||||
/**
|
||||
@@ -24,9 +28,9 @@ var html_elt_js_obj_dict = {};
|
||||
/**
|
||||
* Designates an HTML as a container.
|
||||
*/
|
||||
var is_container = (element, obj) => {
|
||||
function is_container(element, obj) {
|
||||
html_elt_js_obj_dict[element.attr("id")] = obj;
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Make `element` moveable within parent and sibling elements by dragging `handle` (a selector).
|
||||
@@ -37,7 +41,7 @@ var is_container = (element, obj) => {
|
||||
* @param container_selector selector used to identify possible containers for this element
|
||||
* @param element_js_obj JavaScript object associated with element; used
|
||||
*/
|
||||
var moveable = (element, handle_class, container_selector, element_js_obj) => {
|
||||
function moveable(element, handle_class, container_selector, element_js_obj) {
|
||||
// HACK: set default value for container selector.
|
||||
container_selector = ".group";
|
||||
|
||||
@@ -48,11 +52,9 @@ var moveable = (element, handle_class, container_selector, element_js_obj) => {
|
||||
element
|
||||
.bind("drag", { handle: `.${handle_class}`, relative: true }, function(e, d) {
|
||||
var element = $(this);
|
||||
var parent = $(this).parent();
|
||||
|
||||
var // Only sorting amongst tracks and groups.
|
||||
children = parent.children(".track,.group");
|
||||
|
||||
var parent = element.parent();
|
||||
// Only sorting amongst tracks and groups.
|
||||
var children = parent.children(".track,.group");
|
||||
var this_obj = html_elt_js_obj_dict[$(this).attr("id")];
|
||||
var child;
|
||||
var container;
|
||||
@@ -144,46 +146,47 @@ var moveable = (element, handle_class, container_selector, element_js_obj) => {
|
||||
.bind("dragend", function() {
|
||||
$(this).removeClass("dragging");
|
||||
});
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Init constants & functions used throughout trackster.
|
||||
*/
|
||||
var // Padding at the top of tracks for error messages
|
||||
ERROR_PADDING = 20;
|
||||
|
||||
var // Maximum number of rows un a slotted track
|
||||
MAX_FEATURE_DEPTH = 100;
|
||||
// Padding at the top of tracks for error messages
|
||||
const ERROR_PADDING = 20;
|
||||
|
||||
var // Minimum width for window for squish to be used.
|
||||
MIN_SQUISH_VIEW_WIDTH = 12000;
|
||||
// Maximum number of rows un a slotted track
|
||||
const MAX_FEATURE_DEPTH = 100;
|
||||
|
||||
var // Number of pixels per tile, not including left offset.
|
||||
TILE_SIZE = 400;
|
||||
// Minimum width for window for squish to be used.
|
||||
const MIN_SQUISH_VIEW_WIDTH = 12000;
|
||||
|
||||
var DEFAULT_DATA_QUERY_WAIT = 5000;
|
||||
// Number of pixels per tile, not including left offset.
|
||||
const TILE_SIZE = 400;
|
||||
|
||||
var // Maximum number of chromosomes that are selectable at any one time.
|
||||
MAX_CHROMS_SELECTABLE = 100;
|
||||
const DEFAULT_DATA_QUERY_WAIT = 5000;
|
||||
|
||||
var DATA_ERROR = "Cannot display dataset due to an error. ";
|
||||
// Maximum number of chromosomes that are selectable at any one time.
|
||||
const MAX_CHROMS_SELECTABLE = 100;
|
||||
|
||||
var DATA_NOCONVERTER = "A converter for this dataset is not installed. Please check your datatypes_conf.xml file.";
|
||||
const DATA_ERROR = "Cannot display dataset due to an error. ";
|
||||
|
||||
var DATA_NONE = "No data for this chrom/contig.";
|
||||
const DATA_NOCONVERTER = "A converter for this dataset is not installed. Please check your datatypes_conf.xml file.";
|
||||
|
||||
var DATA_PENDING =
|
||||
const DATA_NONE = "No data for this chrom/contig.";
|
||||
|
||||
const DATA_PENDING =
|
||||
"Preparing data. This can take a while for a large dataset. " +
|
||||
"If the visualization is saved and closed, preparation will continue in the background.";
|
||||
|
||||
var DATA_CANNOT_RUN_TOOL = "Tool cannot be rerun: ";
|
||||
var DATA_LOADING = "Loading data...";
|
||||
var DATA_OK = "Ready for display";
|
||||
var TILE_CACHE_SIZE = 10;
|
||||
var DATA_CACHE_SIZE = 20;
|
||||
|
||||
var // Numerical/continuous data display modes.
|
||||
CONTINUOUS_DATA_MODES = ["Histogram", "Line", "Filled", "Intensity"];
|
||||
const DATA_CANNOT_RUN_TOOL = "Tool cannot be rerun: ";
|
||||
//var DATA_LOADING = "Loading data...";
|
||||
const DATA_OK = "Ready for display";
|
||||
const TILE_CACHE_SIZE = 10;
|
||||
//var DATA_CACHE_SIZE = 20;
|
||||
//
|
||||
// Numerical/continuous data display modes.
|
||||
const CONTINUOUS_DATA_MODES = ["Histogram", "Line", "Filled", "Intensity"];
|
||||
|
||||
/**
|
||||
* Round a number to a given number of decimal places.
|
||||
@@ -248,7 +251,10 @@ var Drawable = function(view, container, obj_dict) {
|
||||
this.action_icons = {};
|
||||
|
||||
// -- Set up drawable configuration. --
|
||||
this.config = config_mod.ConfigSettingCollection.from_models_and_saved_values(this.config_params, obj_dict.prefs);
|
||||
this.config = config_mod.ConfigSettingCollection.from_models_and_saved_values(
|
||||
this.build_config_params(),
|
||||
obj_dict.prefs
|
||||
);
|
||||
|
||||
// If there's no saved name, use object name.
|
||||
if (!this.config.get_value("name")) {
|
||||
@@ -342,6 +348,10 @@ extend(Drawable.prototype, {
|
||||
}
|
||||
],
|
||||
|
||||
build_config_params: function() {
|
||||
return this.config_params;
|
||||
},
|
||||
|
||||
config_onchange: function() {},
|
||||
|
||||
init: function() {},
|
||||
@@ -723,7 +733,6 @@ extend(DrawableGroup.prototype, Drawable.prototype, DrawableCollection.prototype
|
||||
for (i = 0; i < num_drawables; i++) {
|
||||
drawable = this.drawables[i];
|
||||
if (drawable.get_type() !== a_type) {
|
||||
can_composite = false;
|
||||
break;
|
||||
}
|
||||
if (drawable instanceof FeatureTrack) {
|
||||
@@ -772,16 +781,12 @@ extend(DrawableGroup.prototype, Drawable.prototype, DrawableCollection.prototype
|
||||
// manager.
|
||||
//
|
||||
this.filters_manager.remove_all();
|
||||
var filters;
|
||||
var new_filter;
|
||||
var min;
|
||||
var max;
|
||||
for (var filter_name in shared_filters) {
|
||||
filters = shared_filters[filter_name];
|
||||
let filters = shared_filters[filter_name];
|
||||
if (filters.length === num_feature_tracks) {
|
||||
// Add new filter.
|
||||
// FIXME: can filter.copy() be used?
|
||||
new_filter = new filters_mod.NumberFilter({
|
||||
let new_filter = new filters_mod.NumberFilter({
|
||||
name: filters[0].name,
|
||||
index: filters[0].index
|
||||
});
|
||||
@@ -834,7 +839,7 @@ extend(DrawableGroup.prototype, Drawable.prototype, DrawableCollection.prototype
|
||||
name: this.config.get_value("name"),
|
||||
drawables: this.drawables
|
||||
});
|
||||
var index = this.container.replace_drawable(this, composite_track, true);
|
||||
this.container.replace_drawable(this, composite_track, true);
|
||||
composite_track.request_draw();
|
||||
},
|
||||
|
||||
@@ -981,7 +986,8 @@ var TracksterView = Backbone.View.extend({
|
||||
this.intro_div = $("<div/>")
|
||||
.addClass("intro")
|
||||
.appendTo(this.viewport_container);
|
||||
var add_tracks_button = $("<div/>")
|
||||
// Add tracks button
|
||||
$("<div/>")
|
||||
.text("Add Datasets to Visualization")
|
||||
.addClass("action-button")
|
||||
.appendTo(this.intro_div)
|
||||
@@ -1270,11 +1276,11 @@ var TracksterView = Backbone.View.extend({
|
||||
$(window).bind("resize", function() {
|
||||
// Stop previous timer.
|
||||
if (this.resize_timer) {
|
||||
clearTimeout(this.resize_timer);
|
||||
window.clearTimeout(this.resize_timer);
|
||||
}
|
||||
|
||||
// When function activated, resize window and redraw.
|
||||
this.resize_timer = setTimeout(() => {
|
||||
this.resize_timer = window.setTimeout(() => {
|
||||
view.resize_window();
|
||||
}, 500);
|
||||
});
|
||||
@@ -1309,18 +1315,17 @@ extend(TracksterView.prototype, DrawableCollection.prototype, {
|
||||
trigger_navigate: function(new_chrom, new_low, new_high, delay) {
|
||||
// Stop previous timer.
|
||||
if (this.timer) {
|
||||
clearTimeout(this.timer);
|
||||
window.clearTimeout(this.timer);
|
||||
}
|
||||
|
||||
if (delay) {
|
||||
// To aggregate calls, use timer and only navigate once
|
||||
// location has stabilized.
|
||||
var self = this;
|
||||
this.timer = setTimeout(() => {
|
||||
self.trigger("navigate", `${new_chrom}:${new_low}-${new_high}`);
|
||||
this.timer = window.setTimeout(() => {
|
||||
this.trigger("navigate", `${new_chrom}:${new_low}-${new_high}`);
|
||||
}, 500);
|
||||
} else {
|
||||
view.trigger("navigate", `${new_chrom}:${new_low}-${new_high}`);
|
||||
this.trigger("navigate", `${new_chrom}:${new_low}-${new_high}`);
|
||||
}
|
||||
},
|
||||
|
||||
@@ -1343,13 +1348,12 @@ extend(TracksterView.prototype, DrawableCollection.prototype, {
|
||||
load_chroms: function(url_parms) {
|
||||
url_parms.num = MAX_CHROMS_SELECTABLE;
|
||||
|
||||
var view = this;
|
||||
var chrom_data = $.Deferred();
|
||||
$.ajax({
|
||||
url: `${Galaxy.root}api/genomes/${this.dbkey}`,
|
||||
data: url_parms,
|
||||
dataType: "json",
|
||||
success: function(result) {
|
||||
success: result => {
|
||||
// Do nothing if could not load chroms.
|
||||
if (result.chrom_info.length === 0) {
|
||||
return;
|
||||
@@ -1357,34 +1361,34 @@ extend(TracksterView.prototype, DrawableCollection.prototype, {
|
||||
|
||||
// Load chroms.
|
||||
if (result.reference) {
|
||||
var ref_track = new ReferenceTrack(view);
|
||||
view.add_label_track(ref_track);
|
||||
view.reference_track = ref_track;
|
||||
var ref_track = new ReferenceTrack(this);
|
||||
this.add_label_track(ref_track);
|
||||
this.reference_track = ref_track;
|
||||
}
|
||||
view.chrom_data = result.chrom_info;
|
||||
this.chrom_data = result.chrom_info;
|
||||
|
||||
view.chrom_select.html("");
|
||||
view.chrom_select.append($('<option value="">Select Chrom/Contig</option>'));
|
||||
this.chrom_select.html("");
|
||||
this.chrom_select.append($('<option value="">Select Chrom/Contig</option>'));
|
||||
|
||||
for (var i = 0, len = view.chrom_data.length; i < len; i++) {
|
||||
var chrom = view.chrom_data[i].chrom;
|
||||
for (var i = 0, len = this.chrom_data.length; i < len; i++) {
|
||||
var chrom = this.chrom_data[i].chrom;
|
||||
var chrom_option = $("<option>");
|
||||
chrom_option.text(chrom);
|
||||
chrom_option.val(chrom);
|
||||
view.chrom_select.append(chrom_option);
|
||||
this.chrom_select.append(chrom_option);
|
||||
}
|
||||
if (result.prev_chroms) {
|
||||
view.chrom_select.append($(`<option value="previous">Previous ${MAX_CHROMS_SELECTABLE}</option>`));
|
||||
this.chrom_select.append($(`<option value="previous">Previous ${MAX_CHROMS_SELECTABLE}</option>`));
|
||||
}
|
||||
if (result.next_chroms) {
|
||||
view.chrom_select.append($(`<option value="next">Next ${MAX_CHROMS_SELECTABLE}</option>`));
|
||||
this.chrom_select.append($(`<option value="next">Next ${MAX_CHROMS_SELECTABLE}</option>`));
|
||||
}
|
||||
view.chrom_start_index = result.start_index;
|
||||
this.chrom_start_index = result.start_index;
|
||||
|
||||
chrom_data.resolve(result.chrom_info);
|
||||
},
|
||||
error: function() {
|
||||
alert(`Could not load chroms for this dbkey: ${view.dbkey}`);
|
||||
alert(`Could not load chroms for this dbkey: ${this.dbkey}`);
|
||||
}
|
||||
});
|
||||
return chrom_data;
|
||||
@@ -1491,7 +1495,7 @@ extend(TracksterView.prototype, DrawableCollection.prototype, {
|
||||
str = str.replace(/,/g, "");
|
||||
|
||||
// Replace colons and hyphens with space for easy parsing.
|
||||
str = str.replace(/:|\-/g, " ");
|
||||
str = str.replace(/:|-/g, " ");
|
||||
|
||||
// Parse new location.
|
||||
var chrom_pos = str.split(/\s+/);
|
||||
@@ -1537,10 +1541,10 @@ extend(TracksterView.prototype, DrawableCollection.prototype, {
|
||||
|
||||
// Set up timeout to redraw with more data when moving stops.
|
||||
if (this.redraw_on_move_fn) {
|
||||
clearTimeout(this.redraw_on_move_fn);
|
||||
window.clearTimeout(this.redraw_on_move_fn);
|
||||
}
|
||||
|
||||
this.redraw_on_move_fn = setTimeout(() => {
|
||||
this.redraw_on_move_fn = window.setTimeout(() => {
|
||||
view.request_redraw();
|
||||
}, 200);
|
||||
|
||||
@@ -1617,7 +1621,7 @@ extend(TracksterView.prototype, DrawableCollection.prototype, {
|
||||
|
||||
// Set up redraw if it has not been requested since last redraw.
|
||||
if (!this.requested_redraw) {
|
||||
requestAnimationFrame(() => {
|
||||
window.requestAnimationFrame(() => {
|
||||
view._redraw();
|
||||
});
|
||||
this.requested_redraw = true;
|
||||
@@ -1779,8 +1783,8 @@ extend(TracksterView.prototype, DrawableCollection.prototype, {
|
||||
this.overview_box.height(this.default_overview_height);
|
||||
this.overview_close.hide();
|
||||
this.overview_highlight.hide();
|
||||
view.resize_window();
|
||||
view.overview_drawable = null;
|
||||
this.resize_window();
|
||||
this.overview_drawable = null;
|
||||
}
|
||||
});
|
||||
|
||||
@@ -1828,7 +1832,7 @@ var ToolParameterView = Backbone.View.extend({
|
||||
var param = this.model;
|
||||
|
||||
// Param label.
|
||||
var label_div = $("<div>")
|
||||
$("<div>")
|
||||
.addClass("param-label")
|
||||
.text(param.get("label"))
|
||||
.appendTo(param_div);
|
||||
@@ -1881,7 +1885,7 @@ var TracksterToolView = Backbone.View.extend({
|
||||
});
|
||||
|
||||
// Add name, inputs.
|
||||
var name_div = $("<div class='tool-name'>")
|
||||
$("<div class='tool-name'>")
|
||||
.appendTo(parent_div)
|
||||
.text(tool.get("name"));
|
||||
tool.get("inputs").each(param => {
|
||||
@@ -1989,44 +1993,42 @@ var TracksterToolView = Backbone.View.extend({
|
||||
regions: [region.toJSON()]
|
||||
};
|
||||
|
||||
var current_track = track;
|
||||
|
||||
var // Set name of track to include tool name, parameters, and region used.
|
||||
track_name = tool.get("name") + current_track.tool_region_and_parameters_str(region);
|
||||
// Set name of track to include tool name, parameters, and region used.
|
||||
var track_name = tool.get("name") + track.tool_region_and_parameters_str(region);
|
||||
|
||||
var container;
|
||||
|
||||
// If track not in a group, create a group for it and add new track to group. If track
|
||||
// already in group, add track to group.
|
||||
if (current_track.container === view) {
|
||||
if (track.container === track.view) {
|
||||
// Create new group.
|
||||
var group = new DrawableGroup(view, view, {
|
||||
var group = new DrawableGroup(track.view, track.view, {
|
||||
name: track.config.get_value("name")
|
||||
});
|
||||
|
||||
// Replace track with group.
|
||||
var index = current_track.container.replace_drawable(current_track, group, false);
|
||||
var index = track.container.replace_drawable(track, group, false);
|
||||
|
||||
// Update HTML.
|
||||
// FIXME: this is ugly way to replace a track with a group -- make this easier via
|
||||
// a Drawable or DrawableCollection function.
|
||||
group.container_div.insertBefore(current_track.view.content_div.children()[index]);
|
||||
group.add_drawable(current_track);
|
||||
current_track.container_div.appendTo(group.content_div);
|
||||
group.container_div.insertBefore(track.view.content_div.children()[index]);
|
||||
group.add_drawable(track);
|
||||
track.container_div.appendTo(group.content_div);
|
||||
container = group;
|
||||
} else {
|
||||
// Use current group.
|
||||
container = current_track.container;
|
||||
container = track.container;
|
||||
}
|
||||
|
||||
// Create and init new track.
|
||||
var new_track = new current_track.constructor(view, container, {
|
||||
var new_track = new track.constructor(track.view, container, {
|
||||
name: track_name,
|
||||
hda_ldda: "hda"
|
||||
});
|
||||
new_track.init_for_tool_data();
|
||||
new_track.change_mode(current_track.mode);
|
||||
new_track.set_filters_manager(current_track.filters_manager.copy(new_track));
|
||||
new_track.change_mode(track.mode);
|
||||
new_track.set_filters_manager(track.filters_manager.copy(new_track));
|
||||
new_track.update_icons();
|
||||
container.add_drawable(new_track);
|
||||
new_track.tiles_div.text("Starting job.");
|
||||
@@ -2441,12 +2443,17 @@ extend(Track.prototype, Drawable.prototype, {
|
||||
name: "param_space_viz_icon",
|
||||
title: _l("Tool parameter space visualization"),
|
||||
css_class: "arrow-split",
|
||||
on_click_fn: function(track) {
|
||||
var html = `<strong>Tool</strong>:${track.tool.get(
|
||||
"name"
|
||||
)}<br/><strong>Dataset</strong>:${track.config.get_value(
|
||||
"name"
|
||||
)}<br/><strong>Region(s)</strong>: <select name="regions"><option value="cur">current viewing area</option><option value="bookmarks">bookmarks</option><option value="both">current viewing area and bookmarks</option></select>`;
|
||||
on_click_fn: track => {
|
||||
var html = `
|
||||
<strong>Tool</strong>:${track.tool.get("name")}<br/>
|
||||
<strong>Dataset</strong>:${track.config.get_value("name")}<br/>
|
||||
<strong>Region(s)</strong>:
|
||||
<select name="regions">
|
||||
<option value="cur">current viewing area</option>
|
||||
<option value="bookmarks">bookmarks</option>
|
||||
<option value="both">current viewing area and bookmarks</option>
|
||||
</select>
|
||||
`;
|
||||
|
||||
var cancel_fn = () => {
|
||||
Galaxy.modal.hide();
|
||||
@@ -2454,22 +2461,22 @@ extend(Track.prototype, Drawable.prototype, {
|
||||
};
|
||||
|
||||
var ok_fn = () => {
|
||||
var regions_to_use = $('select[name="regions"] option:selected').val(),
|
||||
regions,
|
||||
view_region = new visualization.GenomeRegion({
|
||||
chrom: view.chrom,
|
||||
start: view.low,
|
||||
end: view.high
|
||||
}),
|
||||
bookmarked_regions = _.map(
|
||||
$(".bookmark"),
|
||||
elt =>
|
||||
new visualization.GenomeRegion({
|
||||
from_str: $(elt)
|
||||
.children(".position")
|
||||
.text()
|
||||
})
|
||||
);
|
||||
var regions_to_use = $('select[name="regions"] option:selected').val();
|
||||
var regions;
|
||||
var view_region = new visualization.GenomeRegion({
|
||||
chrom: this.view.chrom,
|
||||
start: this.view.low,
|
||||
end: this.view.high
|
||||
});
|
||||
var bookmarked_regions = _.map(
|
||||
$(".bookmark"),
|
||||
elt =>
|
||||
new visualization.GenomeRegion({
|
||||
from_str: $(elt)
|
||||
.children(".position")
|
||||
.text()
|
||||
})
|
||||
);
|
||||
|
||||
// Get regions for visualization.
|
||||
if (regions_to_use === "cur") {
|
||||
@@ -2493,6 +2500,8 @@ extend(Track.prototype, Drawable.prototype, {
|
||||
})}`;
|
||||
};
|
||||
|
||||
/*
|
||||
* TODO: Re-enable this when functional.
|
||||
var check_enter_esc = e => {
|
||||
if ((e.keyCode || e.which) === 27) {
|
||||
// Escape key
|
||||
@@ -2502,6 +2511,7 @@ extend(Track.prototype, Drawable.prototype, {
|
||||
ok_fn();
|
||||
}
|
||||
};
|
||||
*/
|
||||
|
||||
// show dialog
|
||||
Galaxy.modal.show({
|
||||
@@ -2732,7 +2742,7 @@ extend(Track.prototype, Drawable.prototype, {
|
||||
track.container_div.addClass("pending");
|
||||
track.show_message(DATA_PENDING);
|
||||
//$("<img/>").attr("src", image_path + "/yui/rel_interstitial_loading.gif").appendTo(track.tiles_div);
|
||||
setTimeout(() => {
|
||||
window.setTimeout(() => {
|
||||
track.init();
|
||||
}, track.data_query_wait);
|
||||
} else if (result === "data" || result.status === "data") {
|
||||
@@ -3040,7 +3050,7 @@ extend(TiledTrack.prototype, Drawable.prototype, Track.prototype, {
|
||||
var clear_after = options && options.clear_after;
|
||||
var low = this.view.low;
|
||||
var high = this.view.high;
|
||||
var range = high - low;
|
||||
//var range = high - low;
|
||||
var width = this.view.container.width();
|
||||
var w_scale = this.view.resolution_px_b;
|
||||
var resolution = 1 / w_scale;
|
||||
@@ -3049,7 +3059,7 @@ extend(TiledTrack.prototype, Drawable.prototype, Track.prototype, {
|
||||
if (this.is_overview) {
|
||||
low = this.view.max_low;
|
||||
high = this.view.max_high;
|
||||
w_scale = width / (view.max_high - view.max_low);
|
||||
w_scale = width / (this.view.max_high - this.view.max_low);
|
||||
resolution = 1 / w_scale;
|
||||
}
|
||||
|
||||
@@ -3105,17 +3115,16 @@ extend(TiledTrack.prototype, Drawable.prototype, Track.prototype, {
|
||||
}
|
||||
|
||||
// When all tiles are drawn, call post-draw actions.
|
||||
var track = this;
|
||||
$.when.apply($, tile_promises).then(() => {
|
||||
// Step (c) for (re)moving tiles when clear_after is true:
|
||||
track.tiles_div.children(".remove").remove();
|
||||
this.tiles_div.children(".remove").remove();
|
||||
|
||||
// Only do postdraw actions for tiles; instances where tiles may not be drawn include:
|
||||
// (a) ReferenceTrack without sufficient resolution;
|
||||
// (b) data_fetch = false.
|
||||
tiles = _.filter(tiles, t => t !== null);
|
||||
if (tiles.length !== 0) {
|
||||
track.postdraw_actions(tiles, width, w_scale, clear_after);
|
||||
this.postdraw_actions(tiles, width, w_scale, clear_after);
|
||||
}
|
||||
});
|
||||
},
|
||||
@@ -3124,18 +3133,17 @@ extend(TiledTrack.prototype, Drawable.prototype, Track.prototype, {
|
||||
* Add a maximum/minimum label to track.
|
||||
*/
|
||||
_add_yaxis_label: function(type, on_change) {
|
||||
var track = this;
|
||||
var css_class = type === "max" ? "top" : "bottom";
|
||||
var text = type === "max" ? "max" : "min";
|
||||
var pref_name = type === "max" ? "max_value" : "min_value";
|
||||
var label = this.container_div.find(`.yaxislabel.${css_class}`);
|
||||
var value = round(track.config.get_value(pref_name), 1);
|
||||
var value = round(this.config.get_value(pref_name), 1);
|
||||
|
||||
// Default action for on_change is to redraw track.
|
||||
on_change =
|
||||
on_change ||
|
||||
(() => {
|
||||
track.request_draw({ clear_tile_cache: true });
|
||||
this.request_draw({ clear_tile_cache: true });
|
||||
});
|
||||
|
||||
if (label.length !== 0) {
|
||||
@@ -3149,7 +3157,7 @@ extend(TiledTrack.prototype, Drawable.prototype, Track.prototype, {
|
||||
num_cols: 12,
|
||||
on_finish: function(new_val) {
|
||||
$(".tooltip").remove();
|
||||
track.config.set_value(pref_name, round(new_val, 1));
|
||||
this.config.set_value(pref_name, round(new_val, 1));
|
||||
on_change();
|
||||
},
|
||||
help_text: `Set ${text} value`
|
||||
@@ -3176,18 +3184,17 @@ extend(TiledTrack.prototype, Drawable.prototype, Track.prototype, {
|
||||
|
||||
// Clear because this is set when drawing.
|
||||
this.max_height_px = 0;
|
||||
var track = this;
|
||||
_.each(tiles, tile => {
|
||||
if (!(tile instanceof LineTrackTile)) {
|
||||
tile.html_elt.remove();
|
||||
track.draw_helper(tile.region, w_scale, {
|
||||
this.draw_helper(tile.region, w_scale, {
|
||||
force: true,
|
||||
mode: "Coverage"
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
track._add_yaxis_label("max");
|
||||
this._add_yaxis_label("max");
|
||||
} else {
|
||||
// -- Drawing in non-Coverage mode. --
|
||||
|
||||
@@ -3247,19 +3254,16 @@ extend(TiledTrack.prototype, Drawable.prototype, Track.prototype, {
|
||||
var mode = options.mode || this.mode;
|
||||
var resolution = 1 / w_scale;
|
||||
|
||||
var // Useful vars.
|
||||
track = this;
|
||||
|
||||
var drawables = this._get_drawables();
|
||||
var key = this._gen_tile_cache_key(w_scale, region);
|
||||
|
||||
var is_tile = o => o && "track" in o;
|
||||
|
||||
// Check tile cache, if found show existing tile in correct position
|
||||
var tile = force ? undefined : track.tile_cache.get_elt(key);
|
||||
var tile = force ? undefined : this.tile_cache.get_elt(key);
|
||||
if (tile) {
|
||||
if (is_tile(tile)) {
|
||||
track.show_tile(tile, w_scale);
|
||||
this.show_tile(tile, w_scale);
|
||||
}
|
||||
return tile;
|
||||
}
|
||||
@@ -3277,16 +3281,16 @@ extend(TiledTrack.prototype, Drawable.prototype, Track.prototype, {
|
||||
// Map drawable object to data needed for drawing.
|
||||
var tile_data = _.map(drawables, (
|
||||
d // Get the track data/promise.
|
||||
) => d.data_manager.get_data(region, data_mode, resolution, track.data_url_extra_params));
|
||||
) => d.data_manager.get_data(region, data_mode, resolution, this.data_url_extra_params));
|
||||
|
||||
// Get reference data/promise.
|
||||
if (view.reference_track) {
|
||||
if (this.view.reference_track) {
|
||||
tile_data.push(
|
||||
view.reference_track.data_manager.get_data(
|
||||
this.view.reference_track.data_manager.get_data(
|
||||
region,
|
||||
mode,
|
||||
resolution,
|
||||
view.reference_track.data_url_extra_params
|
||||
this.view.reference_track.data_url_extra_params
|
||||
)
|
||||
);
|
||||
}
|
||||
@@ -3298,7 +3302,7 @@ extend(TiledTrack.prototype, Drawable.prototype, Track.prototype, {
|
||||
// When data is available, draw tile.
|
||||
//
|
||||
var tile_drawn = $.Deferred();
|
||||
track.tile_cache.set_elt(key, tile_drawn);
|
||||
this.tile_cache.set_elt(key, tile_drawn);
|
||||
$.when.apply($, get_tile_data()).then(() => {
|
||||
var tile_data = get_tile_data();
|
||||
var tracks_data = tile_data;
|
||||
@@ -3309,41 +3313,36 @@ extend(TiledTrack.prototype, Drawable.prototype, Track.prototype, {
|
||||
// Deferred, try again from the top. NOTE: this condition could (should?) be handled by the
|
||||
// GenomeDataManager in visualization module.
|
||||
if (_.find(tile_data, d => util.is_deferred(d))) {
|
||||
track.tile_cache.set_elt(key, undefined);
|
||||
$.when(track.draw_helper(region, w_scale, options)).then(tile => {
|
||||
this.tile_cache.set_elt(key, undefined);
|
||||
$.when(this.draw_helper(region, w_scale, options)).then(tile => {
|
||||
tile_drawn.resolve(tile);
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
// If sequence data is available, subset to get only data in region.
|
||||
if (view.reference_track) {
|
||||
seq_data = view.reference_track.data_manager.subset_entry(tile_data.pop(), region);
|
||||
if (this.view.reference_track) {
|
||||
seq_data = this.view.reference_track.data_manager.subset_entry(tile_data.pop(), region);
|
||||
}
|
||||
|
||||
// Get drawing modes, heights for all tracks.
|
||||
var drawing_modes = [];
|
||||
|
||||
var drawing_heights = [];
|
||||
|
||||
_.each(drawables, (d, i) => {
|
||||
var mode = d.mode;
|
||||
var data = tracks_data[i];
|
||||
if (mode === "Auto") {
|
||||
mode = d.get_mode(data);
|
||||
d.update_auto_mode(mode);
|
||||
if (d.mode === "Auto") {
|
||||
d.mode = d.get_mode(data);
|
||||
d.update_auto_mode(d.mode);
|
||||
}
|
||||
drawing_modes.push(mode);
|
||||
drawing_heights.push(d.get_canvas_height(data, mode, w_scale, width));
|
||||
drawing_modes.push(d.mode);
|
||||
drawing_heights.push(d.get_canvas_height(data, d.mode, w_scale, width));
|
||||
});
|
||||
|
||||
var canvas = track.view.canvas_manager.new_canvas();
|
||||
var canvas = this.view.canvas_manager.new_canvas();
|
||||
var tile_low = region.get("start");
|
||||
var tile_high = region.get("end");
|
||||
var all_data_index = 0;
|
||||
|
||||
var width = Math.ceil((tile_high - tile_low) * w_scale) + track.left_offset;
|
||||
|
||||
var width = Math.ceil((tile_high - tile_low) * w_scale) + this.left_offset;
|
||||
var height = _.max(drawing_heights);
|
||||
var tile;
|
||||
|
||||
@@ -3354,7 +3353,7 @@ extend(TiledTrack.prototype, Drawable.prototype, Track.prototype, {
|
||||
// Height is specified in options or is the height found above.
|
||||
canvas.height = options.height || height;
|
||||
var ctx = canvas.getContext("2d");
|
||||
ctx.translate(track.left_offset, 0);
|
||||
ctx.translate(this.left_offset, 0);
|
||||
if (drawables.length > 1) {
|
||||
ctx.globalAlpha = 0.5;
|
||||
ctx.globalCompositeOperation = "source-over";
|
||||
@@ -3365,8 +3364,8 @@ extend(TiledTrack.prototype, Drawable.prototype, Track.prototype, {
|
||||
|
||||
// Don't cache, show if no tile.
|
||||
if (tile !== undefined) {
|
||||
track.tile_cache.set_elt(key, tile);
|
||||
track.show_tile(tile, w_scale);
|
||||
this.tile_cache.set_elt(key, tile);
|
||||
this.show_tile(tile, w_scale);
|
||||
}
|
||||
|
||||
tile_drawn.resolve(tile);
|
||||
@@ -3428,7 +3427,6 @@ extend(TiledTrack.prototype, Drawable.prototype, Track.prototype, {
|
||||
* an existing tile rather than reshowing it.
|
||||
*/
|
||||
show_tile: function(tile, w_scale) {
|
||||
var track = this;
|
||||
var tile_element = tile.html_elt;
|
||||
|
||||
// -- Show/move tile element. --
|
||||
@@ -3785,7 +3783,7 @@ extend(CompositeTrack.prototype, TiledTrack.prototype, {
|
||||
}
|
||||
|
||||
// Replace track with group.
|
||||
var index = this.container.replace_drawable(this, group, true);
|
||||
this.container.replace_drawable(this, group, true);
|
||||
group.request_draw({ clear_tile_cache: true });
|
||||
},
|
||||
|
||||
@@ -3819,7 +3817,6 @@ extend(CompositeTrack.prototype, TiledTrack.prototype, {
|
||||
* Update minimum, maximum for component tracks.
|
||||
*/
|
||||
update_all_min_max: function() {
|
||||
var track = this;
|
||||
var min_value = this.config.get_value("min_value");
|
||||
var max_value = this.config.get_value("max_value");
|
||||
_.each(this.drawables, d => {
|
||||
@@ -4127,7 +4124,8 @@ extend(DiagonalHeatmapTrack.prototype, Drawable.prototype, TiledTrack.prototype,
|
||||
hda_ldda: track.dataset.get("hda_ldda")
|
||||
},
|
||||
result => {
|
||||
var data = result.data;
|
||||
// What does this do? Is it meant to be attached to some higher scope state object?
|
||||
// var data = result.data;
|
||||
}
|
||||
);
|
||||
},
|
||||
@@ -4163,10 +4161,9 @@ var FeatureTrack = function(view, container, obj_dict) {
|
||||
this.slotters = {};
|
||||
this.start_end_dct = {};
|
||||
this.left_offset = 200;
|
||||
|
||||
// this.painter = painters.LinkedFeaturePainter;
|
||||
this.set_painter_from_config();
|
||||
};
|
||||
|
||||
extend(FeatureTrack.prototype, Drawable.prototype, TiledTrack.prototype, {
|
||||
display_modes: ["Auto", "Coverage", "Dense", "Squish", "Pack"],
|
||||
|
||||
@@ -4258,9 +4255,6 @@ extend(FeatureTrack.prototype, Drawable.prototype, TiledTrack.prototype, {
|
||||
postdraw_actions: function(tiles, width, w_scale, clear_after) {
|
||||
TiledTrack.prototype.postdraw_actions.call(this, tiles, width, w_scale, clear_after);
|
||||
|
||||
var track = this;
|
||||
var i;
|
||||
|
||||
var line_track_tiles = _.filter(tiles, t => t instanceof LineTrackTile);
|
||||
|
||||
//
|
||||
@@ -4277,7 +4271,6 @@ extend(FeatureTrack.prototype, Drawable.prototype, TiledTrack.prototype, {
|
||||
});
|
||||
|
||||
// Draw incomplete features on each tile.
|
||||
var self = this;
|
||||
_.each(tiles, tile => {
|
||||
// Remove features already drawn on tile originally.
|
||||
var tile_incomplete_features = _.omit(
|
||||
@@ -4296,16 +4289,16 @@ extend(FeatureTrack.prototype, Drawable.prototype, TiledTrack.prototype, {
|
||||
data: _.values(tile_incomplete_features)
|
||||
};
|
||||
|
||||
var new_canvas = self.view.canvas_manager.new_canvas();
|
||||
var new_canvas = this.view.canvas_manager.new_canvas();
|
||||
var new_canvas_ctx = new_canvas.getContext("2d");
|
||||
new_canvas.height = Math.max(
|
||||
tile.canvas.height,
|
||||
self.get_canvas_height(features, tile.mode, tile.w_scale, 100)
|
||||
this.get_canvas_height(features, tile.mode, tile.w_scale, 100)
|
||||
);
|
||||
new_canvas.width = tile.canvas.width;
|
||||
new_canvas_ctx.drawImage(tile.canvas, 0, 0);
|
||||
new_canvas_ctx.translate(track.left_offset, 0);
|
||||
var new_tile = self.draw_tile(
|
||||
new_canvas_ctx.translate(this.left_offset, 0);
|
||||
var new_tile = this.draw_tile(
|
||||
features,
|
||||
new_canvas_ctx,
|
||||
tile.mode,
|
||||
@@ -4348,8 +4341,8 @@ extend(FeatureTrack.prototype, Drawable.prototype, TiledTrack.prototype, {
|
||||
//
|
||||
|
||||
// Update filtering UI.
|
||||
if (track.filters_manager) {
|
||||
var filters = track.filters_manager.filters;
|
||||
if (this.filters_manager) {
|
||||
var filters = this.filters_manager.filters;
|
||||
var f;
|
||||
for (f = 0; f < filters.length; f++) {
|
||||
filters[f].update_ui_elt();
|
||||
@@ -4361,7 +4354,7 @@ extend(FeatureTrack.prototype, Drawable.prototype, TiledTrack.prototype, {
|
||||
|
||||
var example_feature;
|
||||
var filter;
|
||||
for (i = 0; i < tiles.length; i++) {
|
||||
for (let i = 0; i < tiles.length; i++) {
|
||||
if (tiles[i].data.length) {
|
||||
example_feature = tiles[i].data[0];
|
||||
for (f = 0; f < filters.length; f++) {
|
||||
@@ -4375,12 +4368,12 @@ extend(FeatureTrack.prototype, Drawable.prototype, TiledTrack.prototype, {
|
||||
}
|
||||
|
||||
// If filter availability changed, hide filter div if necessary and update menu.
|
||||
if (track.filters_available !== filters_available) {
|
||||
track.filters_available = filters_available;
|
||||
if (!track.filters_available) {
|
||||
track.filters_manager.hide();
|
||||
if (this.filters_available !== filters_available) {
|
||||
this.filters_available = filters_available;
|
||||
if (!this.filters_available) {
|
||||
this.filters_manager.hide();
|
||||
}
|
||||
track.update_icons();
|
||||
this.update_icons();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4389,7 +4382,7 @@ extend(FeatureTrack.prototype, Drawable.prototype, TiledTrack.prototype, {
|
||||
//
|
||||
if (tiles[0] instanceof FeatureTrackTile) {
|
||||
var all_slotted = true;
|
||||
for (i = 0; i < tiles.length; i++) {
|
||||
for (let i = 0; i < tiles.length; i++) {
|
||||
if (!tiles[i].all_slotted) {
|
||||
all_slotted = false;
|
||||
break;
|
||||
@@ -4557,6 +4550,7 @@ extend(FeatureTrack.prototype, Drawable.prototype, TiledTrack.prototype, {
|
||||
);
|
||||
|
||||
var feature_mapper = null;
|
||||
var incomplete_features = null;
|
||||
|
||||
ctx.fillStyle = this.config.get_value("block_color");
|
||||
ctx.font = ctx.canvas.manager.default_font;
|
||||
|
||||
@@ -5,7 +5,6 @@ import util_mod from "viz/trackster/util";
|
||||
import config_mod from "utils/config";
|
||||
import GridView from "mvc/grid/grid-view";
|
||||
import Tabs from "mvc/ui/ui-tabs";
|
||||
import Ui from "mvc/ui/ui-misc";
|
||||
/**
|
||||
* Mixin for returning custom JSON representation from toJSON. Class attribute to_json_keys defines a set of attributes
|
||||
* to include in the representation; to_json_mappers defines mappers for returned objects.
|
||||
@@ -1109,12 +1108,11 @@ var TrackBrowserRouter = Backbone.Router.extend({
|
||||
// Can't put regular expression in routes dictionary.
|
||||
// NOTE: parentheses are used to denote parameters returned to callback.
|
||||
this.route(/([\w]+)$/, "change_location");
|
||||
this.route(/([\w\+]+\:[\d,]+-[\d,]+)$/, "change_location");
|
||||
this.route(/([\w+]+:[\d,]+-[\d,]+)$/, "change_location");
|
||||
|
||||
// Handle navigate events from view.
|
||||
var self = this;
|
||||
self.view.on("navigate", new_loc => {
|
||||
self.navigate(new_loc);
|
||||
this.view.on("navigate", new_loc => {
|
||||
this.navigate(new_loc);
|
||||
});
|
||||
},
|
||||
|
||||
|
||||
@@ -31,6 +31,7 @@
|
||||
@import "jstree.less";
|
||||
@import "tour.less";
|
||||
@import "flex.less";
|
||||
@import "charts.less";
|
||||
|
||||
// Mixins
|
||||
.unselectable {
|
||||
@@ -1177,8 +1178,15 @@ ul.manage-table-actions li {
|
||||
}
|
||||
|
||||
.state-color-deleted {
|
||||
border-color: @state-deleted-border;
|
||||
background: @state-deleted-bg;
|
||||
border-color: darken(@state-default-border, 30%);
|
||||
border-style: dotted;
|
||||
background: darken(@state-default-bg, 30%);
|
||||
}
|
||||
|
||||
.state-color-hidden {
|
||||
border-color: @state-default-border;
|
||||
border-style: dotted;
|
||||
background: @state-default-bg;
|
||||
}
|
||||
|
||||
.state-fg-new {
|
||||
@@ -1797,7 +1805,7 @@ div.toolTitleNoSection
|
||||
display: none;
|
||||
}
|
||||
|
||||
// communication channel bootstrap modal
|
||||
// communication channel bootstrap modal
|
||||
.chat-modal {
|
||||
overflow: hidden;
|
||||
}
|
||||
@@ -1812,7 +1820,7 @@ div.toolTitleNoSection
|
||||
}
|
||||
|
||||
.close-modal {
|
||||
float: right;
|
||||
float: right;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,128 @@
|
||||
.charts-client {
|
||||
.charts-buttons {
|
||||
float: right;
|
||||
margin: 5px 0px;
|
||||
z-index: 1000;
|
||||
.ui-button-icon {
|
||||
display: block;
|
||||
margin-right: 5px;
|
||||
}
|
||||
.ui-button-icon {
|
||||
background: white;
|
||||
color: #666;
|
||||
border-color: white;
|
||||
.icon {
|
||||
font-size: 1.2em;
|
||||
line-height: 1.2em;
|
||||
}
|
||||
}
|
||||
.ui-button-icon.charts-fullscreen-button {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
.charts-center {
|
||||
float: left;
|
||||
width: 70%;
|
||||
overflow: hidden;
|
||||
padding: 10px 5px 10px 10px;
|
||||
.charts-viewer {
|
||||
height: inherit;
|
||||
min-height: 50px;
|
||||
.info {
|
||||
position: absolute;
|
||||
margin: 40px 20px 50px 10px;
|
||||
width: 50%;
|
||||
}
|
||||
.text {
|
||||
position: relative;
|
||||
margin-left: 5px;
|
||||
top: -1px;
|
||||
font-size: 1.0em;
|
||||
display: inline;
|
||||
}
|
||||
.icon {
|
||||
font-size: 1.2em;
|
||||
display: inline-block;
|
||||
}
|
||||
.charts-viewer-container {
|
||||
float: left;
|
||||
display: block;
|
||||
height: 100%;
|
||||
}
|
||||
.charts-viewer-canvas {
|
||||
display: block;
|
||||
width: 100%;
|
||||
min-height: 100px;
|
||||
height: inherit;
|
||||
}
|
||||
}
|
||||
}
|
||||
.charts-right {
|
||||
float: left;
|
||||
width: 30%;
|
||||
height: 100vh;
|
||||
padding-bottom: 32px;
|
||||
overflow: hidden;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
.ui-message {
|
||||
margin: 5px;
|
||||
width: auto;
|
||||
}
|
||||
.charts-editor {
|
||||
display: block;
|
||||
flex: 1;
|
||||
overflow: auto;
|
||||
padding: 5px 10px 10px 5px;
|
||||
.charts-description {
|
||||
margin-bottom: 20px;
|
||||
.charts-description-image-td {
|
||||
vertical-align: top;
|
||||
}
|
||||
.charts-description-image {
|
||||
width: 50px;
|
||||
height: 43px;
|
||||
margin-right: 10px;
|
||||
}
|
||||
.charts-description-title {
|
||||
font-weight: bold;
|
||||
}
|
||||
.charts-description-text {
|
||||
margin-top: 5px;
|
||||
word-break: break-word;
|
||||
}
|
||||
}
|
||||
.tab-icon {
|
||||
margin-right: 0px;
|
||||
color: #666;
|
||||
font-size: 1.2em;
|
||||
}
|
||||
.portlet-content {
|
||||
padding: 0px;
|
||||
}
|
||||
.ui-table-section, .ui-portlet-panel .portlet-content {
|
||||
padding-left: 5px;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.charts-client.charts-fullscreen {
|
||||
.charts-buttons {
|
||||
position: absolute;
|
||||
right: 0px;
|
||||
.ui-button-icon,
|
||||
.ui-button-menu {
|
||||
display: none;
|
||||
}
|
||||
.ui-button-icon.charts-fullscreen-button {
|
||||
display: block;
|
||||
}
|
||||
}
|
||||
.charts-center {
|
||||
width: 100%;
|
||||
}
|
||||
.charts-right {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
@@ -1,5 +1,5 @@
|
||||
// Additional variables that are not used by bootstrap but are used
|
||||
// in Galaxy stylesheets
|
||||
// in Galaxy stylesheets
|
||||
|
||||
@base-bg: @white;
|
||||
@base-text-color: @black;
|
||||
@@ -29,8 +29,8 @@
|
||||
@state-running-border: #AAAA66;
|
||||
@state-running-bg: #FFFFCC;
|
||||
|
||||
@state-deleted-border: #330066;
|
||||
@state-deleted-bg: #3399FF;
|
||||
// @state-deleted-border: #330066;
|
||||
// @state-deleted-bg: #3399FF;
|
||||
|
||||
// Theme, expects tmp-site-config.less written by grunt
|
||||
@import "tmp-site-config.less";
|
||||
|
||||
@@ -294,6 +294,13 @@
|
||||
width: 100px;
|
||||
height: 86px;
|
||||
}
|
||||
.ui-thumbnails-icon {
|
||||
width: 100px;
|
||||
height: 86px;
|
||||
font-size: 4em;
|
||||
padding: 20px 26px;
|
||||
color: lighten(#25537b, 10%);
|
||||
}
|
||||
.ui-thumbnails-description-title {
|
||||
font-weight: bold;
|
||||
}
|
||||
@@ -301,7 +308,18 @@
|
||||
word-wrap: break-word;
|
||||
}
|
||||
}
|
||||
.ui-thumbnail-current {
|
||||
.ui-thumbnails-item:hover {
|
||||
.ui-thumbnails-title {
|
||||
text-decoration: underline;
|
||||
}
|
||||
.ui-thumbnails-description-title {
|
||||
text-decoration: underline;
|
||||
}
|
||||
.ui-thumbnails-description-text {
|
||||
text-decoration: underline;
|
||||
}
|
||||
}
|
||||
.ui-thumbnail-current-highlight {
|
||||
border-color:#66afe9;
|
||||
background-color:#EEEEFF;
|
||||
outline:0;
|
||||
@@ -468,6 +486,9 @@
|
||||
|
||||
.ui-form-composite {
|
||||
max-width: 900px;
|
||||
height: 100%;
|
||||
flex-direction: column;
|
||||
display: flex;
|
||||
.ui-form-header {
|
||||
&:extend(h3);
|
||||
margin-top: 0px;
|
||||
@@ -888,10 +909,6 @@
|
||||
float: right;
|
||||
width: ~'calc(100% - 76px)';
|
||||
}
|
||||
.ui-gs-token-textbox {
|
||||
float: right;
|
||||
width: ~'calc(100% - 76px)';
|
||||
}
|
||||
.ui-gs-browse-button {
|
||||
float: left;
|
||||
.ui-button-icon {
|
||||
@@ -899,15 +916,9 @@
|
||||
margin-right: 5px;
|
||||
}
|
||||
}
|
||||
.ui-gs-token-label {
|
||||
margin-top: 5px;
|
||||
margin-left: 32px;
|
||||
margin-right: 5px;
|
||||
float: left;
|
||||
}
|
||||
.ui-gs-token-field {
|
||||
clear: both;
|
||||
padding-top: 5px;
|
||||
.ui-gs-browse-field {
|
||||
height: @input-height-base;
|
||||
line-height: @line-height-base;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -40,7 +40,7 @@
|
||||
|
||||
.icon-btn-group {
|
||||
display: inline-block;
|
||||
.icon-btn:not(:last-child) {
|
||||
.icon-btn:not(:last-of-type) {
|
||||
margin: 0px;
|
||||
border-radius: 0px;
|
||||
border-right: none;
|
||||
|
||||
+1
-1
@@ -36,7 +36,7 @@
|
||||
"build-production": "NODE_ENV=production gulp stage-libs && concurrently \"yarn run style\" \"yarn run webpack-production\" \"yarn run gulp clean && yarn run gulp-production\" && yarn run save-build-hash",
|
||||
"build-production-maps": "NODE_ENV=production gulp stage-libs && concurrently \"yarn run style\" \"yarn run webpack-production-maps\" \"yarn run gulp clean && yarn run gulp-production-maps\" && yarn run save-build-hash",
|
||||
"webpack": "webpack -d",
|
||||
"save-build-hash": "git rev-parse HEAD > ../static/client_build_hash.txt",
|
||||
"save-build-hash": "(git rev-parse HEAD 2>/dev/null || echo '') >../static/client_build_hash.txt",
|
||||
"webpack-watch": "webpack -d --watch",
|
||||
"webpack-production": "webpack -p",
|
||||
"webpack-production-maps": "GXY_BUILD_SOURCEMAPS=1 webpack -p",
|
||||
|
||||
@@ -43,6 +43,7 @@ let buildconfig = {
|
||||
login: "./galaxy/scripts/apps/login.js",
|
||||
analysis: "./galaxy/scripts/apps/analysis.js",
|
||||
admin: "./galaxy/scripts/apps/admin.js",
|
||||
chart: "./galaxy/scripts/apps/chart.js",
|
||||
extended: "./galaxy/scripts/apps/extended.js"
|
||||
},
|
||||
output: {
|
||||
|
||||
@@ -10,16 +10,23 @@
|
||||
<datatype extension="fli" type="galaxy.datatypes.tabular:FeatureLocationIndex" display_in_upload="false"/>
|
||||
<datatype extension="bam" type="galaxy.datatypes.binary:Bam" mimetype="application/octet-stream" display_in_upload="true" description="A binary file compressed in the BGZF format with a '.bam' file extension." description_url="https://wiki.galaxyproject.org/Learn/Datatypes#BAM">
|
||||
<converter file="bam_to_bigwig_converter.xml" target_datatype="bigwig"/>
|
||||
<converter file="to_qname_sorted_bam.xml" target_datatype="qname_sorted.bam"/>
|
||||
<display file="ucsc/bam.xml" />
|
||||
<display file="ensembl/ensembl_bam.xml" />
|
||||
<display file="igv/bam.xml" />
|
||||
<display file="igb/bam.xml" />
|
||||
<display file="iobio/bam.xml" />
|
||||
</datatype>
|
||||
<datatype extension="bam_native" type="galaxy.datatypes.binary:BamNative" mimetype="application/octet-stream" display_in_upload="true" description="A binary file compressed in the BGZF format with a '.bam' file extension." description_url="https://wiki.galaxyproject.org/Learn/Datatypes#BAM">
|
||||
<converter file="bam_to_bigwig_converter.xml" target_datatype="bigwig"/>
|
||||
<converter file="bam_native_to_bam_converter.xml" target_datatype="bam"/>
|
||||
<datatype extension="qname_input_sorted.bam" type="galaxy.datatypes.binary:BamInputSorted" mimetype="application/octet-stream" display_in_upload="false" description="A binary file compressed in the BGZF format with a '.bam' file extension and sorted based on the aligner output." description_url="https://wiki.galaxyproject.org/Learn/Datatypes#BAM">
|
||||
</datatype>
|
||||
<datatype extension="qname_sorted.bam" type="galaxy.datatypes.binary:BamQuerynameSorted" mimetype="application/octet-stream" display_in_upload="true" description="A binary file compressed in the BGZF format with a '.bam' file extension and sorted by queryname." description_url="https://wiki.galaxyproject.org/Learn/Datatypes#BAM">
|
||||
</datatype>
|
||||
<datatype extension="unsorted.bam" type="galaxy.datatypes.binary:BamNative" mimetype="application/octet-stream" display_in_upload="true" description="A binary file compressed in the BGZF format with a '.bam' file extension." description_url="https://wiki.galaxyproject.org/Learn/Datatypes#BAM">
|
||||
<converter file="bam_to_bigwig_converter.xml" target_datatype="bigwig"/>
|
||||
<converter file="to_coordinate_sorted_bam.xml" target_datatype="bam"/>
|
||||
<converter file="to_qname_sorted_bam.xml" target_datatype="qname_sorted.bam"/>
|
||||
</datatype>
|
||||
<datatype extension="probam" type="galaxy.datatypes.binary:ProBam" mimetype="application/octet-stream" display_in_upload="true"/>
|
||||
<datatype extension="cram" type="galaxy.datatypes.binary:CRAM" mimetype="application/octet-stream" display_in_upload="true" description="CRAM is a file format for highly efficient and tunable reference-based compression of alignment data." description_url="http://www.ebi.ac.uk/ena/software/cram-usage">
|
||||
<converter file="cram_to_bam_converter.xml" target_datatype="bam"/>
|
||||
</datatype>
|
||||
@@ -40,6 +47,7 @@
|
||||
<datatype extension="bed6" type="galaxy.datatypes.interval:Bed6" display_in_upload="true">
|
||||
</datatype>
|
||||
<datatype extension="bed12" type="galaxy.datatypes.interval:Bed12" display_in_upload="true"/>
|
||||
<datatype extension="probed" type="galaxy.datatypes.interval:ProBed" display_in_upload="true"/>
|
||||
<datatype extension="len" type="galaxy.datatypes.chrominfo:ChromInfo" display_in_upload="true">
|
||||
<converter file="len_to_linecount.xml" target_datatype="linecount" />
|
||||
</datatype>
|
||||
@@ -66,6 +74,7 @@
|
||||
<datatype extension="bowtie_base_index" type="galaxy.datatypes.ngsindex:BowtieBaseIndex" mimetype="text/html" display_in_upload="false"/>
|
||||
<datatype extension="csfasta" type="galaxy.datatypes.sequence:csFasta" display_in_upload="true"/>
|
||||
<datatype extension="data" type="galaxy.datatypes.data:Data" mimetype="application/octet-stream" max_optional_metadata_filesize="1048576" />
|
||||
<datatype extension="binary" type="galaxy.datatypes.binary:Binary" mimetype="application/octet-stream" max_optional_metadata_filesize="1048576" />
|
||||
<datatype extension="d3_hierarchy" type="galaxy.datatypes.text:Json" mimetype="application/json" subclass="true" display_in_upload="true"/>
|
||||
<datatype extension="data_manager_json" type="galaxy.datatypes.text:Json" mimetype="application/json" subclass="true" display_in_upload="false"/>
|
||||
<datatype extension="dbn" type="galaxy.datatypes.sequence:DotBracket" display_in_upload="true" description="Dot-Bracket format is a text-based format for storing both an RNA sequence and its corresponding 2D structure." description_url="https://wiki.galaxyproject.org/Learn/Datatypes#Dbn"/>
|
||||
@@ -79,6 +88,9 @@
|
||||
<converter file="fasta_to_fai.xml" target_datatype="fai"/>
|
||||
<display file="igv/genome_fasta.xml" inherit="true"/>
|
||||
</datatype>
|
||||
<datatype extension="fasta.gz" type="galaxy.datatypes.sequence:FastaGz" display_in_upload="true">
|
||||
<converter file="fastagz_to_fasta.xml" target_datatype="fasta"/>
|
||||
</datatype>
|
||||
<datatype extension="fastq" type="galaxy.datatypes.sequence:Fastq" display_in_upload="true" description="FASTQ format is a text-based format for storing both a biological sequence (usually nucleotide sequence) and its corresponding quality scores." description_url="https://wiki.galaxyproject.org/Learn/Datatypes#Fastq">
|
||||
<converter file="fastq_to_fqtoc.xml" target_datatype="fqtoc"/>
|
||||
</datatype>
|
||||
@@ -288,8 +300,9 @@
|
||||
<datatype extension="qual454" type="galaxy.datatypes.qualityscore:QualityScore454" display_in_upload="true"/>
|
||||
<datatype extension="roadmaps" type="galaxy.datatypes.assembly:Roadmaps" display_in_upload="false"/>
|
||||
<datatype extension="sam" type="galaxy.datatypes.tabular:Sam" display_in_upload="true">
|
||||
<converter file="sam_to_bam_native.xml" target_datatype="bam_native"/>
|
||||
<converter file="sam_to_bam.xml" target_datatype="bam"/>
|
||||
<converter file="sam_to_unsorted_bam.xml" target_datatype="unsorted.bam"/>
|
||||
<converter file="to_coordinate_sorted_bam.xml" target_datatype="bam"/>
|
||||
<converter file="to_qname_sorted_bam.xml" target_datatype="qname_sorted.bam"/>
|
||||
<converter file="sam_to_bigwig_converter.xml" target_datatype="bigwig"/>
|
||||
</datatype>
|
||||
<datatype extension="scf" type="galaxy.datatypes.binary:Scf" mimetype="application/octet-stream" display_in_upload="true" description="A binary sequence file in 'scf' format with a '.scf' file extension. You must manually select this 'File Format' when uploading the file." description_url="https://wiki.galaxyproject.org/Learn/Datatypes#Scf"/>
|
||||
@@ -331,7 +344,7 @@
|
||||
<datatype extension="bcf_uncompressed" type="galaxy.datatypes.binary:BcfUncompressed" mimetype="application/octet-stream">
|
||||
<converter file="bcf_uncompressed_to_bcf_converter.xml" target_datatype="bcf"/>
|
||||
</datatype>
|
||||
<datatype extension="velvet" type="galaxy.datatypes.assembly:Velvet" display_in_upload="false"/>
|
||||
<datatype extension="velvet" type="galaxy.datatypes.assembly:Velvet" display_in_upload="true"/>
|
||||
<datatype extension="wig" type="galaxy.datatypes.interval:Wiggle" display_in_upload="true" description="The wiggle format is line-oriented. Wiggle data is preceded by a track definition line, which adds a number of options for controlling the default display of this track." description_url="https://wiki.galaxyproject.org/Learn/Datatypes#Wig">
|
||||
<converter file="wig_to_bigwig_converter.xml" target_datatype="bigwig"/>
|
||||
<converter file="wiggle_to_simple_converter.xml" target_datatype="interval"/>
|
||||
@@ -485,7 +498,7 @@
|
||||
<datatype extension="ncbi" type="galaxy.datatypes.data:Text" subclass="true"/>
|
||||
<datatype extension="nexus" type="galaxy.datatypes.data:Text" subclass="true"/>
|
||||
<datatype extension="nexusnon" type="galaxy.datatypes.data:Text" subclass="true"/>
|
||||
<datatype extension="phylip" type="galaxy.datatypes.phylip:Phylip" subclass="true" display_in_upload="true"/>
|
||||
<datatype extension="phylip" type="galaxy.datatypes.phylip:Phylip" display_in_upload="true"/>
|
||||
<datatype extension="phylipnon" type="galaxy.datatypes.data:Text" subclass="true"/>
|
||||
<datatype extension="pir" type="galaxy.datatypes.data:Text" subclass="true"/>
|
||||
<datatype extension="staden" type="galaxy.datatypes.data:Text" subclass="true"/>
|
||||
@@ -675,6 +688,9 @@
|
||||
<datatype extension="allegro_ihaplo" type="galaxy.datatypes.tabular:Tabular" />
|
||||
<datatype extension="allegro_descent" type="galaxy.datatypes.tabular:Tabular" />
|
||||
<datatype extension="allegro_fparam" type="galaxy.datatypes.genetics:AllegroLOD" />
|
||||
<!-- IDEAS datatypes -->
|
||||
<datatype extension="ideaspre" type="galaxy.datatypes.genetics:IdeasPre" display_in_upload="true"/>
|
||||
<!-- End IDEAS datatypes -->
|
||||
</registration>
|
||||
<sniffers>
|
||||
<!--
|
||||
@@ -763,6 +779,7 @@
|
||||
<!-- TODO: see molecules.py <sniffer type="galaxy.datatypes.molecules:SMILES"/>-->
|
||||
<sniffer type="galaxy.datatypes.phylip:Phylip"/>
|
||||
<sniffer type="galaxy.datatypes.sequence:Fasta"/>
|
||||
<sniffer type="galaxy.datatypes.sequence:FastaGz"/>
|
||||
<sniffer type="galaxy.datatypes.sequence:FastqSanger"/>
|
||||
<sniffer type="galaxy.datatypes.sequence:FastqSangerGz"/>
|
||||
<sniffer type="galaxy.datatypes.sequence:FastqSangerBz2"/>
|
||||
|
||||
@@ -133,6 +133,12 @@ galaxy:
|
||||
# than 5 milliseconds.
|
||||
#slow_query_log_threshold: 0
|
||||
|
||||
# Enable's a per request sql debugging option. If this is set to true,
|
||||
# append ?sql_debug=1 to web request URLs to enable detailed logging
|
||||
# on the backend of SQL queries generated during that request. This is
|
||||
# useful for debugging slow endpoints during development.
|
||||
#enable_per_request_sql_debugging: false
|
||||
|
||||
# By default, Galaxy will use the same database to track user data and
|
||||
# tool shed install data. There are many situations in which it is
|
||||
# valuable to separate these - for instance bootstrapping fresh Galaxy
|
||||
@@ -219,7 +225,7 @@ galaxy:
|
||||
|
||||
# conda channels to enable by default (http://conda.pydata.org/docs
|
||||
# /custom-channels.html)
|
||||
#conda_ensure_channels: 'iuc,bioconda,conda-forge,defaults,r'
|
||||
#conda_ensure_channels: 'iuc,bioconda,conda-forge,defaults'
|
||||
|
||||
# Set to True to instruct Galaxy to look for and install missing tool
|
||||
# dependencies before each job runs.
|
||||
@@ -618,6 +624,14 @@ galaxy:
|
||||
# standard).
|
||||
#pretty_datetime_format: $locale (UTC)
|
||||
|
||||
# Default localization for Galaxy UI. Allowed values are listed at the
|
||||
# end of client/galaxy/scripts/nls/locale.js. With the default value
|
||||
# (auto), the locale will be automatically adjusted to the user's
|
||||
# navigator language. Users can override this settings in their user
|
||||
# preferences if the localization settings are enabled in
|
||||
# user_preferences_extra_conf.yml
|
||||
#default_locale: auto
|
||||
|
||||
# URL (with schema http/https) of the Galaxy instance as accessible
|
||||
# within your local network - if specified used as a default by pulsar
|
||||
# file staging and Jupyter Docker container for communicating back
|
||||
@@ -798,6 +812,11 @@ galaxy:
|
||||
# documentation for the corresponding nginx configuration.
|
||||
#nginx_upload_job_files_path: false
|
||||
|
||||
# Galaxy can upload user files in chunks without using nginx. Enable
|
||||
# the chunk uploader by specifying a chunk size larger than 0. The
|
||||
# chunk size is specified in bytes (default: 100MB).
|
||||
#chunk_upload_size: 104857600
|
||||
|
||||
# Have Galaxy manage dynamic proxy component for routing requests to
|
||||
# other services based on Galaxy's session cookie. It will attempt to
|
||||
# do this by default though you do need to install node+npm and do an
|
||||
@@ -990,6 +1009,13 @@ galaxy:
|
||||
# <project_name> -> Settings -> API Keys.
|
||||
#sentry_dsn: null
|
||||
|
||||
# Sentry slow request logging. Requests slower than the threshold
|
||||
# indicated below will be sent as events to the configured Sentry
|
||||
# server (above, sentry_dsn). A value of '0' is disabled. For
|
||||
# example, you would set this to .005 to log all queries taking longer
|
||||
# than 5 milliseconds.
|
||||
#sentry_sloreq_threshold: 0
|
||||
|
||||
# Log to statsd Statsd is an external statistics aggregator
|
||||
# (https://github.com/etsy/statsd) Enabling the following options will
|
||||
# cause galaxy to log request timing and other statistics to the
|
||||
@@ -1146,6 +1172,15 @@ galaxy:
|
||||
# Set maximum size of ngrams
|
||||
#tool_ngram_maxsize: 4
|
||||
|
||||
# Set tool test data directory. The test framework sets this value to
|
||||
# 'test-data,https://github.com/galaxyproject/galaxy-test-data.git'
|
||||
# which will cause Galaxy to clone down extra test data on the fly for
|
||||
# certain tools distributed with Galaxy but this is likely not
|
||||
# appropriate for production systems. Instead one can simply clone
|
||||
# that repository directly and specify a path here instead of a Git
|
||||
# HTTP repository.
|
||||
#tool_test_data_directories: test-data
|
||||
|
||||
# Galaxy encodes various internal values when these values will be
|
||||
# output in some format (for example, in a URL or cookie). You should
|
||||
# set a key to be used by the algorithm that encodes and decodes these
|
||||
|
||||
@@ -15,7 +15,7 @@
|
||||
<!-- Core plugin captures Galaxy slots, start and end of job (in seconds
|
||||
since epoch) and computes runtime in seconds. -->
|
||||
<core />
|
||||
|
||||
|
||||
<!-- Uncomment to dump processor count for each job - linux only. -->
|
||||
<!-- <cpuinfo /> -->
|
||||
<!-- Uncomment to dump information about all processors for for each
|
||||
@@ -30,13 +30,18 @@
|
||||
only. -->
|
||||
<!-- <uname /> -->
|
||||
|
||||
<!-- Uncomment following to enable plugin dumping complete environment
|
||||
<!-- Uncomment following to enable plugin dumping complete environment
|
||||
for each job, potentially useful for debuging -->
|
||||
<!-- <env /> -->
|
||||
<!-- env plugin can also record more targetted, obviously useful variables
|
||||
as well. -->
|
||||
<!-- <env variables="HOSTNAME,SLURM_CPUS_ON_NODE,SLURM_JOBID" /> -->
|
||||
|
||||
<!-- If galaxy jobs are run in cgroups, like slurm does if memory limits
|
||||
are enforced, we can try to grep some information from this. -->
|
||||
<!-- <cgroup /> -->
|
||||
<!-- <cgroup verbose="true" /> -->
|
||||
|
||||
<!-- <collectl /> -->
|
||||
<!-- Collectl (http://collectl.sourceforge.net/) is a powerful monitoring
|
||||
utility capable of gathering numerous system and process level
|
||||
@@ -46,7 +51,7 @@
|
||||
is highly customiziable - both using the attributes documented below
|
||||
or simply hacking up the code in lib/galaxy/jobs/metrics.
|
||||
|
||||
Warning: In order to use this plugin collectl must be available on the
|
||||
Warning: In order to use this plugin collectl must be available on the
|
||||
compute server the job runs on and on the local Galaxy server as well
|
||||
(unless in this latter case summarize_process_data is set to False).
|
||||
|
||||
@@ -64,12 +69,12 @@
|
||||
full time-series data corresponding to a job run.
|
||||
|
||||
'subsystems': Comma separated list of collectl subystems to collect
|
||||
data for. Plugin doesn't currently expose all of them or offer
|
||||
summary data for any of them except 'process' but extensions
|
||||
would be welcome. May seem pointless to include subsystems
|
||||
data for. Plugin doesn't currently expose all of them or offer
|
||||
summary data for any of them except 'process' but extensions
|
||||
would be welcome. May seem pointless to include subsystems
|
||||
beside process since they won't be processed online by Galaxy -
|
||||
but if 'saved_logs_path' these files can be played back at anytime.
|
||||
|
||||
|
||||
Available subsystems - 'process', 'cpu', 'memory', 'network',
|
||||
'disk', 'network'. (Default 'process').
|
||||
|
||||
@@ -108,13 +113,13 @@
|
||||
|
||||
'flush': Interval (in seconds I think) between when collectl will
|
||||
flush its buffer to disk. Galaxy overrides this to disable
|
||||
flushing by default if not set.
|
||||
flushing by default if not set.
|
||||
|
||||
'local_collectl_path', 'remote_collectl_path', 'collectl_path':
|
||||
By default, jobs will just assume collectl is on the PATH, but
|
||||
it can be overridden with 'local_collectl_path' and
|
||||
'remote_collectl_path' (or simply 'collectl_path' if it is not
|
||||
on the path but installed in the same location both locally and
|
||||
By default, jobs will just assume collectl is on the PATH, but
|
||||
it can be overridden with 'local_collectl_path' and
|
||||
'remote_collectl_path' (or simply 'collectl_path' if it is not
|
||||
on the path but installed in the same location both locally and
|
||||
remotely).
|
||||
|
||||
There are more and more increasingly obsecure options including -
|
||||
|
||||
@@ -1,41 +0,0 @@
|
||||
{
|
||||
"name": "galaxy-charts-plugins",
|
||||
"version": "0.1.0",
|
||||
"description": "Charts visualization plugin.",
|
||||
"keywords": [
|
||||
"galaxy",
|
||||
"visualization",
|
||||
"d3"
|
||||
],
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/guerler/galaxy-charts"
|
||||
},
|
||||
"license": "AFL-3.0",
|
||||
"dependencies": {
|
||||
"amdi18n-loader": "^0.2.0",
|
||||
"grunt": "^0.4.5",
|
||||
"grunt-bower-install-simple": "^1.1.0",
|
||||
"grunt-check-modules": "^1.0.0",
|
||||
"grunt-cli": "^0.1.13",
|
||||
"grunt-contrib-clean": "^0.6.0",
|
||||
"grunt-contrib-copy": "^0.5.0",
|
||||
"grunt-contrib-less": "^1.1.0",
|
||||
"grunt-contrib-uglify": "^0.8.0",
|
||||
"grunt-contrib-watch": "^0.6.1",
|
||||
"grunt-exec": "^0.4.6",
|
||||
"grunt-spritesmith": "^4.7.1",
|
||||
"i18n-webpack-plugin": "^0.2.7",
|
||||
"webpack": "^1.10.1",
|
||||
"webpack-dev-server": "^1.7.0",
|
||||
"backbone": "^1.3.3",
|
||||
"bootstrap": "^3.3.7",
|
||||
"cytoscape": "^2.7.10",
|
||||
"jquery": "^3.1.1",
|
||||
"cytoscape-edgehandles": "^2.6.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"css-loader": "^0.24.0",
|
||||
"style-loader": "^0.13.1"
|
||||
}
|
||||
}
|
||||
@@ -1,83 +0,0 @@
|
||||
.charts-viewer {
|
||||
overflow: auto;
|
||||
}
|
||||
|
||||
.charts-viewer .ui-message {
|
||||
position: absolute;
|
||||
width: calc(100% - 20px);
|
||||
z-index: 100;
|
||||
}
|
||||
|
||||
.charts-description {
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
.charts-description-image-td {
|
||||
vertical-align: top;
|
||||
}
|
||||
|
||||
.charts-description-image {
|
||||
width: 50px;
|
||||
height: 43px;
|
||||
margin-right: 10px;
|
||||
}
|
||||
|
||||
.charts-description-title {
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
.charts-description-text {
|
||||
word-wrap: break-word;
|
||||
}
|
||||
|
||||
.charts-tooltip {
|
||||
position: absolute;
|
||||
text-align: center;
|
||||
padding: 3px 5px 3px 5px;
|
||||
font: 12px sans-serif;
|
||||
background: white;
|
||||
border: 1px solid gray;
|
||||
border-radius: 4px;
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
.charts-tooltip-first {
|
||||
font-weight: bold;
|
||||
text-align: left;
|
||||
padding-right: 5px;
|
||||
}
|
||||
|
||||
.charts-viewport {
|
||||
height: inherit;
|
||||
min-height: 50px;
|
||||
}
|
||||
|
||||
.charts-viewport .info {
|
||||
position: absolute;
|
||||
margin: 40px 20px 50px 10px;
|
||||
}
|
||||
|
||||
.charts-viewport .text {
|
||||
position: relative;
|
||||
margin-left: 5px;
|
||||
top: -1px;
|
||||
font-size: 1.0em;
|
||||
display: inline;
|
||||
}
|
||||
|
||||
.charts-viewport .icon {
|
||||
font-size: 1.2em;
|
||||
display: inline-block;
|
||||
}
|
||||
|
||||
.charts-viewport .charts-viewport-container {
|
||||
float: left;
|
||||
display: block;
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
.charts-viewport .charts-viewport-canvas {
|
||||
display: block;
|
||||
width: 100%;
|
||||
min-height: 100px;
|
||||
height: inherit;
|
||||
}
|
||||
@@ -1,93 +0,0 @@
|
||||
/**
|
||||
* Main application class.
|
||||
*/
|
||||
define(
|
||||
[
|
||||
"mvc/ui/ui-modal",
|
||||
"mvc/ui/ui-portlet",
|
||||
"mvc/ui/ui-misc",
|
||||
"utils/utils",
|
||||
"plugin/components/model",
|
||||
"utils/deferred",
|
||||
"plugin/views/viewer",
|
||||
"plugin/views/editor"
|
||||
],
|
||||
function(Modal, Portlet, Ui, Utils, Chart, Deferred, Viewer, Editor) {
|
||||
return Backbone.View.extend({
|
||||
initialize: function(options) {
|
||||
var self = this;
|
||||
require(["repository/build/registry"], function(Registry) {
|
||||
console.debug("app::initialize() - Loaded Registry:");
|
||||
console.debug(Registry);
|
||||
Utils.get({
|
||||
url: Galaxy.root + "api/datasets/" + options.dataset_id,
|
||||
success: function(dataset) {
|
||||
self.dataset = dataset;
|
||||
self.types = {};
|
||||
_.each(Registry, function(type, type_id) {
|
||||
if (!type.datatypes || type.datatypes.indexOf(dataset.file_ext) != -1) {
|
||||
self.types[type_id] = type;
|
||||
}
|
||||
});
|
||||
if (_.size(self.types) === 0) {
|
||||
self.$el.append(
|
||||
$("<div/>")
|
||||
.addClass("errormessagelarge")
|
||||
.append(
|
||||
$("<p/>").text(
|
||||
"Unfortunately we could not identify a suitable plugin. Feel free to contact us if you are aware of visualizations for this datatype."
|
||||
)
|
||||
)
|
||||
);
|
||||
} else {
|
||||
self._build(options);
|
||||
}
|
||||
}
|
||||
});
|
||||
}, function(err) {
|
||||
self.$el.append(
|
||||
$("<div/>")
|
||||
.addClass("errormessagelarge")
|
||||
.append($("<p/>").text("Unable to access the plugin repository:"))
|
||||
.append($("<pre/>").text("charts_repository_url = " + repository_root))
|
||||
.append(
|
||||
$("<p/>").html(
|
||||
"Please verify that your internet connection works properly and that the above url is correct. Contact your admin if this error persists."
|
||||
)
|
||||
)
|
||||
);
|
||||
});
|
||||
},
|
||||
|
||||
_build: function(options) {
|
||||
this.options = options;
|
||||
this.modal = (parent.Galaxy && parent.Galaxy.modal) || new Modal.View();
|
||||
this.chart = new Chart({}, options);
|
||||
this.deferred = new Deferred.default();
|
||||
this.viewer = new Viewer(this);
|
||||
this.editor = new Editor(this);
|
||||
this.$el.append(this.viewer.$el);
|
||||
this.$el.append(this.editor.$el);
|
||||
this.go(this.chart.load() ? "viewer" : "editor");
|
||||
},
|
||||
|
||||
/** Loads a view and makes sure that all others are hidden */
|
||||
go: function(view_id) {
|
||||
$(".tooltip").hide();
|
||||
this.viewer.hide();
|
||||
this.editor.hide();
|
||||
this[view_id].show();
|
||||
},
|
||||
|
||||
/** Split chart type into path components */
|
||||
split: function(chart_type) {
|
||||
var path = chart_type.split(/_(.+)/);
|
||||
if (path.length >= 2) {
|
||||
return path[0] + "/" + path[1];
|
||||
} else {
|
||||
return chart_type;
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
);
|
||||
@@ -1,101 +0,0 @@
|
||||
define(["utils/utils", "mvc/visualization/visualization-model"], function(Utils) {
|
||||
return Backbone.Model.extend({
|
||||
defaults: {
|
||||
title: "",
|
||||
type: "",
|
||||
date: null,
|
||||
state: "",
|
||||
state_info: "",
|
||||
modified: false,
|
||||
dataset_id: "",
|
||||
dataset_id_job: ""
|
||||
},
|
||||
|
||||
initialize: function(options, viz_options) {
|
||||
this.groups = new Backbone.Collection();
|
||||
this.settings = new Backbone.Model();
|
||||
this.definition = {};
|
||||
this.viz_options = viz_options;
|
||||
console.debug("model::initialize() - Initialized with configuration:");
|
||||
console.debug(viz_options);
|
||||
},
|
||||
|
||||
reset: function() {
|
||||
this.clear().set({
|
||||
title: "New Chart",
|
||||
type: "__first",
|
||||
dataset_id: this.viz_options.dataset_id
|
||||
});
|
||||
this.settings.clear();
|
||||
this.groups.reset();
|
||||
this.groups.add({ id: Utils.uid() });
|
||||
},
|
||||
|
||||
state: function(value, info) {
|
||||
this.set({ state: value, state_info: info });
|
||||
this.trigger("set:state");
|
||||
console.debug("model::state() - " + info + " (" + value + ")");
|
||||
},
|
||||
|
||||
/** Pack and save nested chart model */
|
||||
save: function(options) {
|
||||
var self = this;
|
||||
options = options || {};
|
||||
var chart_dict = {
|
||||
attributes: this.attributes,
|
||||
settings: this.settings.attributes,
|
||||
groups: []
|
||||
};
|
||||
this.groups.each(function(group) {
|
||||
chart_dict.groups.push(group.attributes);
|
||||
});
|
||||
var viz = new Visualization({
|
||||
id: this.viz_options.visualization_id || undefined,
|
||||
type: "charts",
|
||||
title: this.get("title") || "",
|
||||
config: {
|
||||
dataset_id: this.viz_options.dataset_id,
|
||||
chart_dict: (this.viz_options.chart_dict = chart_dict)
|
||||
}
|
||||
});
|
||||
viz
|
||||
.save()
|
||||
.then(function(response) {
|
||||
if (response && response.id) {
|
||||
self.viz_options.visualization_id = response.id;
|
||||
options.success && options.success();
|
||||
console.debug("model::save() - Received visualization id: " + response.id);
|
||||
} else {
|
||||
options.error && options.error();
|
||||
console.debug("model::save() - Unrecognized response. Saving may have failed.");
|
||||
}
|
||||
})
|
||||
.fail(function(response) {
|
||||
options.error && options.error();
|
||||
console.debug("model::save() - Saving failed.");
|
||||
});
|
||||
console.debug("model::save() - Saved with configuration:");
|
||||
console.debug(this.viz_options);
|
||||
},
|
||||
|
||||
/** Load nested models/collections from packed dictionary */
|
||||
load: function() {
|
||||
console.debug("model::load() - Attempting to load with configuration:");
|
||||
console.debug(this.viz_options);
|
||||
var chart_dict = this.viz_options.chart_dict;
|
||||
if (chart_dict && chart_dict.attributes) {
|
||||
this.set(chart_dict.attributes);
|
||||
this.state("ok", "Loading saved visualization...");
|
||||
this.settings.set(chart_dict.settings);
|
||||
this.groups.reset();
|
||||
this.groups.add(chart_dict.groups);
|
||||
this.set("modified", false);
|
||||
console.debug("model::load() - Loading chart model " + chart_dict.attributes.type + ".");
|
||||
this.trigger("redraw");
|
||||
return true;
|
||||
}
|
||||
console.debug("model::load() - Visualization attributes unavailable.");
|
||||
return false;
|
||||
}
|
||||
});
|
||||
});
|
||||
@@ -1,188 +0,0 @@
|
||||
/** This class enables users to export/download a chart as PNG, SVG or PDF. */
|
||||
define(["libs/underscore"], function(_) {
|
||||
/** PNG export */
|
||||
function createPNG(options) {
|
||||
if (options.$el.find("svg").length > 0) {
|
||||
_svg2png(options);
|
||||
} else {
|
||||
_canvas2png(options.$el.find(".charts-viewport-canvas"));
|
||||
}
|
||||
}
|
||||
|
||||
/** Convert canvas to png */
|
||||
function _canvas2png($canvas) {
|
||||
try {
|
||||
if ($canvas.width() !== 0 && $canvas.height() !== 0) {
|
||||
var newCanvas = document.createElement("canvas");
|
||||
newCanvas.width = $canvas.outerWidth(true);
|
||||
newCanvas.height = $canvas.outerHeight(true);
|
||||
var newContext = newCanvas.getContext("2d");
|
||||
newContext.save();
|
||||
newContext.fillStyle = "rgb(255,255,255)";
|
||||
newContext.fillRect(0, 0, newCanvas.width, newCanvas.height);
|
||||
newContext.restore();
|
||||
newContext.translate(0, 0);
|
||||
newContext.textAlign = "left";
|
||||
newContext.textBaseline = "top";
|
||||
function _toImage($el, x_offset, y_offset) {
|
||||
var tagname = $el.prop("tagName").toLowerCase();
|
||||
var p = $el.position();
|
||||
var left =
|
||||
x_offset +
|
||||
p.left +
|
||||
parseInt($el.css("marginLeft"), 10) +
|
||||
parseInt($el.css("borderLeftWidth"), 10) +
|
||||
parseInt($el.css("paddingLeft"), 10);
|
||||
var top =
|
||||
y_offset +
|
||||
p.top +
|
||||
parseInt($el.css("marginTop"), 10) +
|
||||
parseInt($el.css("borderTopWidth"), 10) +
|
||||
parseInt($el.css("paddingTop"), 10);
|
||||
var w = newCanvas.width;
|
||||
if (tagname == "div" || tagname == "span") {
|
||||
$el.children().each(function() {
|
||||
_toImage($(this), left, top);
|
||||
});
|
||||
} else if (tagname == "canvas") {
|
||||
newContext.drawImage($el[0], left, top);
|
||||
}
|
||||
}
|
||||
$canvas.children().each(function() {
|
||||
_toImage($(this), 0, 0);
|
||||
});
|
||||
var imgData = newCanvas.toDataURL("image/png");
|
||||
if (imgData) {
|
||||
window.location.href = imgData.replace("image/png", "image/octet-stream");
|
||||
}
|
||||
}
|
||||
} catch (err) {
|
||||
console.debug("FAILED - screenshot::_canvas2png() - " + err);
|
||||
if (options.error) {
|
||||
options.error("Please reduce your visualization to a single panel and try again.");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/** Convert svg to png */
|
||||
function _svg2png(options) {
|
||||
var scale = 5;
|
||||
var xml = toXML(options);
|
||||
var canvas = document.createElement("canvas");
|
||||
var context = canvas.getContext("2d");
|
||||
var source = new Image();
|
||||
var $container = $('<div style="display:none;"/>').append($(canvas));
|
||||
$("body").append($container);
|
||||
canvas.width = xml.width * scale;
|
||||
canvas.height = xml.height * scale;
|
||||
source.src = "data:image/svg+xml;base64," + btoa(xml.string);
|
||||
source.onload = function() {
|
||||
context.drawImage(source, 0, 0, canvas.width, canvas.height);
|
||||
window.location.href = canvas.toDataURL("image/png").replace("image/png", "image/octet-stream");
|
||||
$container.remove();
|
||||
};
|
||||
}
|
||||
|
||||
/** SVG export */
|
||||
function createSVG(options) {
|
||||
window.location.href = "data:none/none;base64," + btoa(toXML(options).string);
|
||||
}
|
||||
|
||||
/** PDF export */
|
||||
function createPDF(options) {
|
||||
var xml = toXML(options);
|
||||
var data = {
|
||||
filename: "visualization",
|
||||
type: "application/pdf",
|
||||
height: xml.height,
|
||||
width: xml.width,
|
||||
scale: 2,
|
||||
svg: xml.string
|
||||
};
|
||||
var $el = $("body");
|
||||
var form = $el.find("#viewport-form");
|
||||
if (form.length === 0) {
|
||||
form = $("<form>", {
|
||||
id: "viewport-form",
|
||||
method: "post",
|
||||
action: "http://export.highcharts.com/",
|
||||
display: "none"
|
||||
});
|
||||
$el.append(form);
|
||||
}
|
||||
form.empty();
|
||||
for (var name in data) {
|
||||
var input = $("<input/>", {
|
||||
type: "hidden",
|
||||
name: name,
|
||||
value: data[name]
|
||||
});
|
||||
form.append(input);
|
||||
}
|
||||
try {
|
||||
form.submit();
|
||||
} catch (err) {
|
||||
console.log(err);
|
||||
}
|
||||
}
|
||||
|
||||
/** XML export */
|
||||
function toXML(options) {
|
||||
var $svg = options.$el.find("svg");
|
||||
if ($svg.length == 0) {
|
||||
if (options.error) {
|
||||
options.error("No SVG found. This visualization type does not support SVG/PDF export.");
|
||||
return;
|
||||
}
|
||||
}
|
||||
var $el = options.$el;
|
||||
var nsvgs = $svg.length;
|
||||
var height = parseInt($svg.first().css("height"));
|
||||
var width = parseInt($svg.first().css("width"));
|
||||
var serializer = new XMLSerializer();
|
||||
var $composite = $("<svg/>").attr({
|
||||
version: "1.1",
|
||||
xmlns: "http://www.w3.org/2000/svg",
|
||||
width: width * nsvgs,
|
||||
height: height
|
||||
});
|
||||
var xmlString = "";
|
||||
var offsetX = 0;
|
||||
$svg.each(function() {
|
||||
var $svg = $(this).clone();
|
||||
_inline($svg);
|
||||
var $g = $('<g transform="translate(' + offsetX + ', 0)">').attr("xmlns", "http://www.w3.org/2000/svg");
|
||||
$g.append($svg.find("g").first());
|
||||
$composite.append($g);
|
||||
offsetX += width;
|
||||
});
|
||||
return {
|
||||
string: serializer.serializeToString($composite[0]),
|
||||
height: height,
|
||||
width: width
|
||||
};
|
||||
}
|
||||
|
||||
/** inlines CSS code */
|
||||
function _inline($target) {
|
||||
for (var sheet_id in document.styleSheets) {
|
||||
var sheet = document.styleSheets[sheet_id];
|
||||
var rules = sheet.cssRules;
|
||||
if (rules) {
|
||||
for (var idx = 0, len = rules.length; idx < len; idx++) {
|
||||
try {
|
||||
$target.find(rules[idx].selectorText).each(function(i, elem) {
|
||||
elem.style.cssText += rules[idx].style.cssText;
|
||||
});
|
||||
} catch (err) {}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
createPNG: createPNG,
|
||||
createSVG: createSVG,
|
||||
createPDF: createPDF
|
||||
};
|
||||
});
|
||||
@@ -1,48 +0,0 @@
|
||||
/** This class renders the chart configuration form. */
|
||||
define(["utils/utils"], function(Utils) {
|
||||
return Backbone.View.extend({
|
||||
initialize: function(app, options) {
|
||||
var self = this;
|
||||
this.chart = app.chart;
|
||||
this.app = app;
|
||||
this.setElement(this._template());
|
||||
this.$title = this.$(".charts-description-title");
|
||||
this.$image = this.$(".charts-description-image");
|
||||
this.$text = this.$(".charts-description-text");
|
||||
this.listenTo(this.chart, "change", function() {
|
||||
self.render();
|
||||
});
|
||||
this.render();
|
||||
},
|
||||
render: function() {
|
||||
if (this.chart.get("type")) {
|
||||
this.$image.attr(
|
||||
"src",
|
||||
repository_root + "/visualizations/" + this.app.split(this.chart.get("type")) + "/logo.png"
|
||||
);
|
||||
this.$title.html(this.chart.definition.title + " (" + this.chart.definition.library + ")");
|
||||
this.$text.html(Utils.linkify(this.chart.definition.description || ""));
|
||||
this.$el.show();
|
||||
} else {
|
||||
this.$el.hide();
|
||||
}
|
||||
},
|
||||
_template: function() {
|
||||
return (
|
||||
'<div class="charts-description">' +
|
||||
"<table>" +
|
||||
"<tr>" +
|
||||
'<td class="charts-description-image-td">' +
|
||||
'<img class="charts-description-image"/>' +
|
||||
"</td>" +
|
||||
"<td>" +
|
||||
'<div class="charts-description-title ui-form-info"/>' +
|
||||
'<div class="charts-description-text ui-form-info"/>' +
|
||||
"</td>" +
|
||||
"</tr>" +
|
||||
"</table>" +
|
||||
"</div>"
|
||||
);
|
||||
}
|
||||
});
|
||||
});
|
||||
@@ -1,208 +0,0 @@
|
||||
/**
|
||||
* The charts editor holds the tabs for selecting chart types, chart configuration
|
||||
* and data group selections.
|
||||
*/
|
||||
define(
|
||||
[
|
||||
"mvc/ui/ui-tabs",
|
||||
"mvc/ui/ui-misc",
|
||||
"mvc/ui/ui-portlet",
|
||||
"mvc/ui/ui-thumbnails",
|
||||
"utils/utils",
|
||||
"plugin/views/settings",
|
||||
"plugin/views/groups"
|
||||
],
|
||||
function(Tabs, Ui, Portlet, Thumbnails, Utils, SettingsView, GroupsView) {
|
||||
return Backbone.View.extend({
|
||||
initialize: function(app, options) {
|
||||
var self = this;
|
||||
this.app = app;
|
||||
this.chart = this.app.chart;
|
||||
this.message = new Ui.Message({ cls: "ui-margin-bottom" });
|
||||
this.portlet = new Portlet.View({
|
||||
icon: "fa-bar-chart-o",
|
||||
title: "Editor",
|
||||
operations: {
|
||||
draw: new Ui.ButtonIcon({
|
||||
icon: "fa-line-chart",
|
||||
tooltip: "Render Visualization",
|
||||
title: "Visualize",
|
||||
onclick: function() {
|
||||
self._drawChart();
|
||||
}
|
||||
}),
|
||||
back: new Ui.ButtonIcon({
|
||||
icon: "fa-caret-left",
|
||||
tooltip: "Return to Viewer",
|
||||
title: "Cancel",
|
||||
onclick: function() {
|
||||
self.app.go("viewer");
|
||||
self.chart.load();
|
||||
}
|
||||
})
|
||||
}
|
||||
});
|
||||
|
||||
// grid with chart types
|
||||
this.types = new Thumbnails.View({
|
||||
title_default: "Suggested visualizations",
|
||||
title_list: "List of available visualizations",
|
||||
collection: _.map(this.app.types, function(type, type_id) {
|
||||
return {
|
||||
id: type_id,
|
||||
keywords: type.keywords,
|
||||
title: type.title + " (" + type.library + ")",
|
||||
title_icon: type.zoomable && "fa-search-plus",
|
||||
image_src: repository_root + "/visualizations/" + self.app.split(type_id) + "/logo.png",
|
||||
description: type.description
|
||||
};
|
||||
}),
|
||||
ondblclick: function(chart_type) {
|
||||
self._drawChart();
|
||||
},
|
||||
onchange: function(chart_type) {
|
||||
var chart_definition = self.app.types[chart_type];
|
||||
if (!chart_definition) {
|
||||
self.tabs.hideTab("settings");
|
||||
self.tabs.hideTab("groups");
|
||||
self.portlet.hideOperation("draw");
|
||||
console.debug("editor::onchange() - Chart type not found.");
|
||||
self.message.update({
|
||||
message:
|
||||
"The requested visualization type could not be found. Please select a new type from below or contact us.",
|
||||
status: "danger",
|
||||
persistent: true
|
||||
});
|
||||
} else {
|
||||
self.tabs.showTab("settings");
|
||||
self.tabs.showTab("groups");
|
||||
self.portlet.showOperation("draw");
|
||||
self.chart.definition = chart_definition;
|
||||
self.chart.set({ type: chart_type, modified: true });
|
||||
self.message.model.set("message", "");
|
||||
console.debug("editor::onchange() - Switched visualization type.");
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
// input field for chart title
|
||||
this.title = new Ui.Input({
|
||||
placeholder: "Chart title",
|
||||
onchange: function() {
|
||||
self.chart.set("title", self.title.value());
|
||||
}
|
||||
});
|
||||
|
||||
// create tabs
|
||||
this.tabs = new Tabs.View({});
|
||||
this.tabs.add({
|
||||
id: "main",
|
||||
title: "Start",
|
||||
icon: "fa fa-bars",
|
||||
tooltip: "Start by selecting a visualization.",
|
||||
$el: $("<div/>")
|
||||
.append(new Ui.Label({ title: "Provide a title:" }).$el)
|
||||
.append(this.title.$el)
|
||||
.append(
|
||||
$("<div/>")
|
||||
.addClass("ui-form-info ui-margin-bottom")
|
||||
.html("This title will appear in the list of 'Saved Visualizations'.")
|
||||
)
|
||||
.append(new Ui.Label({ title: "Select a visualization:" }).$el.addClass("ui-margin-top"))
|
||||
.append(this.types.$el)
|
||||
});
|
||||
this.tabs.add({
|
||||
id: "settings",
|
||||
title: "Customize",
|
||||
icon: "fa-gear",
|
||||
tooltip: "Customize the visualization.",
|
||||
$el: new SettingsView(this.app).$el
|
||||
});
|
||||
this.tabs.add({
|
||||
id: "groups",
|
||||
title: "Select data",
|
||||
icon: "fa-database",
|
||||
tooltip: "Specify data options.",
|
||||
$el: new GroupsView(this.app).$el
|
||||
});
|
||||
|
||||
// set elements
|
||||
this.portlet.append(this.message.$el);
|
||||
this.portlet.append(this.tabs.$el.addClass("ui-margin-top-large"));
|
||||
this.portlet.hideOperation("back");
|
||||
this.setElement(this.portlet.$el);
|
||||
|
||||
// chart events
|
||||
this.listenTo(this.chart, "change:title", function(chart) {
|
||||
self._refreshTitle();
|
||||
});
|
||||
this.listenTo(this.chart, "change:type", function(chart) {
|
||||
self.types.value(chart.get("type"));
|
||||
});
|
||||
this.listenTo(this.chart, "redraw", function(chart) {
|
||||
self.portlet.showOperation("back");
|
||||
});
|
||||
this.chart.reset();
|
||||
},
|
||||
|
||||
/** Show editor */
|
||||
show: function() {
|
||||
this.$el.show();
|
||||
},
|
||||
|
||||
/** Hide editor */
|
||||
hide: function() {
|
||||
this.$el.hide();
|
||||
},
|
||||
|
||||
/** Refresh title handler */
|
||||
_refreshTitle: function() {
|
||||
var title = this.chart.get("title");
|
||||
this.portlet.title(title);
|
||||
this.title.value(title);
|
||||
},
|
||||
|
||||
/** Draw chart data */
|
||||
_drawChart: function() {
|
||||
var self = this;
|
||||
this.chart.set({
|
||||
type: this.types.value(),
|
||||
title: this.title.value(),
|
||||
date: Utils.time()
|
||||
});
|
||||
if (this.chart.groups.length == 0) {
|
||||
this.message.update({
|
||||
message: "Please specify data options before rendering the visualization.",
|
||||
persistent: false
|
||||
});
|
||||
this.tabs.show("groups");
|
||||
return;
|
||||
}
|
||||
var valid = true;
|
||||
var chart_def = this.chart.definition;
|
||||
this.chart.groups.each(function(group) {
|
||||
if (valid) {
|
||||
_.each(group.get("__data_columns"), function(data_columns, name) {
|
||||
if (group.attributes[name] === null) {
|
||||
self.message.update({
|
||||
status: "danger",
|
||||
message:
|
||||
"This visualization type requires column types not found in your tabular file.",
|
||||
persistent: false
|
||||
});
|
||||
self.tabs.show("groups");
|
||||
valid = false;
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
if (valid) {
|
||||
this.app.go("viewer");
|
||||
this.app.deferred.execute(function() {
|
||||
self.chart.trigger("redraw");
|
||||
});
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
);
|
||||
@@ -1,143 +0,0 @@
|
||||
/** This class renders the chart data selection form with repeats. */
|
||||
define(
|
||||
[
|
||||
"utils/utils",
|
||||
"mvc/ui/ui-misc",
|
||||
"mvc/form/form-view",
|
||||
"mvc/form/form-repeat",
|
||||
"mvc/form/form-data",
|
||||
"plugin/views/description"
|
||||
],
|
||||
function(Utils, Ui, Form, Repeat, FormData, Description) {
|
||||
var GroupView = Backbone.View.extend({
|
||||
initialize: function(app, options) {
|
||||
var self = this;
|
||||
this.app = app;
|
||||
this.chart = app.chart;
|
||||
this.group = options.group;
|
||||
this.setElement($("<div/>"));
|
||||
this.listenTo(this.chart, "change:dataset_id change:type", function() {
|
||||
self.render();
|
||||
});
|
||||
this.render();
|
||||
},
|
||||
|
||||
render: function() {
|
||||
var self = this;
|
||||
var inputs = this.chart.definition.groups ? Utils.clone(this.chart.definition.groups) : {};
|
||||
var dataset_id = this.chart.get("dataset_id");
|
||||
var chart_type = this.chart.get("type");
|
||||
var chart_definition = this.chart.definition;
|
||||
if (dataset_id && chart_type) {
|
||||
this.chart.state("wait", "Loading metadata...");
|
||||
this.app.deferred.execute(function(process) {
|
||||
Utils.get({
|
||||
url: Galaxy.root + "api/datasets/" + dataset_id,
|
||||
cache: true,
|
||||
success: function(dataset) {
|
||||
var data_columns = {};
|
||||
FormData.visitInputs(inputs, function(input, prefixed) {
|
||||
if (input.type == "data_column") {
|
||||
data_columns[prefixed] = Utils.clone(input);
|
||||
var columns = [];
|
||||
input.is_auto && columns.push({ label: "Column: Row Number", value: "auto" });
|
||||
input.is_zero && columns.push({ label: "Column: None", value: "zero" });
|
||||
var meta = dataset.metadata_column_types;
|
||||
for (var key in meta) {
|
||||
var valid =
|
||||
(["int", "float"].indexOf(meta[key]) != -1 && input.is_numeric) ||
|
||||
input.is_label;
|
||||
valid &&
|
||||
columns.push({ label: "Column: " + (parseInt(key) + 1), value: key });
|
||||
}
|
||||
input.data = columns;
|
||||
}
|
||||
var model_value = self.group.get(prefixed);
|
||||
model_value !== undefined && !input.hidden && (input.value = model_value);
|
||||
});
|
||||
inputs["__data_columns"] = {
|
||||
name: "__data_columns",
|
||||
type: "hidden",
|
||||
hidden: true,
|
||||
value: data_columns
|
||||
};
|
||||
self.chart.state("ok", "Metadata initialized...");
|
||||
self.form = new Form.View({
|
||||
inputs: inputs,
|
||||
cls: "ui-portlet-plain",
|
||||
onchange: function() {
|
||||
self.group.set(self.form.data.create());
|
||||
self.chart.set("modified", true);
|
||||
}
|
||||
});
|
||||
self.group.set(self.form.data.create());
|
||||
self.$el.empty().append(self.form.$el);
|
||||
process.resolve();
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
return Backbone.View.extend({
|
||||
initialize: function(app) {
|
||||
var self = this;
|
||||
this.app = app;
|
||||
this.chart = app.chart;
|
||||
this.repeat = new Repeat.View({
|
||||
title: "Data series",
|
||||
title_new: "Data series",
|
||||
min: 1,
|
||||
onnew: function() {
|
||||
self.chart.groups.add({ id: Utils.uid() });
|
||||
}
|
||||
});
|
||||
this.description = new Description(this.app);
|
||||
this.message = new Ui.Message({
|
||||
message: "There are no options for this visualization type.",
|
||||
persistent: true,
|
||||
status: "info"
|
||||
});
|
||||
this.setElement(
|
||||
$("<div/>")
|
||||
.append(this.description.$el)
|
||||
.append(this.repeat.$el.addClass("ui-margin-bottom"))
|
||||
.append(this.message.$el.addClass("ui-margin-bottom"))
|
||||
);
|
||||
this.listenTo(this.chart, "change", function() {
|
||||
self.render();
|
||||
});
|
||||
this.listenTo(this.chart.groups, "add remove reset", function() {
|
||||
self.chart.set("modified", true);
|
||||
});
|
||||
this.listenTo(this.chart.groups, "remove", function(group) {
|
||||
self.repeat.del(group.id);
|
||||
});
|
||||
this.listenTo(this.chart.groups, "reset", function() {
|
||||
self.repeat.delAll();
|
||||
});
|
||||
this.listenTo(this.chart.groups, "add", function(group) {
|
||||
self.repeat.add({
|
||||
id: group.id,
|
||||
cls: "ui-portlet-panel",
|
||||
$el: new GroupView(self.app, { group: group }).$el,
|
||||
ondel: function() {
|
||||
self.chart.groups.remove(group);
|
||||
}
|
||||
});
|
||||
});
|
||||
},
|
||||
|
||||
render: function() {
|
||||
if (_.size(this.chart.definition.groups) > 0) {
|
||||
this.repeat.$el.show();
|
||||
this.message.$el.hide();
|
||||
} else {
|
||||
this.repeat.$el.hide();
|
||||
this.message.$el.show();
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
);
|
||||
@@ -1,61 +0,0 @@
|
||||
/** This class renders the chart configuration form. */
|
||||
define(
|
||||
["utils/utils", "mvc/ui/ui-misc", "mvc/form/form-view", "mvc/form/form-data", "plugin/views/description"],
|
||||
function(Utils, Ui, Form, FormData, Description) {
|
||||
return Backbone.View.extend({
|
||||
initialize: function(app, options) {
|
||||
var self = this;
|
||||
this.app = app;
|
||||
this.chart = app.chart;
|
||||
this.description = new Description(this.app);
|
||||
this.message = new Ui.Message({
|
||||
message: "There are no options for this visualization type.",
|
||||
persistent: true,
|
||||
status: "info"
|
||||
});
|
||||
this.setElement(
|
||||
$("<div/>")
|
||||
.append(this.description.$el)
|
||||
.append(this.message.$el.addClass("ui-margin-bottom"))
|
||||
.append((this.$form = $("<div/>").addClass("ui-margin-bottom")))
|
||||
);
|
||||
this.listenTo(this.chart, "change", function() {
|
||||
self.render();
|
||||
});
|
||||
},
|
||||
render: function() {
|
||||
var self = this;
|
||||
var inputs = Utils.clone(this.chart.definition.settings) || {};
|
||||
var panel_option = this.chart.definition.use_panels;
|
||||
if (panel_option == "both") {
|
||||
inputs["__use_panels"] = {
|
||||
type: "boolean",
|
||||
label: "Use multi-panels",
|
||||
help: "Would you like to separate your data into individual panels?"
|
||||
};
|
||||
} else {
|
||||
this.chart.settings.set("__use_panels", panel_option == "yes" ? "true" : "false");
|
||||
}
|
||||
if (_.size(inputs) > 0) {
|
||||
FormData.visitInputs(inputs, function(input, name) {
|
||||
var model_value = self.chart.settings.get(name);
|
||||
model_value !== undefined && !input.hidden && (input.value = model_value);
|
||||
});
|
||||
this.form = new Form.View({
|
||||
inputs: inputs,
|
||||
cls: "ui-portlet-plain",
|
||||
onchange: function() {
|
||||
self.chart.settings.set(self.form.data.create());
|
||||
}
|
||||
});
|
||||
this.chart.settings.set(this.form.data.create());
|
||||
this.$form.empty().append(this.form.$el);
|
||||
this.message.$el.hide();
|
||||
} else {
|
||||
this.$form.empty();
|
||||
this.message.$el.show();
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
);
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user