mirror of
https://github.com/galaxyproject/galaxy.git
synced 2026-10-25 00:12:08 +08:00
Merge remote-tracking branch 'galaxyproject/dev' into dev
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,13 @@
|
||||
.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/
|
||||
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
|
||||
|
||||
@@ -1,3 +1,3 @@
|
||||
#!/bin/bash
|
||||
|
||||
./run_tests.sh --dockerize --db postgres --external_tmp --clean_pyc -api "$@"
|
||||
./run_tests.sh --dockerize --db postgres --external_tmp --clean_pyc --skip_flakey_fails -api "$@"
|
||||
|
||||
@@ -23,8 +23,8 @@ find test -iname '*pyc' -exec rm -rf {} \;
|
||||
pip install docker-compose
|
||||
|
||||
# TODO: Let docker-compose pick these at random.
|
||||
export GALAXY_PORT=`python -c 'import socket; s=socket.socket(); s.bind(("", 0)); print(s.getsockname()[1]); s.close()'`
|
||||
export SELENIUM_PORT=`python -c 'import socket; s=socket.socket(); s.bind(("", 0)); print(s.getsockname()[1]); s.close()'`
|
||||
export GALAXY_PORT=`python -c 'from __future__ import print_function; import socket; s=socket.socket(); s.bind(("", 0)); print(s.getsockname()[1]); s.close()'`
|
||||
export SELENIUM_PORT=`python -c 'from __future__ import print_function; import socket; s=socket.socket(); s.bind(("", 0)); print(s.getsockname()[1]); s.close()'`
|
||||
|
||||
export TARGET_ROOT=`pwd`
|
||||
export TARGET_PATH=/galaxy
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
|
||||
# Enable retries on tests to reduce chances of transient failures.
|
||||
: ${GALAXY_TEST_SELENIUM_RETRIES:=1}
|
||||
: ${GALAXY_TEST_CLIENT_BUILD_IMAGE:='node:9.4.0'}
|
||||
|
||||
# If in Jenkins environment, use it for artifacts.
|
||||
if [ -n "$BUILD_NUMBER" ];
|
||||
@@ -16,10 +17,18 @@ fi
|
||||
mkdir -p "$GALAXY_TEST_ERRORS_DIRECTORY"
|
||||
mkdir -p "$GALAXY_TEST_SCREENSHOTS_DIRECTORY"
|
||||
|
||||
docker run -v `pwd`:`pwd`:rw -w `pwd` -u $UID node /bin/bash -c 'make client-production-maps'
|
||||
mkdir -p ~/.jenkins-yarn-cache
|
||||
YARN_CACHE_FOLDER=~/.jenkins-yarn-cache
|
||||
|
||||
# Set git environment variables to enable Git. https://github.com/galaxyproject/galaxy/issues/5912
|
||||
# Setup volume and environment variable to cache this users yarn build.
|
||||
docker run -e GIT_COMMITTER_NAME=Jenkins -e GIT_COMMITTER_EMAIL=jenkins@galaxyproject.org \
|
||||
-e YARN_CACHE_FOLDER=$YARN_CACHE_FOLDER -v $YARN_CACHE_FOLDER:$YARN_CACHE_FOLDER:rw \
|
||||
-v `pwd`:`pwd`:rw -w `pwd` -u $UID $GALAXY_TEST_CLIENT_BUILD_IMAGE \
|
||||
/bin/bash -c 'make client-production-maps'
|
||||
|
||||
# Start Selenium server in the test Docker container.
|
||||
DOCKER_RUN_EXTRA_ARGS="-e USE_SELENIUM=1 -e GALAXY_TEST_SELENIUM_RETRIES=${GALAXY_TEST_SELENIUM_RETRIES} -e GALAXY_TEST_ERRORS_DIRECTORY=${GALAXY_TEST_ERRORS_DIRECTORY} -e GALAXY_TEST_SCREENSHOTS_DIRECTORY=${GALAXY_TEST_SCREENSHOTS_DIRECTORY} ${DOCKER_RUN_EXTRA_ARGS}"
|
||||
DOCKER_RUN_EXTRA_ARGS="-v $YARN_CACHE_FOLDER:$YARN_CACHE_FOLDER -e YARN_CACHE_FOLDER=$YARN_CACHE_FOLDER -e USE_SELENIUM=1 -e GALAXY_TEST_SELENIUM_RETRIES=${GALAXY_TEST_SELENIUM_RETRIES} -e GALAXY_TEST_ERRORS_DIRECTORY=${GALAXY_TEST_ERRORS_DIRECTORY} -e GALAXY_TEST_SCREENSHOTS_DIRECTORY=${GALAXY_TEST_SCREENSHOTS_DIRECTORY} ${DOCKER_RUN_EXTRA_ARGS}"
|
||||
export DOCKER_RUN_EXTRA_ARGS
|
||||
|
||||
./run_tests.sh --dockerize --db postgres --external_tmp --clean_pyc --skip_flakey_fails --selenium "$@"
|
||||
|
||||
+5
-3
@@ -7,6 +7,7 @@ scripts/scramble/archives
|
||||
|
||||
# Python virtualenv
|
||||
.venv
|
||||
.venv3
|
||||
|
||||
# Database stuff
|
||||
database/beaker_sessions
|
||||
@@ -120,15 +121,16 @@ test/unit/**.log
|
||||
tool-data/shared/jars/
|
||||
|
||||
# CSS build artifacts.
|
||||
sprite-*.less
|
||||
sprite-*.scss
|
||||
|
||||
# JS, Local node_modules, and bower_components directories
|
||||
static/scripts/bundled/[0-9]*
|
||||
static/maps
|
||||
static/scripts/bundled/*.map
|
||||
static/plugins
|
||||
static/scripts
|
||||
node_modules
|
||||
bower_components
|
||||
client/galaxy/scripts/libs/*
|
||||
client/galaxy/style/less/*
|
||||
|
||||
# Documentation build files.
|
||||
doc/build
|
||||
|
||||
+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
|
||||
|
||||
|
||||
+65
-64
@@ -1,55 +1,53 @@
|
||||
# Contributing
|
||||
|
||||
Galaxy welcomes new development!
|
||||
This document briefly describes how to contribute to the [core
|
||||
galaxy project](https://github.com/galaxyproject/galaxy) -
|
||||
also checkout our 2013 Galaxy Community
|
||||
Conference presentation on the topic
|
||||
Galaxy welcomes new development! This document briefly describes how to
|
||||
contribute to the [core galaxy
|
||||
project](https://github.com/galaxyproject/galaxy) - also checkout our 2013
|
||||
Galaxy Community Conference presentation on the topic
|
||||
([video](https://vimeo.com/channels/581875/73486255),
|
||||
[presentation](https://depot.galaxyproject.org/hub/attachments/documents/presentations/gcc2013/BakerContribute.pdf)). For
|
||||
information on contributing more broadly to the Galaxy ecosystem and a
|
||||
[presentation](https://depot.galaxyproject.org/hub/attachments/documents/presentations/gcc2013/BakerContribute.pdf)).
|
||||
For information on contributing more broadly to the Galaxy ecosystem and a
|
||||
deeper discussion of some of these points - please see the
|
||||
[Develop](https://galaxyproject.org/develop) section of the
|
||||
[Galaxy Community Hub](https://galaxyproject.org).
|
||||
[Develop](https://galaxyproject.org/develop) section of the [Galaxy Community
|
||||
Hub](https://galaxyproject.org).
|
||||
|
||||
## Before you Begin
|
||||
|
||||
If you have an idea for a feature to add or an approach for a bugfix,
|
||||
it is best to communicate with Galaxy developers early. The most
|
||||
common venues for this are
|
||||
[GitHub issues](https://github.com/galaxyproject/galaxy/issues).
|
||||
Browse through existing GitHub issues and Trello cards and if one seems related,
|
||||
comment on it. We also maintain a [tag](https://github.com/galaxyproject/galaxy/issues?q=is%3Aissue+is%3Aopen+label%3Afriendliness%2Ffriendly) on Github for
|
||||
smaller issues we believe would make the best entry points for new
|
||||
developers.
|
||||
Galaxy developers are generally available via
|
||||
[IRC](https://galaxyproject.org/get-involved), [Gitter](https://gitter.im/galaxyproject/Lobby)
|
||||
and on the [development mailing list](http://dev.list.galaxyproject.org).
|
||||
If you have an idea for a feature to add or an approach for a bugfix, it is
|
||||
best to communicate with Galaxy developers early. The primary venue for this is
|
||||
the [GitHub issue tracker](https://github.com/galaxyproject/galaxy/issues).
|
||||
Browse through existing GitHub issues and if one seems related, comment on it.
|
||||
For more direct communication, Galaxy developers are generally available via
|
||||
[IRC](https://galaxyproject.org/get-involved),
|
||||
[Gitter](https://gitter.im/galaxyproject/Lobby) and on the [development mailing
|
||||
list](http://dev.list.galaxyproject.org).
|
||||
|
||||
If you're looking to help but aren't sure where to start, we also maintain a
|
||||
[tag](https://github.com/galaxyproject/galaxy/issues?q=is%3Aissue+is%3Aopen+label%3Afriendliness%2Ffriendly)
|
||||
on GitHub for smaller issues we believe would make the best entry points for
|
||||
new developers.
|
||||
|
||||
## Reporting a new issue
|
||||
|
||||
If no existing Galaxy issue/Trello card seems appropriate, a new issue can be
|
||||
opened using [this form](https://github.com/galaxyproject/galaxy/issues/new).
|
||||
If no existing Galaxy issue seems appropriate, a new issue can be opened using
|
||||
[this form](https://github.com/galaxyproject/galaxy/issues/new).
|
||||
|
||||
## How to Contribute
|
||||
|
||||
* All changes to the [core galaxy project](https://github.com/galaxyproject/galaxy)
|
||||
should be made through pull requests to this repository (with just two
|
||||
exceptions outlined below).
|
||||
* All changes to the [core galaxy
|
||||
project](https://github.com/galaxyproject/galaxy) should be made through pull
|
||||
requests to this repository (with just two exceptions outlined below).
|
||||
|
||||
* If you are new to Git, the [Try Git](http://try.github.com/) tutorial is a good places to start.
|
||||
More learning resources are listed at https://help.github.com/articles/good-resources-for-learning-git-and-github/ .
|
||||
* If you are new to Git, the [Try Git](http://try.github.com/) tutorial is a
|
||||
good places to start. More learning resources are listed at
|
||||
https://help.github.com/articles/good-resources-for-learning-git-and-github/
|
||||
|
||||
* Make sure you have a free [GitHub](https://github.com/) account.
|
||||
|
||||
* Fork the [galaxy repository](https://github.com/galaxyproject/galaxy) on
|
||||
GitHub to make your changes.
|
||||
(While many Galaxy instances track
|
||||
[galaxy-dist](https://bitbucket.org/galaxy/galaxy-dist), active development
|
||||
happens in the galaxy GitHub repository and this is where pull requests
|
||||
should be made).
|
||||
To keep your copy up to date with respect to the main repository, you need to
|
||||
frequently [sync your fork](https://help.github.com/articles/syncing-a-fork/):
|
||||
GitHub to make your changes. To keep your copy up to date with respect to
|
||||
the main repository, you need to frequently [sync your
|
||||
fork](https://help.github.com/articles/syncing-a-fork/):
|
||||
```
|
||||
$ git remote add upstream https://github.com/galaxyproject/galaxy
|
||||
$ git fetch upstream
|
||||
@@ -59,37 +57,40 @@ opened using [this form](https://github.com/galaxyproject/galaxy/issues/new).
|
||||
|
||||
* Choose the correct branch to develop your changes against.
|
||||
|
||||
* Additions of new features to the code base should be pushed to the `dev` branch (`git
|
||||
checkout dev`).
|
||||
* Additions of new features to the code base should be pushed to the `dev`
|
||||
branch (`git checkout dev`).
|
||||
|
||||
* Most bug fixes to previously release components (things in galaxy-dist)
|
||||
should be made against the recent `release_XX.XX` branch (`git checkout release_XX.XX`).
|
||||
should be made against the recent `release_XX.XX` branch (`git checkout
|
||||
release_XX.XX`).
|
||||
|
||||
* Serious security problems should not be fixed via pull request - please
|
||||
see [the Galaxy security policies](SECURITY_POLICY.md) for information
|
||||
about responsibly disclosing security issues.
|
||||
* Serious security problems should not be fixed via pull request - please see
|
||||
[the Galaxy security policies](SECURITY_POLICY.md) for information about
|
||||
responsibly disclosing security issues.
|
||||
|
||||
* If your changes modify code - please ensure the resulting files
|
||||
conform to Galaxy [style
|
||||
guidelines](https://galaxyproject.org/develop/best-practices).
|
||||
* If your changes modify code please ensure the resulting files conform to
|
||||
Galaxy [style guidelines](https://galaxyproject.org/develop/best-practices).
|
||||
|
||||
* Galaxy contains hundreds of tests of different types and complexity
|
||||
and running each is difficult and probably not reasonable at this
|
||||
time (someday we will provide a holistic test procedure to make this
|
||||
possible). For now, please just review the [running tests
|
||||
documentation](https://galaxyproject.org/admin/running-tests)
|
||||
and run any that seem relevant. Developers reviewing your pull
|
||||
request will be happy to help guide you to running the most relevant
|
||||
tests as part of the pull request review process and may request the
|
||||
output of these tests. You can run the continuous integration tests locally
|
||||
using `tox`, example: `tox -e py27-lint,py27-unit`.
|
||||
* If you are working on the Galaxy Client (the user interface -- JavaScript,
|
||||
styles, etc.), see more information in the [client readme](client/README.md).
|
||||
|
||||
* Galaxy contains hundreds of tests of different types and complexity and
|
||||
running each is difficult and probably not reasonable at this time (someday
|
||||
we will provide a holistic test procedure to make this possible). For now,
|
||||
please just review the [running tests
|
||||
documentation](https://galaxyproject.org/admin/running-tests) and run any
|
||||
that seem relevant. Developers reviewing your pull request will be happy to
|
||||
help guide you to running the most relevant tests as part of the pull request
|
||||
review process and may request the output of these tests. You can run the
|
||||
continuous integration tests locally using `tox`, example: `tox -e
|
||||
py27-lint,py27-unit`.
|
||||
|
||||
* Commit and push your changes to your
|
||||
[fork](https://help.github.com/articles/pushing-to-a-remote/).
|
||||
|
||||
* Open a [pull
|
||||
request](https://help.github.com/articles/creating-a-pull-request/)
|
||||
with these changes. You pull request message ideally should include:
|
||||
request](https://help.github.com/articles/creating-a-pull-request/) with
|
||||
these changes. You pull request message ideally should include:
|
||||
|
||||
* A description of why the changes should be made.
|
||||
|
||||
@@ -100,16 +101,16 @@ opened using [this form](https://github.com/galaxyproject/galaxy/issues/new).
|
||||
* The pull request should pass all the continuous integration tests which are
|
||||
automatically run by GitHub using e.g. Travis CI.
|
||||
|
||||
* Your pull request will be handled according to
|
||||
[some rules](doc/source/project/organization.rst#handling-pull-requests).
|
||||
* Your pull request will be handled according to [some
|
||||
rules](doc/source/project/organization.rst#handling-pull-requests).
|
||||
|
||||
## A Quick Note about Tools
|
||||
|
||||
For the most part, Galaxy tools should be published to the
|
||||
[Tool Shed](https://galaxyproject.org/toolshed) and not in this
|
||||
repository directly. If you are looking for migrated
|
||||
core tools that used to exist in this repository - please checkout
|
||||
the [tools-iuc](https://github.com/galaxyproject/tools-iuc)
|
||||
repository on GitHub.
|
||||
For the most part, Galaxy tools should be published to the [Tool
|
||||
Shed](https://galaxyproject.org/toolshed) and not in this repository
|
||||
directly. If you are looking for migrated core tools that used to exist in
|
||||
this repository - please checkout the
|
||||
[tools-iuc](https://github.com/galaxyproject/tools-iuc) repository on GitHub.
|
||||
|
||||
More information about tool development can be found [on the comunity hub](https://galaxyproject.org/develop).
|
||||
More information about tool development can be found [on the comunity
|
||||
hub](https://galaxyproject.org/develop).
|
||||
|
||||
@@ -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
|
||||
@@ -150,18 +141,17 @@ ifndef YARN
|
||||
@echo "Could not find yarn, which is required to build the Galaxy client.\nTo install yarn, please visit \033[0;34mhttps://yarnpkg.com/en/docs/install\033[0m for instructions, and package information for all platforms.\n"
|
||||
false;
|
||||
else
|
||||
cd client && yarn install --check-files
|
||||
cd client && yarn install --network-timeout 120000 --check-files
|
||||
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).
|
||||
client-production: node-deps ## Rebuild client-side artifacts for a production deployment without sourcemaps.
|
||||
cd client && yarn run build-production
|
||||
|
||||
client-production-maps: node-deps ## Rebuild client-side artifacts for a production deployment, and include sourcemaps to aid in debugging efforts.
|
||||
client-production-maps: node-deps ## Rebuild client-side artifacts for a production deployment with sourcemaps.
|
||||
cd client && yarn run build-production-maps
|
||||
|
||||
client-format: node-deps ## Reformat client code
|
||||
@@ -169,18 +159,18 @@ 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
|
||||
_client-test-mocha: ## Run mocha tests via karma
|
||||
cd client && GALAXY_TEST_FRAMEWORK=mocha yarn run test
|
||||
|
||||
_client-test-qunit: ## Run qunit tests via karma
|
||||
cd client && GALAXY_TEST_FRAMEWORK=qunit yarn run test
|
||||
|
||||
client-test: client _client-test-mocha _client-test-qunit ## Run JS unit tests via Karma
|
||||
|
||||
client-test-watch: client ## Watch and run qunit tests on changes via Karma
|
||||
cd client && yarn run test-watch
|
||||
|
||||
charts: node-deps ## Rebuild charts
|
||||
cd client && yarn run build-charts
|
||||
|
||||
|
||||
# Release Targets
|
||||
release-create-rc: release-ensure-upstream ## Create a release-candidate branch
|
||||
git checkout dev
|
||||
|
||||
+1
-1
@@ -64,4 +64,4 @@ Please see `CONTRIBUTING.md <CONTRIBUTING.md>`_ .
|
||||
Roadmap
|
||||
=============================
|
||||
|
||||
Interested in the next steps for Galaxy? Take a look at the `roadmap <https://github.com/galaxyproject/galaxy/issues/1928>`__.
|
||||
Interested in the next steps for Galaxy? Take a look at the `roadmap <https://github.com/galaxyproject/galaxy/projects/8>`__.
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
{
|
||||
"presets": ["env"],
|
||||
"moduleIds": true,
|
||||
"plugins": ["transform-vue-template"],
|
||||
"ignore": [
|
||||
"i18n.js",
|
||||
"utils/localization.js",
|
||||
|
||||
+40
-3
@@ -5,9 +5,46 @@ module.exports = function(grunt) {
|
||||
// this symlink allows us to serve uncompressed scripts in DEV_PATH for use with sourcemaps
|
||||
srcSymlink: "../static/src"
|
||||
};
|
||||
var _ = grunt.util._;
|
||||
var fmt = _.sprintf;
|
||||
var styleDistPath = "../static/style/blue";
|
||||
var imagesPath = "../static/images";
|
||||
var stylePath = "./galaxy/style/scss";
|
||||
|
||||
grunt.config.set("app", "galaxy");
|
||||
grunt.config.set("paths", GALAXY_PATHS);
|
||||
// see the sub directory grunt-tasks/ for individual task definitions
|
||||
grunt.loadTasks("grunt-tasks");
|
||||
grunt.registerTask("default", ["style"]);
|
||||
/**
|
||||
* Grunt task to build static/style resources (css, sprites)
|
||||
* @param {Object} grunt main grunt file
|
||||
* @return {Function} callback to build this task
|
||||
*/
|
||||
/* global module */
|
||||
|
||||
// Create sprite images and .less files
|
||||
grunt.config("sprite", {
|
||||
options: {
|
||||
algorithm: "binary-tree"
|
||||
},
|
||||
"history-buttons": {
|
||||
src: fmt("%s/history-buttons/*.png", imagesPath),
|
||||
dest: fmt("%s/sprite-history-buttons.png", styleDistPath),
|
||||
imgPath: fmt("sprite-history-buttons.png"),
|
||||
destCss: fmt("%s/sprite-history-buttons.scss", stylePath)
|
||||
},
|
||||
"history-states": {
|
||||
src: fmt("%s/history-states/*.png", imagesPath),
|
||||
dest: fmt("%s/sprite-history-states.png", styleDistPath),
|
||||
imgPath: fmt("sprite-history-states.png"),
|
||||
destCss: fmt("%s/sprite-history-states.scss", stylePath)
|
||||
},
|
||||
fugue: {
|
||||
src: fmt("%s/fugue/*.png", imagesPath),
|
||||
dest: fmt("%s/sprite-fugue.png", styleDistPath),
|
||||
imgPath: fmt("sprite-fugue.png"),
|
||||
destCss: fmt("%s/sprite-fugue.scss", stylePath)
|
||||
}
|
||||
});
|
||||
|
||||
grunt.loadNpmTasks("grunt-spritesmith");
|
||||
grunt.registerTask("default", ["sprite"]);
|
||||
};
|
||||
|
||||
+12
-10
@@ -14,7 +14,7 @@ nodejs.org and yarnpkg.com.
|
||||
The Galaxy client build has necessarily grown more complex in the past several
|
||||
years, but we're still trying to keep things as simple as possible for
|
||||
developers (everyone, really). If you're having any trouble with building the
|
||||
client after following the instructions below please create an issue on Github
|
||||
client after following the instructions below please create an issue on GitHub
|
||||
or reach out for help directly on Gitter at
|
||||
https://gitter.im/galaxyproject/Lobby.
|
||||
|
||||
@@ -26,25 +26,27 @@ There are many moving parts to the client build system, but the entrypoint for
|
||||
most people is the 'client' rule in the Makefile at the root of the Galaxy
|
||||
repository. Execute the following to perform a complete build suitable for
|
||||
local development, including dependency staging, style building, script
|
||||
processing and bundling.
|
||||
processing and bundling. This is a development-specific build which includes
|
||||
extra debugging features, and excludes several production optimizations made
|
||||
during the build process.
|
||||
|
||||
make client
|
||||
|
||||
Note: Don't commit the files that 'make client' creates. This is a
|
||||
development-specific build which includes extra debugging features, and
|
||||
excludes several production optimizations made during the build process.
|
||||
|
||||
For a production build, suitable for committing to the repository, or deploying
|
||||
to a live server, use the following:
|
||||
For a production build, suitable for deploying to a live server, use the following:
|
||||
|
||||
make client-production
|
||||
|
||||
And, lastly, if you want a production build that includes sourcemaps to allow
|
||||
for perusal of the page-level javascript to facilitate live-server debugging,
|
||||
use:
|
||||
for inspection of live javascript to facilitate debugging, use:
|
||||
|
||||
make client-production-maps
|
||||
|
||||
Important Note: The development branch of Galaxy does not include client script
|
||||
artifacts, and these should not be committed. When issuing a PR to a stable
|
||||
branch, please run "make client-production-maps", and include those artifacts.
|
||||
Or, if you'd rather, include only the /client source changes and build
|
||||
artifacts can be added by maintainers on merge.
|
||||
|
||||
|
||||
Automatic Rebuilding (Watch Mode)
|
||||
=================================
|
||||
|
||||
@@ -8,8 +8,6 @@
|
||||
],
|
||||
"homepage": "usegalaxy.org",
|
||||
"dependencies": {
|
||||
"bootstrap": "~3.3.2",
|
||||
"bootstrap-tour": "~0.10.2",
|
||||
"farbtastic": "~2.0.0-alpha.1",
|
||||
"toastr": "~2.1.0",
|
||||
"jquery-form": "~3.46.0",
|
||||
|
||||
@@ -0,0 +1,380 @@
|
||||
## Buttons
|
||||
|
||||
Regular buttons
|
||||
|
||||
```
|
||||
<div>
|
||||
<button type="button" class="btn btn-primary">Primary</button>
|
||||
<button type="button" class="btn btn-secondary">Secondary</button>
|
||||
<button type="button" class="btn btn-success">Success</button>
|
||||
<button type="button" class="btn btn-info">Info</button>
|
||||
<button type="button" class="btn btn-warning">Warning</button>
|
||||
<button type="button" class="btn btn-danger">Danger</button>
|
||||
<button type="button" class="btn btn-link">Link</button>
|
||||
</div>
|
||||
```
|
||||
|
||||
Disabled
|
||||
|
||||
```
|
||||
<div>
|
||||
<button type="button" class="btn btn-primary disabled">Primary</button>
|
||||
<button type="button" class="btn btn-secondary disabled">Secondary</button>
|
||||
<button type="button" class="btn btn-success disabled">Success</button>
|
||||
<button type="button" class="btn btn-info disabled">Info</button>
|
||||
<button type="button" class="btn btn-warning disabled">Warning</button>
|
||||
<button type="button" class="btn btn-danger disabled">Danger</button>
|
||||
<button type="button" class="btn btn-link disabled">Link</button>
|
||||
</div>
|
||||
```
|
||||
|
||||
```
|
||||
<div>
|
||||
<button type="button" class="btn btn-outline-primary">Primary</button>
|
||||
<button type="button" class="btn btn-outline-secondary">Secondary</button>
|
||||
<button type="button" class="btn btn-outline-success">Success</button>
|
||||
<button type="button" class="btn btn-outline-info">Info</button>
|
||||
<button type="button" class="btn btn-outline-warning">Warning</button>
|
||||
<button type="button" class="btn btn-outline-danger">Danger</button>
|
||||
</div>
|
||||
```
|
||||
|
||||
```
|
||||
<div class="btn-group" role="group" aria-label="Button group with nested dropdown">
|
||||
<button type="button" class="btn btn-primary">Primary</button>
|
||||
<div class="btn-group" role="group">
|
||||
<button id="btnGroupDrop1" type="button" class="btn btn-primary dropdown-toggle" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false"></button>
|
||||
<div class="dropdown-menu" aria-labelledby="btnGroupDrop1">
|
||||
<a class="dropdown-item" href="#">Dropdown link</a>
|
||||
<a class="dropdown-item" href="#">Dropdown link</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
```
|
||||
|
||||
```
|
||||
<div class="btn-group" role="group" aria-label="Button group with nested dropdown">
|
||||
<button type="button" class="btn btn-success">Success</button>
|
||||
<div class="btn-group" role="group">
|
||||
<button id="btnGroupDrop2" type="button" class="btn btn-success dropdown-toggle" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false"></button>
|
||||
<div class="dropdown-menu" aria-labelledby="btnGroupDrop2">
|
||||
<a class="dropdown-item" href="#">Dropdown link</a>
|
||||
<a class="dropdown-item" href="#">Dropdown link</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
```
|
||||
|
||||
```
|
||||
<div class="btn-group" role="group" aria-label="Button group with nested dropdown">
|
||||
<button type="button" class="btn btn-info">Info</button>
|
||||
<div class="btn-group" role="group">
|
||||
<button id="btnGroupDrop3" type="button" class="btn btn-info dropdown-toggle" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false"></button>
|
||||
<div class="dropdown-menu" aria-labelledby="btnGroupDrop3">
|
||||
<a class="dropdown-item" href="#">Dropdown link</a>
|
||||
<a class="dropdown-item" href="#">Dropdown link</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
```
|
||||
|
||||
```
|
||||
<div class="btn-group" role="group" aria-label="Button group with nested dropdown">
|
||||
<button type="button" class="btn btn-danger">Danger</button>
|
||||
<div class="btn-group" role="group">
|
||||
<button id="btnGroupDrop4" type="button" class="btn btn-danger dropdown-toggle" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false"></button>
|
||||
<div class="dropdown-menu" aria-labelledby="btnGroupDrop4">
|
||||
<a class="dropdown-item" href="#">Dropdown link</a>
|
||||
<a class="dropdown-item" href="#">Dropdown link</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
```
|
||||
|
||||
```
|
||||
<div>
|
||||
<button type="button" class="btn btn-primary btn-lg">Large button</button>
|
||||
<button type="button" class="btn btn-primary">Default button</button>
|
||||
<button type="button" class="btn btn-primary btn-sm">Small button</button>
|
||||
</div>
|
||||
```
|
||||
|
||||
## Alerts
|
||||
|
||||
```
|
||||
<div class="alert alert-dismissable alert-warning">
|
||||
<button type="button" class="close" data-dismiss="alert">×</button>
|
||||
<h4>Warning!</h4>
|
||||
<p>Best check yo self, you're not looking too good. Nulla vitae elit libero, a pharetra augue. Praesent commodo cursus magna, <a href="#" class="alert-link">vel scelerisque nisl consectetur et</a>.</p>
|
||||
</div>
|
||||
```
|
||||
|
||||
```
|
||||
<div class="alert alert-dismissable alert-danger">
|
||||
<button type="button" class="close" data-dismiss="alert">×</button>
|
||||
<strong>Oh snap!</strong> <a href="#" class="alert-link">Change a few things up</a> and try submitting again.
|
||||
</div>
|
||||
```
|
||||
|
||||
```
|
||||
<div class="alert alert-dismissable alert-success">
|
||||
<button type="button" class="close" data-dismiss="alert">×</button>
|
||||
<strong>Well done!</strong> You successfully read <a href="#" class="alert-link">this important alert message</a>.
|
||||
</div>
|
||||
```
|
||||
|
||||
```
|
||||
<div class="alert alert-dismissable alert-info">
|
||||
<button type="button" class="close" data-dismiss="alert">×</button>
|
||||
<strong>Heads up!</strong> This <a href="#" class="alert-link">alert needs your attention</a>, but it's not super important.
|
||||
</div>
|
||||
```
|
||||
|
||||
## Badges
|
||||
|
||||
```
|
||||
<div>
|
||||
<span class="badge badge-default">Default</span>
|
||||
<span class="badge badge-primary">Primary</span>
|
||||
<span class="badge badge-success">Success</span>
|
||||
<span class="badge badge-warning">Warning</span>
|
||||
<span class="badge badge-danger">Danger</span>
|
||||
<span class="badge badge-info">Info</span>
|
||||
</div>
|
||||
```
|
||||
|
||||
```
|
||||
<div class="">
|
||||
<ul class="nav nav-pills">
|
||||
<li class="active"><a href="#">Home <span class="badge">42</span></a></li>
|
||||
<li><a href="#">Profile <span class="badge-pill"></span></a></li>
|
||||
<li><a href="#">Messages <span class="badge-pill">3</span></a></li>
|
||||
</ul>
|
||||
</div>
|
||||
```
|
||||
|
||||
## Tables
|
||||
|
||||
```
|
||||
<div class="">
|
||||
<table class="table table-striped table-bordered table-hover">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>#</th>
|
||||
<th>Column heading</th>
|
||||
<th>Column heading</th>
|
||||
<th>Column heading</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr>
|
||||
<td>1</td>
|
||||
<td>Column content</td>
|
||||
<td>Column content</td>
|
||||
<td>Column content</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>2</td>
|
||||
<td>Column content</td>
|
||||
<td>Column content</td>
|
||||
<td>Column content</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>3</td>
|
||||
<td>Column content</td>
|
||||
<td>Column content</td>
|
||||
<td>Column content</td>
|
||||
</tr>
|
||||
<tr class="table-success">
|
||||
<td>4</td>
|
||||
<td>Column content</td>
|
||||
<td>Column content</td>
|
||||
<td>Column content</td>
|
||||
</tr>
|
||||
<tr class="table-danger">
|
||||
<td>5</td>
|
||||
<td>Column content</td>
|
||||
<td>Column content</td>
|
||||
<td>Column content</td>
|
||||
</tr>
|
||||
<tr class="table-warning">
|
||||
<td>6</td>
|
||||
<td>Column content</td>
|
||||
<td>Column content</td>
|
||||
<td>Column content</td>
|
||||
</tr>
|
||||
<tr class="table-active">
|
||||
<td>7</td>
|
||||
<td>Column content</td>
|
||||
<td>Column content</td>
|
||||
<td>Column content</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
```
|
||||
|
||||
## Cards
|
||||
|
||||
```
|
||||
<div class="row">
|
||||
<div class="col-lg-4">
|
||||
<div class="bs-component">
|
||||
<div class="card text-white bg-primary mb-3" style="max-width: 20rem;">
|
||||
<div class="card-header">Header</div>
|
||||
<div class="card-body">
|
||||
<h4 class="card-title">Primary card title</h4>
|
||||
<p class="card-text">Some quick example text to build on the card title and make up the bulk of the card's content.</p>
|
||||
</div>
|
||||
</div>
|
||||
<div class="card text-white bg-secondary mb-3" style="max-width: 20rem;">
|
||||
<div class="card-header">Header</div>
|
||||
<div class="card-body">
|
||||
<h4 class="card-title">Secondary card title</h4>
|
||||
<p class="card-text">Some quick example text to build on the card title and make up the bulk of the card's content.</p>
|
||||
</div>
|
||||
</div>
|
||||
<div class="card text-white bg-success mb-3" style="max-width: 20rem;">
|
||||
<div class="card-header">Header</div>
|
||||
<div class="card-body">
|
||||
<h4 class="card-title">Success card title</h4>
|
||||
<p class="card-text">Some quick example text to build on the card title and make up the bulk of the card's content.</p>
|
||||
</div>
|
||||
</div>
|
||||
<div class="card text-white bg-danger mb-3" style="max-width: 20rem;">
|
||||
<div class="card-header">Header</div>
|
||||
<div class="card-body">
|
||||
<h4 class="card-title">Danger card title</h4>
|
||||
<p class="card-text">Some quick example text to build on the card title and make up the bulk of the card's content.</p>
|
||||
</div>
|
||||
</div>
|
||||
<div class="card text-white bg-warning mb-3" style="max-width: 20rem;">
|
||||
<div class="card-header">Header</div>
|
||||
<div class="card-body">
|
||||
<h4 class="card-title">Warning card title</h4>
|
||||
<p class="card-text">Some quick example text to build on the card title and make up the bulk of the card's content.</p>
|
||||
</div>
|
||||
</div>
|
||||
<div class="card text-white bg-info mb-3" style="max-width: 20rem;">
|
||||
<div class="card-header">Header</div>
|
||||
<div class="card-body">
|
||||
<h4 class="card-title">Info card title</h4>
|
||||
<p class="card-text">Some quick example text to build on the card title and make up the bulk of the card's content.</p>
|
||||
</div>
|
||||
</div>
|
||||
<div class="card bg-light mb-3" style="max-width: 20rem;">
|
||||
<div class="card-header">Header</div>
|
||||
<div class="card-body">
|
||||
<h4 class="card-title">Light card title</h4>
|
||||
<p class="card-text">Some quick example text to build on the card title and make up the bulk of the card's content.</p>
|
||||
</div>
|
||||
</div>
|
||||
<div class="card text-white bg-dark mb-3" style="max-width: 20rem;">
|
||||
<div class="card-header">Header</div>
|
||||
<div class="card-body">
|
||||
<h4 class="card-title">Dark card title</h4>
|
||||
<p class="card-text">Some quick example text to build on the card title and make up the bulk of the card's content.</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-lg-4">
|
||||
<div class="bs-component">
|
||||
<div class="card border-primary mb-3" style="max-width: 20rem;">
|
||||
<div class="card-header">Header</div>
|
||||
<div class="card-body">
|
||||
<h4 class="card-title">Primary card title</h4>
|
||||
<p class="card-text">Some quick example text to build on the card title and make up the bulk of the card's content.</p>
|
||||
</div>
|
||||
</div>
|
||||
<div class="card border-secondary mb-3" style="max-width: 20rem;">
|
||||
<div class="card-header">Header</div>
|
||||
<div class="card-body">
|
||||
<h4 class="card-title">Secondary card title</h4>
|
||||
<p class="card-text">Some quick example text to build on the card title and make up the bulk of the card's content.</p>
|
||||
</div>
|
||||
</div>
|
||||
<div class="card border-success mb-3" style="max-width: 20rem;">
|
||||
<div class="card-header">Header</div>
|
||||
<div class="card-body">
|
||||
<h4 class="card-title">Success card title</h4>
|
||||
<p class="card-text">Some quick example text to build on the card title and make up the bulk of the card's content.</p>
|
||||
</div>
|
||||
</div>
|
||||
<div class="card border-danger mb-3" style="max-width: 20rem;">
|
||||
<div class="card-header">Header</div>
|
||||
<div class="card-body">
|
||||
<h4 class="card-title">Danger card title</h4>
|
||||
<p class="card-text">Some quick example text to build on the card title and make up the bulk of the card's content.</p>
|
||||
</div>
|
||||
</div>
|
||||
<div class="card border-warning mb-3" style="max-width: 20rem;">
|
||||
<div class="card-header">Header</div>
|
||||
<div class="card-body">
|
||||
<h4 class="card-title">Warning card title</h4>
|
||||
<p class="card-text">Some quick example text to build on the card title and make up the bulk of the card's content.</p>
|
||||
</div>
|
||||
</div>
|
||||
<div class="card border-info mb-3" style="max-width: 20rem;">
|
||||
<div class="card-header">Header</div>
|
||||
<div class="card-body">
|
||||
<h4 class="card-title">Info card title</h4>
|
||||
<p class="card-text">Some quick example text to build on the card title and make up the bulk of the card's content.</p>
|
||||
</div>
|
||||
</div>
|
||||
<div class="card border-light mb-3" style="max-width: 20rem;">
|
||||
<div class="card-header">Header</div>
|
||||
<div class="card-body">
|
||||
<h4 class="card-title">Light card title</h4>
|
||||
<p class="card-text">Some quick example text to build on the card title and make up the bulk of the card's content.</p>
|
||||
</div>
|
||||
</div>
|
||||
<div class="card border-dark mb-3" style="max-width: 20rem;">
|
||||
<div class="card-header">Header</div>
|
||||
<div class="card-body">
|
||||
<h4 class="card-title">Dark card title</h4>
|
||||
<p class="card-text">Some quick example text to build on the card title and make up the bulk of the card's content.</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="col-lg-4">
|
||||
<div class="bs-component">
|
||||
<div class="card mb-3">
|
||||
<h3 class="card-header">Card header</h3>
|
||||
<div class="card-body">
|
||||
<h5 class="card-title">Special title treatment</h5>
|
||||
<h6 class="card-subtitle text-muted">Support card subtitle</h6>
|
||||
</div>
|
||||
<img style="height: 200px; width: 100%; display: block;" src="data:image/svg+xml;charset=UTF-8,%3Csvg%20width%3D%22318%22%20height%3D%22180%22%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20viewBox%3D%220%200%20318%20180%22%20preserveAspectRatio%3D%22none%22%3E%3Cdefs%3E%3Cstyle%20type%3D%22text%2Fcss%22%3E%23holder_158bd1d28ef%20text%20%7B%20fill%3Argba(255%2C255%2C255%2C.75)%3Bfont-weight%3Anormal%3Bfont-family%3AHelvetica%2C%20monospace%3Bfont-size%3A16pt%20%7D%20%3C%2Fstyle%3E%3C%2Fdefs%3E%3Cg%20id%3D%22holder_158bd1d28ef%22%3E%3Crect%20width%3D%22318%22%20height%3D%22180%22%20fill%3D%22%23777%22%3E%3C%2Frect%3E%3Cg%3E%3Ctext%20x%3D%22129.359375%22%20y%3D%2297.35%22%3EImage%3C%2Ftext%3E%3C%2Fg%3E%3C%2Fg%3E%3C%2Fsvg%3E" alt="Card image">
|
||||
<div class="card-body">
|
||||
<p class="card-text">Some quick example text to build on the card title and make up the bulk of the card's content.</p>
|
||||
</div>
|
||||
<ul class="list-group list-group-flush">
|
||||
<li class="list-group-item">Cras justo odio</li>
|
||||
<li class="list-group-item">Dapibus ac facilisis in</li>
|
||||
<li class="list-group-item">Vestibulum at eros</li>
|
||||
</ul>
|
||||
<div class="card-body">
|
||||
<a href="#" class="card-link">Card link</a>
|
||||
<a href="#" class="card-link">Another link</a>
|
||||
</div>
|
||||
<div class="card-footer text-muted">
|
||||
2 days ago
|
||||
</div>
|
||||
</div>
|
||||
<div class="card">
|
||||
<div class="card-body">
|
||||
<h4 class="card-title">Card title</h4>
|
||||
<h6 class="card-subtitle mb-2 text-muted">Card subtitle</h6>
|
||||
<p class="card-text">Some quick example text to build on the card title and make up the bulk of the card's content.</p>
|
||||
<a href="#" class="card-link">Card link</a>
|
||||
<a href="#" class="card-link">Another link</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
```
|
||||
@@ -0,0 +1,236 @@
|
||||
## Galaxy Styles
|
||||
|
||||
### Panel Messages
|
||||
|
||||
Messages that appear across the top of the panel view below the masthead.
|
||||
|
||||
```
|
||||
<div>
|
||||
<div v-for="type in ['done', 'info', 'warning', 'error']">
|
||||
<div v-bind:class="'panel-' + type + '-message'">I'm a panel-{{type}}-message</div>
|
||||
</div>
|
||||
</div>
|
||||
```
|
||||
|
||||
### Large Messages
|
||||
|
||||
Used for providing feedback inline.
|
||||
|
||||
```
|
||||
<div>
|
||||
<div v-for="type in ['done', 'info', 'warning', 'error']">
|
||||
<div v-bind:class="type + 'messagelarge'">I'm a {{type}}messagelarge</div>
|
||||
</div>
|
||||
</div>
|
||||
```
|
||||
|
||||
### Small Messages
|
||||
|
||||
```
|
||||
<div>
|
||||
<div v-for="type in ['done', 'info', 'warning', 'error']">
|
||||
<div v-bind:class="type + 'message'">I'm a {{type}}message</div>
|
||||
</div>
|
||||
</div>
|
||||
```
|
||||
|
||||
## Buttons
|
||||
|
||||
```
|
||||
<button>Just a default button</button>
|
||||
```
|
||||
|
||||
```
|
||||
<a href="#" class="action-button">An anchor with .action-button</a>
|
||||
```
|
||||
|
||||
All the crazy permutations of menu button...
|
||||
|
||||
```
|
||||
<a href="#" class="menubutton">An anchor with .menu-button</a>
|
||||
```
|
||||
|
||||
```
|
||||
<a href="#" class="menubutton popup">An anchor with .menu-button.popup</a>
|
||||
```
|
||||
|
||||
```
|
||||
<a href="#" class="menubutton popup split">
|
||||
<a class="menubutton-label">An anchor with .menu-button.popup.split</a>
|
||||
</a>
|
||||
```
|
||||
|
||||
## Masthead
|
||||
|
||||
As generated by layout.masthead.js
|
||||
|
||||
```
|
||||
<nav id="masthead" class="navbar navbar-expand justify-content-center navbar-dark bg-dark">
|
||||
<a class="navbar-brand">
|
||||
<img class="navbar-brand-image"/>
|
||||
<span class="navbar-brand-title">Title</span>
|
||||
</a>
|
||||
<ul class="navbar-nav">
|
||||
<li class="nav-item">
|
||||
<a class="nav-link">Header 1</a>
|
||||
</li>
|
||||
<li class="nav-item dropdown">
|
||||
<a class="nav-link dropdown-toggle">Header 2</a>
|
||||
</li>
|
||||
</ul>
|
||||
</nav>
|
||||
```
|
||||
|
||||
## Radio Buttons
|
||||
|
||||
As generated by mvc.ui.ui-options
|
||||
|
||||
```
|
||||
<div class="btn-group btn-group-toggle" data-toggle="buttons">
|
||||
<label class="btn btn-secondary active">
|
||||
<input type="radio" name="options" id="option1" autocomplete="off" checked> Active
|
||||
</label>
|
||||
<label class="btn btn-secondary">
|
||||
<input type="radio" name="options" id="option2" autocomplete="off"> Radio
|
||||
</label>
|
||||
<label class="btn btn-secondary">
|
||||
<input type="radio" name="options" id="option3" autocomplete="off"> Radio
|
||||
</label>
|
||||
</div>
|
||||
```
|
||||
|
||||
## Popup menus
|
||||
|
||||
These are the classes for the menu itself, normally floating, so placed inside
|
||||
a relative element.
|
||||
|
||||
```
|
||||
<div style="position: relative; height: 12rem;">
|
||||
<div class="popmenu-wrapper" style="top: 0;">
|
||||
<ul class="dropdown-menu">
|
||||
<li class="head"><a>Heading one</a></li>
|
||||
<li>An item</li>
|
||||
<li>Another item</li>
|
||||
<li class="head"><a>Heading two</a></li>
|
||||
<li>Third item</li>
|
||||
<li>Last item</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
```
|
||||
|
||||
## Tables
|
||||
|
||||
Tables using the .grid class
|
||||
|
||||
```
|
||||
<table class="grid">
|
||||
<thead class="grid-table-header">
|
||||
<tr><th>One</th><th>Two</th></tr>
|
||||
</thead>
|
||||
<tbody class="grid-table-body">
|
||||
<tr><td>Value 1</td><td>Value 2</td></tr>
|
||||
</tbody>
|
||||
</table>
|
||||
```
|
||||
|
||||
## Portlet Form
|
||||
|
||||
|
||||
## Forms
|
||||
|
||||
Manually crafted "tool" form class, this is broken in the BS4 branch I think.
|
||||
|
||||
```
|
||||
<div class="tool-form">
|
||||
<form name="foo">
|
||||
<div class="form-row">
|
||||
<label>Label Input 1</label>
|
||||
<input id="input1" type="text" name="input1" size="40" />
|
||||
</div>
|
||||
<div class="form-row">
|
||||
<input type="submit" id="send" name="submit" value="submit" />
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
```
|
||||
|
||||
Portlet variant generated by mvc.form.form-view
|
||||
|
||||
```
|
||||
<div class="ui-portlet-limited" id="uid-8">
|
||||
<div class="portlet-header">
|
||||
<div class="portlet-operations"></div>
|
||||
<div class="portlet-title">
|
||||
<i class="portlet-title-icon fa fa-unlock-alt" style="display: inline;"></i>
|
||||
<span class="portlet-title-text">Portlet Title</span></div>
|
||||
</div>
|
||||
<div class="portlet-content">
|
||||
<div class="portlet-body">
|
||||
<div class="ui-message alert alert-info"></div>
|
||||
<div>
|
||||
<div class="ui-form-element section-row">
|
||||
<div class="ui-form-error ui-error"><span class="fa fa-arrow-down"></span><span class="ui-form-error-text"></span></div>
|
||||
<div class="ui-form-title">
|
||||
<div class="ui-form-collapsible" style="display: none;">
|
||||
<i class="ui-form-collapsible-icon"></i>
|
||||
<span class="ui-form-collapsible-text"></span>
|
||||
</div>
|
||||
<span class="ui-form-title-text">Text Field 1</span>
|
||||
</div>
|
||||
<div class="ui-form-field">
|
||||
<input class="ui-input" id="field-uid-4" type="text">
|
||||
<span class="ui-form-info"></span>
|
||||
<div class="ui-form-backdrop" style="display: none;"></div>
|
||||
</div>
|
||||
<div class="ui-form-preview" style="display: none;"></div>
|
||||
</div>
|
||||
<div class="ui-form-element section-row">
|
||||
<div class="ui-form-error ui-error" style="display: none;"><span class="fa fa-arrow-down"></span><span class="ui-form-error-text"></span></div>
|
||||
<div class="ui-form-title"><div class="ui-form-collapsible" style="display: none;"><i class="ui-form-collapsible-icon"></i><span class="ui-form-collapsible-text"></span></div><span class="ui-form-title-text" style="display: inline;">Password</span></div>
|
||||
<div class="ui-form-field">
|
||||
<input class="ui-input" type="password"><span class="ui-form-info"></span><div class="ui-form-backdrop" style="display: none;"></div></div><div class="ui-form-preview" style="display: none;"></div></div>
|
||||
<div class="ui-form-element section-row" id="uid-7" style="display: none;"><div class="ui-form-error ui-error" style="display: none;"><span class="fa fa-arrow-down"></span><span class="ui-form-error-text"></span></div><div class="ui-form-title"><div class="ui-form-collapsible" style="display: none;"><i class="ui-form-collapsible-icon"></i><span class="ui-form-collapsible-text"></span></div><span class="ui-form-title-text" style="display: inline;">token</span></div><div class="ui-form-field"><div id="field-uid-7"><div style="display: none;"></div><div></div></div><span class="ui-form-info"></span><div class="ui-form-backdrop" style="display: none;"></div></div><div class="ui-form-preview" style="display: none;"></div></div></div></div><div class="portlet-buttons"><button type="button" class="ui-button-default btn btn-primary ui-clear-float" id="submit" data-original-title="" title=""><i class="icon fa fa-save ui-margin-right"></i><span class="title">Submit</span><div class="progress" style="display: none;"><div class="progress-bar" style="width: 0%;"></div></div></button></div></div><div class="portlet-backdrop"></div></div>
|
||||
```
|
||||
|
||||
|
||||
## Tabs
|
||||
|
||||
Tabs as generated by mvc.ui.ui-tabs
|
||||
|
||||
```
|
||||
<ul style="display: flex;" class="tab-navigation nav nav-tabs">
|
||||
<li class="tab-element nav-item" id="tab-attribute" style="display: list-item;" data-original-title="" title="">
|
||||
<a class="nav-link active" id="tab-title-link-attribute">
|
||||
<i class="tab-icon fa fa-bars"></i>
|
||||
<span id="tab-title-text-attribute" class="tab-title-text">Attributes</span>
|
||||
</a>
|
||||
</li>
|
||||
<li class="tab-element nav-item" id="tab-convert" style="display: list-item;" data-original-title="" title="">
|
||||
<a class="nav-link" id="tab-title-link-convert">
|
||||
<i class="tab-icon fa fa-gear"></i>
|
||||
<span id="tab-title-text-convert" class="tab-title-text">Convert</span>
|
||||
</a>
|
||||
</li>
|
||||
<li class="tab-element nav-item" id="tab-datatype" style="display: list-item;" data-original-title="" title="">
|
||||
<a class="nav-link" id="tab-title-link-datatype">
|
||||
<i class="tab-icon fa fa-database"></i>
|
||||
<span id="tab-title-text-datatype" class="tab-title-text">Datatypes</span>
|
||||
</a>
|
||||
</li>
|
||||
<li class="tab-element nav-item" id="tab-permissions" style="display: list-item;" data-original-title="" title="">
|
||||
<a class="nav-link" id="tab-title-link-permissions">
|
||||
<i class="tab-icon fa fa-user"></i>
|
||||
<span id="tab-title-text-permissions" class="tab-title-text">Permissions</span>
|
||||
</a>
|
||||
</li>
|
||||
</ul>
|
||||
```
|
||||
|
||||
## Pagination
|
||||
|
||||
```
|
||||
<ul class="pagination"><li class="page-item disabled"><a class="page-link" href="#page/1"><span class="fa fa-angle-double-left"></span></a></li><li class="page-item disabled"><a class="page-link" href="#page/1">0</a></li><li class="page-item active"><a class="page-link" href="#page/1">1</a></li><li class="page-item"><a class="page-link" href="#page/2">2</a></li><li class="page-item"><a class="page-link" href="#page/4"><span class="fa fa-angle-double-right"></span></a></li></ul>
|
||||
```
|
||||
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import jQuery from "jquery";
|
||||
var $ = jQuery;
|
||||
import $ from "jquery";
|
||||
import "bootstrap";
|
||||
import * as _ from "underscore";
|
||||
import GalaxyApp from "galaxy";
|
||||
import Router from "layout/router";
|
||||
import ToolPanel from "./panels/tool-panel";
|
||||
@@ -13,8 +14,11 @@ import Tours from "mvc/tours";
|
||||
import GridView from "mvc/grid/grid-view";
|
||||
import GridShared from "mvc/grid/grid-shared";
|
||||
import Workflows from "mvc/workflow/workflow";
|
||||
import WorkflowImport from "components/WorkflowImport.vue";
|
||||
import HistoryImport from "components/HistoryImport.vue";
|
||||
import HistoryView from "components/HistoryView.vue";
|
||||
import HistoryList from "mvc/history/history-list";
|
||||
import PluginList from "components/PluginList.vue";
|
||||
import ToolFormComposite from "mvc/tool/tool-form-composite";
|
||||
import QueryStringParsing from "utils/query-string-parsing";
|
||||
import Utils from "utils/utils";
|
||||
@@ -22,8 +26,11 @@ import Ui from "mvc/ui/ui-misc";
|
||||
import DatasetError from "mvc/dataset/dataset-error";
|
||||
import DatasetEditAttributes from "mvc/dataset/dataset-edit-attributes";
|
||||
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
|
||||
@@ -51,10 +58,11 @@ window.app = function app(options, bootstrapped) {
|
||||
"(/)pages(/)create(/)": "show_pages_create",
|
||||
"(/)pages(/)edit(/)": "show_pages_edit",
|
||||
"(/)pages(/)(:action_id)": "show_pages",
|
||||
"(/)visualizations(/)": "show_plugins",
|
||||
"(/)visualizations(/)edit(/)": "show_visualizations_edit",
|
||||
"(/)visualizations/(:action_id)": "show_visualizations",
|
||||
"(/)workflows/import_workflow": "show_import_workflow",
|
||||
"(/)workflows/run(/)": "show_run",
|
||||
"(/)workflows/import": "show_workflows_import",
|
||||
"(/)workflows/run(/)": "show_workflows_run",
|
||||
"(/)workflows(/)list": "show_workflows",
|
||||
"(/)workflows/list_published(/)": "show_workflows_published",
|
||||
"(/)workflows/create(/)": "show_workflows_create",
|
||||
@@ -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",
|
||||
@@ -91,15 +101,17 @@ window.app = function app(options, bootstrapped) {
|
||||
var model = new UserPreferences.Model({
|
||||
user_id: Galaxy.params.id
|
||||
});
|
||||
this.page.display(new FormWrapper.View(model.get(form_id)));
|
||||
this.page.display(new FormWrapper.View(_.extend(model.get(form_id), { active_tab: "user" })));
|
||||
},
|
||||
|
||||
show_visualizations: function(action_id) {
|
||||
var activeTab = action_id == "list_published" ? "shared" : "visualization";
|
||||
this.page.display(
|
||||
new GridShared.View({
|
||||
action_id: action_id,
|
||||
plural: "Visualizations",
|
||||
item: "visualization"
|
||||
item: "visualization",
|
||||
active_tab: activeTab
|
||||
})
|
||||
);
|
||||
},
|
||||
@@ -108,7 +120,8 @@ window.app = function app(options, bootstrapped) {
|
||||
this.page.display(
|
||||
new FormWrapper.View({
|
||||
url: `visualization/edit?id=${QueryStringParsing.get("id")}`,
|
||||
redirect: "visualizations/list"
|
||||
redirect: "visualizations/list",
|
||||
active_tab: "visualization"
|
||||
})
|
||||
);
|
||||
},
|
||||
@@ -116,11 +129,26 @@ window.app = function app(options, bootstrapped) {
|
||||
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 +188,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,17 +197,20 @@ 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"
|
||||
})
|
||||
);
|
||||
},
|
||||
|
||||
show_pages: function(action_id) {
|
||||
var activeTab = action_id == "list_published" ? "shared" : "user";
|
||||
this.page.display(
|
||||
new GridShared.View({
|
||||
action_id: action_id,
|
||||
plural: "Pages",
|
||||
item: "page"
|
||||
item: "page",
|
||||
active_tab: activeTab
|
||||
})
|
||||
);
|
||||
},
|
||||
@@ -187,7 +219,8 @@ window.app = function app(options, bootstrapped) {
|
||||
this.page.display(
|
||||
new FormWrapper.View({
|
||||
url: "page/create",
|
||||
redirect: "pages/list"
|
||||
redirect: "pages/list",
|
||||
active_tab: "user"
|
||||
})
|
||||
);
|
||||
},
|
||||
@@ -196,11 +229,19 @@ window.app = function app(options, bootstrapped) {
|
||||
this.page.display(
|
||||
new FormWrapper.View({
|
||||
url: `page/edit?id=${QueryStringParsing.get("id")}`,
|
||||
redirect: "pages/list"
|
||||
redirect: "pages/list",
|
||||
active_tab: "user"
|
||||
})
|
||||
);
|
||||
},
|
||||
|
||||
show_plugins: function() {
|
||||
var pluginListInstance = Vue.extend(PluginList);
|
||||
var vm = document.createElement("div");
|
||||
this.page.display(vm);
|
||||
new pluginListInstance().$mount(vm);
|
||||
},
|
||||
|
||||
show_workflows: function() {
|
||||
this.page.display(new Workflows.View());
|
||||
},
|
||||
@@ -209,17 +250,21 @@ window.app = function app(options, bootstrapped) {
|
||||
this.page.display(
|
||||
new FormWrapper.View({
|
||||
url: `workflow/create`,
|
||||
redirect: "workflow/editor"
|
||||
redirect: "workflow/editor",
|
||||
active_tab: "workflow"
|
||||
})
|
||||
);
|
||||
},
|
||||
|
||||
show_run: function() {
|
||||
show_workflows_run: function() {
|
||||
this._loadWorkflow();
|
||||
},
|
||||
|
||||
show_import_workflow: function() {
|
||||
this.page.display(new Workflows.ImportWorkflowView());
|
||||
show_workflows_import: function() {
|
||||
var workflowImportInstance = Vue.extend(WorkflowImport);
|
||||
var vm = document.createElement("div");
|
||||
this.page.display(vm);
|
||||
new workflowImportInstance().$mount(vm);
|
||||
},
|
||||
|
||||
show_custom_builds: function() {
|
||||
@@ -285,14 +330,15 @@ window.app = function app(options, bootstrapped) {
|
||||
Utils.get({
|
||||
url: `${Galaxy.root}api/workflows/${Utils.getQueryString("id")}/download?style=run`,
|
||||
success: response => {
|
||||
this.page.display(new ToolFormComposite.View(response));
|
||||
this.page.display(new ToolFormComposite.View(_.extend(response, { active_tab: "workflow" })));
|
||||
},
|
||||
error: response => {
|
||||
var error_msg = response.err_msg || "Error occurred while loading the resource.";
|
||||
var options = {
|
||||
message: error_msg,
|
||||
status: "danger",
|
||||
persistent: true
|
||||
persistent: true,
|
||||
active_tab: "workflow"
|
||||
};
|
||||
this.page.display(new Ui.Message(options));
|
||||
}
|
||||
@@ -302,6 +348,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,5 @@
|
||||
import _l from "utils/localization";
|
||||
import $ from "jquery";
|
||||
import "bootstrap";
|
||||
import GalaxyApp from "galaxy";
|
||||
import WorkflowView from "mvc/workflow/workflow-view";
|
||||
import Trackster from "viz/trackster";
|
||||
@@ -17,7 +18,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";
|
||||
@@ -27,6 +27,8 @@ import LegacyGridView from "legacy/grid/grid-view";
|
||||
import * as run_stats from "reports/run_stats";
|
||||
import ToolshedGroups from "toolshed/toolshed.groups";
|
||||
|
||||
/* global Galaxy */
|
||||
|
||||
if (window.Galaxy && window.Galaxy.debug === undefined) {
|
||||
//TODO: (kind of a temporary hack?) Must have Galaxy.logging for some of the imports
|
||||
//here; remove when imports are all fixed.
|
||||
@@ -64,89 +66,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,
|
||||
@@ -155,7 +74,6 @@ export function multiHistoryEntry(options) {
|
||||
limitPerFetch: options.limit,
|
||||
currentHistoryId: options.current_history_id
|
||||
});
|
||||
|
||||
let multipanel = new MultiPanel.MultiPanelColumns({
|
||||
el: $("#center").get(0),
|
||||
histories: histories
|
||||
@@ -178,7 +96,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,
|
||||
|
||||
@@ -18,24 +18,29 @@ var AdminPanel = Backbone.View.extend({
|
||||
items: [
|
||||
{
|
||||
title: _l("Data types"),
|
||||
url: "admin/view_datatypes_registry"
|
||||
url: "admin/view_datatypes_registry",
|
||||
id: "admin-link-datatypes"
|
||||
},
|
||||
{
|
||||
title: _l("Data tables"),
|
||||
url: "admin/data_tables",
|
||||
target: "__use_router__"
|
||||
target: "__use_router__",
|
||||
id: "admin-link-data-tables"
|
||||
},
|
||||
{
|
||||
title: _l("Display applications"),
|
||||
url: "admin/display_applications"
|
||||
url: "admin/display_applications",
|
||||
id: "admin-link-display-applications"
|
||||
},
|
||||
{
|
||||
title: _l("Manage jobs"),
|
||||
url: "admin/jobs"
|
||||
url: "admin/jobs",
|
||||
id: "admin-link-jobs"
|
||||
},
|
||||
{
|
||||
title: _l("Local data"),
|
||||
url: "data_manager"
|
||||
url: "data_manager",
|
||||
id: "admin-link-local-data"
|
||||
}
|
||||
]
|
||||
},
|
||||
@@ -45,23 +50,27 @@ var AdminPanel = Backbone.View.extend({
|
||||
{
|
||||
title: _l("Users"),
|
||||
url: "admin/users",
|
||||
target: "__use_router__"
|
||||
target: "__use_router__",
|
||||
id: "admin-link-users"
|
||||
},
|
||||
{
|
||||
title: _l("Quotas"),
|
||||
url: "admin/quotas",
|
||||
target: "__use_router__",
|
||||
enabled: self.config.enable_quotas
|
||||
enabled: self.config.enable_quotas,
|
||||
id: "admin-link-quotas"
|
||||
},
|
||||
{
|
||||
title: _l("Groups"),
|
||||
url: "admin/groups",
|
||||
target: "__use_router__"
|
||||
target: "__use_router__",
|
||||
id: "admin-link-groups"
|
||||
},
|
||||
{
|
||||
title: _l("Roles"),
|
||||
url: "admin/roles",
|
||||
target: "__use_router__"
|
||||
target: "__use_router__",
|
||||
id: "admin-link-roles"
|
||||
},
|
||||
{
|
||||
title: _l("Forms"),
|
||||
@@ -71,12 +80,14 @@ var AdminPanel = Backbone.View.extend({
|
||||
{
|
||||
title: _l("API keys"),
|
||||
url: "admin/api_keys",
|
||||
target: "__use_router__"
|
||||
target: "__use_router__",
|
||||
id: "admin-link-api-keys"
|
||||
},
|
||||
{
|
||||
title: _l("Impersonate a user"),
|
||||
url: "admin/impersonate",
|
||||
enabled: self.config.allow_user_impersonation
|
||||
enabled: self.config.allow_user_impersonation,
|
||||
id: "admin-link-impersonate"
|
||||
}
|
||||
]
|
||||
},
|
||||
@@ -147,6 +158,9 @@ var AdminPanel = Backbone.View.extend({
|
||||
var $link = $("<a/>")
|
||||
.attr({ href: self.root + item.url })
|
||||
.text(_l(item.title));
|
||||
if (item.id) {
|
||||
$link.attr("id", item.id);
|
||||
}
|
||||
if (item.target == "__use_router__") {
|
||||
$link.on("click", e => {
|
||||
e.preventDefault();
|
||||
|
||||
@@ -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() {
|
||||
|
||||
@@ -2,11 +2,11 @@
|
||||
<div class="toolForm">
|
||||
<div class="toolFormTitle">
|
||||
Citations
|
||||
<button v-if="viewRender" v-on:click="toggleViewRender" type="button" class="btn btn-xs citations-to-bibtex" title="Show all in BibTeX format.">
|
||||
<button v-if="viewRender" v-on:click="toggleViewRender" type="button" class="btn btn-sm btn-secondary citations-to-bibtex" title="Show all in BibTeX format.">
|
||||
<i class="fa fa-pencil-square-o"></i>
|
||||
Show BibTeX
|
||||
</button>
|
||||
<button v-else type="button" v-on:click="toggleViewRender" class="btn btn-xs citations-to-formatted" title="Return to formatted citation list.">
|
||||
<button v-else type="button" v-on:click="toggleViewRender" class="btn btn-sm btn-secondary citations-to-formatted" title="Return to formatted citation list.">
|
||||
<i class="fa fa-times"></i>
|
||||
Hide BibTeX
|
||||
</button>
|
||||
|
||||
@@ -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>
|
||||
@@ -7,7 +7,7 @@
|
||||
</div>
|
||||
</div>
|
||||
<div class="portlet-content">
|
||||
<div v-if="errormessage" class="ui-message alert alert-danger">
|
||||
<div v-if="errormessage" class="ui-message ui-show alert alert-danger">
|
||||
{{ errormessage }}
|
||||
</div>
|
||||
<div class="portlet-body">
|
||||
@@ -33,7 +33,7 @@ export default {
|
||||
data() {
|
||||
return {
|
||||
errormessage: null
|
||||
}
|
||||
};
|
||||
},
|
||||
methods: {
|
||||
submit: function() {
|
||||
@@ -45,19 +45,19 @@ export default {
|
||||
processData: false,
|
||||
method: "POST"
|
||||
})
|
||||
.done(response => {
|
||||
window.location = `${Galaxy.root}histories/list?message=${response.message}&status=success`
|
||||
})
|
||||
.fail(response => {
|
||||
let message = response.responseJSON && response.responseJSON.err_msg;
|
||||
this.errormessage = message || "Import failed for unkown reason.";
|
||||
});
|
||||
.done(response => {
|
||||
window.location = `${Galaxy.root}histories/list?message=${response.message}&status=success`;
|
||||
})
|
||||
.fail(response => {
|
||||
let message = response.responseJSON && response.responseJSON.err_msg;
|
||||
this.errormessage = message || "Import failed for unkown reason.";
|
||||
});
|
||||
}
|
||||
}
|
||||
};
|
||||
</script>
|
||||
<style>
|
||||
.ui-message {
|
||||
.ui-show {
|
||||
display: block;
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -0,0 +1,133 @@
|
||||
<template>
|
||||
<div id="structured-history-view">
|
||||
<div id="history-view-controls" class="clear">
|
||||
<div class="float-left">
|
||||
<span v-if="historyHistory['purged'] == false" >
|
||||
<span v-if="historyData.user_is_owner == false" >
|
||||
<button id="import" class="btn btn-secondary">Import and start using history</button>
|
||||
</span>
|
||||
<span v-if="historyData.history_is_current == false">
|
||||
<button id="switch-history" class="btn btn-secondary" v-on:click="switchHistory">Switch to this history</button>
|
||||
</span>
|
||||
<button id="show-structure" class="btn btn-secondary" v-on:click="showStructure">Show structure</button>
|
||||
</span>
|
||||
</div>
|
||||
<div class="float-right">
|
||||
<button id="toggle-deleted" class="btn btn-secondary">Include deleted</button>
|
||||
<button id="toggle-hidden" class="btn btn-secondary">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>
|
||||
@@ -0,0 +1,140 @@
|
||||
<template>
|
||||
<div class="ui-thumbnails">
|
||||
<div v-if="error" class="ui-message ui-show alert alert-danger">
|
||||
{{ error }}
|
||||
</div>
|
||||
<div v-else class="ui-thumbnails-grid">
|
||||
<input class="search-query parent-width" name="query" placeholder="search visualizations" autocomplete="off" type="text" v-model="search">
|
||||
<div v-for="plugin in plugins">
|
||||
<table v-if="match(plugin)">
|
||||
<tr class="ui-thumbnails-item" @click="select(plugin)">
|
||||
<td>
|
||||
<img v-if="plugin.logo" class="ui-thumbnails-image" :src="plugin.logo"/>
|
||||
<div v-else class="ui-thumbnails-icon fa fa-eye"/>
|
||||
</td>
|
||||
<td>
|
||||
<div class="ui-thumbnails-description-title ui-form-info">
|
||||
{{ plugin.html }}
|
||||
</div>
|
||||
<div class="ui-thumbnails-description-text ui-form-info">
|
||||
{{ plugin.description }}
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
<tr v-if="!fixed">
|
||||
<td/>
|
||||
<td v-if="plugin.name == name">
|
||||
<div v-if="hdas && hdas.length > 0">
|
||||
<div class="ui-form-info ui-bold">Select a dataset to visualize:</div>
|
||||
<div class="ui-select">
|
||||
<select class="select" v-model="selected">
|
||||
<option v-for="file in hdas" :value="file.id">{{ file.name }}</option>
|
||||
</select>
|
||||
<div class="icon-dropdown fa fa-caret-down"/>
|
||||
</div>
|
||||
<button type="button" class="ui-button-default ui-float-left btn btn-primary" @click="create(plugin)">
|
||||
<i class="icon fa fa-check ui-margin-right"/>
|
||||
<span class="title">Create Visualization</span>
|
||||
</button>
|
||||
</div>
|
||||
<div v-else class="ui-message ui-show alert alert-danger">
|
||||
There is no suitable dataset in your current history which can be visualized with this plugin.
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
<script>
|
||||
import axios from "axios";
|
||||
export default {
|
||||
data() {
|
||||
return {
|
||||
plugins: [],
|
||||
hdas: [],
|
||||
search: "",
|
||||
selected: null,
|
||||
name: null,
|
||||
error: null,
|
||||
fixed: false
|
||||
};
|
||||
},
|
||||
created() {
|
||||
let url = `${Galaxy.root}api/plugins`;
|
||||
let dataset_id = Galaxy.params.dataset_id;
|
||||
if (dataset_id) {
|
||||
this.fixed = true;
|
||||
this.selected = dataset_id;
|
||||
url += `?dataset_id=${dataset_id}`;
|
||||
}
|
||||
axios
|
||||
.get(url)
|
||||
.then(response => {
|
||||
this.plugins = response.data;
|
||||
})
|
||||
.catch(e => {
|
||||
this.error = this._errorMessage(e);
|
||||
});
|
||||
},
|
||||
methods: {
|
||||
select: function(plugin) {
|
||||
if (this.fixed) {
|
||||
this.create(plugin);
|
||||
} else {
|
||||
let history_id = Galaxy.currHistoryPanel && Galaxy.currHistoryPanel.model.id;
|
||||
if (history_id) {
|
||||
axios
|
||||
.get(`${Galaxy.root}api/plugins/${plugin.name}?history_id=${history_id}`)
|
||||
.then(response => {
|
||||
this.name = plugin.name;
|
||||
this.hdas = response.data && response.data.hdas;
|
||||
if (this.hdas && this.hdas.length > 0) {
|
||||
this.selected = this.hdas[0].id;
|
||||
}
|
||||
})
|
||||
.catch(e => {
|
||||
this.error = this._errorMessage(e);
|
||||
});
|
||||
} else {
|
||||
this.error = "This option requires an accessible history.";
|
||||
}
|
||||
}
|
||||
},
|
||||
create: function(plugin) {
|
||||
let href = `${plugin.href}?dataset_id=${this.selected}`;
|
||||
if (plugin.target == "_top") {
|
||||
window.location.href = href;
|
||||
} else {
|
||||
$("#galaxy_main").attr("src", href);
|
||||
}
|
||||
},
|
||||
match: function(plugin) {
|
||||
return (
|
||||
!this.search ||
|
||||
plugin.name.indexOf(this.search) != -1 ||
|
||||
(plugin.description && plugin.description.indexOf(this.search) != -1)
|
||||
);
|
||||
},
|
||||
_errorMessage: function(e) {
|
||||
let message = e && e.response && e.response.data && e.response.data.err_msg;
|
||||
return message || "Request failed for unkown reason.";
|
||||
}
|
||||
}
|
||||
};
|
||||
</script>
|
||||
<style>
|
||||
.ui-show {
|
||||
display: block;
|
||||
margin-top: 0px;
|
||||
}
|
||||
.ui-bold {
|
||||
font-weight: bold;
|
||||
margin-bottom: 5px;
|
||||
}
|
||||
.ui-float-left {
|
||||
float: left;
|
||||
margin-top: 10px;
|
||||
}
|
||||
</style>
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,119 @@
|
||||
<template>
|
||||
<div>
|
||||
<ol class="rules">
|
||||
<rule-display-preview
|
||||
v-for="(rule, index) in rules"
|
||||
v-bind:rule="rule"
|
||||
v-bind:index="index"
|
||||
v-bind:key="index"
|
||||
:col-headers="columnData.colHeadersPerRule[index]" />
|
||||
<identifier-display-preview v-for="(map, index) in mapping"
|
||||
v-bind="map"
|
||||
v-bind:index="index"
|
||||
v-bind:key="map.type"
|
||||
:col-headers="colHeaders" />
|
||||
</ol>
|
||||
</div>
|
||||
</template>
|
||||
<script>
|
||||
import RuleDefs from "mvc/rules/rule-definitions";
|
||||
import _l from "utils/localization";
|
||||
|
||||
// read-only variants of the components for displaying these rules and mappings in builder widget.
|
||||
const RuleDisplayPreview = {
|
||||
template: `
|
||||
<li class="rule">
|
||||
<span class="rule-display">{{ title }}
|
||||
</span>
|
||||
<span class="rule-warning" v-if="rule.warn">
|
||||
{{ rule.warn }}
|
||||
</span>
|
||||
<span class="rule-error" v-if="rule.error">
|
||||
<span class="alert-message">{{ rule.error }}</span>
|
||||
</span>
|
||||
</li>
|
||||
`,
|
||||
props: {
|
||||
rule: {
|
||||
required: true,
|
||||
type: Object
|
||||
},
|
||||
colHeaders: {
|
||||
type: Array,
|
||||
required: false
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
title() {
|
||||
const ruleType = this.rule.type;
|
||||
return RuleDefs.RULES[ruleType].display(this.rule, this.colHeaders);
|
||||
}
|
||||
},
|
||||
methods: {}
|
||||
};
|
||||
|
||||
const IdentifierDisplayPreview = {
|
||||
template: `
|
||||
<li class="rule" :title="help">
|
||||
Set {{ columnsLabel }} as {{ typeDisplay }}
|
||||
</li>
|
||||
`,
|
||||
props: {
|
||||
type: {
|
||||
type: String,
|
||||
required: true
|
||||
},
|
||||
columns: {
|
||||
required: true
|
||||
},
|
||||
colHeaders: {
|
||||
type: Array,
|
||||
required: true
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
typeDisplay() {
|
||||
return RuleDefs.MAPPING_TARGETS[this.type].label;
|
||||
},
|
||||
help() {
|
||||
return RuleDefs.MAPPING_TARGETS[this.type].help || "";
|
||||
},
|
||||
columnsLabel() {
|
||||
return RuleDefs.columnDisplay(this.columns, this.colHeaders);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
export default {
|
||||
data: function() {
|
||||
return {};
|
||||
},
|
||||
computed: {
|
||||
mapping: function() {
|
||||
return this.inputRules ? this.inputRules.mapping : [];
|
||||
},
|
||||
rules: function() {
|
||||
return this.inputRules ? this.inputRules.rules : [];
|
||||
},
|
||||
columnData: function() {
|
||||
const colHeadersPerRule = [];
|
||||
const hotData = RuleDefs.applyRules([], [], [], this.rules, colHeadersPerRule);
|
||||
return { colHeadersPerRule: colHeadersPerRule, columns: hotData.columns };
|
||||
},
|
||||
colHeaders: function() {
|
||||
const columns = this.columnData.columns;
|
||||
return RuleDefs.colHeadersFor([], columns);
|
||||
}
|
||||
},
|
||||
props: {
|
||||
inputRules: {
|
||||
required: false,
|
||||
type: Object
|
||||
}
|
||||
},
|
||||
components: {
|
||||
RuleDisplayPreview,
|
||||
IdentifierDisplayPreview
|
||||
}
|
||||
};
|
||||
</script>
|
||||
@@ -0,0 +1,76 @@
|
||||
<template>
|
||||
<div class="ui-portlet-limited">
|
||||
<div class="portlet-header">
|
||||
<div class="portlet-title">
|
||||
<i class="portlet-title-icon fa fa-upload"></i>
|
||||
<span class="portlet-title-text"><b>Import Workflow</b></span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="portlet-content">
|
||||
<div v-if="errormessage" class="ui-message alert alert-danger">
|
||||
{{ errormessage }}
|
||||
</div>
|
||||
<div class="portlet-body">
|
||||
<form ref="form">
|
||||
<div class="ui-form-element">
|
||||
<div class="ui-form-title">Archived Workflow URL</div>
|
||||
<input class="ui-input" type="text" name="archive_source"/>
|
||||
<div class="ui-form-info">If the workflow is accessible via a URL, enter the URL above and click Import.</div>
|
||||
</div>
|
||||
<div class="ui-form-element">
|
||||
<div class="ui-form-title">Archived Workflow file</div>
|
||||
<input type="file" name="archive_file"/>
|
||||
<div class="ui-form-info">If the workflow is in a file on your computer, choose it and then click Import.</div>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
<div class="portlet-buttons">
|
||||
<input class="btn btn-primary" type="button" value="Import Workflow" @click="submit"/>
|
||||
</div>
|
||||
<hr/>
|
||||
<div class="ui-form-element">
|
||||
<div class="ui-form-title">Import a Workflow from myExperiment</div>
|
||||
<a :href=myexperiment_target_url>Visit myExperiment</a>
|
||||
<div class="ui-form-info">Click the link above to visit myExperiment and search for Galaxy workflows.</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
<script>
|
||||
export default {
|
||||
data() {
|
||||
return {
|
||||
errormessage: null,
|
||||
myexperiment_target_url: `http://${Galaxy.config.myexperiment_target_url}/galaxy?galaxy_url=${
|
||||
window.location.protocol
|
||||
}//${window.location.host}`
|
||||
};
|
||||
},
|
||||
methods: {
|
||||
submit: function() {
|
||||
$.ajax({
|
||||
url: `${Galaxy.root}api/workflows`,
|
||||
data: new FormData(this.$refs.form),
|
||||
cache: false,
|
||||
contentType: false,
|
||||
processData: false,
|
||||
method: "POST"
|
||||
})
|
||||
.done(response => {
|
||||
window.location = `${Galaxy.root}workflows/list?message=${response.message}&status=${
|
||||
response.status
|
||||
}`;
|
||||
})
|
||||
.fail(response => {
|
||||
let message = response.responseJSON && response.responseJSON.err_msg;
|
||||
this.errormessage = message || "Import failed for unkown reason.";
|
||||
});
|
||||
}
|
||||
}
|
||||
};
|
||||
</script>
|
||||
<style>
|
||||
.ui-message {
|
||||
display: block;
|
||||
}
|
||||
</style>
|
||||
@@ -1,5 +1,5 @@
|
||||
<template>
|
||||
<base-grid :is-loaded="isLoaded" :columns="columns">
|
||||
<base-grid :is-loaded="isLoaded" :columns="columns" id="data-tables-grid">
|
||||
<template slot="title">
|
||||
Current data table registry contains {{ rows.length }} data tables
|
||||
</template>
|
||||
|
||||
@@ -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;
|
||||
|
||||
+10
-10
@@ -1,7 +1,11 @@
|
||||
import _l from "utils/localization";
|
||||
/** Real-time Communication feature **/
|
||||
import * as Backbone from "backbone";
|
||||
import * as _ from "underscore";
|
||||
import _l from "utils/localization";
|
||||
import Modal from "mvc/ui/ui-modal";
|
||||
var GenericNavView = Backbone.View.extend({
|
||||
/* global $ */
|
||||
|
||||
export var CommunicationServerView = Backbone.View.extend({
|
||||
initialize: function() {
|
||||
this.modal = null;
|
||||
},
|
||||
@@ -12,9 +16,9 @@ var GenericNavView = Backbone.View.extend({
|
||||
var host = window.Galaxy.config.communication_server_host;
|
||||
|
||||
var port = window.Galaxy.config.communication_server_port;
|
||||
var username = escape(window.Galaxy.user.attributes.username);
|
||||
var username = _.escape(window.Galaxy.user.attributes.username);
|
||||
|
||||
var persistent_communication_rooms = escape(window.Galaxy.config.persistent_communication_rooms);
|
||||
var persistent_communication_rooms = _.escape(window.Galaxy.config.persistent_communication_rooms);
|
||||
|
||||
var query_string = `?username=${username}&persistent_communication_rooms=${persistent_communication_rooms}`;
|
||||
|
||||
@@ -38,7 +42,7 @@ var GenericNavView = Backbone.View.extend({
|
||||
$(".chat-modal").remove();
|
||||
}
|
||||
// creates a modal
|
||||
GenericNavView.modal = new Modal.View({
|
||||
CommunicationServerView.modal = new Modal.View({
|
||||
body: iframe_template,
|
||||
height: frame_height,
|
||||
width: frame_width,
|
||||
@@ -48,7 +52,7 @@ var GenericNavView = Backbone.View.extend({
|
||||
});
|
||||
|
||||
// shows modal
|
||||
GenericNavView.modal.show();
|
||||
CommunicationServerView.modal.show();
|
||||
$el_chat_modal_header = $(".chat-modal .modal-header");
|
||||
$el_chat_modal_body = $(".chat-modal .modal-body");
|
||||
// adjusts the css of bootstrap modal for chat
|
||||
@@ -98,7 +102,3 @@ var GenericNavView = Backbone.View.extend({
|
||||
return navItem;
|
||||
}
|
||||
});
|
||||
|
||||
export default {
|
||||
GenericNavView: GenericNavView
|
||||
};
|
||||
@@ -1,17 +1,21 @@
|
||||
import Utils from "utils/utils";
|
||||
import * as Backbone from "backbone";
|
||||
import Menu from "layout/menu";
|
||||
import Scratchbook from "layout/scratchbook";
|
||||
import QuotaMeter from "mvc/user/user-quotameter";
|
||||
|
||||
/* global Galaxy */
|
||||
/* global $ */
|
||||
|
||||
/** Masthead **/
|
||||
var View = Backbone.View.extend({
|
||||
initialize: function(options) {
|
||||
var self = this;
|
||||
this.options = options;
|
||||
this.setElement(this._template());
|
||||
this.$navbarBrandLink = this.$(".navbar-brand-link");
|
||||
this.$navbarBrandLink = this.$(".navbar-brand");
|
||||
this.$navbarBrandImage = this.$(".navbar-brand-image");
|
||||
this.$navbarBrandTitle = this.$(".navbar-brand-title");
|
||||
this.$navbarTabs = this.$(".navbar-tabs");
|
||||
this.$navbarTabs = this.$(".navbar-nav");
|
||||
this.$quoteMeter = this.$(".quota-meter-container");
|
||||
|
||||
// build tabs
|
||||
@@ -30,6 +34,9 @@ var View = Backbone.View.extend({
|
||||
})
|
||||
.fetch(this.options);
|
||||
|
||||
// highlight initial active view
|
||||
this.highlight(options.active_view);
|
||||
|
||||
// scratchbook
|
||||
Galaxy.frame = this.frame = new Scratchbook({
|
||||
collection: this.collection
|
||||
@@ -62,7 +69,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;
|
||||
@@ -78,23 +87,23 @@ var View = Backbone.View.extend({
|
||||
return this;
|
||||
},
|
||||
|
||||
highlight: function(id) {
|
||||
this.collection.forEach(function(model) {
|
||||
model.set("active", model.id == id);
|
||||
});
|
||||
},
|
||||
|
||||
/** body template */
|
||||
_template: function() {
|
||||
return (
|
||||
'<div id="masthead" class="navbar navbar-fixed-top navbar-inverse">' +
|
||||
'<div class="navbar-header">' +
|
||||
'<div class="navbar-tabs"/>' +
|
||||
"</div>" +
|
||||
'<div class="navbar-brand">' +
|
||||
'<a class="navbar-brand-link">' +
|
||||
'<img class="navbar-brand-image"/>' +
|
||||
'<span class="navbar-brand-title"/>' +
|
||||
"</a>" +
|
||||
"</div>" +
|
||||
'<div class="quota-meter-container"/>' +
|
||||
'<div class="navbar-icons"/>' +
|
||||
"</div>"
|
||||
);
|
||||
return `
|
||||
<nav id="masthead" class="navbar navbar-expand fixed-top justify-content-center navbar-dark">
|
||||
<a class="navbar-brand">
|
||||
<img class="navbar-brand-image"/>
|
||||
<span class="navbar-brand-title"/>
|
||||
</a>
|
||||
<ul class="navbar-nav"/>
|
||||
<div class="quota-meter-container"/>
|
||||
</nav>`;
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
@@ -1,8 +1,14 @@
|
||||
/** Masthead Collection **/
|
||||
import GenericNav from "layout/generic-nav-view";
|
||||
import * as Backbone from "backbone";
|
||||
import * as _ from "underscore";
|
||||
import { CommunicationServerView } from "layout/communication-server-view";
|
||||
import Webhooks from "mvc/webhooks";
|
||||
import _l from "utils/localization";
|
||||
import Utils from "utils/utils";
|
||||
|
||||
/* global Galaxy */
|
||||
/* global $ */
|
||||
|
||||
var Collection = Backbone.Collection.extend({
|
||||
model: Backbone.Model.extend({
|
||||
defaults: {
|
||||
@@ -11,14 +17,13 @@ var Collection = Backbone.Collection.extend({
|
||||
}
|
||||
}),
|
||||
fetch: function(options) {
|
||||
var self = this;
|
||||
options = options || {};
|
||||
this.reset();
|
||||
|
||||
//
|
||||
// Chat server tab
|
||||
//
|
||||
var extendedNavItem = new GenericNav.GenericNavView();
|
||||
var extendedNavItem = new CommunicationServerView();
|
||||
this.add(extendedNavItem.render());
|
||||
|
||||
//
|
||||
@@ -42,6 +47,33 @@ var Collection = Backbone.Collection.extend({
|
||||
url: "workflows/list"
|
||||
});
|
||||
|
||||
//
|
||||
// Visualization tab.
|
||||
//
|
||||
this.add({
|
||||
id: "visualization",
|
||||
title: _l("Visualize"),
|
||||
url: "visualizations/list",
|
||||
tooltip: _l("Visualize datasets"),
|
||||
disabled: !Galaxy.user.id,
|
||||
menu: [
|
||||
{
|
||||
title: _l("Create Visualization"),
|
||||
url: "visualizations"
|
||||
},
|
||||
{
|
||||
title: _l("Interactive Environments"),
|
||||
url: "visualization/gie_list",
|
||||
target: "galaxy_main"
|
||||
},
|
||||
{
|
||||
title: _l("Saved Visualizations"),
|
||||
url: "visualizations/list",
|
||||
target: "_frame"
|
||||
}
|
||||
]
|
||||
});
|
||||
|
||||
//
|
||||
// 'Shared Items' or Libraries tab.
|
||||
//
|
||||
@@ -74,49 +106,22 @@ var Collection = Backbone.Collection.extend({
|
||||
]
|
||||
});
|
||||
|
||||
//
|
||||
// Visualization tab.
|
||||
//
|
||||
this.add({
|
||||
id: "visualization",
|
||||
title: _l("Visualization"),
|
||||
url: "visualizations/list",
|
||||
tooltip: _l("Visualize datasets"),
|
||||
disabled: !Galaxy.user.id,
|
||||
menu: [
|
||||
{
|
||||
title: _l("New Track Browser"),
|
||||
url: "visualization/trackster",
|
||||
target: "_frame"
|
||||
},
|
||||
{
|
||||
title: _l("Saved Visualizations"),
|
||||
url: "visualizations/list",
|
||||
target: "_frame"
|
||||
},
|
||||
{
|
||||
title: _l("Interactive Environments"),
|
||||
url: "visualization/gie_list",
|
||||
target: "galaxy_main"
|
||||
}
|
||||
]
|
||||
});
|
||||
|
||||
//
|
||||
// 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,
|
||||
/*jslint evil: true */
|
||||
onclick: webhook.config.function && new Function(webhook.config.function)
|
||||
};
|
||||
|
||||
@@ -138,7 +143,7 @@ var Collection = Backbone.Collection.extend({
|
||||
//
|
||||
// Admin.
|
||||
//
|
||||
Galaxy.user.get("is_admin") &&
|
||||
if (Galaxy.user.get("is_admin")) {
|
||||
this.add({
|
||||
id: "admin",
|
||||
title: _l("Admin"),
|
||||
@@ -146,6 +151,7 @@ var Collection = Backbone.Collection.extend({
|
||||
tooltip: _l("Administer this Galaxy"),
|
||||
cls: "admin-only"
|
||||
});
|
||||
}
|
||||
|
||||
//
|
||||
// Help tab.
|
||||
@@ -191,24 +197,27 @@ var Collection = Backbone.Collection.extend({
|
||||
}
|
||||
]
|
||||
};
|
||||
options.terms_url &&
|
||||
if (options.terms_url) {
|
||||
helpTab.menu.push({
|
||||
title: _l("Terms and Conditions"),
|
||||
url: options.terms_url,
|
||||
target: "_blank"
|
||||
});
|
||||
options.biostar_url &&
|
||||
}
|
||||
if (options.biostar_url) {
|
||||
helpTab.menu.unshift({
|
||||
title: _l("Ask a question"),
|
||||
url: "biostar/biostar_question_redirect",
|
||||
target: "_blank"
|
||||
});
|
||||
options.biostar_url &&
|
||||
}
|
||||
if (options.biostar_url) {
|
||||
helpTab.menu.unshift({
|
||||
title: _l("Galaxy Biostar"),
|
||||
url: options.biostar_url_redirect,
|
||||
target: "_blank"
|
||||
});
|
||||
}
|
||||
this.add(helpTab);
|
||||
|
||||
//
|
||||
@@ -273,27 +282,30 @@ var Collection = Backbone.Collection.extend({
|
||||
divider: true
|
||||
},
|
||||
{
|
||||
title: _l("Saved Histories"),
|
||||
url: "histories/list",
|
||||
title: _l("Saved Datasets"),
|
||||
url: "datasets/list",
|
||||
target: "_top"
|
||||
},
|
||||
{
|
||||
title: _l("Saved Datasets"),
|
||||
url: "datasets/list",
|
||||
title: _l("Saved Histories"),
|
||||
url: "histories/list",
|
||||
target: "_top"
|
||||
},
|
||||
{
|
||||
title: _l("Saved Pages"),
|
||||
url: "pages/list",
|
||||
target: "_top"
|
||||
},
|
||||
{
|
||||
title: _l("Saved Visualizations"),
|
||||
url: "visualizations/list",
|
||||
target: "_top"
|
||||
}
|
||||
]
|
||||
};
|
||||
}
|
||||
this.add(userTab);
|
||||
var activeView = this.get(options.active_view);
|
||||
activeView && activeView.set("active", true);
|
||||
return new jQuery.Deferred().resolve().promise();
|
||||
return new $.Deferred().resolve().promise();
|
||||
}
|
||||
});
|
||||
|
||||
@@ -302,49 +314,51 @@ var Tab = Backbone.View.extend({
|
||||
initialize: function(options) {
|
||||
this.model = options.model;
|
||||
this.setElement(this._template());
|
||||
this.$dropdown = this.$(".dropdown");
|
||||
this.$toggle = this.$(".dropdown-toggle");
|
||||
this.$link = this.$(".nav-link");
|
||||
this.$note = this.$(".nav-note");
|
||||
this.$menu = this.$(".dropdown-menu");
|
||||
this.$note = this.$(".dropdown-note");
|
||||
this.listenTo(this.model, "change", this.render, this);
|
||||
},
|
||||
|
||||
events: {
|
||||
"click .dropdown-toggle": "_toggleClick"
|
||||
"click .nav-link": "_toggleClick"
|
||||
},
|
||||
|
||||
render: function() {
|
||||
var self = this;
|
||||
$(".tooltip").remove();
|
||||
this.$el.attr("id", this.model.id).css({
|
||||
visibility: (this.model.get("visible") && "visible") || "hidden"
|
||||
});
|
||||
this.$el
|
||||
.removeClass()
|
||||
.addClass(this.model.get("disabled") && "disabled")
|
||||
.addClass(this.model.get("active") && "active")
|
||||
.addClass(this.model.get("menu") && "dropdown")
|
||||
.attr("id", this.model.id)
|
||||
.css({
|
||||
visibility: (this.model.get("visible") && "visible") || "hidden"
|
||||
});
|
||||
this.model.set("url", this._formatUrl(this.model.get("url")));
|
||||
this.$note
|
||||
.html(this.model.get("note") || "")
|
||||
.removeClass()
|
||||
.addClass("dropdown-note")
|
||||
.addClass("nav-note")
|
||||
.addClass(this.model.get("note_cls"))
|
||||
.css({
|
||||
display: (this.model.get("show_note") && "block") || "none"
|
||||
});
|
||||
this.$toggle
|
||||
this.$link
|
||||
.html(this.model.get("title") || "")
|
||||
.removeClass()
|
||||
.addClass("dropdown-toggle")
|
||||
.addClass("nav-link")
|
||||
.addClass(this.model.get("cls"))
|
||||
.addClass(this.model.get("icon") && `dropdown-icon fa ${this.model.get("icon")}`)
|
||||
.addClass(this.model.get("icon") && `nav-icon fa ${this.model.get("icon")}`)
|
||||
.addClass(this.model.get("menu") && "dropdown-toggle")
|
||||
.addClass(this.model.get("toggle") && "toggle")
|
||||
.attr("target", this.model.get("target"))
|
||||
.attr("href", this.model.get("url"))
|
||||
.attr("title", this.model.get("tooltip"))
|
||||
.tooltip("destroy");
|
||||
this.model.get("tooltip") && this.$toggle.tooltip({ placement: "bottom" });
|
||||
this.$dropdown
|
||||
.removeClass()
|
||||
.addClass("dropdown")
|
||||
.addClass(this.model.get("disabled") && "disabled")
|
||||
.addClass(this.model.get("active") && "active");
|
||||
.tooltip("dispose");
|
||||
if (this.model.get("tooltip")) {
|
||||
this.$link.tooltip({ placement: "bottom" });
|
||||
}
|
||||
if (this.model.get("menu") && this.model.get("show_menu")) {
|
||||
this.$menu.show();
|
||||
$("#dd-helper")
|
||||
@@ -352,90 +366,98 @@ var Tab = Backbone.View.extend({
|
||||
.off()
|
||||
.on("click", () => {
|
||||
$("#dd-helper").hide();
|
||||
self.model.set("show_menu", false);
|
||||
this.model.set("show_menu", false);
|
||||
});
|
||||
} else {
|
||||
self.$menu.hide();
|
||||
this.$menu.hide();
|
||||
$("#dd-helper").hide();
|
||||
}
|
||||
this.$menu.empty().removeClass("dropdown-menu");
|
||||
this.$menu.empty().removeClass();
|
||||
if (this.model.get("menu")) {
|
||||
_.each(this.model.get("menu"), menuItem => {
|
||||
self.$menu.append(self._buildMenuItem(menuItem));
|
||||
menuItem.divider && self.$menu.append($("<li/>").addClass("divider"));
|
||||
this.$menu.append(this._buildMenuItem(menuItem));
|
||||
if (menuItem.divider) {
|
||||
this.$menu.append($("<div/>").addClass("dropdown-divider"));
|
||||
}
|
||||
});
|
||||
self.$menu.addClass("dropdown-menu");
|
||||
self.$toggle.append($("<b/>").addClass("caret"));
|
||||
this.$menu.addClass("dropdown-menu");
|
||||
this.$link.append($("<b/>").addClass("caret"));
|
||||
}
|
||||
return this;
|
||||
},
|
||||
|
||||
/** Add new menu item */
|
||||
_buildMenuItem: function(options) {
|
||||
var self = this;
|
||||
options = _.defaults(options || {}, {
|
||||
title: "",
|
||||
url: "",
|
||||
target: "_parent",
|
||||
noscratchbook: false
|
||||
});
|
||||
options.url = self._formatUrl(options.url);
|
||||
return $("<li/>").append(
|
||||
$("<a/>")
|
||||
.attr("href", options.url)
|
||||
.attr("target", options.target)
|
||||
.html(options.title)
|
||||
.on("click", e => {
|
||||
e.preventDefault();
|
||||
self.model.set("show_menu", false);
|
||||
if (options.onclick) {
|
||||
options.onclick();
|
||||
} else {
|
||||
Galaxy.frame.add(options);
|
||||
}
|
||||
})
|
||||
);
|
||||
options.url = this._formatUrl(options.url);
|
||||
return $("<a/>")
|
||||
.addClass("dropdown-item")
|
||||
.attr("href", options.url)
|
||||
.attr("target", options.target)
|
||||
.html(options.title)
|
||||
.on("click", e => {
|
||||
e.preventDefault();
|
||||
this.model.set("show_menu", false);
|
||||
if (options.onclick) {
|
||||
options.onclick();
|
||||
} else {
|
||||
Galaxy.frame.add(options);
|
||||
}
|
||||
});
|
||||
},
|
||||
|
||||
buildLink: function(label, url) {
|
||||
return $("<div/>")
|
||||
.append(
|
||||
$("<a/>")
|
||||
.attr("href", Galaxy.root + url)
|
||||
.html(label)
|
||||
)
|
||||
.html();
|
||||
},
|
||||
|
||||
/** Handle click event */
|
||||
_toggleClick: function(e) {
|
||||
var self = this;
|
||||
var model = this.model;
|
||||
e.preventDefault();
|
||||
$(".tooltip").hide();
|
||||
model.trigger("dispatch", m => {
|
||||
model.id !== m.id && m.get("menu") && m.set("show_menu", false);
|
||||
if (model.id !== m.id && m.get("menu")) {
|
||||
m.set("show_menu", false);
|
||||
}
|
||||
});
|
||||
if (!model.get("disabled")) {
|
||||
if (!model.get("menu")) {
|
||||
model.get("onclick") ? model.get("onclick")() : Galaxy.frame.add(model.attributes);
|
||||
if (model.get("onclick")) {
|
||||
model.get("onclick")();
|
||||
} else {
|
||||
Galaxy.frame.add(model.attributes);
|
||||
}
|
||||
} else {
|
||||
model.set("show_menu", true);
|
||||
}
|
||||
} else {
|
||||
function buildLink(label, url) {
|
||||
return $("<div/>")
|
||||
.append(
|
||||
$("<a/>")
|
||||
.attr("href", Galaxy.root + url)
|
||||
.html(label)
|
||||
)
|
||||
.html();
|
||||
if (this.$link.popover) {
|
||||
this.$link.popover("dispose");
|
||||
}
|
||||
this.$toggle.popover && this.$toggle.popover("destroy");
|
||||
this.$toggle
|
||||
this.$link
|
||||
.popover({
|
||||
html: true,
|
||||
placement: "bottom",
|
||||
content: `Please ${buildLink("login", "user/login?use_panels=True")} or ${buildLink(
|
||||
content: `Please ${this.buildLink("login", "user/login?use_panels=True")} or ${this.buildLink(
|
||||
"register",
|
||||
"user/create?use_panels=True"
|
||||
)} to use this feature.`
|
||||
})
|
||||
.popover("show");
|
||||
setTimeout(() => {
|
||||
self.$toggle.popover("destroy");
|
||||
}, 5000);
|
||||
window.setTimeout(() => {
|
||||
this.$link.popover("dispose");
|
||||
}, 3000);
|
||||
}
|
||||
},
|
||||
|
||||
@@ -446,15 +468,12 @@ var Tab = Backbone.View.extend({
|
||||
|
||||
/** body tempate */
|
||||
_template: function() {
|
||||
return (
|
||||
'<ul class="nav navbar-nav">' +
|
||||
'<li class="dropdown">' +
|
||||
'<a class="dropdown-toggle"/>' +
|
||||
'<ul class="dropdown-menu"/>' +
|
||||
'<div class="dropdown-note"/>' +
|
||||
"</li>" +
|
||||
"</ul>"
|
||||
);
|
||||
return `
|
||||
<li class="nav-item">
|
||||
<a class="nav-link"/>
|
||||
<div class="nav-note"/>
|
||||
<div class="dropdown-menu"/>
|
||||
</li>`;
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
@@ -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,11 +15,18 @@ 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
|
||||
Galaxy.modal = this.modal = new Modal.View();
|
||||
Galaxy.router = this.router = options.Router && new options.Router(self, options);
|
||||
this.masthead = new Masthead.View(this.config);
|
||||
this.center = new Panel.CenterPanel();
|
||||
|
||||
// display helper
|
||||
Galaxy.display = this.display = view => {
|
||||
if (view.title) {
|
||||
Utils.setWindowTitle(view.title);
|
||||
@@ -27,53 +35,71 @@ var View = Backbone.View.extend({
|
||||
Utils.setWindowTitle();
|
||||
view.allow_title_display = true;
|
||||
}
|
||||
if (view.active_tab) {
|
||||
self.masthead.highlight(view.active_tab);
|
||||
}
|
||||
self.center.display(view);
|
||||
};
|
||||
Galaxy.router = this.router = options.Router && new options.Router(self, options);
|
||||
this.masthead = new Masthead.View(this.config);
|
||||
this.center = new Panel.CenterPanel();
|
||||
|
||||
// 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 +151,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 +161,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("");
|
||||
},
|
||||
|
||||
|
||||
@@ -1,9 +1,15 @@
|
||||
/** Frame manager uses the ui-frames to create the scratch book masthead icon and functionality **/
|
||||
import * as Backbone from "backbone";
|
||||
import * as _ from "underscore";
|
||||
import Frames from "mvc/ui/ui-frames";
|
||||
import DATA from "mvc/dataset/data";
|
||||
import visualization from "viz/visualization";
|
||||
import trackster from "viz/trackster";
|
||||
import _l from "utils/localization";
|
||||
|
||||
/* global Galaxy */
|
||||
/* global $ */
|
||||
|
||||
export default Backbone.View.extend({
|
||||
initialize: function(options) {
|
||||
var self = this;
|
||||
@@ -21,7 +27,9 @@ export default Backbone.View.extend({
|
||||
show_note: self.active,
|
||||
note_cls: self.active && "fa fa-check"
|
||||
});
|
||||
!self.active && self.frames.hide();
|
||||
if (!self.active) {
|
||||
self.frames.hide();
|
||||
}
|
||||
},
|
||||
onbeforeunload: function() {
|
||||
if (self.frames.length() > 0) {
|
||||
@@ -36,12 +44,18 @@ export default Backbone.View.extend({
|
||||
show_note: true,
|
||||
visible: false,
|
||||
onclick: function(e) {
|
||||
self.frames.visible ? self.frames.hide() : self.frames.show();
|
||||
if (self.frames.visible) {
|
||||
self.frames.hide();
|
||||
} else {
|
||||
self.frames.show();
|
||||
}
|
||||
}
|
||||
});
|
||||
this.frames
|
||||
.on("add remove", function() {
|
||||
this.visible && this.length() == 0 && this.hide();
|
||||
if (this.visible && this.length() === 0) {
|
||||
this.hide();
|
||||
}
|
||||
self.buttonLoad.set({
|
||||
note: this.length(),
|
||||
visible: this.length() > 0
|
||||
@@ -67,9 +81,9 @@ export default Backbone.View.extend({
|
||||
dataset_ids: []
|
||||
};
|
||||
Galaxy.currHistoryPanel.collection.each(model => {
|
||||
!model.get("deleted") &&
|
||||
model.get("visible") &&
|
||||
if (!model.get("deleted") && model.get("visible")) {
|
||||
self.history_cache[history_id].dataset_ids.push(model.get("id"));
|
||||
}
|
||||
});
|
||||
}
|
||||
var _findDataset = (dataset, offset) => {
|
||||
@@ -195,7 +209,7 @@ export default Backbone.View.extend({
|
||||
id: d.dataset_id
|
||||
};
|
||||
});
|
||||
view = ui.create_visualization(
|
||||
ui.create_visualization(
|
||||
view_config,
|
||||
latest_revision.config.viewport,
|
||||
latest_revision.config.view.drawables,
|
||||
@@ -218,13 +232,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;
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
@@ -17,6 +17,7 @@ export default Backbone.View.extend({
|
||||
this.grid = new GridModel();
|
||||
this.dict_format = grid_config.dict_format;
|
||||
this.title = grid_config.title;
|
||||
this.title_id = grid_config.title_id;
|
||||
var self = this;
|
||||
window.add_tag_to_grid_filter = (tag_name, tag_value) => {
|
||||
// Put tag name and value together.
|
||||
|
||||
-927
@@ -1,927 +0,0 @@
|
||||
/* ========================================================================
|
||||
* bootstrap-tour - v0.10.2
|
||||
* http://bootstraptour.com
|
||||
* ========================================================================
|
||||
* Copyright 2012-2015 Ulrich Sossou
|
||||
*
|
||||
* ========================================================================
|
||||
* Licensed under the MIT License (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://opensource.org/licenses/MIT
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
* ========================================================================
|
||||
*/
|
||||
|
||||
(function($, window) {
|
||||
var Tour, document;
|
||||
document = window.document;
|
||||
Tour = (function() {
|
||||
function Tour(options) {
|
||||
var storage;
|
||||
try {
|
||||
storage = window.localStorage;
|
||||
} catch (_error) {
|
||||
storage = false;
|
||||
}
|
||||
this._options = $.extend({
|
||||
name: 'tour',
|
||||
steps: [],
|
||||
container: 'body',
|
||||
autoscroll: true,
|
||||
keyboard: true,
|
||||
storage: storage,
|
||||
debug: false,
|
||||
backdrop: false,
|
||||
backdropContainer: 'body',
|
||||
backdropPadding: 0,
|
||||
redirect: true,
|
||||
orphan: false,
|
||||
duration: false,
|
||||
delay: false,
|
||||
basePath: '',
|
||||
template: '<div class="popover" role="tooltip"> <div class="arrow"></div> <h3 class="popover-title"></h3> <div class="popover-content"></div> <div class="popover-navigation"> <div class="btn-group"> <button class="btn btn-sm btn-default" data-role="prev">« Prev</button> <button class="btn btn-sm btn-default" data-role="next">Next »</button> <button class="btn btn-sm btn-default" data-role="pause-resume" data-pause-text="Pause" data-resume-text="Resume">Pause</button> </div> <button class="btn btn-sm btn-default" data-role="end">End tour</button> </div> </div>',
|
||||
afterSetState: function(key, value) {},
|
||||
afterGetState: function(key, value) {},
|
||||
afterRemoveState: function(key) {},
|
||||
onStart: function(tour) {},
|
||||
onEnd: function(tour) {},
|
||||
onShow: function(tour) {},
|
||||
onShown: function(tour) {},
|
||||
onHide: function(tour) {},
|
||||
onHidden: function(tour) {},
|
||||
onNext: function(tour) {},
|
||||
onPrev: function(tour) {},
|
||||
onPause: function(tour, duration) {},
|
||||
onResume: function(tour, duration) {},
|
||||
onRedirectError: function(tour) {}
|
||||
}, options);
|
||||
this._force = false;
|
||||
this._inited = false;
|
||||
this._current = null;
|
||||
this.backdrop = {
|
||||
overlay: null,
|
||||
$element: null,
|
||||
$background: null,
|
||||
backgroundShown: false,
|
||||
overlayElementShown: false
|
||||
};
|
||||
this;
|
||||
}
|
||||
|
||||
Tour.prototype.addSteps = function(steps) {
|
||||
var step, _i, _len;
|
||||
for (_i = 0, _len = steps.length; _i < _len; _i++) {
|
||||
step = steps[_i];
|
||||
this.addStep(step);
|
||||
}
|
||||
return this;
|
||||
};
|
||||
|
||||
Tour.prototype.addStep = function(step) {
|
||||
this._options.steps.push(step);
|
||||
return this;
|
||||
};
|
||||
|
||||
Tour.prototype.getStep = function(i) {
|
||||
if (this._options.steps[i] != null) {
|
||||
return $.extend({
|
||||
id: "step-" + i,
|
||||
path: '',
|
||||
host: '',
|
||||
placement: 'right',
|
||||
title: '',
|
||||
content: '<p></p>',
|
||||
next: i === this._options.steps.length - 1 ? -1 : i + 1,
|
||||
prev: i - 1,
|
||||
animation: true,
|
||||
container: this._options.container,
|
||||
autoscroll: this._options.autoscroll,
|
||||
backdrop: this._options.backdrop,
|
||||
backdropContainer: this._options.backdropContainer,
|
||||
backdropPadding: this._options.backdropPadding,
|
||||
redirect: this._options.redirect,
|
||||
reflexElement: this._options.steps[i].element,
|
||||
orphan: this._options.orphan,
|
||||
duration: this._options.duration,
|
||||
delay: this._options.delay,
|
||||
template: this._options.template,
|
||||
onShow: this._options.onShow,
|
||||
onShown: this._options.onShown,
|
||||
onHide: this._options.onHide,
|
||||
onHidden: this._options.onHidden,
|
||||
onNext: this._options.onNext,
|
||||
onPrev: this._options.onPrev,
|
||||
onPause: this._options.onPause,
|
||||
onResume: this._options.onResume,
|
||||
onRedirectError: this._options.onRedirectError
|
||||
}, this._options.steps[i]);
|
||||
}
|
||||
};
|
||||
|
||||
Tour.prototype.init = function(force) {
|
||||
this._force = force;
|
||||
if (this.ended()) {
|
||||
this._debug('Tour ended, init prevented.');
|
||||
return this;
|
||||
}
|
||||
this.setCurrentStep();
|
||||
this._initMouseNavigation();
|
||||
this._initKeyboardNavigation();
|
||||
this._onResize((function(_this) {
|
||||
return function() {
|
||||
return _this.showStep(_this._current);
|
||||
};
|
||||
})(this));
|
||||
if (this._current !== null) {
|
||||
this.showStep(this._current);
|
||||
}
|
||||
this._inited = true;
|
||||
return this;
|
||||
};
|
||||
|
||||
Tour.prototype.start = function(force) {
|
||||
var promise;
|
||||
if (force == null) {
|
||||
force = false;
|
||||
}
|
||||
if (!this._inited) {
|
||||
this.init(force);
|
||||
}
|
||||
if (this._current === null) {
|
||||
promise = this._makePromise(this._options.onStart != null ? this._options.onStart(this) : void 0);
|
||||
this._callOnPromiseDone(promise, this.showStep, 0);
|
||||
}
|
||||
return this;
|
||||
};
|
||||
|
||||
Tour.prototype.next = function() {
|
||||
var promise;
|
||||
promise = this.hideStep(this._current);
|
||||
return this._callOnPromiseDone(promise, this._showNextStep);
|
||||
};
|
||||
|
||||
Tour.prototype.prev = function() {
|
||||
var promise;
|
||||
promise = this.hideStep(this._current);
|
||||
return this._callOnPromiseDone(promise, this._showPrevStep);
|
||||
};
|
||||
|
||||
Tour.prototype.goTo = function(i) {
|
||||
var promise;
|
||||
promise = this.hideStep(this._current);
|
||||
return this._callOnPromiseDone(promise, this.showStep, i);
|
||||
};
|
||||
|
||||
Tour.prototype.end = function() {
|
||||
var endHelper, promise;
|
||||
endHelper = (function(_this) {
|
||||
return function(e) {
|
||||
$(document).off("click.tour-" + _this._options.name);
|
||||
$(document).off("keyup.tour-" + _this._options.name);
|
||||
$(window).off("resize.tour-" + _this._options.name);
|
||||
_this._setState('end', 'yes');
|
||||
_this._inited = false;
|
||||
_this._force = false;
|
||||
_this._clearTimer();
|
||||
if (_this._options.onEnd != null) {
|
||||
return _this._options.onEnd(_this);
|
||||
}
|
||||
};
|
||||
})(this);
|
||||
promise = this.hideStep(this._current);
|
||||
return this._callOnPromiseDone(promise, endHelper);
|
||||
};
|
||||
|
||||
Tour.prototype.ended = function() {
|
||||
return !this._force && !!this._getState('end');
|
||||
};
|
||||
|
||||
Tour.prototype.restart = function() {
|
||||
this._removeState('current_step');
|
||||
this._removeState('end');
|
||||
this._removeState('redirect_to');
|
||||
return this.start();
|
||||
};
|
||||
|
||||
Tour.prototype.pause = function() {
|
||||
var step;
|
||||
step = this.getStep(this._current);
|
||||
if (!(step && step.duration)) {
|
||||
return this;
|
||||
}
|
||||
this._paused = true;
|
||||
this._duration -= new Date().getTime() - this._start;
|
||||
window.clearTimeout(this._timer);
|
||||
this._debug("Paused/Stopped step " + (this._current + 1) + " timer (" + this._duration + " remaining).");
|
||||
if (step.onPause != null) {
|
||||
return step.onPause(this, this._duration);
|
||||
}
|
||||
};
|
||||
|
||||
Tour.prototype.resume = function() {
|
||||
var step;
|
||||
step = this.getStep(this._current);
|
||||
if (!(step && step.duration)) {
|
||||
return this;
|
||||
}
|
||||
this._paused = false;
|
||||
this._start = new Date().getTime();
|
||||
this._duration = this._duration || step.duration;
|
||||
this._timer = window.setTimeout((function(_this) {
|
||||
return function() {
|
||||
if (_this._isLast()) {
|
||||
return _this.next();
|
||||
} else {
|
||||
return _this.end();
|
||||
}
|
||||
};
|
||||
})(this), this._duration);
|
||||
this._debug("Started step " + (this._current + 1) + " timer with duration " + this._duration);
|
||||
if ((step.onResume != null) && this._duration !== step.duration) {
|
||||
return step.onResume(this, this._duration);
|
||||
}
|
||||
};
|
||||
|
||||
Tour.prototype.hideStep = function(i) {
|
||||
var hideStepHelper, promise, step;
|
||||
step = this.getStep(i);
|
||||
if (!step) {
|
||||
return;
|
||||
}
|
||||
this._clearTimer();
|
||||
promise = this._makePromise(step.onHide != null ? step.onHide(this, i) : void 0);
|
||||
hideStepHelper = (function(_this) {
|
||||
return function(e) {
|
||||
var $element;
|
||||
$element = $(step.element);
|
||||
if (!($element.data('bs.popover') || $element.data('popover'))) {
|
||||
$element = $('body');
|
||||
}
|
||||
$element.popover('destroy').removeClass("tour-" + _this._options.name + "-element tour-" + _this._options.name + "-" + i + "-element");
|
||||
$element.removeData('bs.popover');
|
||||
if (step.reflex) {
|
||||
$(step.reflexElement).removeClass('tour-step-element-reflex').off("" + (_this._reflexEvent(step.reflex)) + ".tour-" + _this._options.name);
|
||||
}
|
||||
if (step.backdrop) {
|
||||
_this._hideBackdrop();
|
||||
}
|
||||
if (step.onHidden != null) {
|
||||
return step.onHidden(_this);
|
||||
}
|
||||
};
|
||||
})(this);
|
||||
this._callOnPromiseDone(promise, hideStepHelper);
|
||||
return promise;
|
||||
};
|
||||
|
||||
Tour.prototype.showStep = function(i) {
|
||||
var promise, showStepHelper, skipToPrevious, step;
|
||||
if (this.ended()) {
|
||||
this._debug('Tour ended, showStep prevented.');
|
||||
return this;
|
||||
}
|
||||
step = this.getStep(i);
|
||||
if (!step) {
|
||||
return;
|
||||
}
|
||||
skipToPrevious = i < this._current;
|
||||
promise = this._makePromise(step.onShow != null ? step.onShow(this, i) : void 0);
|
||||
showStepHelper = (function(_this) {
|
||||
return function(e) {
|
||||
var path, showPopoverAndOverlay;
|
||||
_this.setCurrentStep(i);
|
||||
path = (function() {
|
||||
switch ({}.toString.call(step.path)) {
|
||||
case '[object Function]':
|
||||
return step.path();
|
||||
case '[object String]':
|
||||
return this._options.basePath + step.path;
|
||||
default:
|
||||
return step.path;
|
||||
}
|
||||
}).call(_this);
|
||||
if (_this._isRedirect(step.host, path, document.location)) {
|
||||
_this._redirect(step, i, path);
|
||||
if (!_this._isJustPathHashDifferent(step.host, path, document.location)) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
if (_this._isOrphan(step)) {
|
||||
if (step.orphan === false) {
|
||||
_this._debug("Skip the orphan step " + (_this._current + 1) + ".\nOrphan option is false and the element does not exist or is hidden.");
|
||||
if (skipToPrevious) {
|
||||
_this._showPrevStep();
|
||||
} else {
|
||||
_this._showNextStep();
|
||||
}
|
||||
return;
|
||||
}
|
||||
_this._debug("Show the orphan step " + (_this._current + 1) + ". Orphans option is true.");
|
||||
}
|
||||
if (step.backdrop) {
|
||||
_this._showBackdrop(step);
|
||||
}
|
||||
showPopoverAndOverlay = function() {
|
||||
if (_this.getCurrentStep() !== i || _this.ended()) {
|
||||
return;
|
||||
}
|
||||
if ((step.element != null) && step.backdrop) {
|
||||
_this._showOverlayElement(step);
|
||||
}
|
||||
_this._showPopover(step, i);
|
||||
if (step.onShown != null) {
|
||||
step.onShown(_this);
|
||||
}
|
||||
return _this._debug("Step " + (_this._current + 1) + " of " + _this._options.steps.length);
|
||||
};
|
||||
if (step.autoscroll) {
|
||||
_this._scrollIntoView(step.element, showPopoverAndOverlay);
|
||||
} else {
|
||||
showPopoverAndOverlay();
|
||||
}
|
||||
if (step.duration) {
|
||||
return _this.resume();
|
||||
}
|
||||
};
|
||||
})(this);
|
||||
if (step.delay) {
|
||||
this._debug("Wait " + step.delay + " milliseconds to show the step " + (this._current + 1));
|
||||
window.setTimeout((function(_this) {
|
||||
return function() {
|
||||
return _this._callOnPromiseDone(promise, showStepHelper);
|
||||
};
|
||||
})(this), step.delay);
|
||||
} else {
|
||||
this._callOnPromiseDone(promise, showStepHelper);
|
||||
}
|
||||
return promise;
|
||||
};
|
||||
|
||||
Tour.prototype.getCurrentStep = function() {
|
||||
return this._current;
|
||||
};
|
||||
|
||||
Tour.prototype.setCurrentStep = function(value) {
|
||||
if (value != null) {
|
||||
this._current = value;
|
||||
this._setState('current_step', value);
|
||||
} else {
|
||||
this._current = this._getState('current_step');
|
||||
this._current = this._current === null ? null : parseInt(this._current, 10);
|
||||
}
|
||||
return this;
|
||||
};
|
||||
|
||||
Tour.prototype.redraw = function() {
|
||||
return this._showOverlayElement(this.getStep(this.getCurrentStep()).element, true);
|
||||
};
|
||||
|
||||
Tour.prototype._setState = function(key, value) {
|
||||
var e, keyName;
|
||||
if (this._options.storage) {
|
||||
keyName = "" + this._options.name + "_" + key;
|
||||
try {
|
||||
this._options.storage.setItem(keyName, value);
|
||||
} catch (_error) {
|
||||
e = _error;
|
||||
if (e.code === DOMException.QUOTA_EXCEEDED_ERR) {
|
||||
this._debug('LocalStorage quota exceeded. State storage failed.');
|
||||
}
|
||||
}
|
||||
return this._options.afterSetState(keyName, value);
|
||||
} else {
|
||||
if (this._state == null) {
|
||||
this._state = {};
|
||||
}
|
||||
return this._state[key] = value;
|
||||
}
|
||||
};
|
||||
|
||||
Tour.prototype._removeState = function(key) {
|
||||
var keyName;
|
||||
if (this._options.storage) {
|
||||
keyName = "" + this._options.name + "_" + key;
|
||||
this._options.storage.removeItem(keyName);
|
||||
return this._options.afterRemoveState(keyName);
|
||||
} else {
|
||||
if (this._state != null) {
|
||||
return delete this._state[key];
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
Tour.prototype._getState = function(key) {
|
||||
var keyName, value;
|
||||
if (this._options.storage) {
|
||||
keyName = "" + this._options.name + "_" + key;
|
||||
value = this._options.storage.getItem(keyName);
|
||||
} else {
|
||||
if (this._state != null) {
|
||||
value = this._state[key];
|
||||
}
|
||||
}
|
||||
if (value === void 0 || value === 'null') {
|
||||
value = null;
|
||||
}
|
||||
this._options.afterGetState(key, value);
|
||||
return value;
|
||||
};
|
||||
|
||||
Tour.prototype._showNextStep = function() {
|
||||
var promise, showNextStepHelper, step;
|
||||
step = this.getStep(this._current);
|
||||
showNextStepHelper = (function(_this) {
|
||||
return function(e) {
|
||||
return _this.showStep(step.next);
|
||||
};
|
||||
})(this);
|
||||
promise = this._makePromise(step.onNext != null ? step.onNext(this) : void 0);
|
||||
return this._callOnPromiseDone(promise, showNextStepHelper);
|
||||
};
|
||||
|
||||
Tour.prototype._showPrevStep = function() {
|
||||
var promise, showPrevStepHelper, step;
|
||||
step = this.getStep(this._current);
|
||||
showPrevStepHelper = (function(_this) {
|
||||
return function(e) {
|
||||
return _this.showStep(step.prev);
|
||||
};
|
||||
})(this);
|
||||
promise = this._makePromise(step.onPrev != null ? step.onPrev(this) : void 0);
|
||||
return this._callOnPromiseDone(promise, showPrevStepHelper);
|
||||
};
|
||||
|
||||
Tour.prototype._debug = function(text) {
|
||||
if (this._options.debug) {
|
||||
return window.console.log("Bootstrap Tour '" + this._options.name + "' | " + text);
|
||||
}
|
||||
};
|
||||
|
||||
Tour.prototype._isRedirect = function(host, path, location) {
|
||||
var currentPath;
|
||||
if (host !== '') {
|
||||
if (this._isHostDifferent(host, location.href)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
currentPath = [location.pathname, location.search, location.hash].join('');
|
||||
return (path != null) && path !== '' && (({}.toString.call(path) === '[object RegExp]' && !path.test(currentPath)) || ({}.toString.call(path) === '[object String]' && this._isPathDifferent(path, currentPath)));
|
||||
};
|
||||
|
||||
Tour.prototype._isHostDifferent = function(host, currentURL) {
|
||||
return this._getProtocol(host) !== this._getProtocol(currentURL) || this._getHost(host) !== this._getHost(currentURL);
|
||||
};
|
||||
|
||||
Tour.prototype._isPathDifferent = function(path, currentPath) {
|
||||
return this._getPath(path) !== this._getPath(currentPath) || !this._equal(this._getQuery(path), this._getQuery(currentPath)) || !this._equal(this._getHash(path), this._getHash(currentPath));
|
||||
};
|
||||
|
||||
Tour.prototype._isJustPathHashDifferent = function(host, path, location) {
|
||||
var currentPath;
|
||||
if (host !== '') {
|
||||
if (this._isHostDifferent(host, location.href)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
currentPath = [location.pathname, location.search, location.hash].join('');
|
||||
if ({}.toString.call(path) === '[object String]') {
|
||||
return this._getPath(path) === this._getPath(currentPath) && this._equal(this._getQuery(path), this._getQuery(currentPath)) && !this._equal(this._getHash(path), this._getHash(currentPath));
|
||||
}
|
||||
return false;
|
||||
};
|
||||
|
||||
Tour.prototype._redirect = function(step, i, path) {
|
||||
if ($.isFunction(step.redirect)) {
|
||||
return step.redirect.call(this, path);
|
||||
} else if (step.redirect === true) {
|
||||
this._debug("Redirect to " + step.host + path);
|
||||
if (this._getState('redirect_to') === ("" + i)) {
|
||||
this._debug("Error redirection loop to " + path);
|
||||
this._removeState('redirect_to');
|
||||
if (step.onRedirectError != null) {
|
||||
return step.onRedirectError(this);
|
||||
}
|
||||
} else {
|
||||
this._setState('redirect_to', "" + i);
|
||||
return document.location.href = "" + step.host + path;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
Tour.prototype._isOrphan = function(step) {
|
||||
return (step.element == null) || !$(step.element).length || $(step.element).is(':hidden') && ($(step.element)[0].namespaceURI !== 'http://www.w3.org/2000/svg');
|
||||
};
|
||||
|
||||
Tour.prototype._isLast = function() {
|
||||
return this._current < this._options.steps.length - 1;
|
||||
};
|
||||
|
||||
Tour.prototype._showPopover = function(step, i) {
|
||||
var $element, $tip, isOrphan, options, shouldAddSmart;
|
||||
$(".tour-" + this._options.name).remove();
|
||||
options = $.extend({}, this._options);
|
||||
isOrphan = this._isOrphan(step);
|
||||
step.template = this._template(step, i);
|
||||
if (isOrphan) {
|
||||
step.element = 'body';
|
||||
step.placement = 'top';
|
||||
}
|
||||
$element = $(step.element);
|
||||
$element.addClass("tour-" + this._options.name + "-element tour-" + this._options.name + "-" + i + "-element");
|
||||
if (step.options) {
|
||||
$.extend(options, step.options);
|
||||
}
|
||||
if (step.reflex && !isOrphan) {
|
||||
$(step.reflexElement).addClass('tour-step-element-reflex').off("" + (this._reflexEvent(step.reflex)) + ".tour-" + this._options.name).on("" + (this._reflexEvent(step.reflex)) + ".tour-" + this._options.name, (function(_this) {
|
||||
return function() {
|
||||
if (_this._isLast()) {
|
||||
return _this.next();
|
||||
} else {
|
||||
return _this.end();
|
||||
}
|
||||
};
|
||||
})(this));
|
||||
}
|
||||
shouldAddSmart = step.smartPlacement === true && step.placement.search(/auto/i) === -1;
|
||||
$element.popover({
|
||||
placement: shouldAddSmart ? "auto " + step.placement : step.placement,
|
||||
trigger: 'manual',
|
||||
title: step.title,
|
||||
content: step.content,
|
||||
html: true,
|
||||
animation: step.animation,
|
||||
container: step.container,
|
||||
template: step.template,
|
||||
selector: step.element
|
||||
}).popover('show');
|
||||
$tip = $element.data('bs.popover') ? $element.data('bs.popover').tip() : $element.data('popover').tip();
|
||||
$tip.attr('id', step.id);
|
||||
this._reposition($tip, step);
|
||||
if (isOrphan) {
|
||||
return this._center($tip);
|
||||
}
|
||||
};
|
||||
|
||||
Tour.prototype._template = function(step, i) {
|
||||
var $navigation, $next, $prev, $resume, $template, template;
|
||||
template = step.template;
|
||||
if (this._isOrphan(step) && {}.toString.call(step.orphan) !== '[object Boolean]') {
|
||||
template = step.orphan;
|
||||
}
|
||||
$template = $.isFunction(template) ? $(template(i, step)) : $(template);
|
||||
$navigation = $template.find('.popover-navigation');
|
||||
$prev = $navigation.find('[data-role="prev"]');
|
||||
$next = $navigation.find('[data-role="next"]');
|
||||
$resume = $navigation.find('[data-role="pause-resume"]');
|
||||
if (this._isOrphan(step)) {
|
||||
$template.addClass('orphan');
|
||||
}
|
||||
$template.addClass("tour-" + this._options.name + " tour-" + this._options.name + "-" + i);
|
||||
if (step.reflex) {
|
||||
$template.addClass("tour-" + this._options.name + "-reflex");
|
||||
}
|
||||
if (step.prev < 0) {
|
||||
$prev.addClass('disabled');
|
||||
$prev.prop('disabled', true);
|
||||
}
|
||||
if (step.next < 0) {
|
||||
$next.addClass('disabled');
|
||||
$next.prop('disabled', true);
|
||||
}
|
||||
if (!step.duration) {
|
||||
$resume.remove();
|
||||
}
|
||||
return $template.clone().wrap('<div>').parent().html();
|
||||
};
|
||||
|
||||
Tour.prototype._reflexEvent = function(reflex) {
|
||||
if ({}.toString.call(reflex) === '[object Boolean]') {
|
||||
return 'click';
|
||||
} else {
|
||||
return reflex;
|
||||
}
|
||||
};
|
||||
|
||||
Tour.prototype._reposition = function($tip, step) {
|
||||
var offsetBottom, offsetHeight, offsetRight, offsetWidth, originalLeft, originalTop, tipOffset;
|
||||
offsetWidth = $tip[0].offsetWidth;
|
||||
offsetHeight = $tip[0].offsetHeight;
|
||||
tipOffset = $tip.offset();
|
||||
originalLeft = tipOffset.left;
|
||||
originalTop = tipOffset.top;
|
||||
offsetBottom = $(document).outerHeight() - tipOffset.top - $tip.outerHeight();
|
||||
if (offsetBottom < 0) {
|
||||
tipOffset.top = tipOffset.top + offsetBottom;
|
||||
}
|
||||
offsetRight = $('html').outerWidth() - tipOffset.left - $tip.outerWidth();
|
||||
if (offsetRight < 0) {
|
||||
tipOffset.left = tipOffset.left + offsetRight;
|
||||
}
|
||||
if (tipOffset.top < 0) {
|
||||
tipOffset.top = 0;
|
||||
}
|
||||
if (tipOffset.left < 0) {
|
||||
tipOffset.left = 0;
|
||||
}
|
||||
$tip.offset(tipOffset);
|
||||
if (step.placement === 'bottom' || step.placement === 'top') {
|
||||
if (originalLeft !== tipOffset.left) {
|
||||
return this._replaceArrow($tip, (tipOffset.left - originalLeft) * 2, offsetWidth, 'left');
|
||||
}
|
||||
} else {
|
||||
if (originalTop !== tipOffset.top) {
|
||||
return this._replaceArrow($tip, (tipOffset.top - originalTop) * 2, offsetHeight, 'top');
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
Tour.prototype._center = function($tip) {
|
||||
return $tip.css('top', $(window).outerHeight() / 2 - $tip.outerHeight() / 2);
|
||||
};
|
||||
|
||||
Tour.prototype._replaceArrow = function($tip, delta, dimension, position) {
|
||||
return $tip.find('.arrow').css(position, delta ? 50 * (1 - delta / dimension) + '%' : '');
|
||||
};
|
||||
|
||||
Tour.prototype._scrollIntoView = function(element, callback) {
|
||||
var $element, $window, counter, offsetTop, scrollTop, windowHeight;
|
||||
$element = $(element);
|
||||
if (!$element.length) {
|
||||
return callback();
|
||||
}
|
||||
$window = $(window);
|
||||
offsetTop = $element.offset().top;
|
||||
windowHeight = $window.height();
|
||||
scrollTop = Math.max(0, offsetTop - (windowHeight / 2));
|
||||
this._debug("Scroll into view. ScrollTop: " + scrollTop + ". Element offset: " + offsetTop + ". Window height: " + windowHeight + ".");
|
||||
counter = 0;
|
||||
return $('body, html').stop(true, true).animate({
|
||||
scrollTop: Math.ceil(scrollTop)
|
||||
}, (function(_this) {
|
||||
return function() {
|
||||
if (++counter === 2) {
|
||||
callback();
|
||||
return _this._debug("Scroll into view.\nAnimation end element offset: " + ($element.offset().top) + ".\nWindow height: " + ($window.height()) + ".");
|
||||
}
|
||||
};
|
||||
})(this));
|
||||
};
|
||||
|
||||
Tour.prototype._onResize = function(callback, timeout) {
|
||||
return $(window).on("resize.tour-" + this._options.name, function() {
|
||||
clearTimeout(timeout);
|
||||
return timeout = setTimeout(callback, 100);
|
||||
});
|
||||
};
|
||||
|
||||
Tour.prototype._initMouseNavigation = function() {
|
||||
var _this;
|
||||
_this = this;
|
||||
return $(document).off("click.tour-" + this._options.name, ".popover.tour-" + this._options.name + " *[data-role='prev']").off("click.tour-" + this._options.name, ".popover.tour-" + this._options.name + " *[data-role='next']").off("click.tour-" + this._options.name, ".popover.tour-" + this._options.name + " *[data-role='end']").off("click.tour-" + this._options.name, ".popover.tour-" + this._options.name + " *[data-role='pause-resume']").on("click.tour-" + this._options.name, ".popover.tour-" + this._options.name + " *[data-role='next']", (function(_this) {
|
||||
return function(e) {
|
||||
e.preventDefault();
|
||||
return _this.next();
|
||||
};
|
||||
})(this)).on("click.tour-" + this._options.name, ".popover.tour-" + this._options.name + " *[data-role='prev']", (function(_this) {
|
||||
return function(e) {
|
||||
e.preventDefault();
|
||||
return _this.prev();
|
||||
};
|
||||
})(this)).on("click.tour-" + this._options.name, ".popover.tour-" + this._options.name + " *[data-role='end']", (function(_this) {
|
||||
return function(e) {
|
||||
e.preventDefault();
|
||||
return _this.end();
|
||||
};
|
||||
})(this)).on("click.tour-" + this._options.name, ".popover.tour-" + this._options.name + " *[data-role='pause-resume']", function(e) {
|
||||
var $this;
|
||||
e.preventDefault();
|
||||
$this = $(this);
|
||||
$this.text(_this._paused ? $this.data('pause-text') : $this.data('resume-text'));
|
||||
if (_this._paused) {
|
||||
return _this.resume();
|
||||
} else {
|
||||
return _this.pause();
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
Tour.prototype._initKeyboardNavigation = function() {
|
||||
if (!this._options.keyboard) {
|
||||
return;
|
||||
}
|
||||
return $(document).on("keyup.tour-" + this._options.name, (function(_this) {
|
||||
return function(e) {
|
||||
if (!e.which) {
|
||||
return;
|
||||
}
|
||||
switch (e.which) {
|
||||
case 39:
|
||||
e.preventDefault();
|
||||
if (_this._isLast()) {
|
||||
return _this.next();
|
||||
} else {
|
||||
return _this.end();
|
||||
}
|
||||
break;
|
||||
case 37:
|
||||
e.preventDefault();
|
||||
if (_this._current > 0) {
|
||||
return _this.prev();
|
||||
}
|
||||
break;
|
||||
case 27:
|
||||
e.preventDefault();
|
||||
return _this.end();
|
||||
}
|
||||
};
|
||||
})(this));
|
||||
};
|
||||
|
||||
Tour.prototype._makePromise = function(result) {
|
||||
if (result && $.isFunction(result.then)) {
|
||||
return result;
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
};
|
||||
|
||||
Tour.prototype._callOnPromiseDone = function(promise, cb, arg) {
|
||||
if (promise) {
|
||||
return promise.then((function(_this) {
|
||||
return function(e) {
|
||||
return cb.call(_this, arg);
|
||||
};
|
||||
})(this));
|
||||
} else {
|
||||
return cb.call(this, arg);
|
||||
}
|
||||
};
|
||||
|
||||
Tour.prototype._showBackdrop = function(step) {
|
||||
if (this.backdrop.backgroundShown) {
|
||||
return;
|
||||
}
|
||||
this.backdrop = $('<div>', {
|
||||
"class": 'tour-backdrop'
|
||||
});
|
||||
this.backdrop.backgroundShown = true;
|
||||
return $(step.backdropContainer).append(this.backdrop);
|
||||
};
|
||||
|
||||
Tour.prototype._hideBackdrop = function() {
|
||||
this._hideOverlayElement();
|
||||
return this._hideBackground();
|
||||
};
|
||||
|
||||
Tour.prototype._hideBackground = function() {
|
||||
if (this.backdrop) {
|
||||
this.backdrop.remove();
|
||||
this.backdrop.overlay = null;
|
||||
return this.backdrop.backgroundShown = false;
|
||||
}
|
||||
};
|
||||
|
||||
Tour.prototype._showOverlayElement = function(step, force) {
|
||||
var $element, elementData;
|
||||
$element = $(step.element);
|
||||
if (!$element || $element.length === 0 || this.backdrop.overlayElementShown && !force) {
|
||||
return;
|
||||
}
|
||||
if (!this.backdrop.overlayElementShown) {
|
||||
this.backdrop.$element = $element.addClass('tour-step-backdrop');
|
||||
this.backdrop.$background = $('<div>', {
|
||||
"class": 'tour-step-background'
|
||||
});
|
||||
this.backdrop.$background.appendTo(step.backdropContainer);
|
||||
this.backdrop.overlayElementShown = true;
|
||||
}
|
||||
elementData = {
|
||||
width: $element.innerWidth(),
|
||||
height: $element.innerHeight(),
|
||||
offset: $element.offset()
|
||||
};
|
||||
if (step.backdropPadding) {
|
||||
elementData = this._applyBackdropPadding(step.backdropPadding, elementData);
|
||||
}
|
||||
return this.backdrop.$background.width(elementData.width).height(elementData.height).offset(elementData.offset);
|
||||
};
|
||||
|
||||
Tour.prototype._hideOverlayElement = function() {
|
||||
if (!this.backdrop.overlayElementShown) {
|
||||
return;
|
||||
}
|
||||
this.backdrop.$element.removeClass('tour-step-backdrop');
|
||||
this.backdrop.$background.remove();
|
||||
this.backdrop.$element = null;
|
||||
this.backdrop.$background = null;
|
||||
return this.backdrop.overlayElementShown = false;
|
||||
};
|
||||
|
||||
Tour.prototype._applyBackdropPadding = function(padding, data) {
|
||||
if (typeof padding === 'object') {
|
||||
if (padding.top == null) {
|
||||
padding.top = 0;
|
||||
}
|
||||
if (padding.right == null) {
|
||||
padding.right = 0;
|
||||
}
|
||||
if (padding.bottom == null) {
|
||||
padding.bottom = 0;
|
||||
}
|
||||
if (padding.left == null) {
|
||||
padding.left = 0;
|
||||
}
|
||||
data.offset.top = data.offset.top - padding.top;
|
||||
data.offset.left = data.offset.left - padding.left;
|
||||
data.width = data.width + padding.left + padding.right;
|
||||
data.height = data.height + padding.top + padding.bottom;
|
||||
} else {
|
||||
data.offset.top = data.offset.top - padding;
|
||||
data.offset.left = data.offset.left - padding;
|
||||
data.width = data.width + (padding * 2);
|
||||
data.height = data.height + (padding * 2);
|
||||
}
|
||||
return data;
|
||||
};
|
||||
|
||||
Tour.prototype._clearTimer = function() {
|
||||
window.clearTimeout(this._timer);
|
||||
this._timer = null;
|
||||
return this._duration = null;
|
||||
};
|
||||
|
||||
Tour.prototype._getProtocol = function(url) {
|
||||
url = url.split('://');
|
||||
if (url.length > 1) {
|
||||
return url[0];
|
||||
} else {
|
||||
return 'http';
|
||||
}
|
||||
};
|
||||
|
||||
Tour.prototype._getHost = function(url) {
|
||||
url = url.split('//');
|
||||
url = url.length > 1 ? url[1] : url[0];
|
||||
return url.split('/')[0];
|
||||
};
|
||||
|
||||
Tour.prototype._getPath = function(path) {
|
||||
return path.replace(/\/?$/, '').split('?')[0].split('#')[0];
|
||||
};
|
||||
|
||||
Tour.prototype._getQuery = function(path) {
|
||||
return this._getParams(path, '?');
|
||||
};
|
||||
|
||||
Tour.prototype._getHash = function(path) {
|
||||
return this._getParams(path, '#');
|
||||
};
|
||||
|
||||
Tour.prototype._getParams = function(path, start) {
|
||||
var param, params, paramsObject, _i, _len;
|
||||
params = path.split(start);
|
||||
if (params.length === 1) {
|
||||
return {};
|
||||
}
|
||||
params = params[1].split('&');
|
||||
paramsObject = {};
|
||||
for (_i = 0, _len = params.length; _i < _len; _i++) {
|
||||
param = params[_i];
|
||||
param = param.split('=');
|
||||
paramsObject[param[0]] = param[1] || '';
|
||||
}
|
||||
return paramsObject;
|
||||
};
|
||||
|
||||
Tour.prototype._equal = function(obj1, obj2) {
|
||||
var k, v;
|
||||
if ({}.toString.call(obj1) === '[object Object]' && {}.toString.call(obj2) === '[object Object]') {
|
||||
for (k in obj1) {
|
||||
v = obj1[k];
|
||||
if (obj2[k] !== v) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
for (k in obj2) {
|
||||
v = obj2[k];
|
||||
if (obj1[k] !== v) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
return obj1 === obj2;
|
||||
};
|
||||
|
||||
return Tour;
|
||||
|
||||
})();
|
||||
return window.Tour = Tour;
|
||||
})(jQuery, window);
|
||||
-850
@@ -1,850 +0,0 @@
|
||||
/* ========================================================================
|
||||
* Bootstrap: transition.js v3.0.0
|
||||
* http://twbs.github.com/bootstrap/javascript.html#transitions
|
||||
* ========================================================================
|
||||
* Copyright 2013 Twitter, Inc.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
* ======================================================================== */
|
||||
|
||||
|
||||
+function ($) { "use strict";
|
||||
|
||||
// CSS TRANSITION SUPPORT (Shoutout: http://www.modernizr.com/)
|
||||
// ============================================================
|
||||
|
||||
function transitionEnd() {
|
||||
var el = document.createElement('bootstrap')
|
||||
|
||||
var transEndEventNames = {
|
||||
'WebkitTransition' : 'webkitTransitionEnd'
|
||||
, 'MozTransition' : 'transitionend'
|
||||
, 'OTransition' : 'oTransitionEnd otransitionend'
|
||||
, 'transition' : 'transitionend'
|
||||
}
|
||||
|
||||
for (var name in transEndEventNames) {
|
||||
if (el.style[name] !== undefined) {
|
||||
return { end: transEndEventNames[name] }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// http://blog.alexmaccaw.com/css-transitions
|
||||
$.fn.emulateTransitionEnd = function (duration) {
|
||||
var called = false, $el = this
|
||||
$(this).one($.support.transition.end, function () { called = true })
|
||||
var callback = function () { if (!called) $($el).trigger($.support.transition.end) }
|
||||
setTimeout(callback, duration)
|
||||
return this
|
||||
}
|
||||
|
||||
$(function () {
|
||||
$.support.transition = transitionEnd()
|
||||
})
|
||||
|
||||
}(window.jQuery);
|
||||
/* ========================================================================
|
||||
* Bootstrap: tab.js v3.0.0
|
||||
* http://twbs.github.com/bootstrap/javascript.html#tabs
|
||||
* ========================================================================
|
||||
* Copyright 2012 Twitter, Inc.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
* ======================================================================== */
|
||||
|
||||
|
||||
+function ($) { "use strict";
|
||||
|
||||
// TAB CLASS DEFINITION
|
||||
// ====================
|
||||
|
||||
var Tab = function (element) {
|
||||
this.element = $(element)
|
||||
}
|
||||
|
||||
Tab.prototype.show = function () {
|
||||
var $this = this.element
|
||||
var $ul = $this.closest('ul:not(.dropdown-menu)')
|
||||
var selector = $this.attr('data-target')
|
||||
|
||||
if (!selector) {
|
||||
selector = $this.attr('href')
|
||||
selector = selector && selector.replace(/.*(?=#[^\s]*$)/, '') //strip for ie7
|
||||
}
|
||||
|
||||
if ($this.parent('li').hasClass('active')) return
|
||||
|
||||
var previous = $ul.find('.active:last a')[0]
|
||||
var e = $.Event('show.bs.tab', {
|
||||
relatedTarget: previous
|
||||
})
|
||||
|
||||
$this.trigger(e)
|
||||
|
||||
if (e.isDefaultPrevented()) return
|
||||
|
||||
var $target = $(selector)
|
||||
|
||||
this.activate($this.parent('li'), $ul)
|
||||
this.activate($target, $target.parent(), function () {
|
||||
$this.trigger({
|
||||
type: 'shown.bs.tab'
|
||||
, relatedTarget: previous
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
Tab.prototype.activate = function (element, container, callback) {
|
||||
var $active = container.find('> .active')
|
||||
var transition = callback
|
||||
&& $.support.transition
|
||||
&& $active.hasClass('fade')
|
||||
|
||||
function next() {
|
||||
$active
|
||||
.removeClass('active')
|
||||
.find('> .dropdown-menu > .active')
|
||||
.removeClass('active')
|
||||
|
||||
element.addClass('active')
|
||||
|
||||
if (transition) {
|
||||
element[0].offsetWidth // reflow for transition
|
||||
element.addClass('in')
|
||||
} else {
|
||||
element.removeClass('fade')
|
||||
}
|
||||
|
||||
if (element.parent('.dropdown-menu')) {
|
||||
element.closest('li.dropdown').addClass('active')
|
||||
}
|
||||
|
||||
callback && callback()
|
||||
}
|
||||
|
||||
transition ?
|
||||
$active
|
||||
.one($.support.transition.end, next)
|
||||
.emulateTransitionEnd(150) :
|
||||
next()
|
||||
|
||||
$active.removeClass('in')
|
||||
}
|
||||
|
||||
|
||||
// TAB PLUGIN DEFINITION
|
||||
// =====================
|
||||
|
||||
var old = $.fn.tab
|
||||
|
||||
$.fn.tab = function ( option ) {
|
||||
return this.each(function () {
|
||||
var $this = $(this)
|
||||
var data = $this.data('bs.tab')
|
||||
|
||||
if (!data) $this.data('bs.tab', (data = new Tab(this)))
|
||||
if (typeof option == 'string') data[option]()
|
||||
})
|
||||
}
|
||||
|
||||
$.fn.tab.Constructor = Tab
|
||||
|
||||
|
||||
// TAB NO CONFLICT
|
||||
// ===============
|
||||
|
||||
$.fn.tab.noConflict = function () {
|
||||
$.fn.tab = old
|
||||
return this
|
||||
}
|
||||
|
||||
|
||||
// TAB DATA-API
|
||||
// ============
|
||||
|
||||
$(document).on('click.bs.tab.data-api', '[data-toggle="tab"], [data-toggle="pill"]', function (e) {
|
||||
e.preventDefault()
|
||||
$(this).tab('show')
|
||||
})
|
||||
|
||||
}(window.jQuery);
|
||||
/* ========================================================================
|
||||
* Bootstrap: tooltip.js v3.0.0
|
||||
* http://twbs.github.com/bootstrap/javascript.html#tooltip
|
||||
* Inspired by the original jQuery.tipsy by Jason Frame
|
||||
* ========================================================================
|
||||
* Copyright 2012 Twitter, Inc.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
* ======================================================================== */
|
||||
|
||||
|
||||
+function ($) { "use strict";
|
||||
|
||||
// TOOLTIP PUBLIC CLASS DEFINITION
|
||||
// ===============================
|
||||
|
||||
var Tooltip = function (element, options) {
|
||||
this.type =
|
||||
this.options =
|
||||
this.enabled =
|
||||
this.timeout =
|
||||
this.hoverState =
|
||||
this.$element = null
|
||||
|
||||
this.init('tooltip', element, options)
|
||||
}
|
||||
|
||||
Tooltip.DEFAULTS = {
|
||||
animation: true
|
||||
, placement: 'top'
|
||||
, selector: false
|
||||
, template: '<div class="tooltip"><div class="tooltip-arrow"></div><div class="tooltip-inner"></div></div>'
|
||||
, trigger: 'hover focus'
|
||||
, title: ''
|
||||
, delay: 0
|
||||
, html: false
|
||||
, container: 'body'
|
||||
}
|
||||
|
||||
Tooltip.prototype.init = function (type, element, options) {
|
||||
this.enabled = true
|
||||
this.type = type
|
||||
this.$element = $(element)
|
||||
this.options = this.getOptions(options)
|
||||
|
||||
var triggers = this.options.trigger.split(' ')
|
||||
|
||||
for (var i = triggers.length; i--;) {
|
||||
var trigger = triggers[i]
|
||||
|
||||
if (trigger == 'click') {
|
||||
this.$element.on('click.' + this.type, this.options.selector, $.proxy(this.toggle, this))
|
||||
} else if (trigger != 'manual') {
|
||||
var eventIn = trigger == 'hover' ? 'mouseenter' : 'focus'
|
||||
var eventOut = trigger == 'hover' ? 'mouseleave' : 'blur'
|
||||
|
||||
this.$element.on(eventIn + '.' + this.type, this.options.selector, $.proxy(this.enter, this))
|
||||
this.$element.on(eventOut + '.' + this.type, this.options.selector, $.proxy(this.leave, this))
|
||||
}
|
||||
}
|
||||
|
||||
this.options.selector ?
|
||||
(this._options = $.extend({}, this.options, { trigger: 'manual', selector: '' })) :
|
||||
this.fixTitle()
|
||||
}
|
||||
|
||||
Tooltip.prototype.getDefaults = function () {
|
||||
return Tooltip.DEFAULTS
|
||||
}
|
||||
|
||||
Tooltip.prototype.getOptions = function (options) {
|
||||
options = $.extend({}, this.getDefaults(), this.$element.data(), options)
|
||||
|
||||
if (options.delay && typeof options.delay == 'number') {
|
||||
options.delay = {
|
||||
show: options.delay
|
||||
, hide: options.delay
|
||||
}
|
||||
}
|
||||
|
||||
return options
|
||||
}
|
||||
|
||||
Tooltip.prototype.getDelegateOptions = function () {
|
||||
var options = {}
|
||||
var defaults = this.getDefaults()
|
||||
|
||||
this._options && $.each(this._options, function (key, value) {
|
||||
if (defaults[key] != value) options[key] = value
|
||||
})
|
||||
|
||||
return options
|
||||
}
|
||||
|
||||
Tooltip.prototype.enter = function (obj) {
|
||||
var self = obj instanceof this.constructor ?
|
||||
obj : $(obj.currentTarget)[this.type](this.getDelegateOptions()).data('bs.' + this.type)
|
||||
|
||||
clearTimeout(self.timeout)
|
||||
|
||||
self.hoverState = 'in'
|
||||
|
||||
if (!self.options.delay || !self.options.delay.show) return self.show()
|
||||
|
||||
self.timeout = setTimeout(function () {
|
||||
if (self.hoverState == 'in') self.show()
|
||||
}, self.options.delay.show)
|
||||
}
|
||||
|
||||
Tooltip.prototype.leave = function (obj) {
|
||||
var self = obj instanceof this.constructor ?
|
||||
obj : $(obj.currentTarget)[this.type](this.getDelegateOptions()).data('bs.' + this.type)
|
||||
|
||||
clearTimeout(self.timeout)
|
||||
|
||||
self.hoverState = 'out'
|
||||
|
||||
if (!self.options.delay || !self.options.delay.hide) return self.hide()
|
||||
|
||||
self.timeout = setTimeout(function () {
|
||||
if (self.hoverState == 'out') self.hide()
|
||||
}, self.options.delay.hide)
|
||||
}
|
||||
|
||||
Tooltip.prototype.show = function () {
|
||||
var e = $.Event('show.bs.'+ this.type)
|
||||
|
||||
if (this.hasContent() && this.enabled) {
|
||||
this.$element.trigger(e)
|
||||
|
||||
if (e.isDefaultPrevented()) return
|
||||
|
||||
var $tip = this.tip()
|
||||
|
||||
this.setContent()
|
||||
|
||||
if (this.options.animation) $tip.addClass('fade')
|
||||
|
||||
var placement = typeof this.options.placement == 'function' ?
|
||||
this.options.placement.call(this, $tip[0], this.$element[0]) :
|
||||
this.options.placement
|
||||
|
||||
var autoToken = /\s?auto?\s?/i
|
||||
var autoPlace = autoToken.test(placement)
|
||||
if (autoPlace) placement = placement.replace(autoToken, '') || 'top'
|
||||
|
||||
$tip
|
||||
.detach()
|
||||
.css({ top: 0, left: 0, display: 'block' })
|
||||
.addClass(placement)
|
||||
|
||||
this.options.container ? $tip.appendTo(this.options.container) : $tip.insertAfter(this.$element)
|
||||
|
||||
var pos = this.getPosition()
|
||||
var actualWidth = $tip[0].offsetWidth
|
||||
var actualHeight = $tip[0].offsetHeight
|
||||
|
||||
if (autoPlace) {
|
||||
var $parent = this.$element.parent()
|
||||
|
||||
var orgPlacement = placement
|
||||
var docScroll = document.documentElement.scrollTop || document.body.scrollTop
|
||||
var parentWidth = this.options.container == 'body' ? window.innerWidth : $parent.outerWidth()
|
||||
var parentHeight = this.options.container == 'body' ? window.innerHeight : $parent.outerHeight()
|
||||
var parentLeft = this.options.container == 'body' ? 0 : $parent.offset().left
|
||||
|
||||
placement = placement == 'bottom' && pos.top + pos.height + actualHeight - docScroll > parentHeight ? 'top' :
|
||||
placement == 'top' && pos.top - docScroll - actualHeight < 0 ? 'bottom' :
|
||||
placement == 'right' && pos.right + actualWidth > parentWidth ? 'left' :
|
||||
placement == 'left' && pos.left - actualWidth < parentLeft ? 'right' :
|
||||
placement
|
||||
|
||||
$tip
|
||||
.removeClass(orgPlacement)
|
||||
.addClass(placement)
|
||||
}
|
||||
|
||||
var calculatedOffset = this.getCalculatedOffset(placement, pos, actualWidth, actualHeight)
|
||||
|
||||
this.applyPlacement(calculatedOffset, placement)
|
||||
this.$element.trigger('shown.bs.' + this.type)
|
||||
}
|
||||
}
|
||||
|
||||
Tooltip.prototype.applyPlacement = function(offset, placement) {
|
||||
var replace
|
||||
var $tip = this.tip()
|
||||
var width = $tip[0].offsetWidth
|
||||
var height = $tip[0].offsetHeight
|
||||
|
||||
// manually read margins because getBoundingClientRect includes difference
|
||||
var marginTop = parseInt($tip.css('margin-top'), 10)
|
||||
var marginLeft = parseInt($tip.css('margin-left'), 10)
|
||||
|
||||
// we must check for NaN for ie 8/9
|
||||
if (isNaN(marginTop)) marginTop = 0
|
||||
if (isNaN(marginLeft)) marginLeft = 0
|
||||
|
||||
offset.top = offset.top + marginTop
|
||||
offset.left = offset.left + marginLeft
|
||||
|
||||
$tip
|
||||
.offset(offset)
|
||||
.addClass('in')
|
||||
|
||||
// check to see if placing tip in new offset caused the tip to resize itself
|
||||
var actualWidth = $tip[0].offsetWidth
|
||||
var actualHeight = $tip[0].offsetHeight
|
||||
|
||||
if (placement == 'top' && actualHeight != height) {
|
||||
replace = true
|
||||
offset.top = offset.top + height - actualHeight
|
||||
}
|
||||
|
||||
if (/bottom|top/.test(placement)) {
|
||||
var delta = 0
|
||||
|
||||
if (offset.left < 0) {
|
||||
delta = offset.left * -2
|
||||
offset.left = 0
|
||||
|
||||
$tip.offset(offset)
|
||||
|
||||
actualWidth = $tip[0].offsetWidth
|
||||
actualHeight = $tip[0].offsetHeight
|
||||
}
|
||||
|
||||
this.replaceArrow(delta - width + actualWidth, actualWidth, 'left')
|
||||
} else {
|
||||
this.replaceArrow(actualHeight - height, actualHeight, 'top')
|
||||
}
|
||||
|
||||
if (replace) $tip.offset(offset)
|
||||
}
|
||||
|
||||
Tooltip.prototype.replaceArrow = function(delta, dimension, position) {
|
||||
this.arrow().css(position, delta ? (50 * (1 - delta / dimension) + "%") : '')
|
||||
}
|
||||
|
||||
Tooltip.prototype.setContent = function () {
|
||||
var $tip = this.tip()
|
||||
var title = this.getTitle()
|
||||
|
||||
$tip.find('.tooltip-inner')[this.options.html ? 'html' : 'text'](title)
|
||||
$tip.removeClass('fade in top bottom left right')
|
||||
}
|
||||
|
||||
Tooltip.prototype.hide = function () {
|
||||
var that = this
|
||||
var $tip = this.tip()
|
||||
var e = $.Event('hide.bs.' + this.type)
|
||||
|
||||
function complete() {
|
||||
if (that.hoverState != 'in') $tip.detach()
|
||||
}
|
||||
|
||||
this.$element.trigger(e)
|
||||
|
||||
if (e.isDefaultPrevented()) return
|
||||
|
||||
$tip.removeClass('in')
|
||||
|
||||
$.support.transition && this.$tip.hasClass('fade') ?
|
||||
$tip
|
||||
.one($.support.transition.end, complete)
|
||||
.emulateTransitionEnd(150) :
|
||||
complete()
|
||||
|
||||
this.$element.trigger('hidden.bs.' + this.type)
|
||||
|
||||
return this
|
||||
}
|
||||
|
||||
Tooltip.prototype.fixTitle = function () {
|
||||
var $e = this.$element
|
||||
if ($e.attr('title') || typeof($e.attr('data-original-title')) != 'string') {
|
||||
$e.attr('data-original-title', $e.attr('title') || '').attr('title', '')
|
||||
}
|
||||
}
|
||||
|
||||
Tooltip.prototype.hasContent = function () {
|
||||
return this.getTitle()
|
||||
}
|
||||
|
||||
Tooltip.prototype.getPosition = function () {
|
||||
var el = this.$element[0]
|
||||
return $.extend({}, (typeof el.getBoundingClientRect == 'function') ? el.getBoundingClientRect() : {
|
||||
width: el.offsetWidth
|
||||
, height: el.offsetHeight
|
||||
}, this.$element.offset())
|
||||
}
|
||||
|
||||
Tooltip.prototype.getCalculatedOffset = function (placement, pos, actualWidth, actualHeight) {
|
||||
return placement == 'bottom' ? { top: pos.top + pos.height, left: pos.left + pos.width / 2 - actualWidth / 2 } :
|
||||
placement == 'top' ? { top: pos.top - actualHeight, left: pos.left + pos.width / 2 - actualWidth / 2 } :
|
||||
placement == 'left' ? { top: pos.top + pos.height / 2 - actualHeight / 2, left: pos.left - actualWidth } :
|
||||
/* placement == 'right' */ { top: pos.top + pos.height / 2 - actualHeight / 2, left: pos.left + pos.width }
|
||||
}
|
||||
|
||||
Tooltip.prototype.getTitle = function () {
|
||||
var title
|
||||
var $e = this.$element
|
||||
var o = this.options
|
||||
|
||||
title = $e.attr('data-original-title')
|
||||
|| (typeof o.title == 'function' ? o.title.call($e[0]) : o.title)
|
||||
|
||||
return title
|
||||
}
|
||||
|
||||
Tooltip.prototype.tip = function () {
|
||||
return this.$tip = this.$tip || $(this.options.template)
|
||||
}
|
||||
|
||||
Tooltip.prototype.arrow = function () {
|
||||
return this.$arrow = this.$arrow || this.tip().find('.tooltip-arrow')
|
||||
}
|
||||
|
||||
Tooltip.prototype.validate = function () {
|
||||
if (!this.$element[0].parentNode) {
|
||||
this.hide()
|
||||
this.$element = null
|
||||
this.options = null
|
||||
}
|
||||
}
|
||||
|
||||
Tooltip.prototype.enable = function () {
|
||||
this.enabled = true
|
||||
}
|
||||
|
||||
Tooltip.prototype.disable = function () {
|
||||
this.enabled = false
|
||||
}
|
||||
|
||||
Tooltip.prototype.toggleEnabled = function () {
|
||||
this.enabled = !this.enabled
|
||||
}
|
||||
|
||||
Tooltip.prototype.toggle = function (e) {
|
||||
var self = e ? $(e.currentTarget)[this.type](this.getDelegateOptions()).data('bs.' + this.type) : this
|
||||
self.tip().hasClass('in') ? self.leave(self) : self.enter(self)
|
||||
}
|
||||
|
||||
Tooltip.prototype.destroy = function () {
|
||||
this.hide().$element.off('.' + this.type).removeData('bs.' + this.type)
|
||||
}
|
||||
|
||||
|
||||
// TOOLTIP PLUGIN DEFINITION
|
||||
// =========================
|
||||
|
||||
var old = $.fn.tooltip
|
||||
|
||||
$.fn.tooltip = function (option) {
|
||||
return this.each(function () {
|
||||
var $this = $(this)
|
||||
var data = $this.data('bs.tooltip')
|
||||
var options = typeof option == 'object' && option
|
||||
|
||||
if (!data) $this.data('bs.tooltip', (data = new Tooltip(this, options)))
|
||||
if (typeof option == 'string') data[option]()
|
||||
})
|
||||
}
|
||||
|
||||
$.fn.tooltip.Constructor = Tooltip
|
||||
|
||||
|
||||
// TOOLTIP NO CONFLICT
|
||||
// ===================
|
||||
|
||||
$.fn.tooltip.noConflict = function () {
|
||||
$.fn.tooltip = old
|
||||
return this
|
||||
}
|
||||
|
||||
}(window.jQuery);
|
||||
|
||||
/* ========================================================================
|
||||
* Bootstrap: dropdown.js v3.0.0
|
||||
* http://twbs.github.com/bootstrap/javascript.html#dropdowns
|
||||
* ========================================================================
|
||||
* Copyright 2012 Twitter, Inc.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
* ======================================================================== */
|
||||
|
||||
|
||||
+function ($) { "use strict";
|
||||
|
||||
// DROPDOWN CLASS DEFINITION
|
||||
// =========================
|
||||
|
||||
var backdrop = '.dropdown-backdrop'
|
||||
var toggle = '[data-toggle=dropdown]'
|
||||
var Dropdown = function (element) {
|
||||
var $el = $(element).on('click.bs.dropdown', this.toggle)
|
||||
}
|
||||
|
||||
Dropdown.prototype.toggle = function (e) {
|
||||
var $this = $(this)
|
||||
|
||||
if ($this.is('.disabled, :disabled')) return
|
||||
|
||||
var $parent = getParent($this)
|
||||
var isActive = $parent.hasClass('open')
|
||||
|
||||
clearMenus()
|
||||
|
||||
if (!isActive) {
|
||||
if ('ontouchstart' in document.documentElement && !$parent.closest('.navbar-nav').length) {
|
||||
// if mobile we we use a backdrop because click events don't delegate
|
||||
$('<div class="dropdown-backdrop"/>').insertAfter($(this)).on('click', clearMenus)
|
||||
}
|
||||
|
||||
$parent.trigger(e = $.Event('show.bs.dropdown'))
|
||||
|
||||
if (e.isDefaultPrevented()) return
|
||||
|
||||
$parent
|
||||
.toggleClass('open')
|
||||
.trigger('shown.bs.dropdown')
|
||||
|
||||
$this.focus()
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
Dropdown.prototype.keydown = function (e) {
|
||||
if (!/(38|40|27)/.test(e.keyCode)) return
|
||||
|
||||
var $this = $(this)
|
||||
|
||||
e.preventDefault()
|
||||
e.stopPropagation()
|
||||
|
||||
if ($this.is('.disabled, :disabled')) return
|
||||
|
||||
var $parent = getParent($this)
|
||||
var isActive = $parent.hasClass('open')
|
||||
|
||||
if (!isActive || (isActive && e.keyCode == 27)) {
|
||||
if (e.which == 27) $parent.find(toggle).focus()
|
||||
return $this.click()
|
||||
}
|
||||
|
||||
var $items = $('[role=menu] li:not(.divider):visible a', $parent)
|
||||
|
||||
if (!$items.length) return
|
||||
|
||||
var index = $items.index($items.filter(':focus'))
|
||||
|
||||
if (e.keyCode == 38 && index > 0) index-- // up
|
||||
if (e.keyCode == 40 && index < $items.length - 1) index++ // down
|
||||
if (!~index) index=0
|
||||
|
||||
$items.eq(index).focus()
|
||||
}
|
||||
|
||||
function clearMenus() {
|
||||
$(backdrop).remove()
|
||||
$(toggle).each(function (e) {
|
||||
var $parent = getParent($(this))
|
||||
if (!$parent.hasClass('open')) return
|
||||
$parent.trigger(e = $.Event('hide.bs.dropdown'))
|
||||
if (e.isDefaultPrevented()) return
|
||||
$parent.removeClass('open').trigger('hidden.bs.dropdown')
|
||||
})
|
||||
}
|
||||
|
||||
function getParent($this) {
|
||||
var selector = $this.attr('data-target')
|
||||
|
||||
if (!selector) {
|
||||
selector = $this.attr('href')
|
||||
selector = selector && /#/.test(selector) && selector.replace(/.*(?=#[^\s]*$)/, '') //strip for ie7
|
||||
}
|
||||
|
||||
var $parent = selector && $(selector)
|
||||
|
||||
return $parent && $parent.length ? $parent : $this.parent()
|
||||
}
|
||||
|
||||
|
||||
// DROPDOWN PLUGIN DEFINITION
|
||||
// ==========================
|
||||
|
||||
var old = $.fn.dropdown
|
||||
|
||||
$.fn.dropdown = function (option) {
|
||||
return this.each(function () {
|
||||
var $this = $(this)
|
||||
var data = $this.data('dropdown')
|
||||
|
||||
if (!data) $this.data('dropdown', (data = new Dropdown(this)))
|
||||
if (typeof option == 'string') data[option].call($this)
|
||||
})
|
||||
}
|
||||
|
||||
$.fn.dropdown.Constructor = Dropdown
|
||||
|
||||
|
||||
// DROPDOWN NO CONFLICT
|
||||
// ====================
|
||||
|
||||
$.fn.dropdown.noConflict = function () {
|
||||
$.fn.dropdown = old
|
||||
return this
|
||||
}
|
||||
|
||||
|
||||
// APPLY TO STANDARD DROPDOWN ELEMENTS
|
||||
// ===================================
|
||||
|
||||
$(document)
|
||||
.on('click.bs.dropdown.data-api', clearMenus)
|
||||
.on('click.bs.dropdown.data-api', '.dropdown form', function (e) { e.stopPropagation() })
|
||||
.on('click.bs.dropdown.data-api' , toggle, Dropdown.prototype.toggle)
|
||||
.on('keydown.bs.dropdown.data-api', toggle + ', [role=menu]' , Dropdown.prototype.keydown)
|
||||
|
||||
}(window.jQuery);
|
||||
|
||||
/* ========================================================================
|
||||
* Bootstrap: popover.js v3.0.3
|
||||
* http://getbootstrap.com/javascript/#popovers
|
||||
* ========================================================================
|
||||
* Copyright 2013 Twitter, Inc.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
* ======================================================================== */
|
||||
|
||||
|
||||
+function ($) { "use strict";
|
||||
|
||||
// POPOVER PUBLIC CLASS DEFINITION
|
||||
// ===============================
|
||||
|
||||
var Popover = function (element, options) {
|
||||
this.init('popover', element, options)
|
||||
}
|
||||
|
||||
if (!$.fn.tooltip) throw new Error('Popover requires tooltip.js')
|
||||
|
||||
Popover.DEFAULTS = $.extend({} , $.fn.tooltip.Constructor.DEFAULTS, {
|
||||
placement: 'right'
|
||||
, trigger: 'click'
|
||||
, content: ''
|
||||
, template: '<div class="popover"><div class="arrow"></div><h3 class="popover-title"></h3><div class="popover-content"></div></div>'
|
||||
})
|
||||
|
||||
|
||||
// NOTE: POPOVER EXTENDS tooltip.js
|
||||
// ================================
|
||||
|
||||
Popover.prototype = $.extend({}, $.fn.tooltip.Constructor.prototype)
|
||||
|
||||
Popover.prototype.constructor = Popover
|
||||
|
||||
Popover.prototype.getDefaults = function () {
|
||||
return Popover.DEFAULTS
|
||||
}
|
||||
|
||||
Popover.prototype.setContent = function () {
|
||||
var $tip = this.tip()
|
||||
var title = this.getTitle()
|
||||
var content = this.getContent()
|
||||
|
||||
$tip.find('.popover-title')[this.options.html ? 'html' : 'text'](title)
|
||||
$tip.find('.popover-content')[this.options.html ? 'html' : 'text'](content)
|
||||
|
||||
$tip.removeClass('fade top bottom left right in')
|
||||
|
||||
// IE8 doesn't accept hiding via the `:empty` pseudo selector, we have to do
|
||||
// this manually by checking the contents.
|
||||
if (!$tip.find('.popover-title').html()) $tip.find('.popover-title').hide()
|
||||
}
|
||||
|
||||
Popover.prototype.hasContent = function () {
|
||||
return this.getTitle() || this.getContent()
|
||||
}
|
||||
|
||||
Popover.prototype.getContent = function () {
|
||||
var $e = this.$element
|
||||
var o = this.options
|
||||
|
||||
return $e.attr('data-content')
|
||||
|| (typeof o.content == 'function' ?
|
||||
o.content.call($e[0]) :
|
||||
o.content)
|
||||
}
|
||||
|
||||
Popover.prototype.arrow = function () {
|
||||
return this.$arrow = this.$arrow || this.tip().find('.arrow')
|
||||
}
|
||||
|
||||
Popover.prototype.tip = function () {
|
||||
if (!this.$tip) this.$tip = $(this.options.template)
|
||||
return this.$tip
|
||||
}
|
||||
|
||||
|
||||
// POPOVER PLUGIN DEFINITION
|
||||
// =========================
|
||||
|
||||
var old = $.fn.popover
|
||||
|
||||
$.fn.popover = function (option) {
|
||||
return this.each(function () {
|
||||
var $this = $(this)
|
||||
var data = $this.data('bs.popover')
|
||||
var options = typeof option == 'object' && option
|
||||
|
||||
if (!data) $this.data('bs.popover', (data = new Popover(this, options)))
|
||||
if (typeof option == 'string') data[option]()
|
||||
})
|
||||
}
|
||||
|
||||
$.fn.popover.Constructor = Popover
|
||||
|
||||
|
||||
// POPOVER NO CONFLICT
|
||||
// ===================
|
||||
|
||||
$.fn.popover.noConflict = function () {
|
||||
$.fn.popover = old
|
||||
return this
|
||||
}
|
||||
|
||||
}(jQuery);
|
||||
@@ -0,0 +1 @@
|
||||
import rules from "mocha/tests/rules_tests";
|
||||
@@ -0,0 +1 @@
|
||||
../../../../../test/base/data/rules_dsl_spec.yml
|
||||
@@ -0,0 +1,43 @@
|
||||
import chai from "chai";
|
||||
import RuleDefs from "mvc/rules/rule-definitions";
|
||||
import SPEC_TEST_CASES from "json-loader!yaml-loader!./rules_dsl_spec.yml";
|
||||
|
||||
const RULES = RuleDefs.RULES;
|
||||
|
||||
function applyRules(rules, data, sources) {
|
||||
let columns = [];
|
||||
if (data[0]) {
|
||||
for (const index in data[0]) {
|
||||
columns.push("new");
|
||||
}
|
||||
}
|
||||
return RuleDefs.applyRules(data, sources, columns, rules);
|
||||
}
|
||||
|
||||
function itShouldConform(specTestCase, i) {
|
||||
it("should pass conformance test case " + i, function() {
|
||||
chai.assert.property(specTestCase, "rules");
|
||||
if (specTestCase.initial) {
|
||||
chai.assert.property(specTestCase, "final");
|
||||
|
||||
const rules = specTestCase.rules;
|
||||
const initial = specTestCase.initial;
|
||||
const expectedFinal = specTestCase.final;
|
||||
|
||||
const final = applyRules(rules, initial.data, initial.sources);
|
||||
const finalData = final.data;
|
||||
const finalSources = final.sources;
|
||||
chai.assert.deepEqual(finalData, expectedFinal.data);
|
||||
if (expectedFinal.sources !== undefined) {
|
||||
chai.assert.deepEqual(finalSources, expectedFinal.sources);
|
||||
}
|
||||
} else {
|
||||
chai.assert(specTestCase.error);
|
||||
// TODO: test these...
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
describe("Rules DSL Spec", function() {
|
||||
SPEC_TEST_CASES.forEach(itShouldConform);
|
||||
});
|
||||
@@ -2,6 +2,9 @@ import * as _ from "libs/underscore";
|
||||
import * as Backbone from "libs/backbone";
|
||||
import BASE_MVC from "mvc/base-mvc";
|
||||
|
||||
/* global Galaxy */
|
||||
/* global jQuery */
|
||||
|
||||
//=============================================================================
|
||||
/**
|
||||
* A Collection that can be limited/offset/re-ordered/filtered.
|
||||
@@ -35,7 +38,6 @@ var ControlledFetchCollection = Backbone.Collection.extend({
|
||||
// note: we normally want options passed in to override the defaults built here
|
||||
// so most of these fns will generate defaults
|
||||
options = _.clone(options) || {};
|
||||
var self = this;
|
||||
|
||||
// jquery ajax option; allows multiple q/qv for filters (instead of 'q[]')
|
||||
options.traditional = true;
|
||||
@@ -47,7 +49,7 @@ var ControlledFetchCollection = Backbone.Collection.extend({
|
||||
// to the pagination options too
|
||||
// (i.e. this.on( 'sync', function( options ){ if( options.limit ){ ... } }))
|
||||
// however, when we send to xhr/jquery we copy them to data also so that they become API query params
|
||||
options.data = options.data || self._buildFetchData(options);
|
||||
options.data = options.data || this._buildFetchData(options);
|
||||
Galaxy.debug("data:", options.data);
|
||||
|
||||
// options.data.filters --> options.data.q, options.data.qv
|
||||
|
||||
@@ -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 = [];
|
||||
|
||||
@@ -5,6 +5,9 @@ import baseCreator from "mvc/collection/base-creator";
|
||||
import UI_MODAL from "mvc/ui/ui-modal";
|
||||
import naturalSort from "utils/natural-sort";
|
||||
import _l from "utils/localization";
|
||||
import RuleCollectionBuilder from "components/RuleCollectionBuilder.vue";
|
||||
import Vue from "vue";
|
||||
|
||||
import "ui/hoverhighlight";
|
||||
|
||||
var logNamespace = "collections";
|
||||
@@ -826,30 +829,30 @@ var ListCollectionCreator = Backbone.View.extend(BASE_MVC.LoggableMixin)
|
||||
[
|
||||
'<div class="attributes clear">',
|
||||
'<div class="clear">',
|
||||
'<label class="setting-prompt pull-right">',
|
||||
'<label class="setting-prompt float-right">',
|
||||
_l("Hide original elements"),
|
||||
"?",
|
||||
'<input class="hide-originals pull-right" type="checkbox" />',
|
||||
'<input class="hide-originals float-right" type="checkbox" />',
|
||||
"</label>",
|
||||
"</div>",
|
||||
'<div class="clear">',
|
||||
'<input class="collection-name form-control pull-right" ',
|
||||
'<input class="collection-name form-control float-right" ',
|
||||
'placeholder="',
|
||||
_l("Enter a name for your new collection"),
|
||||
'" />',
|
||||
'<div class="collection-name-prompt pull-right">',
|
||||
'<div class="collection-name-prompt float-right">',
|
||||
_l("Name"),
|
||||
":</div>",
|
||||
"</div>",
|
||||
"</div>",
|
||||
|
||||
'<div class="actions clear vertically-spaced">',
|
||||
'<div class="other-options pull-left">',
|
||||
'<div class="other-options float-left">',
|
||||
'<button class="cancel-create btn" tabindex="-1">',
|
||||
_l("Cancel"),
|
||||
"</button>",
|
||||
'<div class="create-other btn-group dropup">',
|
||||
'<button class="btn btn-default dropdown-toggle" data-toggle="dropdown">',
|
||||
'<button class="btn btn-secondary dropdown-toggle" data-toggle="dropdown">',
|
||||
_l("Create a different kind of collection"),
|
||||
' <span class="caret"></span>',
|
||||
"</button>",
|
||||
@@ -864,7 +867,7 @@ var ListCollectionCreator = Backbone.View.extend(BASE_MVC.LoggableMixin)
|
||||
"</div>",
|
||||
"</div>",
|
||||
|
||||
'<div class="main-options pull-right">',
|
||||
'<div class="main-options float-right">',
|
||||
'<button class="create-collection btn btn-primary">',
|
||||
_l("Create list"),
|
||||
"</button>",
|
||||
@@ -992,7 +995,7 @@ var ListCollectionCreator = Backbone.View.extend(BASE_MVC.LoggableMixin)
|
||||
"</div>",
|
||||
'<div class="footer flex-row no-flex">',
|
||||
'<div class="actions clear vertically-spaced">',
|
||||
'<div class="other-options pull-left">',
|
||||
'<div class="other-options float-left">',
|
||||
'<button class="cancel-create btn" tabindex="-1">',
|
||||
_l("Cancel"),
|
||||
"</button>",
|
||||
@@ -1011,17 +1014,11 @@ var ListCollectionCreator = Backbone.View.extend(BASE_MVC.LoggableMixin)
|
||||
}
|
||||
});
|
||||
|
||||
//=============================================================================
|
||||
/** Create a modal and load its body with the given CreatorClass creator type
|
||||
* @returns {Deferred} resolved when creator has built a collection.
|
||||
*/
|
||||
var collectionCreatorModal = function _collectionCreatorModal(elements, options, CreatorClass) {
|
||||
var deferred = jQuery.Deferred();
|
||||
var modal = Galaxy.modal || new UI_MODAL.View();
|
||||
var creator;
|
||||
const collectionCreatorModalSetup = function _collectionCreatorModalSetup(options) {
|
||||
const deferred = jQuery.Deferred();
|
||||
const modal = Galaxy.modal || new UI_MODAL.View();
|
||||
|
||||
options = _.defaults(options || {}, {
|
||||
elements: elements,
|
||||
const creatorOptions = _.defaults(options || {}, {
|
||||
oncancel: function() {
|
||||
modal.hide();
|
||||
deferred.reject("cancelled");
|
||||
@@ -1032,18 +1029,74 @@ var collectionCreatorModal = function _collectionCreatorModal(elements, options,
|
||||
}
|
||||
});
|
||||
|
||||
creator = new CreatorClass(options);
|
||||
modal.show({
|
||||
title: options.title || _l("Create a collection"),
|
||||
body: creator.$el,
|
||||
width: "80%",
|
||||
height: "100%",
|
||||
closing_events: true
|
||||
});
|
||||
creator.render();
|
||||
window._collectionCreator = creator;
|
||||
const showEl = function(el) {
|
||||
modal.show({
|
||||
title: options.title || _l("Create a collection"),
|
||||
body: el,
|
||||
width: "85%",
|
||||
height: "100%",
|
||||
xlarge: true,
|
||||
closing_events: true
|
||||
});
|
||||
};
|
||||
|
||||
//TODO: remove modal header
|
||||
return { deferred, creatorOptions, showEl };
|
||||
};
|
||||
|
||||
//=============================================================================
|
||||
/** Create a modal and load its body with the given CreatorClass creator type
|
||||
* @returns {Deferred} resolved when creator has built a collection.
|
||||
*/
|
||||
var collectionCreatorModal = function _collectionCreatorModal(elements, options, CreatorClass) {
|
||||
options = _.defaults(options || {}, {
|
||||
elements: elements
|
||||
});
|
||||
const { deferred, creatorOptions, showEl } = collectionCreatorModalSetup(options);
|
||||
var creator = new CreatorClass(creatorOptions);
|
||||
showEl(creator.$el);
|
||||
creator.render();
|
||||
return deferred;
|
||||
};
|
||||
|
||||
var ruleBasedCollectionCreatorModal = function _ruleBasedCollectionCreatorModal(
|
||||
elements,
|
||||
elementsType,
|
||||
importType,
|
||||
options
|
||||
) {
|
||||
// importType in [datasets, collection]
|
||||
// elementsType in [raw, ftp, datasets]
|
||||
let title;
|
||||
if (importType == "datasets") {
|
||||
title = _l("Build Rules for Uploading Datasets");
|
||||
} else if (elementsType == "collection_contents") {
|
||||
title = _l("Build Rules for Applying to Existing Collection");
|
||||
} else if (elementsType == "datasets" || elementsType == "library_datasets") {
|
||||
title = _l("Build Rules for Creating Collection(s)");
|
||||
} else {
|
||||
title = _l("Build Rules for Uploading Collections");
|
||||
}
|
||||
options = _.defaults(options || {}, {
|
||||
title: title
|
||||
});
|
||||
const { deferred, creatorOptions, showEl } = collectionCreatorModalSetup(options);
|
||||
var ruleCollectionBuilderInstance = Vue.extend(RuleCollectionBuilder);
|
||||
var vm = document.createElement("div");
|
||||
showEl(vm);
|
||||
new ruleCollectionBuilderInstance({
|
||||
propsData: {
|
||||
initialElements: elements,
|
||||
elementsType: elementsType,
|
||||
importType: importType,
|
||||
ftpUploadSite: options.ftpUploadSite,
|
||||
creationFn: options.creationFn,
|
||||
oncancel: options.oncancel,
|
||||
oncreate: options.oncreate,
|
||||
defaultHideSourceItems: options.defaultHideSourceItems,
|
||||
saveRulesFn: options.saveRulesFn,
|
||||
initialRules: options.initialRules
|
||||
}
|
||||
}).$mount(vm);
|
||||
return deferred;
|
||||
};
|
||||
|
||||
@@ -1059,15 +1112,14 @@ var listCollectionCreatorModal = function _listCollectionCreatorModal(elements,
|
||||
* @returns {Deferred} resolved when the collection is added to the history.
|
||||
*/
|
||||
function createListCollection(contents, defaultHideSourceItems) {
|
||||
var elements = contents.toJSON();
|
||||
const elements = contents.toJSON();
|
||||
|
||||
var promise = listCollectionCreatorModal(elements, {
|
||||
const promise = listCollectionCreatorModal(elements, {
|
||||
defaultHideSourceItems: defaultHideSourceItems,
|
||||
creationFn: function(elements, name, hideSourceItems) {
|
||||
elements = elements.map(element => ({
|
||||
id: element.id,
|
||||
name: element.name,
|
||||
|
||||
//TODO: this allows for list:list even if the filter above does not - reconcile
|
||||
src: element.history_content_type === "dataset" ? "hda" : "hdca"
|
||||
}));
|
||||
@@ -1078,12 +1130,52 @@ function createListCollection(contents, defaultHideSourceItems) {
|
||||
return promise;
|
||||
}
|
||||
|
||||
function createCollectionViaRules(selection, defaultHideSourceItems) {
|
||||
let elements, elementsType, importType;
|
||||
const selectionType = selection.selectionType;
|
||||
if (!selectionType) {
|
||||
// Have HDAs from the history panel.
|
||||
elements = selection.toJSON();
|
||||
elementsType = "datasets";
|
||||
importType = "collections";
|
||||
} else {
|
||||
const hasNonWhitespaceChars = RegExp(/[^\s]/);
|
||||
// Have pasted data, data from a history dataset, or FTP list.
|
||||
const lines = selection.content
|
||||
.split(/[\n\r]/)
|
||||
.filter(line => line.length > 0 && hasNonWhitespaceChars.exec(line));
|
||||
|
||||
// Really poor tabular parser - we should get a library for this or expose options? I'm not
|
||||
// sure.
|
||||
let hasTabs = false;
|
||||
if (lines.length > 0) {
|
||||
const firstLine = lines[0];
|
||||
if (firstLine.indexOf("\t") >= 0) {
|
||||
hasTabs = true;
|
||||
}
|
||||
}
|
||||
const regex = hasTabs ? /\t/ : /\s+/;
|
||||
elements = lines.map(line => line.split(regex));
|
||||
elementsType = selection.selectionType;
|
||||
importType = selection.dataType || "collections";
|
||||
}
|
||||
const promise = ruleBasedCollectionCreatorModal(elements, elementsType, importType, {
|
||||
ftpUploadSite: selection.ftpUploadSite,
|
||||
defaultHideSourceItems: defaultHideSourceItems,
|
||||
creationFn: function(elements, collectionType, name, hideSourceItems) {
|
||||
return selection.createHDCA(elements, collectionType, name, hideSourceItems);
|
||||
}
|
||||
});
|
||||
return promise;
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
export default {
|
||||
DatasetCollectionElementView: DatasetCollectionElementView,
|
||||
ListCollectionCreator: ListCollectionCreator,
|
||||
|
||||
collectionCreatorModal: collectionCreatorModal,
|
||||
listCollectionCreatorModal: listCollectionCreatorModal,
|
||||
createListCollection: createListCollection
|
||||
createListCollection: createListCollection,
|
||||
createCollectionViaRules: createCollectionViaRules,
|
||||
ruleBasedCollectionCreatorModal: ruleBasedCollectionCreatorModal
|
||||
};
|
||||
|
||||
@@ -593,8 +593,7 @@ var PairedCollectionCreator = Backbone.View.extend(baseMVC.LoggableMixin)
|
||||
|
||||
// ------------------------------------------------------------------------ API
|
||||
/** convert a pair into JSON compatible with the collections API */
|
||||
_pairToJSON: function(pair, src) {
|
||||
src = src || "hda";
|
||||
_pairToJSON: function(pair) {
|
||||
//TODO: consider making this the pair structure when created instead
|
||||
return {
|
||||
collection_type: "paired",
|
||||
@@ -604,12 +603,12 @@ var PairedCollectionCreator = Backbone.View.extend(baseMVC.LoggableMixin)
|
||||
{
|
||||
name: "forward",
|
||||
id: pair.forward.id,
|
||||
src: src
|
||||
src: pair.forward.src || "hda"
|
||||
},
|
||||
{
|
||||
name: "reverse",
|
||||
id: pair.reverse.id,
|
||||
src: src
|
||||
src: pair.reverse.src || "hda"
|
||||
}
|
||||
]
|
||||
};
|
||||
@@ -1558,7 +1557,7 @@ var PairedCollectionCreator = Backbone.View.extend(baseMVC.LoggableMixin)
|
||||
"</span>",
|
||||
'<span class="title-info unpaired-info"></span>',
|
||||
"</div>",
|
||||
'<div class="unpaired-filter forward-unpaired-filter pull-left">',
|
||||
'<div class="unpaired-filter forward-unpaired-filter float-left">',
|
||||
'<input class="search-query" placeholder="',
|
||||
_l("Filter this list"),
|
||||
'" />',
|
||||
@@ -1586,7 +1585,7 @@ var PairedCollectionCreator = Backbone.View.extend(baseMVC.LoggableMixin)
|
||||
"</span>",
|
||||
'<span class="title-info unpaired-info"></span>',
|
||||
"</div>",
|
||||
'<div class="unpaired-filter reverse-unpaired-filter pull-left">',
|
||||
'<div class="unpaired-filter reverse-unpaired-filter float-left">',
|
||||
'<input class="search-query" placeholder="',
|
||||
_l("Filter this list"),
|
||||
'" />',
|
||||
@@ -1636,35 +1635,35 @@ var PairedCollectionCreator = Backbone.View.extend(baseMVC.LoggableMixin)
|
||||
[
|
||||
'<div class="attributes clear">',
|
||||
'<div class="clear">',
|
||||
'<label class="setting-prompt pull-right">',
|
||||
'<label class="setting-prompt float-right">',
|
||||
_l("Hide original elements"),
|
||||
"?",
|
||||
'<input class="hide-originals pull-right" type="checkbox" />',
|
||||
'<input class="hide-originals float-right" type="checkbox" />',
|
||||
"</label>",
|
||||
'<label class="setting-prompt pull-right">',
|
||||
'<label class="setting-prompt float-right">',
|
||||
_l("Remove file extensions from pair names"),
|
||||
"?",
|
||||
'<input class="remove-extensions pull-right" type="checkbox" />',
|
||||
'<input class="remove-extensions float-right" type="checkbox" />',
|
||||
"</label>",
|
||||
"</div>",
|
||||
'<div class="clear">',
|
||||
'<input class="collection-name form-control pull-right" ',
|
||||
'<input class="collection-name form-control float-right" ',
|
||||
'placeholder="',
|
||||
_l("Enter a name for your new list"),
|
||||
'" />',
|
||||
'<div class="collection-name-prompt pull-right">',
|
||||
'<div class="collection-name-prompt float-right">',
|
||||
_l("Name"),
|
||||
":</div>",
|
||||
"</div>",
|
||||
"</div>",
|
||||
|
||||
'<div class="actions clear vertically-spaced">',
|
||||
'<div class="other-options pull-left">',
|
||||
'<div class="other-options float-left">',
|
||||
'<button class="cancel-create btn" tabindex="-1">',
|
||||
_l("Cancel"),
|
||||
"</button>",
|
||||
'<div class="create-other btn-group dropup">',
|
||||
'<button class="btn btn-default dropdown-toggle" data-toggle="dropdown">',
|
||||
'<button class="btn btn-secondary dropdown-toggle" data-toggle="dropdown">',
|
||||
_l("Create a different kind of collection"),
|
||||
' <span class="caret"></span>',
|
||||
"</button>",
|
||||
@@ -1679,7 +1678,7 @@ var PairedCollectionCreator = Backbone.View.extend(baseMVC.LoggableMixin)
|
||||
"</div>",
|
||||
"</div>",
|
||||
|
||||
'<div class="main-options pull-right">',
|
||||
'<div class="main-options float-right">',
|
||||
'<button class="create-collection btn btn-primary">',
|
||||
_l("Create list"),
|
||||
"</button>",
|
||||
|
||||
@@ -227,7 +227,7 @@ var PairCollectionCreator = _super.extend({
|
||||
"</div>",
|
||||
'<div class="footer flex-row no-flex">',
|
||||
'<div class="actions clear vertically-spaced">',
|
||||
'<div class="other-options pull-left">',
|
||||
'<div class="other-options float-left">',
|
||||
'<button class="cancel-create btn" tabindex="-1">',
|
||||
_l("Cancel"),
|
||||
"</button>",
|
||||
|
||||
@@ -12,6 +12,7 @@ var View = Backbone.View.extend({
|
||||
});
|
||||
this.message = new Ui.Message({ persistent: true });
|
||||
this.tabs = this._createTabs();
|
||||
this.active_tab = "user";
|
||||
this.$el
|
||||
.append($("<h4/>").append("Edit dataset attributes"))
|
||||
.append(this.message.$el)
|
||||
|
||||
@@ -9,6 +9,7 @@ var View = Backbone.View.extend({
|
||||
this.model = new Backbone.Model({
|
||||
dataset_id: Galaxy.params.dataset_id
|
||||
});
|
||||
this.active_tab = "user";
|
||||
this.render();
|
||||
},
|
||||
|
||||
|
||||
@@ -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")}
|
||||
@@ -326,7 +326,7 @@ var DatasetListItemView = _super.extend(
|
||||
[
|
||||
"<% _.each(_.sortBy(_.uniq(tags), function(x) { return x }), function(tag){ %>",
|
||||
'<% if (tag.indexOf("name:") == 0){ %>',
|
||||
'<span class="label label-info"><%- tag.slice(5) %></span>',
|
||||
'<span class="badge badge-primary badge-tags"><%- tag.slice(5) %></span>',
|
||||
"<% } %>",
|
||||
"<% }); %>"
|
||||
].join("")
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -85,11 +85,13 @@ export default Backbone.View.extend({
|
||||
// render visibility
|
||||
this.$el[this.model.get("hidden") ? "hide" : "show"]();
|
||||
// render preview view for collapsed fields
|
||||
// allow at least newlines to render properly after escape
|
||||
const html = _.escape(this.model.get("text_value")).replace(/\n/g, "<br />");
|
||||
this.$preview[
|
||||
(this.field.collapsed && this.model.get("collapsible_preview")) || this.model.get("disabled")
|
||||
? "show"
|
||||
: "hide"
|
||||
]().html(_.escape(this.model.get("text_value")));
|
||||
]().html(html);
|
||||
// render error messages
|
||||
var error_text = this.model.get("error_text");
|
||||
this.$error[error_text ? "show" : "hide"]();
|
||||
@@ -106,7 +108,14 @@ export default Backbone.View.extend({
|
||||
style: this.model.get("style")
|
||||
});
|
||||
// render collapsible options
|
||||
if (!this.model.get("disabled") && this.model.get("collapsible_value") !== undefined) {
|
||||
const workflowRuntimeCompatible =
|
||||
this.field.workflowRuntimeCompatible === undefined ? true : this.field.workflowRuntimeCompatible;
|
||||
if (
|
||||
workflowRuntimeCompatible &&
|
||||
!this.model.get("disabled") &&
|
||||
this.model.get("collapsible_value") !== undefined
|
||||
) {
|
||||
console.log(this.field);
|
||||
var collapsible_state = this.field.collapsed ? "enable" : "disable";
|
||||
this.$title_text.hide();
|
||||
this.$collapsible.show();
|
||||
|
||||
@@ -7,6 +7,7 @@ import SelectContent from "mvc/ui/ui-select-content";
|
||||
import SelectLibrary from "mvc/ui/ui-select-library";
|
||||
import SelectFtp from "mvc/ui/ui-select-ftp";
|
||||
import SelectGenomeSpace from "mvc/ui/ui-select-genomespace";
|
||||
import RulesEdit from "mvc/ui/ui-rules-edit";
|
||||
import ColorPicker from "mvc/ui/ui-color-picker";
|
||||
// create form view
|
||||
export default Backbone.Model.extend({
|
||||
@@ -30,6 +31,7 @@ export default Backbone.Model.extend({
|
||||
library_data: "_fieldLibrary",
|
||||
ftpfile: "_fieldFtp",
|
||||
upload: "_fieldUpload",
|
||||
rules: "_fieldRulesEdit",
|
||||
genomespacefile: "_fieldGenomeSpace"
|
||||
},
|
||||
|
||||
@@ -83,24 +85,17 @@ export default Backbone.Model.extend({
|
||||
});
|
||||
}
|
||||
|
||||
// identify display type
|
||||
var SelectClass = Ui.Select;
|
||||
switch (input_def.display) {
|
||||
case "checkboxes":
|
||||
SelectClass = Ui.Checkbox;
|
||||
break;
|
||||
case "radio":
|
||||
SelectClass = Ui.Radio;
|
||||
break;
|
||||
case "radiobutton":
|
||||
SelectClass = Ui.RadioButton;
|
||||
break;
|
||||
}
|
||||
|
||||
// create select field
|
||||
return new SelectClass.View({
|
||||
// pick selection display
|
||||
var classes = {
|
||||
checkboxes: Ui.Checkbox,
|
||||
radio: Ui.Radio,
|
||||
radiobutton: Ui.RadioButton
|
||||
};
|
||||
var SelectClass = classes[input_def.display] || Ui.Select;
|
||||
var select = new SelectClass.View({
|
||||
id: `field-${input_def.id}`,
|
||||
data: data,
|
||||
display: input_def.display,
|
||||
error_text: input_def.error_text || "No options available",
|
||||
readonly: input_def.readonly,
|
||||
multiple: input_def.multiple,
|
||||
@@ -109,6 +104,7 @@ export default Backbone.Model.extend({
|
||||
individual: input_def.individual,
|
||||
searchable: input_def.flavor !== "workflow"
|
||||
});
|
||||
return input_def.textable ? new Ui.TextSelect({ select: select }) : select;
|
||||
},
|
||||
|
||||
/** Drill down options field */
|
||||
@@ -221,12 +217,17 @@ export default Backbone.Model.extend({
|
||||
/** GenomeSpace file select field
|
||||
*/
|
||||
_fieldGenomeSpace: function(input_def) {
|
||||
var self = this;
|
||||
return new SelectGenomeSpace.View({
|
||||
id: `field-${input_def.id}`,
|
||||
onchange: function() {
|
||||
self.app.trigger("change");
|
||||
}
|
||||
onchange: input_def.onchange
|
||||
});
|
||||
},
|
||||
|
||||
_fieldRulesEdit: function(input_def) {
|
||||
return new RulesEdit.View({
|
||||
id: `field-${input_def.id}`,
|
||||
onchange: input_def.onchange,
|
||||
target: input_def.target
|
||||
});
|
||||
},
|
||||
|
||||
|
||||
@@ -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":
|
||||
@@ -49,11 +48,18 @@ var View = Backbone.View.extend({
|
||||
_addConditional: function(input_def) {
|
||||
var self = this;
|
||||
input_def.test_param.id = input_def.id;
|
||||
input_def.test_param.textable = false;
|
||||
this.app.model.get("sustain_conditionals") && (input_def.test_param.disabled = true);
|
||||
var field = this._addRow(input_def.test_param);
|
||||
|
||||
// set onchange event for test parameter
|
||||
field.model &&
|
||||
if (field.model) {
|
||||
// add conditional sub sections
|
||||
for (var i in input_def.cases) {
|
||||
var sub_section = new View(this.app, {
|
||||
inputs: input_def.cases[i].inputs
|
||||
});
|
||||
this._append(sub_section.$el.addClass("ui-form-section"), `${input_def.id}-section-${i}`);
|
||||
}
|
||||
field.model.set("onchange", value => {
|
||||
var selectedCase = self.app.data.matchCase(input_def, value);
|
||||
for (var i in input_def.cases) {
|
||||
@@ -74,17 +80,9 @@ var View = Backbone.View.extend({
|
||||
}
|
||||
self.app.trigger("change");
|
||||
});
|
||||
|
||||
// add conditional sub sections
|
||||
for (var i in input_def.cases) {
|
||||
var sub_section = new View(this.app, {
|
||||
inputs: input_def.cases[i].inputs
|
||||
});
|
||||
this._append(sub_section.$el.addClass("ui-form-section"), `${input_def.id}-section-${i}`);
|
||||
// trigger refresh on conditional input field after all input elements have been created
|
||||
field.trigger("change");
|
||||
}
|
||||
|
||||
// trigger refresh on conditional input field after all input elements have been created
|
||||
field.trigger("change");
|
||||
},
|
||||
|
||||
/** Add a repeat block */
|
||||
|
||||
@@ -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,
|
||||
@@ -26,10 +25,13 @@ export var View = Backbone.View.extend({
|
||||
var self = this;
|
||||
this.data.matchModel(new_model, (node, input_id) => {
|
||||
var input = self.input_list[input_id];
|
||||
var field = self.field_list[input_id];
|
||||
if (field.refreshDefinition) {
|
||||
field.refreshDefinition(node);
|
||||
}
|
||||
if (input && input.options) {
|
||||
if (!_.isEqual(input.options, node.options)) {
|
||||
input.options = node.options;
|
||||
var field = self.field_list[input_id];
|
||||
if (field.update) {
|
||||
var new_options = [];
|
||||
if (["data", "data_collection", "drill_down"].indexOf(input.type) != -1) {
|
||||
@@ -81,7 +83,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
|
||||
);
|
||||
@@ -151,6 +153,7 @@ export var View = Backbone.View.extend({
|
||||
this.portlet = new Portlet.View({
|
||||
icon: options.icon,
|
||||
title: options.title,
|
||||
title_id: options.title_id,
|
||||
cls: options.cls,
|
||||
operations: !options.hide_operations && options.operations,
|
||||
buttons: options.buttons,
|
||||
@@ -174,5 +177,3 @@ export var View = Backbone.View.extend({
|
||||
Galaxy.emit.debug("form-view::initialize()", "Completed");
|
||||
}
|
||||
});
|
||||
|
||||
export default View;
|
||||
|
||||
@@ -6,6 +6,9 @@ var View = Backbone.View.extend({
|
||||
this.model = new Backbone.Model(options);
|
||||
this.url = this.model.get("url");
|
||||
this.redirect = this.model.get("redirect");
|
||||
if (options && options.active_tab) {
|
||||
this.active_tab = options.active_tab;
|
||||
}
|
||||
this.setElement("<div/>");
|
||||
this.render();
|
||||
},
|
||||
@@ -20,6 +23,7 @@ var View = Backbone.View.extend({
|
||||
var options = $.extend({}, self.model.attributes, response);
|
||||
var form = new Form({
|
||||
title: options.title,
|
||||
title_id: options.title_id,
|
||||
message: options.message,
|
||||
status: options.status || "warning",
|
||||
icon: options.icon,
|
||||
|
||||
@@ -4,6 +4,7 @@
|
||||
export default Backbone.Model.extend({
|
||||
defaults: {
|
||||
url_base: "",
|
||||
title_id: undefined,
|
||||
async: false,
|
||||
async_ops: [],
|
||||
categorical_filters: [],
|
||||
|
||||
@@ -1,12 +1,17 @@
|
||||
/** 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");
|
||||
if (options && options.active_tab) {
|
||||
this.active_tab = options.active_tab;
|
||||
}
|
||||
$.ajax({
|
||||
url: `${Galaxy.root + this.item}/${this.model.get("action_id")}?${$.param(Galaxy.params)}`,
|
||||
success: function(response) {
|
||||
@@ -24,7 +29,7 @@ var View = Backbone.View.extend({
|
||||
|
||||
_templateShared: function() {
|
||||
var self = this;
|
||||
var $tmpl = $(`<div><h2>${this.model.get("plural")} shared with you by others</h2></div>`);
|
||||
var $tmpl = $(`<div><br/><h2>${this.model.get("plural")} shared with you by others</h2></div>`);
|
||||
var options = this.model.attributes;
|
||||
if (options.shared_by_others && options.shared_by_others.length > 0) {
|
||||
var $table = $(
|
||||
|
||||
@@ -1,18 +1,33 @@
|
||||
// dependencies
|
||||
import Utils from "utils/utils";
|
||||
import * as _ from "underscore";
|
||||
|
||||
/* global $ */
|
||||
|
||||
// grid view templates
|
||||
export default {
|
||||
// template
|
||||
grid: function(options) {
|
||||
var tmpl = "";
|
||||
let tmpl;
|
||||
if (options.embedded) {
|
||||
tmpl = this.grid_header(options) + this.grid_table(options);
|
||||
} else {
|
||||
tmpl = `<div class="loading-elt-overlay"></div><table><tr><td width="75%">${this.grid_header(
|
||||
options
|
||||
)}</td><td></td><td></td></tr><tr><td width="100%" id="grid-message" valign="top"></td><td></td><td></td></tr></table>${this.grid_table(
|
||||
options
|
||||
)}`;
|
||||
tmpl = `
|
||||
<div class="loading-elt-overlay"></div>
|
||||
<table class="grid-table">
|
||||
<tr>
|
||||
<td width="75%">${this.grid_header(options)}</td>
|
||||
<td></td>
|
||||
<td></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td width="100%" id="grid-message" valign="top"></td>
|
||||
<td></td>
|
||||
<td></td>
|
||||
</tr>
|
||||
</table>
|
||||
${this.grid_table(options)}
|
||||
`;
|
||||
}
|
||||
|
||||
// add info text
|
||||
@@ -40,7 +55,11 @@ export default {
|
||||
grid_header: function(options) {
|
||||
var tmpl = '<div class="grid-header">';
|
||||
if (!options.embedded) {
|
||||
tmpl += `<h2>${options.title}</h2>`;
|
||||
let id_str = "";
|
||||
if (options.title_id) {
|
||||
id_str += ` id="${options.title_id}"`;
|
||||
}
|
||||
tmpl += `<h2${id_str}>${options.title}</h2>`;
|
||||
}
|
||||
if (options.global_actions) {
|
||||
tmpl += '<ul class="manage-table-actions">';
|
||||
@@ -92,8 +111,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;
|
||||
}
|
||||
@@ -154,38 +175,35 @@ export default {
|
||||
var target = column_settings.target;
|
||||
|
||||
// unescape value
|
||||
if (jQuery.type(value) === "string") {
|
||||
if ($.type(value) === "string") {
|
||||
value = value.replace(/\/\//g, "/");
|
||||
}
|
||||
|
||||
// Attach popup menu?
|
||||
var id = "";
|
||||
var cls = "";
|
||||
var popup_id = "";
|
||||
if (column.attach_popup) {
|
||||
id = `grid-${item.encode_id}-popup`;
|
||||
cls = "menubutton";
|
||||
if (link !== "") {
|
||||
cls += " split";
|
||||
}
|
||||
cls += " popup";
|
||||
popup_id = `grid-${item.encode_id}-popup`;
|
||||
}
|
||||
|
||||
// Check for row wrapping
|
||||
tmpl += `<td ${nowrap}>`;
|
||||
|
||||
// Link
|
||||
if (link) {
|
||||
if (options.operations.length !== 0) {
|
||||
tmpl += `<div id="${id}" class="${cls}" style="float: left;">`;
|
||||
}
|
||||
tmpl += `<a class="menubutton-label use-target" target="${target}" href="${link}" onclick="return false;">${value}</a>`;
|
||||
if (options.operations.length !== 0) {
|
||||
tmpl += "</div>";
|
||||
}
|
||||
} else {
|
||||
tmpl += `<div id="${id}" class="${cls}"><label id="${column.label_id_prefix}${
|
||||
// Determine cell content
|
||||
if (column.delayed) {
|
||||
tmpl += `<div class="delayed-value-${column.key}" data-id="${
|
||||
item.encode_id
|
||||
}" for="${item.encode_id}">${value || ""}</label></div>`;
|
||||
}" data-value="${value}"><span class="fa fa-spinner fa-spin"></span></div>`;
|
||||
} else if (column.attach_popup && link) {
|
||||
tmpl += `<div class="btn-group">
|
||||
<button class="btn btn-secondary use-target" target="${target}" href="${link}" onclick="return false;">${value}</button>
|
||||
<button id="${popup_id}" class="btn btn-secondary dropdown-toggle dropdown-toggle-split" data-toggle="dropdown"/>
|
||||
</div>`;
|
||||
} else if (column.attach_popup) {
|
||||
tmpl += `<button id="${popup_id}" class="btn dropdown-toggle" data-toggle="dropdown">${value}</button>`;
|
||||
} else if (link) {
|
||||
tmpl += `<a class="use-target" target="${target}" href="${link}" onclick="return false;">${value}</a>`;
|
||||
} else {
|
||||
tmpl += `<label>${value || ""}</label>`;
|
||||
}
|
||||
tmpl += "</td>";
|
||||
}
|
||||
@@ -249,27 +267,27 @@ export default {
|
||||
|
||||
if (min_page > 1) {
|
||||
tmpl +=
|
||||
'<span class="page-link" id="page-link-1"><a href="javascript:void(0);" page_num="1" onclick="return false;">1</a></span> ...';
|
||||
'<span class="page-link-grid" id="page-link-1"><a href="javascript:void(0);" page_num="1" onclick="return false;">1</a></span> ...';
|
||||
}
|
||||
|
||||
// create page urls
|
||||
for (var page_index = min_page; page_index < max_page + 1; page_index++) {
|
||||
if (page_index == options.cur_page_num) {
|
||||
tmpl += `<span class="page-link inactive-link" id="page-link-${page_index}">${page_index}</span>`;
|
||||
tmpl += `<span class="page-link-grid inactive-link" id="page-link-${page_index}">${page_index}</span>`;
|
||||
} else {
|
||||
tmpl += `<span class="page-link" id="page-link-${page_index}"><a href="javascript:void(0);" onclick="return false;" page_num="${page_index}">${page_index}</a></span>`;
|
||||
tmpl += `<span class="page-link-grid" id="page-link-${page_index}"><a href="javascript:void(0);" onclick="return false;" page_num="${page_index}">${page_index}</a></span>`;
|
||||
}
|
||||
}
|
||||
|
||||
// show last page
|
||||
if (max_page < num_pages) {
|
||||
tmpl += `...<span class="page-link" id="page-link-${num_pages}"><a href="javascript:void(0);" onclick="return false;" page_num="${num_pages}">${num_pages}</a></span>`;
|
||||
tmpl += `...<span class="page-link-grid" id="page-link-${num_pages}"><a href="javascript:void(0);" onclick="return false;" page_num="${num_pages}">${num_pages}</a></span>`;
|
||||
}
|
||||
tmpl += "</span>";
|
||||
|
||||
// Show all link
|
||||
tmpl += `
|
||||
<span class="page-link" id="show-all-link-span"> | <a href="javascript:void(0);" onclick="return false;" page_num="all">Show All</a></span>
|
||||
<span class="page-link-grid" id="show-all-link-span"> | <a href="javascript:void(0);" onclick="return false;" page_num="all">Show All</a></span>
|
||||
</td>
|
||||
</tr>`;
|
||||
}
|
||||
@@ -452,7 +470,7 @@ export default {
|
||||
var column_filter = filters[column_key];
|
||||
if (column_filter) {
|
||||
// identify type
|
||||
var type = jQuery.type(column_filter);
|
||||
var type = $.type(column_filter);
|
||||
|
||||
// single filter value
|
||||
if (type == "string") {
|
||||
|
||||
@@ -1,12 +1,16 @@
|
||||
// This is necessary so that, when nested arrays are used in ajax/post/get methods, square brackets ('[]') are
|
||||
// not appended to the identifier of a nested array.
|
||||
jQuery.ajaxSettings.traditional = true;
|
||||
|
||||
// dependencies
|
||||
import * as Backbone from "backbone";
|
||||
import * as _ from "underscore";
|
||||
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";
|
||||
|
||||
/* global $ */
|
||||
// This is necessary so that, when nested arrays are used in ajax/post/get methods, square brackets ('[]') are
|
||||
// not appended to the identifier of a nested array.
|
||||
$.ajaxSettings.traditional = true;
|
||||
|
||||
// grid view
|
||||
export default Backbone.View.extend({
|
||||
// model
|
||||
@@ -16,6 +20,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 +33,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 +48,8 @@ export default Backbone.View.extend({
|
||||
}
|
||||
});
|
||||
} else {
|
||||
// set element
|
||||
this.setElement("<div>");
|
||||
this.init_grid(grid_config);
|
||||
}
|
||||
|
||||
@@ -98,7 +104,7 @@ export default Backbone.View.extend({
|
||||
this.$el.find("#grid-message").html(Templates.message(options));
|
||||
var self = this;
|
||||
if (options.use_hide_message) {
|
||||
setTimeout(() => {
|
||||
window.setTimeout(() => {
|
||||
self.$el.find("#grid-message").html("");
|
||||
}, 5000);
|
||||
}
|
||||
@@ -232,7 +238,7 @@ export default Backbone.View.extend({
|
||||
//
|
||||
// add page click events
|
||||
//
|
||||
this.$el.find(".page-link > a").each(function() {
|
||||
this.$el.find(".page-link-grid > a").each(function() {
|
||||
$(this).click(function() {
|
||||
self.set_page($(this).attr("page_num"));
|
||||
return false;
|
||||
@@ -263,13 +269,13 @@ export default Backbone.View.extend({
|
||||
var button = self.$(`#grid-${item.encode_id}-popup`).off();
|
||||
var popup = new PopupMenu(button);
|
||||
_.each(options.operations, operation => {
|
||||
self._add_operation(popup, operation, item);
|
||||
self.add_operation(popup, operation, item);
|
||||
});
|
||||
});
|
||||
},
|
||||
|
||||
/** Add an operation to the items menu */
|
||||
_add_operation: function(popup, operation, item) {
|
||||
add_operation: function(popup, operation, item) {
|
||||
var self = this;
|
||||
var settings = item.operation_config[operation.label];
|
||||
if (settings.allowed && operation.allow_popup) {
|
||||
@@ -548,7 +554,7 @@ export default Backbone.View.extend({
|
||||
confirmation_text != "None" &&
|
||||
confirmation_text != "null"
|
||||
)
|
||||
if (!confirm(confirmation_text)) return false;
|
||||
if (!window.confirm(confirmation_text)) return false;
|
||||
|
||||
// use small characters for operation?!
|
||||
operation = operation.toLowerCase();
|
||||
|
||||
@@ -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 float-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,5 +1,5 @@
|
||||
function _templateNametag(tag) {
|
||||
return `<span class="label label-info">${_.escape(tag.slice(5))}</span>`;
|
||||
return `<span class="badge badge-primary badge-tags">${_.escape(tag.slice(5))}</span>`;
|
||||
}
|
||||
|
||||
function nametagTemplate(historyItem) {
|
||||
|
||||
@@ -1,11 +1,65 @@
|
||||
import _l from "utils/localization";
|
||||
/** This class renders the grid list. */
|
||||
import _l from "utils/localization";
|
||||
import AjaxQueue from "utils/ajax-queue";
|
||||
import Utils from "utils/utils";
|
||||
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";
|
||||
import * as Backbone from "backbone";
|
||||
|
||||
/* global $ */
|
||||
/* global Galaxy */
|
||||
|
||||
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("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-id='${historyId}']`).html(stateHtml);
|
||||
$(`.delayed-value-disk_size[data-id='${historyId}']`).html(req.nice_size);
|
||||
};
|
||||
var xhr = $.ajax(options);
|
||||
return xhr;
|
||||
};
|
||||
})
|
||||
);
|
||||
fetchDetails.forEach(fn => ajaxQueue.add(fn));
|
||||
ajaxQueue.start();
|
||||
},
|
||||
_showCopyDialog: function(id) {
|
||||
var history = new HistoryModel.History({ id: id });
|
||||
history
|
||||
@@ -23,45 +77,32 @@ var HistoryGridView = GridView.extend({
|
||||
});
|
||||
},
|
||||
/** Add an operation to the items menu */
|
||||
_add_operation: function(popup, operation, item) {
|
||||
var self = this;
|
||||
var settings = item.operation_config[operation.label];
|
||||
add_operation: function(popup, operation, item) {
|
||||
if (operation.label == "Copy") {
|
||||
operation.onclick = id => {
|
||||
self._showCopyDialog(id);
|
||||
this._showCopyDialog(id);
|
||||
};
|
||||
}
|
||||
if (settings.allowed && operation.allow_popup) {
|
||||
popup.addItem({
|
||||
html: operation.label,
|
||||
href: settings.url_args,
|
||||
target: settings.target,
|
||||
confirmation_text: operation.confirm,
|
||||
func: function(e) {
|
||||
e.preventDefault();
|
||||
var label = $(e.target).html();
|
||||
if (operation.onclick) {
|
||||
operation.onclick(item.encode_id);
|
||||
} else {
|
||||
self.execute(this.findItemByHtml(label));
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
GridView.prototype.add_operation.call(this, popup, operation, item);
|
||||
}
|
||||
});
|
||||
|
||||
var View = Backbone.View.extend({
|
||||
title: _l("Histories"),
|
||||
initialize: function(options) {
|
||||
var self = this;
|
||||
this.setElement($("<div/>"));
|
||||
LoadingIndicator.markViewAsLoading(this);
|
||||
|
||||
if (options.action_id == "list_published") {
|
||||
this.active_tab = "shared";
|
||||
} else if (options.action_id == "list") {
|
||||
this.active_tab = "user";
|
||||
}
|
||||
this.model = new Backbone.Model();
|
||||
Utils.get({
|
||||
url: `${Galaxy.root}history/${options.action_id}?${$.param(Galaxy.params)}`,
|
||||
success: function(response) {
|
||||
self.model.set(response);
|
||||
self.render();
|
||||
success: response => {
|
||||
this.model.set(response);
|
||||
this.render();
|
||||
}
|
||||
});
|
||||
},
|
||||
|
||||
@@ -1,9 +1,13 @@
|
||||
import HISTORY_CONTENTS from "mvc/history/history-contents";
|
||||
import HISTORY_PREFS from "mvc/history/history-preferences";
|
||||
import CONTROLLED_FETCH_COLLECTION from "mvc/base/controlled-fetch-collection";
|
||||
import UTILS from "utils/utils";
|
||||
import BASE_MVC from "mvc/base-mvc";
|
||||
import _l from "utils/localization";
|
||||
import * as _ from "libs/underscore";
|
||||
import * as Backbone from "libs/backbone";
|
||||
|
||||
/* global jQuery */
|
||||
/* global Galaxy */
|
||||
|
||||
//==============================================================================
|
||||
/** @class Model for a Galaxy history resource - both a record of user
|
||||
@@ -161,7 +165,7 @@ var History = Backbone.Model.extend(BASE_MVC.LoggableMixin).extend(
|
||||
_fetchContentRelatedAttributes: function() {
|
||||
var contentRelatedAttrs = ["size", "non_ready_jobs", "contents_active", "hid_counter"];
|
||||
return this.fetch({
|
||||
data: $.param({
|
||||
data: jQuery.param({
|
||||
keys: contentRelatedAttrs.join(",")
|
||||
})
|
||||
});
|
||||
@@ -185,7 +189,9 @@ var History = Backbone.Model.extend(BASE_MVC.LoggableMixin).extend(
|
||||
var serverResponseDatetime;
|
||||
try {
|
||||
serverResponseDatetime = new Date(xhr.getResponseHeader("Date"));
|
||||
} catch (err) {}
|
||||
} catch (err) {
|
||||
console.error(err);
|
||||
}
|
||||
this.lastUpdateTime = serverResponseDatetime || new Date();
|
||||
this.checkForUpdates(options);
|
||||
});
|
||||
@@ -277,11 +283,10 @@ var History = Backbone.Model.extend(BASE_MVC.LoggableMixin).extend(
|
||||
/** fetch this histories contents, adjusting options based on the stored history preferences */
|
||||
fetchContents: function(options) {
|
||||
options = options || {};
|
||||
var self = this;
|
||||
|
||||
// we're updating, reset the update time
|
||||
self.lastUpdateTime = new Date();
|
||||
return self.contents.fetchCurrentPage(options);
|
||||
this.lastUpdateTime = new Date();
|
||||
return this.contents.fetchCurrentPage(options);
|
||||
},
|
||||
|
||||
/** save this history, _Mark_ing it as deleted (just a flag) */
|
||||
@@ -438,7 +443,7 @@ var HistoryCollection = _collectionSuper.extend(BASE_MVC.LoggableMixin).extend({
|
||||
_buildFetchFilters: function(options) {
|
||||
var superFilters = _collectionSuper.prototype._buildFetchFilters.call(this, options) || {};
|
||||
var filters = {};
|
||||
if (!this.includeDeleted) {
|
||||
if (this.includeDeleted !== true) {
|
||||
filters.deleted = false;
|
||||
filters.purged = false;
|
||||
} else {
|
||||
@@ -451,25 +456,24 @@ var HistoryCollection = _collectionSuper.extend(BASE_MVC.LoggableMixin).extend({
|
||||
|
||||
/** override to fetch current as well (as it may be outside the first 10, etc.) */
|
||||
fetchFirst: function(options) {
|
||||
var self = this;
|
||||
// TODO: batch?
|
||||
var xhr = $.when();
|
||||
var xhr = jQuery.when();
|
||||
if (this.currentHistoryId) {
|
||||
xhr = _collectionSuper.prototype.fetchFirst.call(self, {
|
||||
xhr = _collectionSuper.prototype.fetchFirst.call(this, {
|
||||
silent: true,
|
||||
limit: 1,
|
||||
filters: {
|
||||
"encoded_id-in": this.currentHistoryId,
|
||||
// without these a deleted current history will return [] here and block the other xhr
|
||||
purged: "",
|
||||
deleted: "",
|
||||
"encoded_id-in": this.currentHistoryId
|
||||
deleted: null,
|
||||
purged: ""
|
||||
}
|
||||
});
|
||||
}
|
||||
return xhr.then(() => {
|
||||
options = options || {};
|
||||
options.offset = 0;
|
||||
return self.fetchMore(options);
|
||||
return this.fetchMore(options);
|
||||
});
|
||||
},
|
||||
|
||||
|
||||
@@ -168,7 +168,8 @@ var HistoryViewEdit = _super.extend(
|
||||
$activator: faIconButton({
|
||||
title: _l("Edit history tags"),
|
||||
classes: "history-tag-btn",
|
||||
faIcon: "fa-tags"
|
||||
faIcon: "fa-tags",
|
||||
tooltipConfig: { placement: "top" }
|
||||
}).appendTo($where.find(".controls .actions"))
|
||||
});
|
||||
},
|
||||
@@ -191,7 +192,8 @@ var HistoryViewEdit = _super.extend(
|
||||
$activator: faIconButton({
|
||||
title: _l("Edit history annotation"),
|
||||
classes: "history-annotate-btn",
|
||||
faIcon: "fa-comment"
|
||||
faIcon: "fa-comment",
|
||||
tooltipConfig: { placement: "top" }
|
||||
}).appendTo($where.find(".controls .actions"))
|
||||
});
|
||||
},
|
||||
@@ -275,7 +277,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);
|
||||
}
|
||||
}
|
||||
});
|
||||
@@ -290,22 +296,20 @@ var HistoryViewEdit = _super.extend(
|
||||
return [
|
||||
{
|
||||
html: _l("Build Dataset List"),
|
||||
func: function() {
|
||||
panel.buildCollection("list");
|
||||
}
|
||||
func: () => panel.buildCollection("list")
|
||||
},
|
||||
// TODO: Only show quick pair if two things selected.
|
||||
{
|
||||
html: _l("Build Dataset Pair"),
|
||||
func: function() {
|
||||
panel.buildCollection("paired");
|
||||
}
|
||||
func: () => panel.buildCollection("paired")
|
||||
},
|
||||
{
|
||||
html: _l("Build List of Dataset Pairs"),
|
||||
func: function() {
|
||||
panel.buildCollection("list:paired");
|
||||
}
|
||||
func: () => panel.buildCollection("list:paired")
|
||||
},
|
||||
{
|
||||
html: _l("Build Collection from Rules"),
|
||||
func: () => panel.buildCollection("rules")
|
||||
}
|
||||
];
|
||||
},
|
||||
@@ -321,6 +325,8 @@ var HistoryViewEdit = _super.extend(
|
||||
createFunc = PAIR_COLLECTION_CREATOR.createPairCollection;
|
||||
} else if (collectionType == "list:paired") {
|
||||
createFunc = LIST_OF_PAIRS_COLLECTION_CREATOR.createListOfPairsCollection;
|
||||
} else if (collectionType.startsWith("rules")) {
|
||||
createFunc = LIST_COLLECTION_CREATOR.createCollectionViaRules;
|
||||
} else {
|
||||
console.warn(`Unknown collectionType encountered ${collectionType}`);
|
||||
}
|
||||
|
||||
@@ -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:
|
||||
@@ -179,7 +185,8 @@ var HistoryView = _super.extend(
|
||||
return faIconButton({
|
||||
title: _l("Operations on multiple datasets"),
|
||||
classes: "show-selectors-btn",
|
||||
faIcon: "fa-check-square-o"
|
||||
faIcon: "fa-check-square-o",
|
||||
tooltipConfig: { placement: "top" }
|
||||
}).prependTo($where.find(".controls .actions"));
|
||||
},
|
||||
|
||||
@@ -413,7 +420,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();
|
||||
@@ -568,11 +575,11 @@ HistoryView.prototype.templates = (() => {
|
||||
|
||||
'<div class="list-actions">',
|
||||
'<div class="btn-group">',
|
||||
'<button class="select-all btn btn-default"',
|
||||
'<button class="select-all btn btn-secondary"',
|
||||
'data-mode="select">',
|
||||
_l("All"),
|
||||
"</button>",
|
||||
'<button class="deselect-all btn btn-default"',
|
||||
'<button class="deselect-all btn btn-secondary"',
|
||||
'data-mode="select">',
|
||||
_l("None"),
|
||||
"</button>",
|
||||
@@ -611,7 +618,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
|
||||
};
|
||||
|
||||
@@ -165,4 +165,4 @@ var JobStatesSummaryCollection = Backbone.Collection.extend({
|
||||
}
|
||||
});
|
||||
|
||||
export default { JobStatesSummary, JobStatesSummaryCollection, FETCH_STATE_ON_ADD };
|
||||
export default { JobStatesSummary, JobStatesSummaryCollection, FETCH_STATE_ON_ADD, NON_TERMINAL_STATES, ERROR_STATES };
|
||||
|
||||
@@ -1,4 +1,6 @@
|
||||
import _l from "utils/localization";
|
||||
import * as _ from "libs/underscore";
|
||||
import * as Backbone from "libs/backbone";
|
||||
import HISTORY_MODEL from "mvc/history/history-model";
|
||||
import HISTORY_VIEW_EDIT from "mvc/history/history-view-edit";
|
||||
import JOB_STATES_MODEL from "mvc/history/job-states-model";
|
||||
@@ -6,9 +8,11 @@ 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 $ */
|
||||
/* global Galaxy */
|
||||
|
||||
var logNamespace = "history";
|
||||
/* ==============================================================================
|
||||
TODO:
|
||||
@@ -54,25 +58,23 @@ var HistoryViewColumn = Backbone.View.extend(baseMVC.LoggableMixin).extend({
|
||||
|
||||
/** set up reflexive listeners */
|
||||
setUpListeners: function setUpListeners() {
|
||||
var column = this;
|
||||
//this.log( 'setUpListeners', this );
|
||||
this.once("rendered", () => {
|
||||
column.trigger("rendered:initial", column);
|
||||
this.trigger("rendered:initial", this);
|
||||
});
|
||||
this.setUpPanelListeners();
|
||||
},
|
||||
|
||||
/** set listeners needed for panel */
|
||||
setUpPanelListeners: function setUpPanelListeners() {
|
||||
var column = this;
|
||||
this.listenTo(
|
||||
this.panel,
|
||||
{
|
||||
//'all': function(){ console.info( 'panel of ' + this, arguments ); },
|
||||
|
||||
// assumes panel will take the longest to render
|
||||
rendered: function() {
|
||||
column.trigger("rendered", column);
|
||||
rendered: () => {
|
||||
this.trigger("rendered", this);
|
||||
},
|
||||
// when a panel's view expands turn off the click handler on the rerun button so that it uses it's href
|
||||
// this allows the button to open the tool rerun form in a new tab (instead of erroring)
|
||||
@@ -125,7 +127,6 @@ var HistoryViewColumn = Backbone.View.extend(baseMVC.LoggableMixin).extend({
|
||||
/** set up plugins */
|
||||
setUpBehaviors: function setUpBehaviors() {
|
||||
//this.log( 'setUpBehaviors:', this );
|
||||
//var column = this;
|
||||
// on panel size change, ...
|
||||
},
|
||||
|
||||
@@ -134,26 +135,16 @@ var HistoryViewColumn = Backbone.View.extend(baseMVC.LoggableMixin).extend({
|
||||
data = _.extend(data || {}, {
|
||||
isCurrentHistory: this.currentHistory
|
||||
});
|
||||
return $(
|
||||
[
|
||||
'<div class="panel-controls clear flex-row">',
|
||||
this.controlsLeftTemplate({
|
||||
history: data,
|
||||
view: this
|
||||
}),
|
||||
//'<button class="btn btn-default">Herp</button>',
|
||||
this.controlsRightTemplate({
|
||||
history: data,
|
||||
view: this
|
||||
}),
|
||||
"</div>",
|
||||
'<div class="inner flex-row flex-column-container">',
|
||||
'<div id="history-',
|
||||
data.id,
|
||||
'" class="history-column history-panel flex-column"></div>',
|
||||
"</div>"
|
||||
].join("")
|
||||
);
|
||||
return $(`
|
||||
<div class="panel-controls mb-1">
|
||||
<div class="flex-row flex-column-container no-gutters justify-content-between">
|
||||
${this.controlsLeftTemplate({ history: data, view: this })}
|
||||
${this.controlsRightTemplate({ history: data, view: this })}
|
||||
</div>
|
||||
</div>
|
||||
<div class="inner flex-row flex-column-container">
|
||||
<div id="history-${data.id}" class="history-column history-panel flex-column"></div>
|
||||
</div>`);
|
||||
},
|
||||
|
||||
/** render the panel contained in the column using speed for fx speed */
|
||||
@@ -175,22 +166,19 @@ var HistoryViewColumn = Backbone.View.extend(baseMVC.LoggableMixin).extend({
|
||||
},
|
||||
//TODO: remove boiler plate from next 3
|
||||
"click .delete-history": function() {
|
||||
var column = this;
|
||||
this.model._delete().done(data => {
|
||||
column.render();
|
||||
this.render();
|
||||
});
|
||||
},
|
||||
"click .undelete-history": function() {
|
||||
var column = this;
|
||||
this.model.undelete().done(data => {
|
||||
column.render();
|
||||
this.render();
|
||||
});
|
||||
},
|
||||
"click .purge-history": function() {
|
||||
if (confirm(_l("This will permanently remove the data. Are you sure?"))) {
|
||||
var column = this;
|
||||
if (window.confirm(_l("This will permanently remove the data. Are you sure?"))) {
|
||||
this.model.purge().done(data => {
|
||||
column.render();
|
||||
this.render();
|
||||
});
|
||||
}
|
||||
},
|
||||
@@ -207,57 +195,51 @@ var HistoryViewColumn = Backbone.View.extend(baseMVC.LoggableMixin).extend({
|
||||
// ------------------------------------------------------------------------ templates
|
||||
/** controls template displaying controls above the panel based on this.currentHistory */
|
||||
controlsLeftTemplate: _.template(
|
||||
[
|
||||
'<div class="pull-left">',
|
||||
"<% if( data.history.isCurrentHistory ){ %>",
|
||||
'<strong class="current-label">',
|
||||
_l("Current History"),
|
||||
"</strong>",
|
||||
"<% } else { %>",
|
||||
'<button class="switch-to btn btn-default">',
|
||||
_l("Switch to"),
|
||||
"</button>",
|
||||
"<% } %>",
|
||||
"</div>"
|
||||
].join(""),
|
||||
`
|
||||
<div class="text-left col-8">
|
||||
<% if( data.history.isCurrentHistory ){ %>
|
||||
<strong class="current-label">
|
||||
${_l("Current History")}
|
||||
</strong>
|
||||
<% } else { %>
|
||||
<button class="switch-to btn btn-secondary">
|
||||
${_l("Switch to")}
|
||||
</button>
|
||||
<% } %>
|
||||
</div>`,
|
||||
{ variable: "data" }
|
||||
),
|
||||
|
||||
/** controls template displaying controls above the panel based on this.currentHistory */
|
||||
controlsRightTemplate: _.template(
|
||||
[
|
||||
'<div class="pull-right">',
|
||||
"<% if( !data.history.purged ){ %>",
|
||||
'<div class="panel-menu btn-group">',
|
||||
'<button type="button" class="btn btn-default dropdown-toggle" data-toggle="dropdown">',
|
||||
'<span class="caret"></span>',
|
||||
"</button>",
|
||||
'<ul class="dropdown-menu pull-right" role="menu">',
|
||||
"<% if( !data.history.deleted ){ %>",
|
||||
'<li><a href="javascript:void(0);" class="copy-history">',
|
||||
_l("Copy"),
|
||||
"</a></li>",
|
||||
//'<li><a href="javascript:void(0);" class="publish-history">',
|
||||
// _l( 'Publish' ),
|
||||
//'</a></li>',
|
||||
'<li><a href="javascript:void(0);" class="delete-history">',
|
||||
_l("Delete"),
|
||||
"</a></li>",
|
||||
"<% } else /* if is deleted */ { %>",
|
||||
'<li><a href="javascript:void(0);" class="undelete-history">',
|
||||
_l("Undelete"),
|
||||
"</a></li>",
|
||||
"<% } %>",
|
||||
"<% if( data.view.purgeAllowed ){ %>",
|
||||
'<li><a href="javascript:void(0);" class="purge-history">',
|
||||
_l("Purge"),
|
||||
"</a></li>",
|
||||
"<% } %>",
|
||||
"</ul>",
|
||||
"</div>",
|
||||
"<% } %>",
|
||||
"</div>"
|
||||
].join(""),
|
||||
`<div class="text-right col-4">
|
||||
<% if( !data.history.purged ){ %>
|
||||
<div class="panel-menu btn-group">
|
||||
<button type="button" class="btn btn-secondary dropdown-toggle" data-toggle="dropdown">
|
||||
<span class="caret"></span>
|
||||
</button>
|
||||
<ul class="dropdown-menu" role="menu">
|
||||
<% if( !data.history.deleted ){ %>
|
||||
<li><a href="javascript:void(0);" class="copy-history">
|
||||
${_l("Copy")}
|
||||
</a></li>
|
||||
<li><a href="javascript:void(0);" class="delete-history">
|
||||
${_l("Delete")}
|
||||
</a></li>
|
||||
<% } else /* if is deleted */ { %>
|
||||
<li><a href="javascript:void(0);" class="undelete-history">
|
||||
${_l("Undelete")}
|
||||
</a></li>
|
||||
<% } %>
|
||||
<% if( data.view.purgeAllowed ){ %>
|
||||
<li><a href="javascript:void(0);" class="purge-history">
|
||||
${_l("Purge")}
|
||||
</a></li>
|
||||
<% } %>
|
||||
</ul>
|
||||
</div>
|
||||
<% } %>
|
||||
</div>`,
|
||||
{ variable: "data" }
|
||||
),
|
||||
|
||||
@@ -321,10 +303,9 @@ var MultiPanelColumns = Backbone.View.extend(baseMVC.LoggableMixin).extend({
|
||||
|
||||
/** Set up reflexive listeners */
|
||||
setUpListeners: function setUpListeners() {
|
||||
var multipanel = this;
|
||||
//multipanel.log( 'setUpListeners', multipanel );
|
||||
this.on("end-of-scroll", () => {
|
||||
multipanel.collection.fetchMore();
|
||||
this.collection.fetchMore();
|
||||
});
|
||||
},
|
||||
|
||||
@@ -352,10 +333,9 @@ var MultiPanelColumns = Backbone.View.extend(baseMVC.LoggableMixin).extend({
|
||||
addModels: function addModels(models, collection, options) {
|
||||
// console.log( 'addModels:', models, collection, options );
|
||||
options = options || {};
|
||||
var multipanel = this;
|
||||
models = _.isArray(models) ? models : [models];
|
||||
models.forEach(model => {
|
||||
multipanel.addColumn(model, false);
|
||||
this.addColumn(model, false);
|
||||
// if this is from a fetch, sort will be called and it will call render
|
||||
});
|
||||
return this;
|
||||
@@ -363,23 +343,22 @@ var MultiPanelColumns = Backbone.View.extend(baseMVC.LoggableMixin).extend({
|
||||
|
||||
/** Set up listeners for the collection - handling: added histories, change of current, deletion, and sorting */
|
||||
setUpCollectionListeners: function() {
|
||||
var multipanel = this;
|
||||
multipanel.listenTo(multipanel.collection, {
|
||||
this.listenTo(this.collection, {
|
||||
// handle ajax errors from the collection
|
||||
error: multipanel.errorHandler,
|
||||
error: this.errorHandler,
|
||||
// add multiple models
|
||||
add: multipanel.addModels,
|
||||
add: this.addModels,
|
||||
// when all the histories a user has have been fetched
|
||||
"all-fetched": multipanel._postFetchAll,
|
||||
"all-fetched": this._postFetchAll,
|
||||
// handle addition of histories, triggered by column copy and create new
|
||||
"new-current": multipanel.addAsCurrentColumn,
|
||||
"new-current": this.addAsCurrentColumn,
|
||||
// handle setting a history as current, triggered by history.setAsCurrent
|
||||
"set-as-current": multipanel.setCurrentHistory,
|
||||
"set-as-current": this.setCurrentHistory,
|
||||
// handle deleting a history (depends on whether panels is including deleted or not)
|
||||
"change:deleted change:purged": multipanel.handleDeletedHistory,
|
||||
"change:deleted change:purged": this.handleDeletedHistory,
|
||||
// re-render columns after a sort
|
||||
sort: function() {
|
||||
multipanel.renderColumns(0);
|
||||
this.renderColumns(0);
|
||||
}
|
||||
});
|
||||
},
|
||||
@@ -420,8 +399,7 @@ var MultiPanelColumns = Backbone.View.extend(baseMVC.LoggableMixin).extend({
|
||||
handleDeletedHistory: function handleDeletedHistory(history) {
|
||||
if (history.get("deleted") || history.get("purged")) {
|
||||
this.log("handleDeletedHistory", this.collection.includeDeleted, history);
|
||||
var multipanel = this;
|
||||
var column = multipanel.columnMap[history.id];
|
||||
var column = this.columnMap[history.id];
|
||||
if (!column) {
|
||||
return;
|
||||
}
|
||||
@@ -433,8 +411,8 @@ var MultiPanelColumns = Backbone.View.extend(baseMVC.LoggableMixin).extend({
|
||||
//multipanel.collection.create().done( function(){
|
||||
// if( !multipanel.collection.includeDeleted ){ multipanel.removeColumn( column, false ); }
|
||||
//});
|
||||
} else if (!multipanel.collection.includeDeleted) {
|
||||
multipanel.removeColumn(column);
|
||||
} else if (!this.collection.includeDeleted) {
|
||||
this.removeColumn(column);
|
||||
}
|
||||
}
|
||||
},
|
||||
@@ -485,13 +463,11 @@ var MultiPanelColumns = Backbone.View.extend(baseMVC.LoggableMixin).extend({
|
||||
/** create columns from collection */
|
||||
createColumns: function createColumns(models, columnOptions) {
|
||||
columnOptions = columnOptions || this.options.columnOptions;
|
||||
var multipanel = this;
|
||||
// clear column map
|
||||
// TODO: make cummulative
|
||||
multipanel.columnMap = {};
|
||||
multipanel.collection.each((model, i) => {
|
||||
var column = multipanel.createColumn(model, columnOptions);
|
||||
multipanel.columnMap[model.id] = column;
|
||||
this.columnMap = {};
|
||||
this.collection.each((model, i) => {
|
||||
this.columnMap[model.id] = this.createColumn(model, columnOptions);
|
||||
});
|
||||
},
|
||||
|
||||
@@ -516,7 +492,6 @@ var MultiPanelColumns = Backbone.View.extend(baseMVC.LoggableMixin).extend({
|
||||
|
||||
/** add a new column for history and render all columns if render is true */
|
||||
addColumn: function add(history, render) {
|
||||
// console.debug( 'adding column for:', history, render );
|
||||
render = render !== undefined ? render : true;
|
||||
var newColumn = this.createColumn(history);
|
||||
this.columnMap[history.id] = newColumn;
|
||||
@@ -528,13 +503,10 @@ var MultiPanelColumns = Backbone.View.extend(baseMVC.LoggableMixin).extend({
|
||||
|
||||
/** add a new column for history and make it the current history/column */
|
||||
addAsCurrentColumn: function add(history, collection, options) {
|
||||
//this.log( 'adding current column for:', history );
|
||||
var multipanel = this;
|
||||
|
||||
var newColumn = this.addColumn(history, false);
|
||||
this.setCurrentHistory(history);
|
||||
newColumn.once("rendered", () => {
|
||||
multipanel.queueHdaFetch(newColumn);
|
||||
this.queueHdaFetch(newColumn);
|
||||
});
|
||||
return newColumn;
|
||||
},
|
||||
@@ -546,47 +518,45 @@ var MultiPanelColumns = Backbone.View.extend(baseMVC.LoggableMixin).extend({
|
||||
if (!column) {
|
||||
return;
|
||||
}
|
||||
var multipanel = this;
|
||||
var widthToRemove = this.options.columnWidth + this.options.columnGap;
|
||||
column.$el.fadeOut("fast", function() {
|
||||
column.$el.fadeOut("fast", () => {
|
||||
if (render) {
|
||||
$(this).remove();
|
||||
multipanel.$(".middle").width(multipanel.$(".middle").width() - widthToRemove);
|
||||
multipanel.checkColumnsInView();
|
||||
multipanel._recalcFirstColumnHeight();
|
||||
this.$(".middle").width(this.$(".middle").width() - widthToRemove);
|
||||
this.checkColumnsInView();
|
||||
this._recalcFirstColumnHeight();
|
||||
}
|
||||
|
||||
//TODO: to freeColumn (where Columns have freePanel)
|
||||
multipanel.stopListening(column.panel);
|
||||
multipanel.stopListening(column);
|
||||
delete multipanel.columnMap[column.model.id];
|
||||
this.stopListening(column.panel);
|
||||
this.stopListening(column);
|
||||
delete this.columnMap[column.model.id];
|
||||
column.remove();
|
||||
});
|
||||
},
|
||||
|
||||
/** set up listeners for a column and it's panel - handling: hda lazy-loading, drag and drop */
|
||||
setUpColumnListeners: function setUpColumnListeners(column) {
|
||||
var multipanel = this;
|
||||
multipanel.listenTo(column, {
|
||||
this.listenTo(column, {
|
||||
//'all': function(){ console.info( 'column ' + column + ':', arguments ) },
|
||||
"in-view": multipanel.queueHdaFetch
|
||||
"in-view": this.queueHdaFetch
|
||||
});
|
||||
|
||||
multipanel.listenTo(column.panel, {
|
||||
this.listenTo(column.panel, {
|
||||
//'all': function(){ console.info( 'panel ' + column.panel + ':', arguments ) },
|
||||
|
||||
"view:draggable:dragstart": function(ev, view, panel, column) {
|
||||
multipanel._dropData = JSON.parse(ev.dataTransfer.getData("text"));
|
||||
multipanel.currentColumnDropTargetOn();
|
||||
"view:draggable:dragstart": (ev, view, panel, column) => {
|
||||
this._dropData = JSON.parse(ev.dataTransfer.getData("text"));
|
||||
this.currentColumnDropTargetOn();
|
||||
},
|
||||
"view:draggable:dragend": function(ev, view, panel, column) {
|
||||
multipanel._dropData = null;
|
||||
multipanel.currentColumnDropTargetOff();
|
||||
"view:draggable:dragend": (ev, view, panel, column) => {
|
||||
this._dropData = null;
|
||||
this.currentColumnDropTargetOff();
|
||||
},
|
||||
"droptarget:drop": function(ev, data, panel) {
|
||||
"droptarget:drop": (ev, data, panel) => {
|
||||
//note: bad copy sources fail silently
|
||||
var toCopy = multipanel._dropData.filter(json => panel.model.contents.isCopyable(json));
|
||||
multipanel._dropData = null;
|
||||
var toCopy = this._dropData.filter(json => panel.model.contents.isCopyable(json));
|
||||
this._dropData = null;
|
||||
|
||||
var queue = new ajaxQueue.NamedAjaxQueue();
|
||||
if (panel.model.contents.currentPage !== 0) {
|
||||
@@ -628,8 +598,7 @@ var MultiPanelColumns = Backbone.View.extend(baseMVC.LoggableMixin).extend({
|
||||
if (!filters || !filters.length) {
|
||||
return this.sortedColumns();
|
||||
}
|
||||
var multipanel = this;
|
||||
return multipanel.sortedColumns().filter((column, index) => {
|
||||
return this.sortedColumns().filter((column, index) => {
|
||||
var filtered = column.currentHistory || _.every(filters.map(filter => filter.call(column)));
|
||||
return filtered;
|
||||
});
|
||||
@@ -637,8 +606,7 @@ var MultiPanelColumns = Backbone.View.extend(baseMVC.LoggableMixin).extend({
|
||||
|
||||
/** return array of Columns sorted to match the collection */
|
||||
sortedColumns: function() {
|
||||
var multipanel = this;
|
||||
var sorted = this.collection.map((history, index) => multipanel.columnMap[history.id]);
|
||||
var sorted = this.collection.map((history, index) => this.columnMap[history.id]);
|
||||
return sorted;
|
||||
},
|
||||
|
||||
@@ -646,18 +614,17 @@ var MultiPanelColumns = Backbone.View.extend(baseMVC.LoggableMixin).extend({
|
||||
/** Render this view, columns, and set up view plugins */
|
||||
render: function render(speed) {
|
||||
speed = speed !== undefined ? speed : this.fxSpeed;
|
||||
var multipanel = this;
|
||||
|
||||
multipanel.log(`${multipanel}.render`);
|
||||
multipanel.$el.html(multipanel.mainTemplate(multipanel));
|
||||
multipanel.renderColumns(speed);
|
||||
this.log(`${this}.render`);
|
||||
this.$el.html(this.mainTemplate);
|
||||
this.renderColumns(speed);
|
||||
|
||||
// set the columns to full height allowed and set up behaviors for thie multipanel
|
||||
multipanel.setUpBehaviors();
|
||||
this.setUpBehaviors();
|
||||
//TODO: wrong - has to wait for columns to render
|
||||
// - create a column listener that fires this when all columns are rendered
|
||||
multipanel.trigger("rendered", multipanel);
|
||||
return multipanel;
|
||||
this.trigger("rendered", this);
|
||||
return this;
|
||||
},
|
||||
|
||||
/** Render the columns and panels */
|
||||
@@ -665,24 +632,22 @@ var MultiPanelColumns = Backbone.View.extend(baseMVC.LoggableMixin).extend({
|
||||
speed = _.isNumber(speed) ? speed : this.fxSpeed;
|
||||
// console.log( 'renderColumns:', speed );
|
||||
// render columns and track the total number rendered, firing an event when all are rendered
|
||||
var self = this;
|
||||
var sortedAndFiltered = self.sortedFilteredColumns();
|
||||
var sortedAndFiltered = this.sortedFilteredColumns();
|
||||
// console.log( '\t sortedAndFiltered:', sortedAndFiltered );
|
||||
var $middle = self.$(".middle").empty();
|
||||
var $middle = this.$(".middle").empty();
|
||||
|
||||
self._addColumns(sortedAndFiltered, speed);
|
||||
if (!self.collection.allFetched) {
|
||||
$middle.append(self.loadingIndicatorTemplate(self));
|
||||
this._addColumns(sortedAndFiltered, speed);
|
||||
if (!this.collection.allFetched) {
|
||||
$middle.append(this.loadingIndicatorTemplate);
|
||||
}
|
||||
//TODO: sorta - at least their fx queue has started the re-rendering
|
||||
self.trigger("columns-rendered", sortedAndFiltered, self);
|
||||
this.trigger("columns-rendered", sortedAndFiltered, this);
|
||||
|
||||
if (self.datasetSearch && sortedAndFiltered.length <= 1) {
|
||||
} else {
|
||||
if (!this.datasetSearch || sortedAndFiltered.length > 1) {
|
||||
// check for in-view, hda lazy-loading if so
|
||||
self.checkColumnsInView();
|
||||
this.checkColumnsInView();
|
||||
// the first, current column has position: fixed and flex css will not apply - adjust height manually
|
||||
self._recalcFirstColumnHeight();
|
||||
this._recalcFirstColumnHeight();
|
||||
}
|
||||
return sortedAndFiltered;
|
||||
},
|
||||
@@ -817,8 +782,7 @@ var MultiPanelColumns = Backbone.View.extend(baseMVC.LoggableMixin).extend({
|
||||
},
|
||||
toggleDeletedDatasets: function(show) {
|
||||
show = show !== undefined ? show : false;
|
||||
var multipanel = this;
|
||||
multipanel.sortedFilteredColumns().forEach((column, i) => {
|
||||
this.sortedFilteredColumns().forEach((column, i) => {
|
||||
_.delay(() => {
|
||||
column.panel.toggleShowDeleted(show, false);
|
||||
}, i * 200);
|
||||
@@ -831,8 +795,7 @@ var MultiPanelColumns = Backbone.View.extend(baseMVC.LoggableMixin).extend({
|
||||
},
|
||||
toggleHiddenDatasets: function(show) {
|
||||
show = show !== undefined ? show : false;
|
||||
var multipanel = this;
|
||||
multipanel.sortedFilteredColumns().forEach((column, i) => {
|
||||
this.sortedFilteredColumns().forEach((column, i) => {
|
||||
_.delay(() => {
|
||||
column.panel.toggleShowHidden(show, false);
|
||||
}, i * 200);
|
||||
@@ -841,12 +804,11 @@ var MultiPanelColumns = Backbone.View.extend(baseMVC.LoggableMixin).extend({
|
||||
|
||||
/** change the collection order and re-fetch when the drop down in the options menu is changed */
|
||||
_chooseOrder: function(ev) {
|
||||
var multipanel = this;
|
||||
var collection = multipanel.collection;
|
||||
var collection = this.collection;
|
||||
var orderKey = $(ev.currentTarget).data("order");
|
||||
// set the sort order text also
|
||||
multipanel.$(".current-order").text(multipanel.orderDescriptions[orderKey]);
|
||||
multipanel.toggleOptionsPopover();
|
||||
this.$(".current-order").text(this.orderDescriptions[orderKey]);
|
||||
this.toggleOptionsPopover();
|
||||
// set the order and re-fetch using the new order, saving the current history as the first
|
||||
collection.setOrder(orderKey);
|
||||
var currentHistoryModel = collection.slice(0, 1);
|
||||
@@ -854,12 +816,12 @@ var MultiPanelColumns = Backbone.View.extend(baseMVC.LoggableMixin).extend({
|
||||
collection.unshift(currentHistoryModel, {
|
||||
silent: true
|
||||
});
|
||||
multipanel.createColumns();
|
||||
this.createColumns();
|
||||
// need to clear this or previously fetched contents won't refetch now (bc of named queue)
|
||||
multipanel.hdaQueue.clear();
|
||||
multipanel.render();
|
||||
this.hdaQueue.clear();
|
||||
this.render();
|
||||
});
|
||||
multipanel.once("columns-rendered", multipanel._scrollLeft);
|
||||
this.once("columns-rendered", this._scrollLeft);
|
||||
//TODO: check allFetched and do not reset if so - just sort instead
|
||||
},
|
||||
|
||||
@@ -871,70 +833,69 @@ var MultiPanelColumns = Backbone.View.extend(baseMVC.LoggableMixin).extend({
|
||||
|
||||
/** Set up any view plugins */
|
||||
setUpBehaviors: function() {
|
||||
var multipanel = this;
|
||||
multipanel._moreOptionsPopover();
|
||||
this._moreOptionsPopover();
|
||||
|
||||
// input to search histories
|
||||
multipanel.$("#search-histories").searchInput({
|
||||
this.$("#search-histories").searchInput({
|
||||
name: "search-histories",
|
||||
placeholder: _l("search histories"),
|
||||
|
||||
onfirstsearch: function(searchFor) {
|
||||
multipanel.$("#search-histories").searchInput("toggle-loading");
|
||||
multipanel.renderInfo(_l("loading all histories for search"));
|
||||
multipanel.collection.fetchAll().done(() => {
|
||||
multipanel.$("#search-histories").searchInput("toggle-loading");
|
||||
multipanel.renderInfo("");
|
||||
onfirstsearch: searchFor => {
|
||||
this.$("#search-histories").searchInput("toggle-loading");
|
||||
this.renderInfo(_l("loading all histories for search"));
|
||||
this.collection.fetchAll().done(() => {
|
||||
this.$("#search-histories").searchInput("toggle-loading");
|
||||
this.renderInfo("");
|
||||
});
|
||||
},
|
||||
onsearch: function(searchFor) {
|
||||
multipanel.historySearch = searchFor;
|
||||
multipanel.filters = [
|
||||
function() {
|
||||
return this.model.matchesAll(multipanel.historySearch);
|
||||
onsearch: searchFor => {
|
||||
this.historySearch = searchFor;
|
||||
this.filters = [
|
||||
() => {
|
||||
return this.model.matchesAll(this.historySearch);
|
||||
}
|
||||
];
|
||||
multipanel.renderColumns(0);
|
||||
this.renderColumns(0);
|
||||
},
|
||||
onclear: function(searchFor) {
|
||||
multipanel.historySearch = null;
|
||||
onclear: searchFor => {
|
||||
this.historySearch = null;
|
||||
//TODO: remove specifically not just reset
|
||||
multipanel.filters = [];
|
||||
multipanel.renderColumns(0);
|
||||
this.filters = [];
|
||||
this.renderColumns(0);
|
||||
}
|
||||
});
|
||||
|
||||
// input to search datasets
|
||||
multipanel.$("#search-datasets").searchInput({
|
||||
this.$("#search-datasets").searchInput({
|
||||
name: "search-datasets",
|
||||
placeholder: _l("search all datasets"),
|
||||
|
||||
onfirstsearch: function(searchFor) {
|
||||
multipanel.hdaQueue.clear();
|
||||
multipanel.$("#search-datasets").searchInput("toggle-loading");
|
||||
multipanel.datasetSearch = searchFor;
|
||||
multipanel.sortedFilteredColumns().forEach(column => {
|
||||
onfirstsearch: searchFor => {
|
||||
this.hdaQueue.clear();
|
||||
this.$("#search-datasets").searchInput("toggle-loading");
|
||||
this.datasetSearch = searchFor;
|
||||
this.sortedFilteredColumns().forEach(column => {
|
||||
column.panel.searchItems(searchFor);
|
||||
// load details for them that need
|
||||
multipanel.queueHdaFetchDetails(column);
|
||||
this.queueHdaFetchDetails(column);
|
||||
});
|
||||
multipanel.hdaQueue.progress(progress => {
|
||||
multipanel.renderInfo([_l("searching"), progress.curr + 1, _l("of"), progress.total].join(" "));
|
||||
this.hdaQueue.progress(progress => {
|
||||
this.renderInfo([_l("searching"), progress.curr + 1, _l("of"), progress.total].join(" "));
|
||||
});
|
||||
multipanel.hdaQueue.deferred.done(() => {
|
||||
multipanel.renderInfo("");
|
||||
multipanel.$("#search-datasets").searchInput("toggle-loading");
|
||||
this.hdaQueue.deferred.done(() => {
|
||||
this.renderInfo("");
|
||||
this.$("#search-datasets").searchInput("toggle-loading");
|
||||
});
|
||||
},
|
||||
onsearch: function(searchFor) {
|
||||
multipanel.datasetSearch = searchFor;
|
||||
multipanel.sortedFilteredColumns().forEach(column => {
|
||||
onsearch: searchFor => {
|
||||
this.datasetSearch = searchFor;
|
||||
this.sortedFilteredColumns().forEach(column => {
|
||||
column.panel.searchItems(searchFor);
|
||||
});
|
||||
},
|
||||
onclear: function(searchFor) {
|
||||
multipanel.datasetSearch = null;
|
||||
multipanel.sortedFilteredColumns().forEach(column => {
|
||||
onclear: searchFor => {
|
||||
this.datasetSearch = null;
|
||||
this.sortedFilteredColumns().forEach(column => {
|
||||
column.panel.clearSearch();
|
||||
});
|
||||
}
|
||||
@@ -942,15 +903,15 @@ var MultiPanelColumns = Backbone.View.extend(baseMVC.LoggableMixin).extend({
|
||||
|
||||
// resize first (fixed position) column on page resize
|
||||
$(window).resize(() => {
|
||||
multipanel._recalcFirstColumnHeight();
|
||||
this._recalcFirstColumnHeight();
|
||||
});
|
||||
|
||||
// when scrolling - check for histories now in view: they will fire 'in-view' and queueHdaLoading if necc.
|
||||
//TODO:?? might be able to simplify and not use pub-sub
|
||||
var debouncedInView = _.debounce(function _debouncedInner() {
|
||||
var viewport = multipanel._viewport();
|
||||
multipanel.checkColumnsInView(viewport);
|
||||
multipanel.checkForEndOfScroll(viewport);
|
||||
var debouncedInView = _.debounce(() => {
|
||||
var viewport = this._viewport();
|
||||
this.checkColumnsInView(viewport);
|
||||
this.checkForEndOfScroll(viewport);
|
||||
}, 100);
|
||||
this.$(".middle")
|
||||
.parent()
|
||||
@@ -1053,50 +1014,35 @@ var MultiPanelColumns = Backbone.View.extend(baseMVC.LoggableMixin).extend({
|
||||
},
|
||||
|
||||
// ------------------------------------------------------------------------ templates
|
||||
mainTemplate: _.template(
|
||||
[
|
||||
'<div class="header flex-column-container">',
|
||||
'<div class="control-column control-column-left flex-column">',
|
||||
'<button class="done btn btn-default" tabindex="1">',
|
||||
_l("Done"),
|
||||
"</button>",
|
||||
'<div id="search-histories" class="search-control"></div>',
|
||||
'<div id="search-datasets" class="search-control"></div>',
|
||||
'<a class="open-more-options btn btn-default" tabindex="3">',
|
||||
'<span class="fa fa-ellipsis-h"></span>',
|
||||
"</a>",
|
||||
"</div>",
|
||||
// feedback
|
||||
'<div class="control-column control-column-center flex-column">',
|
||||
'<div class="header-info">',
|
||||
"</div>",
|
||||
"</div>",
|
||||
'<div class="control-column control-column-right flex-column">',
|
||||
'<button class="create-new btn btn-default" tabindex="4">',
|
||||
_l("Create new"),
|
||||
"</button> ",
|
||||
"</div>",
|
||||
"</div>",
|
||||
// middle - where the columns go
|
||||
'<div class="outer-middle flex-row flex-row-container">',
|
||||
'<div class="middle flex-column-container flex-row"></div>',
|
||||
"</div>",
|
||||
// footer
|
||||
'<div class="footer flex-column-container"></div>'
|
||||
].join(""),
|
||||
{ variable: "view" }
|
||||
),
|
||||
mainTemplate: `
|
||||
<div class="header flex-column-container">
|
||||
<div class="control-column control-column-left flex-column">
|
||||
<div id="search-histories" class="search-control"></div>
|
||||
<div id="search-datasets" class="search-control"></div>
|
||||
<a class="open-more-options btn btn-secondary" tabindex="3">
|
||||
<span class="fa fa-ellipsis-h"></span>
|
||||
</a>
|
||||
</div>
|
||||
<div class="control-column control-column-center flex-column">
|
||||
<div class="header-info">
|
||||
</div>
|
||||
</div>
|
||||
<div class="control-column control-column-right flex-column">
|
||||
<button class="create-new btn btn-secondary" tabindex="4">
|
||||
${_l("Create new")}
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
<div class="outer-middle flex-row flex-row-container">
|
||||
<div class="middle flex-column-container flex-row"></div>
|
||||
</div>
|
||||
<div class="footer flex-column-container"></div>`,
|
||||
|
||||
loadingIndicatorTemplate: _.template(
|
||||
[
|
||||
'<div class="histories-loading-indicator">',
|
||||
'<span class="fa fa-spin fa-spinner"></span>',
|
||||
_l("Loading histories"),
|
||||
"...",
|
||||
"</div>"
|
||||
].join(""),
|
||||
{ variable: "view" }
|
||||
),
|
||||
loadingIndicatorTemplate: `
|
||||
<div class="histories-loading-indicator">
|
||||
<span class="fa fa-spin fa-spinner"></span>
|
||||
${_l("Loading histories")} ...
|
||||
</div>`,
|
||||
|
||||
orderDescriptions: {
|
||||
update_time: _l("most recent first"),
|
||||
@@ -1109,16 +1055,16 @@ var MultiPanelColumns = Backbone.View.extend(baseMVC.LoggableMixin).extend({
|
||||
|
||||
optionsPopoverTemplate: _.template(
|
||||
[
|
||||
'<div class="more-options">',
|
||||
'<div class="order btn-group">',
|
||||
'<button type="button" class="btn btn-default dropdown-toggle" data-toggle="dropdown">',
|
||||
'<div class="more-options d-flex flex-column">',
|
||||
'<div class="order btn-group mb-2">',
|
||||
'<button type="button" class="btn btn-secondary dropdown-toggle" data-toggle="dropdown">',
|
||||
`${_l("Order histories by")} `,
|
||||
'<span class="current-order"><%- view.orderDescriptions[ view.collection.order ] %></span> ',
|
||||
'<span class="caret"></span>',
|
||||
"</button>",
|
||||
'<ul class="dropdown-menu" role="menu">',
|
||||
"<% _.each( view.orderDescriptions, function( text, order ){ %>",
|
||||
'<li><a href="javascript:void(0);" class="set-order" data-order="<%- order %>">',
|
||||
'<li class="dropdown-item"><a href="javascript:void(0);" class="set-order" data-order="<%- order %>">',
|
||||
"<%- text %>",
|
||||
"</a></li>",
|
||||
"<% }); %>",
|
||||
@@ -1130,8 +1076,6 @@ var MultiPanelColumns = Backbone.View.extend(baseMVC.LoggableMixin).extend({
|
||||
_l("Include deleted histories"),
|
||||
"</label></div>",
|
||||
|
||||
"<hr />",
|
||||
|
||||
'<div class="checkbox"><label><input id="toggle-deleted" type="checkbox">',
|
||||
_l("Include deleted datasets"),
|
||||
"</label></div>",
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
};
|
||||
|
||||
@@ -99,7 +99,7 @@ var LibraryDatasetView = Backbone.View.extend({
|
||||
var template = this.templateDataset();
|
||||
this.$el.html(template({ item: this.model }));
|
||||
$(".peek").html(this.model.get("peek"));
|
||||
$("#center [data-toggle]").tooltip();
|
||||
$('#center [data-toggle="tooltip"]').tooltip({ trigger: "hover" });
|
||||
},
|
||||
|
||||
fetchVersion: function(options) {
|
||||
@@ -144,7 +144,7 @@ var LibraryDatasetView = Backbone.View.extend({
|
||||
file_ext: this.model.get("file_ext")
|
||||
});
|
||||
$(".peek").html(this.model.get("peek"));
|
||||
$("#center [data-toggle]").tooltip();
|
||||
$('#center [data-toggle="tooltip"]').tooltip({ trigger: "hover" });
|
||||
},
|
||||
|
||||
downloadDataset: function() {
|
||||
@@ -302,7 +302,7 @@ var LibraryDatasetView = Backbone.View.extend({
|
||||
.fail(() => {
|
||||
mod_toastr.error("An error occurred while attempting to fetch dataset permissions.");
|
||||
});
|
||||
$("#center [data-toggle]").tooltip();
|
||||
$('#center [data-toggle="tooltip"]').tooltip({ trigger: "hover" });
|
||||
$("#center").css("overflow", "auto");
|
||||
},
|
||||
|
||||
@@ -594,24 +594,24 @@ var LibraryDatasetView = Backbone.View.extend({
|
||||
[
|
||||
// CONTAINER START
|
||||
'<div class="library_style_container">',
|
||||
'<div id="library_toolbar">',
|
||||
'<button data-toggle="tooltip" data-placement="top" title="Download dataset" class="btn btn-default toolbtn-download-dataset primary-button toolbar-item" type="button">',
|
||||
'<div class="d-flex mb-2">',
|
||||
'<button data-toggle="tooltip" data-placement="top" title="Download dataset" class="btn btn-secondary toolbtn-download-dataset toolbar-item mr-1" type="button">',
|
||||
'<span class="fa fa-download"></span>',
|
||||
" Download",
|
||||
"</button>",
|
||||
'<button data-toggle="tooltip" data-placement="top" title="Import dataset into history" class="btn btn-default toolbtn-import-dataset primary-button toolbar-item" type="button">',
|
||||
'<button data-toggle="tooltip" data-placement="top" title="Import dataset into history" class="btn btn-secondary toolbtn-import-dataset toolbar-item mr-1" type="button">',
|
||||
'<span class="fa fa-book"></span>',
|
||||
" to History",
|
||||
"</button>",
|
||||
'<% if (item.get("can_user_modify")) { %>',
|
||||
'<button data-toggle="tooltip" data-placement="top" title="Modify library item" class="btn btn-default toolbtn_modify_dataset primary-button toolbar-item" type="button">',
|
||||
'<button data-toggle="tooltip" data-placement="top" title="Modify library item" class="btn btn-secondary toolbtn_modify_dataset toolbar-item mr-1" type="button">',
|
||||
'<span class="fa fa-pencil"></span>',
|
||||
" Modify",
|
||||
"</button>",
|
||||
"<% } %>",
|
||||
'<% if (item.get("can_user_manage")) { %>',
|
||||
'<a href="#folders/<%- item.get("folder_id") %>/datasets/<%- item.id %>/permissions">',
|
||||
'<button data-toggle="tooltip" data-placement="top" title="Manage permissions" class="btn btn-default toolbtn_change_permissions primary-button toolbar-item" type="button">',
|
||||
'<button data-toggle="tooltip" data-placement="top" title="Manage permissions" class="btn btn-secondary toolbtn_change_permissions toolbar-item mr-1" type="button">',
|
||||
'<span class="fa fa-group"></span>',
|
||||
" Permissions",
|
||||
"</button>",
|
||||
@@ -621,12 +621,12 @@ var LibraryDatasetView = Backbone.View.extend({
|
||||
|
||||
// BREADCRUMBS
|
||||
'<ol class="breadcrumb">',
|
||||
'<li><a title="Return to the list of libraries" href="#">Libraries</a></li>',
|
||||
'<li class="breadcrumb-item"><a title="Return to the list of libraries" href="#">Libraries</a></li>',
|
||||
'<% _.each(item.get("full_path"), function(path_item) { %>',
|
||||
"<% if (path_item[0] != item.id) { %>",
|
||||
'<li><a title="Return to this folder" href="#/folders/<%- path_item[0] %>"><%- path_item[1] %></a> </li> ',
|
||||
'<li class="breadcrumb-item"><a title="Return to this folder" href="#/folders/<%- path_item[0] %>"><%- path_item[1] %></a> </li> ',
|
||||
"<% } else { %>",
|
||||
'<li class="active"><span title="You are here"><%- path_item[1] %></span></li>',
|
||||
'<li class="breadcrumb-item active"><span title="You are here"><%- path_item[1] %></span></li>',
|
||||
"<% } %>",
|
||||
"<% }); %>",
|
||||
"</ol>",
|
||||
@@ -639,7 +639,7 @@ var LibraryDatasetView = Backbone.View.extend({
|
||||
|
||||
// TABLE START
|
||||
'<div class="dataset_table">',
|
||||
'<table class="grid table table-striped table-condensed">',
|
||||
'<table class="grid table table-striped table-sm">',
|
||||
"<tr>",
|
||||
'<th class="dataset-first-column" scope="row" id="id_row" data-id="<%= _.escape(item.get("ldda_id")) %>">Name</th>',
|
||||
'<td><%= _.escape(item.get("name")) %></td>',
|
||||
@@ -778,9 +778,9 @@ var LibraryDatasetView = Backbone.View.extend({
|
||||
[
|
||||
// CONTAINER START
|
||||
'<div class="library_style_container">',
|
||||
'<div id="library_toolbar">',
|
||||
'<div class="d-flex mb-2">',
|
||||
'<a href="#folders/<%- item.get("folder_id") %>/datasets/<%- item.id %>">',
|
||||
'<button data-toggle="tooltip" data-placement="top" title="Go to latest dataset" class="btn btn-default primary-button toolbar-item" type="button">',
|
||||
'<button data-toggle="tooltip" data-placement="top" title="Go to latest dataset" class="btn btn-secondary toolbar-item mr-1" type="button">',
|
||||
'<span class="fa fa-caret-left fa-lg"></span>',
|
||||
" Latest dataset",
|
||||
"</button>",
|
||||
@@ -789,12 +789,12 @@ var LibraryDatasetView = Backbone.View.extend({
|
||||
|
||||
// BREADCRUMBS
|
||||
'<ol class="breadcrumb">',
|
||||
'<li><a title="Return to the list of libraries" href="#">Libraries</a></li>',
|
||||
'<li class="breadcrumb-item"><a title="Return to the list of libraries" href="#">Libraries</a></li>',
|
||||
'<% _.each(item.get("full_path"), function(path_item) { %>',
|
||||
"<% if (path_item[0] != item.id) { %>",
|
||||
'<li><a title="Return to this folder" href="#/folders/<%- path_item[0] %>"><%- path_item[1] %></a> </li> ',
|
||||
'<li class="breadcrumb-item"><a title="Return to this folder" href="#/folders/<%- path_item[0] %>"><%- path_item[1] %></a> </li> ',
|
||||
"<% } else { %>",
|
||||
'<li class="active"><span title="You are here"><%- path_item[1] %></span></li>',
|
||||
'<li class="breadcrumb-item active"><span title="You are here"><%- path_item[1] %></span></li>',
|
||||
"<% } %>",
|
||||
"<% }); %>",
|
||||
"</ol>",
|
||||
@@ -802,7 +802,7 @@ var LibraryDatasetView = Backbone.View.extend({
|
||||
'<div class="alert alert-warning">This is an expired version of the library dataset: <%= _.escape(item.get("name")) %></div>',
|
||||
// DATASET START
|
||||
'<div class="dataset_table">',
|
||||
'<table class="grid table table-striped table-condensed">',
|
||||
'<table class="grid table table-striped table-sm">',
|
||||
"<tr>",
|
||||
'<th scope="row" id="id_row" data-id="<%= _.escape(ldda.id) %>">Name</th>',
|
||||
'<td><%= _.escape(ldda.get("name")) %></td>',
|
||||
@@ -903,31 +903,21 @@ var LibraryDatasetView = Backbone.View.extend({
|
||||
[
|
||||
// CONTAINER START
|
||||
'<div class="library_style_container">',
|
||||
'<div id="library_toolbar">',
|
||||
'<button data-toggle="tooltip" data-placement="top" title="Cancel modifications" class="btn btn-default toolbtn_cancel_modifications primary-button toolbar-item" type="button">',
|
||||
'<span class="fa fa-times"></span>',
|
||||
" Cancel",
|
||||
"</button>",
|
||||
'<button data-toggle="tooltip" data-placement="top" title="Save modifications" class="btn btn-default toolbtn_save_modifications primary-button toolbar-item" type="button">',
|
||||
'<span class="fa fa-floppy-o"></span>',
|
||||
" Save",
|
||||
"</button>",
|
||||
"</div>",
|
||||
|
||||
// BREADCRUMBS
|
||||
'<ol class="breadcrumb">',
|
||||
'<li><a title="Return to the list of libraries" href="#">Libraries</a></li>',
|
||||
'<li class="breadcrumb-item"><a title="Return to the list of libraries" href="#">Libraries</a></li>',
|
||||
'<% _.each(item.get("full_path"), function(path_item) { %>',
|
||||
"<% if (path_item[0] != item.id) { %>",
|
||||
'<li><a title="Return to this folder" href="#/folders/<%- path_item[0] %>"><%- path_item[1] %></a> </li> ',
|
||||
'<li class="breadcrumb-item"><a title="Return to this folder" href="#/folders/<%- path_item[0] %>"><%- path_item[1] %></a> </li> ',
|
||||
"<% } else { %>",
|
||||
'<li class="active"><span title="You are here"><%- path_item[1] %></span></li>',
|
||||
'<li class="breadcrumb-item active"><span title="You are here"><%- path_item[1] %></span></li>',
|
||||
"<% } %>",
|
||||
"<% }); %>",
|
||||
"</ol>",
|
||||
|
||||
'<div class="dataset_table">',
|
||||
'<table class="grid table table-striped table-condensed">',
|
||||
'<table class="grid table table-striped table-sm">',
|
||||
"<tr>",
|
||||
'<th class="dataset-first-column" scope="row" id="id_row" data-id="<%= _.escape(item.get("ldda_id")) %>">Name</th>',
|
||||
'<td><input class="input_dataset_name form-control" type="text" placeholder="name" value="<%= _.escape(item.get("name")) %>"></td>',
|
||||
@@ -1000,6 +990,18 @@ var LibraryDatasetView = Backbone.View.extend({
|
||||
"</pre>",
|
||||
"</div>",
|
||||
"</div>",
|
||||
|
||||
'<div class="d-flex">',
|
||||
'<button data-toggle="tooltip" data-placement="top" title="Cancel modifications" class="btn btn-secondary toolbtn_cancel_modifications toolbar-item mr-1" type="button">',
|
||||
'<span class="fa fa-times"></span>',
|
||||
" Cancel",
|
||||
"</button>",
|
||||
'<button data-toggle="tooltip" data-placement="top" title="Save modifications" class="btn btn-secondary toolbtn_save_modifications toolbar-item mr-1" type="button">',
|
||||
'<span class="fa fa-floppy-o"></span>',
|
||||
" Save",
|
||||
"</button>",
|
||||
"</div>",
|
||||
|
||||
// CONTAINER END
|
||||
"</div>"
|
||||
].join("")
|
||||
@@ -1011,9 +1013,9 @@ var LibraryDatasetView = Backbone.View.extend({
|
||||
[
|
||||
// CONTAINER START
|
||||
'<div class="library_style_container">',
|
||||
'<div id="library_toolbar">',
|
||||
'<div class="d-flex mb-2">',
|
||||
'<a href="#folders/<%- item.get("folder_id") %>/datasets/<%- item.id %>">',
|
||||
'<button data-toggle="tooltip" data-placement="top" title="Go back to dataset" class="btn btn-default primary-button toolbar-item" type="button">',
|
||||
'<button data-toggle="tooltip" data-placement="top" title="Go back to dataset" class="btn btn-secondary toolbar-item mr-1" type="button">',
|
||||
'<span class="fa fa-file-o"></span>',
|
||||
" Dataset Details",
|
||||
"</button>",
|
||||
@@ -1022,12 +1024,12 @@ var LibraryDatasetView = Backbone.View.extend({
|
||||
|
||||
// BREADCRUMBS
|
||||
'<ol class="breadcrumb">',
|
||||
'<li><a title="Return to the list of libraries" href="#">Libraries</a></li>',
|
||||
'<li class="breadcrumb-item"><a title="Return to the list of libraries" href="#">Libraries</a></li>',
|
||||
'<% _.each(item.get("full_path"), function(path_item) { %>',
|
||||
"<% if (path_item[0] != item.id) { %>",
|
||||
'<li><a title="Return to this folder" href="#/folders/<%- path_item[0] %>"><%- path_item[1] %></a> </li> ',
|
||||
'<li class="breadcrumb-item"><a title="Return to this folder" href="#/folders/<%- path_item[0] %>"><%- path_item[1] %></a> </li> ',
|
||||
"<% } else { %>",
|
||||
'<li class="active"><span title="You are here"><%- path_item[1] %></span></li>',
|
||||
'<li class="breadcrumb-item active"><span title="You are here"><%- path_item[1] %></span></li>',
|
||||
"<% } %>",
|
||||
"<% }); %>",
|
||||
"</ol>",
|
||||
@@ -1065,7 +1067,7 @@ var LibraryDatasetView = Backbone.View.extend({
|
||||
'<div class="alert alert-info roles-selection">',
|
||||
"User with <strong>any</strong> of these roles can manage permissions of this dataset. If you remove yourself you will lose the ability manage this dataset unless you are an admin.",
|
||||
"</div>",
|
||||
'<button data-toggle="tooltip" data-placement="top" title="Save modifications made on this page" class="btn btn-default toolbtn_save_permissions primary-button" type="button">',
|
||||
'<button data-toggle="tooltip" data-placement="top" title="Save modifications made on this page" class="btn btn-secondary toolbtn_save_permissions type="button">',
|
||||
'<span class="fa fa-floppy-o"></span>',
|
||||
" Save",
|
||||
"</button>",
|
||||
|
||||
@@ -71,7 +71,7 @@ var FolderView = Backbone.View.extend({
|
||||
mod_toastr.error("An error occurred while attempting to fetch folder permissions.");
|
||||
});
|
||||
|
||||
$("#center [data-toggle]").tooltip();
|
||||
$('#center [data-toggle="tooltip"]').tooltip({ trigger: "hover" });
|
||||
//hack to show scrollbars
|
||||
$("#center").css("overflow", "auto");
|
||||
},
|
||||
@@ -196,9 +196,9 @@ var FolderView = Backbone.View.extend({
|
||||
return _.template(
|
||||
[
|
||||
'<div class="library_style_container">',
|
||||
'<div id="library_toolbar">',
|
||||
"<div>",
|
||||
'<a href="#/folders/<%= folder.get("parent_id") %>">',
|
||||
'<button data-toggle="tooltip" data-placement="top" title="Go back to the parent folder" class="btn btn-default primary-button" type="button">',
|
||||
'<button data-toggle="tooltip" data-placement="top" title="Go back to the parent folder" class="btn btn-secondary primary-button" type="button">',
|
||||
'<span class="fa fa-caret-left fa-lg"/>',
|
||||
" Parent folder",
|
||||
"</button>",
|
||||
@@ -237,7 +237,7 @@ var FolderView = Backbone.View.extend({
|
||||
'<div class="alert alert-info roles-selection">',
|
||||
"User with <strong>any</strong> of these roles can modify this folder (name, etc.).",
|
||||
"</div>",
|
||||
'<button data-toggle="tooltip" data-placement="top" title="Save modifications" class="btn btn-default toolbtn_save_permissions primary-button" type="button">',
|
||||
'<button data-toggle="tooltip" data-placement="top" title="Save modifications" class="btn btn-secondary toolbtn_save_permissions primary-button" type="button">',
|
||||
'<span class="fa fa-floppy-o"/>',
|
||||
" Save",
|
||||
"</button>",
|
||||
|
||||
@@ -132,7 +132,7 @@ var FolderListView = Backbone.View.extend({
|
||||
}
|
||||
this.paginate();
|
||||
}
|
||||
$("#center [data-toggle]").tooltip();
|
||||
$('#center [data-toggle="tooltip"]').tooltip({ trigger: "hover" });
|
||||
$("#center").css("overflow", "auto");
|
||||
},
|
||||
|
||||
@@ -181,7 +181,7 @@ var FolderListView = Backbone.View.extend({
|
||||
current_sort_order: false
|
||||
});
|
||||
});
|
||||
$("#center [data-toggle]").tooltip();
|
||||
$('#center [data-toggle="tooltip"]').tooltip({ trigger: "hover" });
|
||||
this.checkEmptiness();
|
||||
this.postRender();
|
||||
},
|
||||
@@ -367,23 +367,22 @@ var FolderListView = Backbone.View.extend({
|
||||
templateFolder: function() {
|
||||
return _.template(
|
||||
[
|
||||
// BREADCRUMBS
|
||||
'<ol class="breadcrumb">',
|
||||
'<li><a title="Return to the list of libraries" href="#">Libraries</a></li>',
|
||||
'<li class="breadcrumb-item"><a title="Return to the list of libraries" href="#">Libraries</a></li>',
|
||||
"<% _.each(path, function(path_item) { %>",
|
||||
"<% if (path_item[0] != id) { %>",
|
||||
'<li><a title="Return to this folder" href="#/folders/<%- path_item[0] %>"><%- path_item[1] %></a> </li> ',
|
||||
'<li class="breadcrumb-item"><a title="Return to this folder" href="#/folders/<%- path_item[0] %>"><%- path_item[1] %></a> </li> ',
|
||||
"<% } else { %>",
|
||||
'<li class="active"><span title="You are in this folder"><%- path_item[1] %></span></li>',
|
||||
'<li class="breadcrumb-item active"><span title="You are in this folder"><%- path_item[1] %></span></li>',
|
||||
"<% } %>",
|
||||
"<% }); %>",
|
||||
"</ol>",
|
||||
|
||||
// FOLDER CONTENT
|
||||
'<table data-library-id="<%- parent_library_id %>" class="grid table table-condensed">',
|
||||
'<table data-library-id="<%- parent_library_id %>" class="grid table table-sm">',
|
||||
"<thead>",
|
||||
'<th class="button_heading"></th>',
|
||||
'<th style="text-align: center; width: 20px; " title="Check to select all datasets"><input id="select-all-checkboxes" style="margin: 0;" type="checkbox"></th>',
|
||||
'<th class="mid" style="width: 20px; " title="Check to select all datasets"><input id="select-all-checkboxes" style="margin: 0;" type="checkbox"></th>',
|
||||
'<th><a class="sort-folder-name" title="Click to reverse order" href="#">name</a> <span title="Sorted alphabetically" class="sort-icon-name fa fa-sort-alpha-<%- order %>"></span></th>',
|
||||
'<th style="width:20%;"><a class="sort-folder-description" title="Click to reverse order" href="#">description</a> <span title="Sorted alphabetically" class="sort-icon-description fa"></span></th>',
|
||||
'<th style="width:5%;"><a class="sort-folder-file_ext" title="Click to reverse order" href="#">data type</a> <span title="Sorted alphabetically" class="sort-icon-file_ext fa"></span></th>',
|
||||
@@ -395,7 +394,7 @@ var FolderListView = Backbone.View.extend({
|
||||
'<tbody id="folder_list_body">',
|
||||
'<tr id="first_folder_item">',
|
||||
"<td>",
|
||||
'<a href="#<% if (upper_folder_id !== 0){ print("folders/" + upper_folder_id)} %>" title="Go to parent folder" class="btn_open_folder btn btn-default btn-xs">..<a>',
|
||||
'<a href="#<% if (upper_folder_id !== 0){ print("folders/" + upper_folder_id)} %>" title="Go to parent folder" class="btn_open_folder btn btn-secondary btn-sm">..<a>',
|
||||
"</td>",
|
||||
"<td></td>",
|
||||
"<td></td>",
|
||||
|
||||
@@ -226,17 +226,17 @@ var FolderRowView = Backbone.View.extend({
|
||||
this.render();
|
||||
old_element.replaceWith(this.$el);
|
||||
/* now we attach new tooltips to the newly created row element */
|
||||
this.$el.find("[data-toggle]").tooltip();
|
||||
this.$el.find('[data-toggle="tooltip"]').tooltip({ trigger: "hover" });
|
||||
},
|
||||
|
||||
templateRowFolder: function() {
|
||||
return _.template(
|
||||
[
|
||||
'<tr class="folder_row light library-row" data-id="<%- content_item.id %>">',
|
||||
"<td>",
|
||||
'<td class="mid">',
|
||||
'<span title="Folder" class="fa fa-folder-o"/>',
|
||||
"</td>",
|
||||
'<td style="text-align: center; "><input style="margin: 0;" type="checkbox"></td>',
|
||||
'<td class="mid"><input style="margin: 0;" type="checkbox"></td>',
|
||||
"<% if(!edit_mode) { %>",
|
||||
"<td>",
|
||||
'<a href="#folders/<%- content_item.id %>"><%- content_item.get("name") %></a>',
|
||||
@@ -256,18 +256,18 @@ var FolderRowView = Backbone.View.extend({
|
||||
"<td></td>",
|
||||
"<td>",
|
||||
"<% if(edit_mode) { %>", // start edit mode
|
||||
'<button data-toggle="tooltip" data-placement="top" title="Save changes" class="primary-button btn-xs save_folder_btn" type="button" style="<% if(button_config.save_folder_btn === false) { print("display:none;") } %>">',
|
||||
'<button data-toggle="tooltip" data-placement="top" title="Save changes" class="primary-button btn-sm save_folder_btn" type="button" style="<% if(button_config.save_folder_btn === false) { print("display:none;") } %>">',
|
||||
'<span class="fa fa-floppy-o"/> Save',
|
||||
"</button>",
|
||||
'<button data-toggle="tooltip" data-placement="top" title="Discard changes" class="primary-button btn-xs cancel_folder_btn" type="button" style="<% if(button_config.cancel_folder_btn === false) { print("display:none;") } %>">',
|
||||
'<button data-toggle="tooltip" data-placement="top" title="Discard changes" class="primary-button btn-sm cancel_folder_btn" type="button" style="<% if(button_config.cancel_folder_btn === false) { print("display:none;") } %>">',
|
||||
'<span class="fa fa-times"/> Cancel',
|
||||
"</button>",
|
||||
"<% } else if (!edit_mode){%>", // start no edit mode
|
||||
'<button data-toggle="tooltip" data-placement="top" title="Modify \'<%- content_item.get("name") %>\'" class="primary-button btn-xs edit_folder_btn" type="button" style="<% if(button_config.edit_folder_btn === false) { print("display:none;") } %>">',
|
||||
'<button data-toggle="tooltip" data-placement="top" title="Modify \'<%- content_item.get("name") %>\'" class="primary-button btn-sm edit_folder_btn" type="button" style="<% if(button_config.edit_folder_btn === false) { print("display:none;") } %>">',
|
||||
'<span class="fa fa-pencil"/> Edit',
|
||||
"</button>",
|
||||
'<a href="#/folders/<%- content_item.id %>/permissions">',
|
||||
'<button data-toggle="tooltip" data-placement="top" class="primary-button btn-xs permission_folder_btn" title="Permissions of \'<%- content_item.get("name") %>\'" style="<% if(button_config.permission_folder_btn === false) { print("display:none;") } %>">',
|
||||
'<button data-toggle="tooltip" data-placement="top" class="primary-button btn-sm permission_folder_btn" title="Permissions of \'<%- content_item.get("name") %>\'" style="<% if(button_config.permission_folder_btn === false) { print("display:none;") } %>">',
|
||||
'<span class="fa fa-group"/> Manage',
|
||||
"</button>",
|
||||
"</a>",
|
||||
@@ -282,10 +282,10 @@ var FolderRowView = Backbone.View.extend({
|
||||
return _.template(
|
||||
[
|
||||
'<tr class="dataset_row light library-row" data-id="<%- content_item.id %>">',
|
||||
"<td>",
|
||||
'<td class="mid">',
|
||||
'<span title="Dataset" class="fa fa-file-o"/>',
|
||||
"</td>",
|
||||
'<td style="text-align: center; ">',
|
||||
'<td class="mid">',
|
||||
'<input style="margin: 0;" type="checkbox">',
|
||||
"</td>",
|
||||
"<td>",
|
||||
@@ -304,17 +304,17 @@ var FolderRowView = Backbone.View.extend({
|
||||
"</td>",
|
||||
"<td>",
|
||||
'<% if (content_item.get("is_unrestricted")) { %>',
|
||||
'<span data-toggle="tooltip" data-placement="top" title="Unrestricted dataset" style="color:grey;" class="fa fa-globe fa-lg"/>',
|
||||
'<span data-toggle="tooltip" data-placement="top" title="Unrestricted dataset" style="color:grey;" class="fa fa-globe"/>',
|
||||
"<% } %>",
|
||||
'<% if (content_item.get("is_private")) { %>',
|
||||
'<span data-toggle="tooltip" data-placement="top" title="Private dataset" style="color:grey;" class="fa fa-key fa-lg"/>',
|
||||
'<span data-toggle="tooltip" data-placement="top" title="Private dataset" style="color:grey;" class="fa fa-key"/>',
|
||||
"<% } %>",
|
||||
'<% if ((content_item.get("is_unrestricted") === false) && (content_item.get("is_private") === false)) { %>',
|
||||
'<span data-toggle="tooltip" data-placement="top" title="Restricted dataset" style="color:grey;" class="fa fa-shield fa-lg"/>',
|
||||
'<span data-toggle="tooltip" data-placement="top" title="Restricted dataset" style="color:grey;" class="fa fa-shield"/>',
|
||||
"<% } %>",
|
||||
'<% if (content_item.get("can_manage")) { %>',
|
||||
'<a href="#folders/<%- content_item.get("folder_id") %>/datasets/<%- content_item.id %>/permissions">',
|
||||
'<button data-toggle="tooltip" data-placement="top" class="primary-button btn-xs permissions-dataset-btn" title="Permissions of \'<%- content_item.get("name") %>\'">',
|
||||
'<button data-toggle="tooltip" data-placement="top" class="primary-button btn-sm permissions-dataset-btn" title="Permissions of \'<%- content_item.get("name") %>\'">',
|
||||
'<span class="fa fa-group"/> Manage',
|
||||
"</button>",
|
||||
"</a>",
|
||||
@@ -329,7 +329,7 @@ var FolderRowView = Backbone.View.extend({
|
||||
return _.template(
|
||||
[
|
||||
'<tr class="active deleted_dataset library-row" data-id="<%- content_item.id %>">',
|
||||
"<td>",
|
||||
'<td class="mid">',
|
||||
'<span title="Dataset" class="fa fa-file-o"/>',
|
||||
"</td>",
|
||||
"<td></td>",
|
||||
@@ -354,8 +354,8 @@ var FolderRowView = Backbone.View.extend({
|
||||
"<% } %>",
|
||||
"</td>",
|
||||
"<td>",
|
||||
'<span data-toggle="tooltip" data-placement="top" title="Marked deleted" style="color:grey;" class="fa fa-ban fa-lg"/>',
|
||||
'<button data-toggle="tooltip" data-placement="top" title="Undelete \'<%- content_item.get("name") %>\'" class="primary-button btn-xs undelete_dataset_btn" type="button" style="margin-left:1em;">',
|
||||
'<span data-toggle="tooltip" data-placement="top" title="Marked deleted" style="color:grey;" class="fa fa-ban"/>',
|
||||
'<button data-toggle="tooltip" data-placement="top" title="Undelete \'<%- content_item.get("name") %>\'" class="primary-button btn-sm undelete_dataset_btn" type="button" style="margin-left:1em;">',
|
||||
'<span class="fa fa-unlock"/> Undelete',
|
||||
"</button>",
|
||||
"</td>",
|
||||
@@ -368,7 +368,7 @@ var FolderRowView = Backbone.View.extend({
|
||||
return _.template(
|
||||
[
|
||||
'<tr class="active deleted_folder light library-row" data-id="<%- content_item.id %>">',
|
||||
"<td>",
|
||||
'<td class="mid">',
|
||||
'<span title="Folder" class="fa fa-folder-o"/>',
|
||||
"</td>",
|
||||
"<td></td>",
|
||||
@@ -387,8 +387,8 @@ var FolderRowView = Backbone.View.extend({
|
||||
"</td>",
|
||||
"<td></td>",
|
||||
"<td>",
|
||||
'<span data-toggle="tooltip" data-placement="top" title="Marked deleted" style="color:grey;" class="fa fa-ban fa-lg"/>',
|
||||
'<button data-toggle="tooltip" data-placement="top" title="Undelete \'<%- content_item.get("name") %>\'" class="primary-button btn-xs undelete_folder_btn" type="button" style="margin-left:1em;">',
|
||||
'<span data-toggle="tooltip" data-placement="top" title="Marked deleted" style="color:grey;" class="fa fa-ban"/>',
|
||||
'<button data-toggle="tooltip" data-placement="top" title="Undelete \'<%- content_item.get("name") %>\'" class="primary-button btn-sm undelete_folder_btn" type="button" style="margin-left:1em;">',
|
||||
'<span class="fa fa-unlock"/> Undelete',
|
||||
"</button>",
|
||||
"</td>",
|
||||
|
||||
@@ -78,7 +78,7 @@ var FolderToolbarView = Backbone.View.extend({
|
||||
id: this.options.id,
|
||||
is_admin: false,
|
||||
is_anonym: true,
|
||||
mutiple_add_dataset_options: false
|
||||
multiple_add_dataset_options: false
|
||||
};
|
||||
if (Galaxy.user) {
|
||||
template_defaults.is_admin = Galaxy.user.isAdmin();
|
||||
@@ -88,7 +88,7 @@ var FolderToolbarView = Backbone.View.extend({
|
||||
Galaxy.config.allow_library_path_paste !== false ||
|
||||
Galaxy.config.library_import_dir !== null
|
||||
) {
|
||||
template_defaults.mutiple_add_dataset_options = true;
|
||||
template_defaults.multiple_add_dataset_options = true;
|
||||
}
|
||||
}
|
||||
this.$el.html(toolbar_template(template_defaults));
|
||||
@@ -140,7 +140,7 @@ var FolderToolbarView = Backbone.View.extend({
|
||||
$(".logged-dataset-manipulation").hide();
|
||||
$(".dataset-manipulation").hide();
|
||||
}
|
||||
this.$el.find("[data-toggle]").tooltip();
|
||||
this.$el.find('[data-toggle="tooltip"]').tooltip({ trigger: "hover" });
|
||||
},
|
||||
|
||||
createFolderFromModal: function(event) {
|
||||
@@ -1241,7 +1241,8 @@ var FolderToolbarView = Backbone.View.extend({
|
||||
data: [
|
||||
{ id: "list", text: "List" },
|
||||
{ id: "paired", text: "Paired" },
|
||||
{ id: "list:paired", text: "List of Pairs" }
|
||||
{ id: "list:paired", text: "List of Pairs" },
|
||||
{ id: "rules", text: "From Rules" }
|
||||
],
|
||||
value: "list",
|
||||
onchange: collectionType => {
|
||||
@@ -1353,6 +1354,14 @@ var FolderToolbarView = Backbone.View.extend({
|
||||
title: modal_title,
|
||||
defaultHideSourceItems: true
|
||||
});
|
||||
} else if (this.collectionType === "rules") {
|
||||
const creationFn = (elements, collectionType, name, hideSourceItems) => {
|
||||
return this.createHDCA(elements, collectionType, name, hideSourceItems, history_id);
|
||||
};
|
||||
LIST_CREATOR.ruleBasedCollectionCreatorModal(collection_elements, "library_datasets", "collections", {
|
||||
creationFn: creationFn,
|
||||
defaultHideSourceItems: true
|
||||
});
|
||||
}
|
||||
},
|
||||
|
||||
@@ -1371,102 +1380,90 @@ var FolderToolbarView = Backbone.View.extend({
|
||||
templateToolBar: function() {
|
||||
return _.template(
|
||||
[
|
||||
// container start
|
||||
'<div class="library_style_container">',
|
||||
// toolbar start
|
||||
'<div id="library_toolbar">',
|
||||
'<form class="form-inline" role="form">',
|
||||
"<span><strong>DATA LIBRARIES</strong></span>",
|
||||
// paginator will append here
|
||||
'<span class="library-paginator folder-paginator"></span>',
|
||||
// include deleted checkbox
|
||||
'<div class="checkbox toolbar-item logged-dataset-manipulation" style="height: 20px; display:none;">',
|
||||
"<label>",
|
||||
'<input type="checkbox" class="include-deleted-datasets-chk">include deleted</input>',
|
||||
"</label>",
|
||||
'<div class="library_style_container">', // container start
|
||||
'<div class="d-flex align-items-center mb-2">',
|
||||
'<span class="mr-1">DATA LIBRARIES</span>',
|
||||
"<div>", // toolbar start
|
||||
'<form class="form-inline">',
|
||||
'<div class="form-check logged-dataset-manipulation mr-1" style="display:none;">', // include deleted checkbox
|
||||
'<input class="form-check-input include-deleted-datasets-chk" id="include_deleted_datasets_chk" type="checkbox">',
|
||||
'<label class="form-check-label" for="include_deleted_datasets_chk">include deleted</label>',
|
||||
"</div>",
|
||||
// create new folder button
|
||||
'<button style="display:none;" data-toggle="tooltip" data-placement="top" title="Create New Folder" class="btn btn-default primary-button toolbtn-create-folder add-library-items add-library-items-folder toolbar-item" type="button">',
|
||||
'<span class="fa fa-plus"></span><span class="fa fa-folder"></span> Create Folder ',
|
||||
'<button style="display:none;" title="Create new folder" class="btn btn-secondary toolbtn-create-folder add-library-items add-library-items-folder mr-1" type="button">',
|
||||
'<span class="fa fa-plus"></span> <span class="fa fa-folder"></span> Create Folder ',
|
||||
"</button>",
|
||||
// add datasets button
|
||||
"<% if(mutiple_add_dataset_options) { %>",
|
||||
'<div class="btn-group add-library-items add-library-items-datasets toolbar-item" style="display:none;">',
|
||||
'<button title="Add Datasets to Current Folder" id="" type="button" class="primary-button dropdown-toggle" data-toggle="dropdown">',
|
||||
'<span class="fa fa-plus"></span><span class="fa fa-file"></span> Add Datasets <span class="caret"></span>',
|
||||
"<% if(multiple_add_dataset_options) { %>", // add datasets button
|
||||
'<div data-toggle="tooltip" data-placement="right" title="Add datasets to current folder" class="dropdown add-library-items add-library-items-datasets mr-1" style="display:none;">',
|
||||
'<button type="button" class="btn btn-secondary dropdown-toggle" data-toggle="dropdown">',
|
||||
'<span class="fa fa-plus"></span> <span class="fa fa-file"></span> Add Datasets <span class="caret"></span>',
|
||||
"</button>",
|
||||
'<ul class="dropdown-menu" role="menu">',
|
||||
'<li><a href="#folders/<%= id %>/import/history"> from History</a></li>',
|
||||
'<div class="dropdown-menu">',
|
||||
'<a class="dropdown-item" href="#folders/<%= id %>/import/history"> from History</a>',
|
||||
"<% if(Galaxy.config.user_library_import_dir !== null) { %>",
|
||||
'<li><a href="#folders/<%= id %>/import/userdir"> from User Directory</a></li>',
|
||||
'<a class="dropdown-item" href="#folders/<%= id %>/import/userdir"> from User Directory</a>',
|
||||
"<% } %>",
|
||||
"<% if(Galaxy.config.allow_library_path_paste) { %>",
|
||||
'<li class="divider"></li>',
|
||||
'<li class="dropdown-header">Admins only</li>',
|
||||
"<% if(Galaxy.config.library_import_dir !== null || Galaxy.config.allow_library_path_paste) { %>",
|
||||
'<h5 class="dropdown-header">Admins only</h5>',
|
||||
"<% if(Galaxy.config.library_import_dir !== null) { %>",
|
||||
'<li><a href="#folders/<%= id %>/import/importdir">from Import Directory</a></li>',
|
||||
'<a class="dropdown-item" href="#folders/<%= id %>/import/importdir">from Import Directory</a>',
|
||||
"<% } %>",
|
||||
"<% if(Galaxy.config.allow_library_path_paste) { %>",
|
||||
'<li><a href="#folders/<%= id %>/import/path">from Path</a></li>',
|
||||
'<a class="dropdown-item" href="#folders/<%= id %>/import/path">from Path</a>',
|
||||
"<% } %>",
|
||||
"<% } %>",
|
||||
"</ul>",
|
||||
"</div>",
|
||||
"</div>",
|
||||
|
||||
"<% } else { %>",
|
||||
'<a data-placement="top" title="Add Datasets to Current Folder" style="display:none;" class="btn btn-default add-library-items add-library-items-datasets" href="#folders/<%= id %>/import/history" role="button">',
|
||||
'<a data-placement="top" title="Add Datasets to Current Folder" style="display:none;" class="btn btn-secondary add-library-items add-library-items-datasets" href="#folders/<%= id %>/import/history" role="button">',
|
||||
'<span class="fa fa-plus"></span><span class="fa fa-file"></span>',
|
||||
"</a>",
|
||||
"<% } %>",
|
||||
// import to history button
|
||||
'<div class="btn-group toolbar-item">',
|
||||
'<button title="Import to history" type="button" class="primary-button dropdown-toggle add-to-history" data-toggle="dropdown">',
|
||||
'<div data-toggle="tooltip" data-placement="right" title="Import to history" class="dropdown mr-1">',
|
||||
'<button type="button" class="primary-button dropdown-toggle add-to-history" data-toggle="dropdown">',
|
||||
'<span class="fa fa-book"></span> To History <span class="caret"></span>',
|
||||
"</button>",
|
||||
'<ul class="dropdown-menu" role="menu">',
|
||||
'<li><a href="" class="toolbtn-bulk-import add-to-history-datasets">as Datasets</a></li>',
|
||||
'<li><a href="" class="toolbtn-collection-import add-to-history-collection">as a Collection</a></li>',
|
||||
"</ul>",
|
||||
'<div class="dropdown-menu" role="menu">',
|
||||
'<a href="" class="toolbtn-bulk-import add-to-history-datasets dropdown-item">as Datasets</a>',
|
||||
'<a href="" class="toolbtn-collection-import add-to-history-collection dropdown-item">as a Collection</a>',
|
||||
"</div>",
|
||||
"</div>",
|
||||
// download button
|
||||
'<div class="btn-group dataset-manipulation toolbar-item" style="display:none; ">',
|
||||
'<button title="Download items as archive" type="button" class="primary-button dropdown-toggle" data-toggle="dropdown">',
|
||||
'<div data-toggle="tooltip" data-placement="right" title="Download items as archive" class="dropdown dataset-manipulation mr-1" style="display:none; ">',
|
||||
'<button type="button" class="primary-button dropdown-toggle" data-toggle="dropdown">',
|
||||
'<span class="fa fa-save"></span> Download <span class="caret"></span>',
|
||||
"</button>",
|
||||
'<ul class="dropdown-menu" role="menu">',
|
||||
'<li><a href="#/folders/<%= id %>/download/tgz">.tar.gz</a></li>',
|
||||
'<li><a href="#/folders/<%= id %>/download/tbz">.tar.bz</a></li>',
|
||||
'<li><a href="#/folders/<%= id %>/download/zip">.zip</a></li>',
|
||||
"</ul>",
|
||||
'<div class="dropdown-menu" role="menu">',
|
||||
'<a class="dropdown-item" href="#/folders/<%= id %>/download/tgz">.tar.gz</a>',
|
||||
'<a class="dropdown-item" href="#/folders/<%= id %>/download/tbz">.tar.bz</a>',
|
||||
'<a class="dropdown-item" href="#/folders/<%= id %>/download/zip">.zip</a>',
|
||||
"</div>",
|
||||
"</div>",
|
||||
// delete button
|
||||
'<button data-toggle="tooltip" data-placement="top" title="Mark items deleted" class="primary-button toolbtn-bulk-delete logged-dataset-manipulation toolbar-item" style="display:none;" type="button">',
|
||||
'<button data-toggle="tooltip" data-placement="right" title="Mark items deleted" class="primary-button toolbtn-bulk-delete logged-dataset-manipulation mr-1" style="display:none;" type="button">',
|
||||
'<span class="fa fa-trash"></span> Delete',
|
||||
"</button>",
|
||||
// help button
|
||||
'<span class="right-float" data-toggle="tooltip" data-placement="top" title="See this screen annotated">',
|
||||
'<a href="https://galaxyproject.org/data-libraries/screen/folder-contents/" target="_blank">',
|
||||
'<span class="mr-1" data-toggle="tooltip" data-placement="right" title="Show location details">', // location button
|
||||
'<button data-id="<%- id %>" class="primary-button toolbtn-show-locinfo" type="button">',
|
||||
'<span class="fa fa-info-circle"></span>',
|
||||
" Details",
|
||||
"</button>",
|
||||
"</span>",
|
||||
'<span data-toggle="tooltip" data-placement="right" title="See this screen annotated">', // help button
|
||||
'<a class="library-help-button" href="https://galaxyproject.org/data-libraries/screen/folder-contents/" target="_blank">',
|
||||
'<button class="primary-button" type="button">',
|
||||
'<span class="fa fa-question-circle"></span>',
|
||||
" Help",
|
||||
"</button>",
|
||||
"</a>",
|
||||
"</span>",
|
||||
// location button
|
||||
'<span class="right-float" data-toggle="tooltip" data-placement="top" title="Show location details">',
|
||||
'<button data-id="<%- id %>" class="primary-button toolbtn-show-locinfo toolbar-item" type="button">',
|
||||
'<span class="fa fa-info-circle"></span>',
|
||||
" Details",
|
||||
"</button>",
|
||||
"</span>",
|
||||
// toolbar end
|
||||
"</div>",
|
||||
"</form>",
|
||||
// folder contents will append here
|
||||
'<div id="folder_items_element" />',
|
||||
// paginator will append here
|
||||
'<div class="folder-paginator paginator-bottom" />',
|
||||
// container end
|
||||
"</div>"
|
||||
"</div>", // toolbar end
|
||||
"</div>", // end flex
|
||||
'<div id="folder_items_element" />', // folder contents will append here
|
||||
'<div class="d-flex justify-content-center align-items-center folder-paginator mt-2 mb-2" />', // paginator will append here
|
||||
"</div>" // container end
|
||||
].join("")
|
||||
);
|
||||
},
|
||||
@@ -1475,7 +1472,7 @@ var FolderToolbarView = Backbone.View.extend({
|
||||
return _.template(
|
||||
[
|
||||
"<div>",
|
||||
'<table class="grid table table-condensed">',
|
||||
'<table class="grid table table-sm">',
|
||||
"<thead>",
|
||||
'<th style="width: 25%;">library</th>',
|
||||
"<th></th>",
|
||||
@@ -1509,7 +1506,7 @@ var FolderToolbarView = Backbone.View.extend({
|
||||
"</tr>",
|
||||
"</tbody>",
|
||||
"</table>",
|
||||
'<table class="grid table table-condensed">',
|
||||
'<table class="grid table table-sm">',
|
||||
"<thead>",
|
||||
'<th style="width: 25%;">folder</th>',
|
||||
"<th></th>",
|
||||
@@ -1618,8 +1615,6 @@ var FolderToolbarView = Backbone.View.extend({
|
||||
return _.template(
|
||||
[
|
||||
'<div id="file_browser_modal">',
|
||||
'<div class="alert alert-info jstree-files-message">All files you select will be imported into the current folder ignoring their folder structure.</div>',
|
||||
'<div class="alert alert-info jstree-folders-message" style="display:none;">All files within the selected folders and their subfolders will be imported into the current folder.</div>',
|
||||
'<div style="margin-bottom:1em;">',
|
||||
'<label title="Switch to selecting files" class="radio-inline import-type-switch">',
|
||||
'<input type="radio" name="jstree-radio" value="jstree-disable-folders" checked="checked"> Choose Files',
|
||||
@@ -1628,6 +1623,8 @@ var FolderToolbarView = Backbone.View.extend({
|
||||
'<input type="radio" name="jstree-radio" value="jstree-disable-files"> Choose Folders',
|
||||
"</label>",
|
||||
"</div>",
|
||||
'<div class="alert alert-info jstree-files-message">All files you select will be imported into the current folder ignoring their folder structure.</div>',
|
||||
'<div class="alert alert-info jstree-folders-message" style="display:none;">All files within the selected folders and their subfolders will be imported into the current folder.</div>',
|
||||
'<div style="margin-bottom:1em;">',
|
||||
'<label class="checkbox-inline jstree-preserve-structure" style="display:none;">',
|
||||
'<input class="preserve-checkbox" type="checkbox" value="preserve_directory_structure">',
|
||||
@@ -1789,34 +1786,34 @@ var FolderToolbarView = Backbone.View.extend({
|
||||
templatePaginator: function() {
|
||||
return _.template(
|
||||
[
|
||||
'<ul class="pagination pagination-sm">',
|
||||
'<ul class="pagination mr-1">',
|
||||
"<% if ( ( show_page - 1 ) > 0 ) { %>",
|
||||
"<% if ( ( show_page - 1 ) > page_count ) { %>", // we are on higher page than total page count
|
||||
'<li><a href="#folders/<%= id %>/page/1"><span class="fa fa-angle-double-left"></span></a></li>',
|
||||
'<li class="disabled"><a href="#folders/<%= id %>/page/<% print( show_page ) %>"><% print( show_page - 1 ) %></a></li>',
|
||||
'<li class="page-item"><a class="page-link" href="#folders/<%= id %>/page/1"><span class="fa fa-angle-double-left"></span></a></li>',
|
||||
'<li class="page-item disabled"><a class="page-link" href="#folders/<%= id %>/page/<% print( show_page ) %>"><% print( show_page - 1 ) %></a></li>',
|
||||
"<% } else { %>",
|
||||
'<li><a href="#folders/<%= id %>/page/1"><span class="fa fa-angle-double-left"></span></a></li>',
|
||||
'<li><a href="#folders/<%= id %>/page/<% print( show_page - 1 ) %>"><% print( show_page - 1 ) %></a></li>',
|
||||
'<li class="page-item"><a class="page-link" href="#folders/<%= id %>/page/1"><span class="fa fa-angle-double-left"></span></a></li>',
|
||||
'<li class="page-item"><a class="page-link" href="#folders/<%= id %>/page/<% print( show_page - 1 ) %>"><% print( show_page - 1 ) %></a></li>',
|
||||
"<% } %>",
|
||||
"<% } else { %>", // we are on the first page
|
||||
'<li class="disabled"><a href="#folders/<%= id %>/page/1"><span class="fa fa-angle-double-left"></span></a></li>',
|
||||
'<li class="disabled"><a href="#folders/<%= id %>/page/<% print( show_page ) %>"><% print( show_page - 1 ) %></a></li>',
|
||||
'<li class="page-item disabled"><a class="page-link" href="#folders/<%= id %>/page/1"><span class="fa fa-angle-double-left"></span></a></li>',
|
||||
'<li class="page-item disabled"><a class="page-link" href="#folders/<%= id %>/page/<% print( show_page ) %>"><% print( show_page - 1 ) %></a></li>',
|
||||
"<% } %>",
|
||||
'<li class="active">',
|
||||
'<a href="#folders/<%= id %>/page/<% print( show_page ) %>"><% print( show_page ) %></a>',
|
||||
'<li class="page-item active">',
|
||||
'<a class="page-link" href="#folders/<%= id %>/page/<% print( show_page ) %>"><% print( show_page ) %></a>',
|
||||
"</li>",
|
||||
"<% if ( ( show_page ) < page_count ) { %>",
|
||||
'<li><a href="#folders/<%= id %>/page/<% print( show_page + 1 ) %>"><% print( show_page + 1 ) %></a></li>',
|
||||
'<li><a href="#folders/<%= id %>/page/<% print( page_count ) %>"><span class="fa fa-angle-double-right"></span></a></li>',
|
||||
'<li class="page-item"><a class="page-link" href="#folders/<%= id %>/page/<% print( show_page + 1 ) %>"><% print( show_page + 1 ) %></a></li>',
|
||||
'<li class="page-item"><a class="page-link" href="#folders/<%= id %>/page/<% print( page_count ) %>"><span class="fa fa-angle-double-right"></span></a></li>',
|
||||
"<% } else { %>",
|
||||
'<li class="disabled"><a href="#folders/<%= id %>/page/<% print( show_page ) %>"><% print( show_page + 1 ) %></a></li>',
|
||||
'<li class="disabled"><a href="#folders/<%= id %>/page/<% print( page_count ) %>"><span class="fa fa-angle-double-right"></span></a></li>',
|
||||
'<li class="page-item disabled"><a class="page-link" href="#folders/<%= id %>/page/<% print( show_page ) %>"><% print( show_page + 1 ) %></a></li>',
|
||||
'<li class="page-item disabled"><a class="page-link" href="#folders/<%= id %>/page/<% print( page_count ) %>"><span class="fa fa-angle-double-right"></span></a></li>',
|
||||
"<% } %>",
|
||||
"</ul>",
|
||||
"<span>",
|
||||
'<span class="mr-1">',
|
||||
' <%- items_shown %> items shown <a href="" data-toggle="tooltip" data-placement="top" title="currently <%- folder_page_size %> per page" class="page-size-prompt">(change)</a>',
|
||||
"</span>",
|
||||
"<span>",
|
||||
'<span class="mr-1">',
|
||||
" <%- total_items_count %> total",
|
||||
"</span>"
|
||||
].join("")
|
||||
@@ -1857,6 +1854,9 @@ var FolderToolbarView = Backbone.View.extend({
|
||||
"<li>",
|
||||
"List of Pairs: Advanced collection containing any number of Pairs; imagine as Pair-type collections inside of a List-type collection.",
|
||||
"</li>",
|
||||
"<li>",
|
||||
"From Rules: Use Galaxy's rule builder to describe collections. This is more of an advanced feature that allows building any number of collections or any type.",
|
||||
"</li>",
|
||||
"</ul>",
|
||||
"</div>",
|
||||
// history selection/creation
|
||||
|
||||
@@ -78,7 +78,7 @@ var LibraryView = Backbone.View.extend({
|
||||
mod_toastr.error("An error occurred while attempting to fetch library permissions.");
|
||||
});
|
||||
|
||||
$("#center [data-toggle]").tooltip();
|
||||
$('#center [data-toggle="tooltip"]').tooltip({ trigger: "hover" });
|
||||
//hack to show scrollbars
|
||||
$("#center").css("overflow", "auto");
|
||||
},
|
||||
@@ -239,9 +239,9 @@ var LibraryView = Backbone.View.extend({
|
||||
return _.template(
|
||||
[
|
||||
'<div class="library_style_container">',
|
||||
'<div id="library_toolbar">',
|
||||
"<div>",
|
||||
'<a href="#">',
|
||||
'<button data-toggle="tooltip" data-placement="top" title="Go back to the list of Libraries" class="btn btn-default primary-button" type="button">',
|
||||
'<button data-toggle="tooltip" data-placement="top" title="Go back to the list of Libraries" class="btn btn-secondary primary-button" type="button">',
|
||||
'<span class="fa fa-list"/>',
|
||||
" Libraries",
|
||||
"</button>",
|
||||
@@ -279,7 +279,7 @@ var LibraryView = Backbone.View.extend({
|
||||
'<div class="alert alert-info roles-selection">',
|
||||
"User with <strong>any</strong> of these roles can modify this library (name, synopsis, etc.).",
|
||||
"</div>",
|
||||
'<button data-toggle="tooltip" data-placement="top" title="Save modifications made on this page" class="btn btn-default toolbtn_save_permissions primary-button" type="button">',
|
||||
'<button data-toggle="tooltip" data-placement="top" title="Save modifications made on this page" class="btn btn-secondary toolbtn_save_permissions primary-button" type="button">',
|
||||
'<span class="fa fa-floppy-o"/>',
|
||||
" Save",
|
||||
"</button>",
|
||||
|
||||
@@ -124,7 +124,7 @@ var LibraryListView = Backbone.View.extend({
|
||||
);
|
||||
Galaxy.libraries.libraryToolbarView.renderPaginator(this.options);
|
||||
}
|
||||
$("#center [data-toggle]").tooltip();
|
||||
$('#center [data-toggle="tooltip"]').tooltip({ trigger: "hover" });
|
||||
$("#center").css("overflow", "auto");
|
||||
},
|
||||
|
||||
@@ -238,7 +238,7 @@ var LibraryListView = Backbone.View.extend({
|
||||
"</div>",
|
||||
"<% }%>",
|
||||
"<% } else{ %>",
|
||||
'<table class="grid table table-condensed">',
|
||||
'<table class="grid table table-sm">',
|
||||
"<thead>",
|
||||
'<th style="width:30%;">',
|
||||
'<a class="sort-libraries-link" title="Click to reverse order" href="#">',
|
||||
|
||||
@@ -29,6 +29,7 @@ var LibraryRowView = Backbone.View.extend({
|
||||
render: function(library) {
|
||||
if (typeof library === "undefined") {
|
||||
library = Galaxy.libraries.libraryListView.collection.get(this.$el.data("id"));
|
||||
console.log(library);
|
||||
}
|
||||
this.prepareButtons(library);
|
||||
var tmpl = this.templateRow();
|
||||
@@ -44,9 +45,9 @@ var LibraryRowView = Backbone.View.extend({
|
||||
},
|
||||
|
||||
repaint: function(library) {
|
||||
/* need to hide manually because of the element removal in setElement
|
||||
/* need to dispose manually because of the element removal in setElement
|
||||
invoked in render() */
|
||||
$(".tooltip").hide();
|
||||
this.$el.find('[data-toggle="tooltip"]').tooltip("dispose");
|
||||
/* we need to store the old element to be able to replace it with
|
||||
new one */
|
||||
var old_element = this.$el;
|
||||
@@ -55,7 +56,7 @@ var LibraryRowView = Backbone.View.extend({
|
||||
this.render();
|
||||
old_element.replaceWith(this.$el);
|
||||
/* now we attach new tooltips to the newly created row element */
|
||||
this.$el.find("[data-toggle]").tooltip();
|
||||
this.$el.find('[data-toggle="tooltip"]').tooltip({ trigger: "hover" });
|
||||
},
|
||||
|
||||
/**
|
||||
@@ -256,16 +257,16 @@ var LibraryRowView = Backbone.View.extend({
|
||||
"<% } %>",
|
||||
'<td class="right-center">',
|
||||
'<% if( (library.get("public")) && (library.get("deleted") === false) ) { %>',
|
||||
'<span data-toggle="tooltip" data-placement="top" title="Unrestricted library" style="color:grey;" class="fa fa-globe fa-lg"> </span>',
|
||||
'<span data-toggle="tooltip" data-placement="left" title="Unrestricted library" style="color:grey;" class="fa fa-globe fa-lg"> </span>',
|
||||
"<% }%>",
|
||||
'<button data-toggle="tooltip" data-placement="top" title="Modify \'<%- library.get("name") %>\'" class="primary-button btn-xs edit_library_btn" type="button" style="<% if(button_config.edit_library_btn === false) { print("display:none;") } %>"><span class="fa fa-pencil"></span> Edit</button>',
|
||||
'<a href="#library/<%- library.get("id") %>/permissions"><button data-toggle="tooltip" data-placement="top" title="Permissions of \'<%- library.get("name") %>\'" class="primary-button btn-xs permission_library_btn" type="button" style="<% if(button_config.permission_library_btn === false) { print("display:none;") } %>"><span class="fa fa-group"></span> Manage</button></a>',
|
||||
'<button data-toggle="tooltip" data-placement="top" title="Save changes" class="primary-button btn-xs save_library_btn" type="button" style="<% if(button_config.save_library_btn === false) { print("display:none;") } %>"><span class="fa fa-floppy-o"></span> Save</button>',
|
||||
'<button data-toggle="tooltip" data-placement="top" title="Discard changes" class="primary-button btn-xs cancel_library_btn" type="button" style="<% if(button_config.cancel_library_btn === false) { print("display:none;") } %>"><span class="fa fa-times"></span> Cancel</button>',
|
||||
'<button data-toggle="tooltip" data-placement="top" title="Delete \'<%- library.get("name") %>\'" class="primary-button btn-xs delete_library_btn" type="button" style="<% if(button_config.delete_library_btn === false) { print("display:none;") } %>"><span class="fa fa-trash-o"></span> Delete</button>',
|
||||
'<button data-toggle="tooltip" data-placement="left" title="Modify \'<%- library.get("name") %>\'" class="btn btn-sm edit_library_btn" type="button" style="<% if(button_config.edit_library_btn === false) { print("display:none;") } %>"><span class="fa fa-pencil"></span> Edit</button>',
|
||||
'<a data-toggle="tooltip" data-placement="left" title="Permissions of \'<%- library.get("name") %>\'" href="#library/<%- library.get("id") %>/permissions"><button class="btn btn-sm permission_library_btn" type="button" style="<% if(button_config.permission_library_btn === false) { print("display:none;") } %>"><span class="fa fa-group"></span> Manage</button></a>',
|
||||
'<button data-toggle="tooltip" data-placement="left" title="Save changes" class="btn btn-sm save_library_btn" type="button" style="<% if(button_config.save_library_btn === false) { print("display:none;") } %>"><span class="fa fa-floppy-o"></span> Save</button>',
|
||||
'<button data-toggle="tooltip" data-placement="left" title="Discard changes" class="btn btn-sm cancel_library_btn" type="button" style="<% if(button_config.cancel_library_btn === false) { print("display:none;") } %>"><span class="fa fa-times"></span> Cancel</button>',
|
||||
'<button data-toggle="tooltip" data-placement="left" title="Delete \'<%- library.get("name") %>\'" class="btn btn-sm delete_library_btn" type="button" style="<% if(button_config.delete_library_btn === false) { print("display:none;") } %>"><span class="fa fa-trash-o"></span> Delete</button>',
|
||||
// For deleted libraries
|
||||
'<span data-toggle="tooltip" data-placement="top" title="Marked deleted" style="color:grey; <% if(button_config.undelete_library_btn === false) { print("display:none;") } %>" class="fa fa-ban fa-lg"></span>',
|
||||
'<button data-toggle="tooltip" data-placement="top" title="Undelete \'<%- library.get("name") %>\' " class="primary-button btn-xs undelete_library_btn" type="button" style="<% if(button_config.undelete_library_btn === false) { print("display:none;") } %>"><span class="fa fa-unlock"></span> Undelete</button>',
|
||||
'<span data-toggle="tooltip" data-placement="left" title="Marked deleted" style="color:grey; <% if(button_config.undelete_library_btn === false) { print("display:none;") } %>" class="fa fa-ban fa-lg"></span>',
|
||||
'<button data-toggle="tooltip" data-placement="left" title="Undelete \'<%- library.get("name") %>\' " class="btn btn-sm undelete_library_btn" type="button" style="<% if(button_config.undelete_library_btn === false) { print("display:none;") } %>"><span class="fa fa-unlock"></span> Undelete</button>',
|
||||
"</td>",
|
||||
"</tr>"
|
||||
].join("")
|
||||
|
||||
@@ -48,7 +48,7 @@ var LibraryToolbarView = Backbone.View.extend({
|
||||
renderPaginator: function(options) {
|
||||
this.options = _.extend(this.options, options);
|
||||
var paginator_template = this.templatePaginator();
|
||||
this.$el.find("#library_paginator").html(
|
||||
this.$el.find(".library-paginator").html(
|
||||
paginator_template({
|
||||
show_page: parseInt(this.options.show_page),
|
||||
page_count: parseInt(this.options.page_count),
|
||||
@@ -196,46 +196,29 @@ var LibraryToolbarView = Backbone.View.extend({
|
||||
return _.template(
|
||||
[
|
||||
'<div class="library_style_container">',
|
||||
'<div id="toolbar_form">',
|
||||
'<div id="library_toolbar">',
|
||||
'<form class="form-inline" role="form">',
|
||||
'<span><strong><a href="#" title="Go to first page">DATA LIBRARIES</a></strong></span>',
|
||||
'<span id="library_paginator" class="library-paginator">',
|
||||
// paginator will append here
|
||||
"</span>",
|
||||
'<div class="form-group toolbar-item">',
|
||||
'<input type="text" class="form-control library-search-input" placeholder="Search" size="30">',
|
||||
'<div class="d-flex align-items-center mb-2">',
|
||||
'<a class="mr-1" href="#" data-toggle="tooltip" data-placement="top" title="Go to first page">DATA LIBRARIES</a>',
|
||||
'<div class="d-flex align-items-center library-paginator mr-1" />', // paginator will append here
|
||||
'<form class="form-inline mr-1">',
|
||||
'<input type="text" class="form-control library-search-input mr-1" placeholder="Great Library" size="15">',
|
||||
"<% if(admin_user === true) { %>", // only admins
|
||||
'<div class="form-check mr-1">',
|
||||
'<input class="form-check-input" id="include_deleted_chk" type="checkbox">',
|
||||
'<label class="form-check-label" for="include_deleted_chk">include deleted</label>',
|
||||
"</div>",
|
||||
// only admins see the following
|
||||
"<% if(admin_user === true) { %>",
|
||||
'<div class="checkbox toolbar-item" style="height: 20px;">',
|
||||
"<label>",
|
||||
'<input id="include_deleted_chk" type="checkbox">',
|
||||
"include deleted ",
|
||||
"</input>",
|
||||
"</label>",
|
||||
"<label>",
|
||||
'<input id="exclude_restricted_chk" type="checkbox">',
|
||||
"exclude restricted",
|
||||
"</input>",
|
||||
"</label>",
|
||||
'<div class="form-check mr-1">',
|
||||
'<input class="form-check-input" id="exclude_restricted_chk" type="checkbox">',
|
||||
'<label class="form-check-label" for="exclude_restricted_chk">exclude restricted</label>',
|
||||
"</div>",
|
||||
'<span class="toolbar-item" data-toggle="tooltip" data-placement="top" title="Create New Library">',
|
||||
'<button id="create_new_library_btn" class="primary-button btn-xs" type="button"><span class="fa fa-plus"></span> New Library</button>',
|
||||
"</span>",
|
||||
"<% } %>",
|
||||
'<span class="help-button" data-toggle="tooltip" data-placement="top" title="See this screen annotated">',
|
||||
'<a href="https://galaxyproject.org/data-libraries/screen/list-of-libraries/" target="_blank">',
|
||||
'<button class="primary-button" type="button"><span class="fa fa-question-circle"></span> Help</button>',
|
||||
"</a>",
|
||||
"</span>",
|
||||
'<button data-toggle="tooltip" data-placement="top" title="Create new library" id="create_new_library_btn" class="btn btn-secondary" type="button"><span class="fa fa-plus"></span> New Library</button>',
|
||||
"<% } %>", // end admins
|
||||
"</form>",
|
||||
"</div>",
|
||||
"</div>",
|
||||
'<div id="libraries_element">',
|
||||
// table with libraries will append here
|
||||
"</div>",
|
||||
"</div>"
|
||||
'<a class="library-help-button" data-toggle="tooltip" data-placement="top" title="See this screen annotated" href="https://galaxyproject.org/data-libraries/screen/list-of-libraries/" target="_blank">',
|
||||
'<button class="btn btn-secondary" type="button"><span class="fa fa-question-circle"></span> Help</button>',
|
||||
"</a>",
|
||||
"</div>", // end flex
|
||||
'<div id="libraries_element" />', // table with libraries will append here
|
||||
"</div>" // end library_style_container
|
||||
].join("")
|
||||
);
|
||||
},
|
||||
@@ -243,34 +226,34 @@ var LibraryToolbarView = Backbone.View.extend({
|
||||
templatePaginator: function() {
|
||||
return _.template(
|
||||
[
|
||||
'<ul class="pagination pagination-sm">',
|
||||
'<ul class="pagination mr-1">',
|
||||
"<% if ( ( show_page - 1 ) > 0 ) { %>",
|
||||
"<% if ( ( show_page - 1 ) > page_count ) { %>", // we are on higher page than total page count
|
||||
'<li><a href="#page/1"><span class="fa fa-angle-double-left"></span></a></li>',
|
||||
'<li class="disabled"><a href="#page/<% print( show_page ) %>"><% print( show_page - 1 ) %></a></li>',
|
||||
'<li class="page-item"><a class="page-link" href="#page/1"><span class="fa fa-angle-double-left"></span></a></li>',
|
||||
'<li class="page-item disabled"><a class="page-link" href="#page/<% print( show_page ) %>"><% print( show_page - 1 ) %></a></li>',
|
||||
"<% } else { %>",
|
||||
'<li><a href="#page/1"><span class="fa fa-angle-double-left"></span></a></li>',
|
||||
'<li><a href="#page/<% print( show_page - 1 ) %>"><% print( show_page - 1 ) %></a></li>',
|
||||
'<li class="page-item"><a class="page-link" href="#page/1"><span class="fa fa-angle-double-left"></span></a></li>',
|
||||
'<li class="page-item"><a class="page-link" href="#page/<% print( show_page - 1 ) %>"><% print( show_page - 1 ) %></a></li>',
|
||||
"<% } %>",
|
||||
"<% } else { %>", // we are on the first page
|
||||
'<li class="disabled"><a href="#page/1"><span class="fa fa-angle-double-left"></span></a></li>',
|
||||
'<li class="disabled"><a href="#page/<% print( show_page ) %>"><% print( show_page - 1 ) %></a></li>',
|
||||
'<li class="page-item disabled"><a class="page-link" href="#page/1"><span class="fa fa-angle-double-left"></span></a></li>',
|
||||
'<li class="page-item disabled"><a class="page-link" href="#page/<% print( show_page ) %>"><% print( show_page - 1 ) %></a></li>',
|
||||
"<% } %>",
|
||||
'<li class="active">',
|
||||
'<a href="#page/<% print( show_page ) %>"><% print( show_page ) %></a>',
|
||||
'<li class="page-item active">',
|
||||
'<a class="page-link" href="#page/<% print( show_page ) %>"><% print( show_page ) %></a>',
|
||||
"</li>",
|
||||
"<% if ( ( show_page ) < page_count ) { %>",
|
||||
'<li><a href="#page/<% print( show_page + 1 ) %>"><% print( show_page + 1 ) %></a></li>',
|
||||
'<li><a href="#page/<% print( page_count ) %>"><span class="fa fa-angle-double-right"></span></a></li>',
|
||||
'<li class="page-item"><a class="page-link" href="#page/<% print( show_page + 1 ) %>"><% print( show_page + 1 ) %></a></li>',
|
||||
'<li class="page-item"><a class="page-link" href="#page/<% print( page_count ) %>"><span class="fa fa-angle-double-right"></span></a></li>',
|
||||
"<% } else { %>",
|
||||
'<li class="disabled"><a href="#page/<% print( show_page ) %>"><% print( show_page + 1 ) %></a></li>',
|
||||
'<li class="disabled"><a href="#page/<% print( page_count ) %>"><span class="fa fa-angle-double-right"></span></a></li>',
|
||||
'<li class="page-item disabled"><a class="page-link" href="#page/<% print( show_page ) %>"><% print( show_page + 1 ) %></a></li>',
|
||||
'<li class="page-item disabled"><a class="page-link" href="#page/<% print( page_count ) %>"><span class="fa fa-angle-double-right"></span></a></li>',
|
||||
"<% } %>",
|
||||
"</ul>",
|
||||
"<span>",
|
||||
'<span class="mr-1">',
|
||||
' <%- libraries_shown %> libraries shown <a href="" data-toggle="tooltip" data-placement="top" title="currently <%- library_page_size %> per page" class="page_size_prompt">(change)</a>',
|
||||
"</span>",
|
||||
"<span>",
|
||||
'<span class="mr-1">',
|
||||
" <%- total_libraries_count %> total",
|
||||
"</span>"
|
||||
].join("")
|
||||
|
||||
@@ -301,11 +301,11 @@ var ListPanel = Backbone.View.extend(BASE_MVC.LoggableMixin).extend(
|
||||
var $newMenu = $(
|
||||
[
|
||||
'<div class="list-action-menu btn-group">',
|
||||
'<button class="list-action-menu-btn btn btn-default dropdown-toggle" data-toggle="dropdown">',
|
||||
'<button class="list-action-menu-btn btn btn-secondary dropdown-toggle" data-toggle="dropdown">',
|
||||
_l("For all selected"),
|
||||
"...",
|
||||
"</button>",
|
||||
'<ul class="dropdown-menu pull-right" role="menu">',
|
||||
'<ul class="dropdown-menu float-right" role="menu">',
|
||||
"</ul>",
|
||||
"</div>"
|
||||
].join("")
|
||||
@@ -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")) {
|
||||
@@ -887,11 +887,11 @@ ListPanel.prototype.templates = (() => {
|
||||
// show when selectors are shown
|
||||
'<div class="list-actions">',
|
||||
'<div class="btn-group">',
|
||||
'<button class="select-all btn btn-default"',
|
||||
'<button class="select-all btn btn-secondary"',
|
||||
'data-mode="select">',
|
||||
_l("All"),
|
||||
"</button>",
|
||||
'<button class="deselect-all btn btn-default"',
|
||||
'<button class="deselect-all btn btn-secondary"',
|
||||
'data-mode="select">',
|
||||
_l("None"),
|
||||
"</button>",
|
||||
@@ -1036,11 +1036,11 @@ ModelListPanel.prototype.templates = (() => {
|
||||
|
||||
'<div class="list-actions">',
|
||||
'<div class="btn-group">',
|
||||
'<button class="select-all btn btn-default"',
|
||||
'<button class="select-all btn btn-secondary"',
|
||||
'data-mode="select">',
|
||||
_l("All"),
|
||||
"</button>",
|
||||
'<button class="deselect-all btn btn-default"',
|
||||
'<button class="deselect-all btn btn-secondary"',
|
||||
'data-mode="select">',
|
||||
_l("None"),
|
||||
"</button>",
|
||||
|
||||
@@ -0,0 +1,818 @@
|
||||
import _l from "utils/localization";
|
||||
import pyre from "pyre-to-regexp";
|
||||
|
||||
const NEW_COLUMN = "new";
|
||||
|
||||
const multiColumnsToString = function(targetColumns, colHeaders) {
|
||||
if (targetColumns.length == 0) {
|
||||
return `no columns`;
|
||||
} else if (targetColumns.length == 1) {
|
||||
return `column ${colHeaders[targetColumns[0]]}`;
|
||||
} else {
|
||||
const targetHeaders = targetColumns.map(el => colHeaders[el]);
|
||||
// https://stackoverflow.com/questions/16251822/array-to-comma-separated-string-and-for-last-tag-use-the-and-instead-of-comma
|
||||
return `columns ${[targetHeaders.slice(0, -1).join(", "), targetHeaders.slice(-1)[0]].join(" and ")}`;
|
||||
}
|
||||
};
|
||||
|
||||
const removeColumns = function(columns, targetColumns) {
|
||||
const newColumns = [];
|
||||
for (let index in columns) {
|
||||
if (targetColumns.indexOf(index) === -1) {
|
||||
newColumns.push(columns[index]);
|
||||
}
|
||||
}
|
||||
return newColumns;
|
||||
};
|
||||
|
||||
const applyRegex = function(regex, target, data, replacement, groupCount) {
|
||||
let regExp;
|
||||
try {
|
||||
regExp = pyre(String(regex));
|
||||
} catch (error) {
|
||||
return { error: `Invalid regular expression specified.` };
|
||||
}
|
||||
let failedCount = 0;
|
||||
function newRow(row) {
|
||||
const source = row[target];
|
||||
const match = regExp.exec(source);
|
||||
if (!match) {
|
||||
failedCount++;
|
||||
return null;
|
||||
}
|
||||
if (!replacement) {
|
||||
groupCount = groupCount && parseInt(groupCount);
|
||||
if (groupCount) {
|
||||
if (match.length != groupCount + 1) {
|
||||
failedCount++;
|
||||
return null;
|
||||
}
|
||||
return row.concat(match.splice(1, match.length));
|
||||
} else {
|
||||
return row.concat([match[0]]);
|
||||
}
|
||||
} else {
|
||||
return row.concat([regExp.pyreReplace(match[0], replacement)]);
|
||||
}
|
||||
}
|
||||
data = data.map(newRow);
|
||||
if (failedCount > 0) {
|
||||
return { error: `${failedCount} row(s) failed to match specified regular expression.` };
|
||||
}
|
||||
return { data };
|
||||
};
|
||||
|
||||
const flatMap = (f, xs) => {
|
||||
return xs.reduce((acc, x) => acc.concat(f(x)), []);
|
||||
};
|
||||
|
||||
const RULES = {
|
||||
add_column_basename: {
|
||||
title: _l("Basename of Path of URL"),
|
||||
display: (rule, colHeaders) => {
|
||||
return `Add column using basename of column ${colHeaders[rule.target_column]}`;
|
||||
},
|
||||
init: (component, rule) => {
|
||||
if (!rule) {
|
||||
component.addColumnBasenameTarget = 0;
|
||||
} else {
|
||||
component.addColumnBasenameTarget = rule.target_column;
|
||||
}
|
||||
},
|
||||
save: (component, rule) => {
|
||||
rule.target_column = component.addColumnBasenameTarget;
|
||||
},
|
||||
apply: (rule, data, sources, columns) => {
|
||||
// https://github.com/kgryte/regex-basename-posix/blob/master/lib/index.js
|
||||
//const re = /^(?:\/?|)(?:[\s\S]*?)((?:\.{1,2}|[^\/]+?|)(?:\.[^.\/]*|))(?:[\/]*)$/;
|
||||
// https://stackoverflow.com/questions/8376525/get-value-of-a-string-after-a-slash-in-javascript
|
||||
const re = "[^/]*$";
|
||||
const target = rule.target_column;
|
||||
const rval = applyRegex(re, target, data);
|
||||
columns.push(NEW_COLUMN);
|
||||
rval.columns = columns;
|
||||
return rval;
|
||||
}
|
||||
},
|
||||
add_column_rownum: {
|
||||
title: _l("Row Number"),
|
||||
display: (rule, colHeaders) => {
|
||||
return `Add column for the current row number.`;
|
||||
},
|
||||
init: (component, rule) => {
|
||||
if (!rule) {
|
||||
component.addColumnRownumStart = 1;
|
||||
} else {
|
||||
component.addColumnRownumStart = rule.start;
|
||||
}
|
||||
},
|
||||
save: (component, rule) => {
|
||||
rule.start = parseInt(component.addColumnRownumStart);
|
||||
},
|
||||
apply: (rule, data, sources, columns) => {
|
||||
let rownum = rule.start;
|
||||
function newRow(row) {
|
||||
const newRow = row.slice();
|
||||
newRow.push(String(rownum));
|
||||
rownum += 1;
|
||||
return newRow;
|
||||
}
|
||||
data = data.map(newRow);
|
||||
columns.push(NEW_COLUMN);
|
||||
return { data, columns };
|
||||
}
|
||||
},
|
||||
add_column_value: {
|
||||
title: _l("Fixed Value"),
|
||||
display: (rule, colHeaders) => {
|
||||
return `Add column for the constant value of ${rule.value}.`;
|
||||
},
|
||||
init: (component, rule) => {
|
||||
if (!rule) {
|
||||
component.addColumnValue = "";
|
||||
} else {
|
||||
component.addColumnValue = rule.value;
|
||||
}
|
||||
},
|
||||
save: (component, rule) => {
|
||||
rule.value = component.addColumnValue;
|
||||
},
|
||||
apply: (rule, data, sources, columns) => {
|
||||
const addValue = rule.value;
|
||||
function newRow(row) {
|
||||
const newRow = row.slice();
|
||||
newRow.push(addValue);
|
||||
return newRow;
|
||||
}
|
||||
data = data.map(newRow);
|
||||
columns.push(NEW_COLUMN);
|
||||
return { data, columns };
|
||||
}
|
||||
},
|
||||
add_column_metadata: {
|
||||
title: _l("Add Column from Metadata"),
|
||||
display: (rule, colHeaders) => {
|
||||
return `Add column for ${rule.value}.`;
|
||||
},
|
||||
init: (component, rule) => {
|
||||
if (!rule) {
|
||||
component.addColumnMetadataValue = null;
|
||||
} else {
|
||||
component.addColumnMetadataValue = rule.value;
|
||||
}
|
||||
},
|
||||
save: (component, rule) => {
|
||||
rule.value = component.addColumnMetadataValue;
|
||||
},
|
||||
apply: (rule, data, sources, columns) => {
|
||||
const ruleValue = rule.value;
|
||||
const identifierIndex = parseInt(ruleValue.substring("identifier".length));
|
||||
function newRow(row, index) {
|
||||
const newRow = row.slice();
|
||||
newRow.push(sources[index]["identifiers"][identifierIndex]);
|
||||
return newRow;
|
||||
}
|
||||
data = data.map(newRow);
|
||||
columns.push(NEW_COLUMN);
|
||||
return { data, columns };
|
||||
}
|
||||
},
|
||||
add_column_regex: {
|
||||
title: _l("Using a Regular Expression"),
|
||||
display: (rule, colHeaders) => {
|
||||
return `Add new column using ${rule.expression} applied to column ${colHeaders[rule.target_column]}`;
|
||||
},
|
||||
init: (component, rule) => {
|
||||
if (!rule) {
|
||||
component.addColumnRegexTarget = 0;
|
||||
component.addColumnRegexExpression = "";
|
||||
component.addColumnRegexReplacement = null;
|
||||
component.addColumnRegexGroupCount = null;
|
||||
} else {
|
||||
component.addColumnRegexTarget = rule.target_column;
|
||||
component.addColumnRegexExpression = rule.expression;
|
||||
component.addColumnRegexReplacement = rule.replacement;
|
||||
component.addColumnRegexGroupCount = rule.group_count;
|
||||
}
|
||||
let addColumnRegexType = "global";
|
||||
if (component.addColumnRegexGroupCount) {
|
||||
addColumnRegexType = "groups";
|
||||
} else if (component.addColumnRegexReplacement) {
|
||||
addColumnRegexType = "replacement";
|
||||
}
|
||||
component.addColumnRegexType = addColumnRegexType;
|
||||
},
|
||||
save: (component, rule) => {
|
||||
rule.target_column = component.addColumnRegexTarget;
|
||||
rule.expression = component.addColumnRegexExpression;
|
||||
if (component.addColumnRegexReplacement) {
|
||||
rule.replacement = component.addColumnRegexReplacement;
|
||||
}
|
||||
if (component.addColumnRegexGroupCount) {
|
||||
rule.group_count = component.addColumnRegexGroupCount;
|
||||
}
|
||||
},
|
||||
apply: (rule, data, sources, columns) => {
|
||||
const target = rule.target_column;
|
||||
const rval = applyRegex(rule.expression, target, data, rule.replacement, rule.group_count);
|
||||
columns.push(NEW_COLUMN);
|
||||
rval.columns = columns;
|
||||
return rval;
|
||||
}
|
||||
},
|
||||
add_column_concatenate: {
|
||||
title: _l("Concatenate Columns"),
|
||||
display: (rule, colHeaders) => {
|
||||
return `Concatenate column ${colHeaders[rule.target_column_0]} and column ${
|
||||
colHeaders[rule.target_column_1]
|
||||
}`;
|
||||
},
|
||||
init: (component, rule) => {
|
||||
if (!rule) {
|
||||
component.addColumnConcatenateTarget0 = 0;
|
||||
component.addColumnConcatenateTarget1 = 0;
|
||||
} else {
|
||||
component.addColumnConcatenateTarget0 = rule.target_column_0;
|
||||
component.addColumnConcatenateTarget1 = rule.target_column_1;
|
||||
}
|
||||
},
|
||||
save: (component, rule) => {
|
||||
rule.target_column_0 = component.addColumnConcatenateTarget0;
|
||||
rule.target_column_1 = component.addColumnConcatenateTarget1;
|
||||
},
|
||||
apply: (rule, data, sources, columns) => {
|
||||
const target0 = rule.target_column_0;
|
||||
const target1 = rule.target_column_1;
|
||||
function newRow(row) {
|
||||
const newRow = row.slice();
|
||||
newRow.push(row[target0] + row[target1]);
|
||||
return newRow;
|
||||
}
|
||||
data = data.map(newRow);
|
||||
columns.push(NEW_COLUMN);
|
||||
return { data, columns };
|
||||
}
|
||||
},
|
||||
add_column_substr: {
|
||||
title: _l("Keep or Trim Prefix or Suffix"),
|
||||
display: (rule, colHeaders) => {
|
||||
const type = rule.substr_type;
|
||||
let display;
|
||||
if (type == "keep_prefix") {
|
||||
display = `Keep only ${rule.length} characters from the start of column ${
|
||||
colHeaders[rule.target_column]
|
||||
}`;
|
||||
} else if (type == "drop_prefix") {
|
||||
display = `Remove ${rule.length} characters from the start of column ${colHeaders[rule.target_column]}`;
|
||||
} else if (type == "keep_suffix") {
|
||||
display = `Keep only ${rule.length} characters from the end of column ${
|
||||
colHeaders[rule.target_column]
|
||||
}`;
|
||||
} else {
|
||||
display = `Remove ${rule.length} characters from the end of column ${colHeaders[rule.target_column]}`;
|
||||
}
|
||||
return display;
|
||||
},
|
||||
init: (component, rule) => {
|
||||
if (!rule) {
|
||||
component.addColumnSubstrTarget = 0;
|
||||
component.addColumnSubstrType = "keep_prefix";
|
||||
component.addColumnSubstrLength = 1;
|
||||
} else {
|
||||
component.addColumnSubstrTarget = rule.target_column;
|
||||
component.addColumnSubstrLength = rule.length;
|
||||
component.addColumnSubstrType = rule.substr_type;
|
||||
}
|
||||
},
|
||||
save: (component, rule) => {
|
||||
rule.target_column = component.addColumnSubstrTarget;
|
||||
rule.length = component.addColumnSubstrLength;
|
||||
rule.substr_type = component.addColumnSubstrType;
|
||||
},
|
||||
apply: (rule, data, sources, columns) => {
|
||||
const target = rule.target_column;
|
||||
const length = rule.length;
|
||||
const type = rule.substr_type;
|
||||
function newRow(row) {
|
||||
const newRow = row.slice();
|
||||
const originalValue = row[target];
|
||||
let start = 0,
|
||||
end = originalValue.length;
|
||||
if (type == "keep_prefix") {
|
||||
end = length;
|
||||
} else if (type == "drop_prefix") {
|
||||
start = length;
|
||||
} else if (type == "keep_suffix") {
|
||||
start = end - length;
|
||||
if (start < 0) {
|
||||
start = 0;
|
||||
}
|
||||
} else {
|
||||
end = end - length;
|
||||
if (end < 0) {
|
||||
end = 0;
|
||||
}
|
||||
}
|
||||
newRow.push(originalValue.substr(start, end));
|
||||
return newRow;
|
||||
}
|
||||
data = data.map(newRow);
|
||||
columns.push(NEW_COLUMN);
|
||||
return { data };
|
||||
}
|
||||
},
|
||||
remove_columns: {
|
||||
title: _l("Remove Column(s)"),
|
||||
display: (rule, colHeaders) => {
|
||||
const targetColumns = rule.target_columns;
|
||||
return `Remove ${multiColumnsToString(targetColumns, colHeaders)}`;
|
||||
},
|
||||
init: (component, rule) => {
|
||||
if (!rule) {
|
||||
component.removeColumnTargets = [];
|
||||
} else {
|
||||
component.removeColumnTargets = rule.target_columns;
|
||||
}
|
||||
},
|
||||
save: (component, rule) => {
|
||||
rule.target_columns = component.removeColumnTargets;
|
||||
},
|
||||
apply: (rule, data, sources, columns) => {
|
||||
const targets = rule.target_columns;
|
||||
function newRow(row) {
|
||||
const newRow = [];
|
||||
for (let index in row) {
|
||||
if (targets.indexOf(parseInt(index)) == -1) {
|
||||
newRow.push(row[index]);
|
||||
}
|
||||
}
|
||||
return newRow;
|
||||
}
|
||||
data = data.map(newRow);
|
||||
columns = removeColumns(columns, targets);
|
||||
return { data, columns };
|
||||
}
|
||||
},
|
||||
add_filter_regex: {
|
||||
title: _l("Using a Regular Expression"),
|
||||
display: (rule, colHeaders) => {
|
||||
return `Filter rows using regular expression ${rule.expression} on column ${
|
||||
colHeaders[rule.target_column]
|
||||
}`;
|
||||
},
|
||||
init: (component, rule) => {
|
||||
if (!rule) {
|
||||
component.addFilterRegexTarget = 0;
|
||||
component.addFilterRegexExpression = "";
|
||||
component.addFilterRegexInvert = false;
|
||||
} else {
|
||||
component.addFilterRegexTarget = rule.target_column;
|
||||
component.addFilterRegexExpression = rule.expression;
|
||||
component.addFilterRegexInvert = rule.invert;
|
||||
}
|
||||
},
|
||||
save: (component, rule) => {
|
||||
rule.target_column = component.addFilterRegexTarget;
|
||||
rule.expression = component.addFilterRegexExpression;
|
||||
rule.invert = component.addFilterRegexInvert;
|
||||
},
|
||||
apply: (rule, data, sources, columns) => {
|
||||
const regex = String(rule.expression);
|
||||
var regExp;
|
||||
try {
|
||||
regExp = pyre(regex);
|
||||
} catch (error) {
|
||||
return { error: `Invalid regular expression specified.` };
|
||||
}
|
||||
const target = rule.target_column;
|
||||
const invert = rule.invert;
|
||||
const filterFunction = function(el, index) {
|
||||
const row = data[parseInt(index)];
|
||||
return regExp.exec(row[target]) ? !invert : invert;
|
||||
};
|
||||
sources = sources.filter(filterFunction);
|
||||
data = data.filter(filterFunction);
|
||||
return { data, sources };
|
||||
}
|
||||
},
|
||||
add_filter_count: {
|
||||
title: _l("First or Last N Rows"),
|
||||
display: (rule, colHeaders) => {
|
||||
const which = rule.which;
|
||||
const invert = rule.invert;
|
||||
if (which == "first" && !invert) {
|
||||
return `Filter out first ${rule.count} row(s).`;
|
||||
} else if (which == "first" && invert) {
|
||||
return `Keep only first ${rule.count} row(s).`;
|
||||
} else if (which == "last" && !invert) {
|
||||
return `Filter out last ${rule.count} row(s).`;
|
||||
} else {
|
||||
return `Keep only last ${rule.count} row(s).`;
|
||||
}
|
||||
},
|
||||
init: (component, rule) => {
|
||||
if (!rule) {
|
||||
component.addFilterCountN = 0;
|
||||
component.addFilterCountWhich = "first";
|
||||
component.addFilterCountInvert = false;
|
||||
} else {
|
||||
component.addFilterCountN = rule.count;
|
||||
component.addFilterCountWhich = rule.which;
|
||||
component.addFilterCountInvert = rule.inverse;
|
||||
}
|
||||
},
|
||||
save: (component, rule) => {
|
||||
rule.count = component.addFilterCountN;
|
||||
rule.which = component.addFilterCountWhich;
|
||||
rule.invert = component.addFilterCountInvert;
|
||||
},
|
||||
apply: (rule, data, sources, columns) => {
|
||||
const count = rule.count;
|
||||
const invert = rule.invert;
|
||||
const which = rule.which;
|
||||
const dataLength = data.length;
|
||||
const filterFunction = function(el, index) {
|
||||
let matches;
|
||||
if (which == "first") {
|
||||
matches = index >= count;
|
||||
} else {
|
||||
matches = index < dataLength - count;
|
||||
}
|
||||
return matches ? !invert : invert;
|
||||
};
|
||||
sources = sources.filter(filterFunction);
|
||||
data = data.filter(filterFunction);
|
||||
return { data, sources };
|
||||
}
|
||||
},
|
||||
add_filter_empty: {
|
||||
title: _l("On Emptiness"),
|
||||
display: (rule, colHeaders) => {
|
||||
return `Filter rows if no value for column ${colHeaders[rule.target_column]}`;
|
||||
},
|
||||
init: (component, rule) => {
|
||||
if (!rule) {
|
||||
component.addFilterEmptyTarget = 0;
|
||||
component.addFilterEmptyInvert = false;
|
||||
} else {
|
||||
component.addFilterEmptyTarget = rule.target_column;
|
||||
component.addFilterEmptyInvert = rule.invert;
|
||||
}
|
||||
},
|
||||
save: (component, rule) => {
|
||||
rule.target_column = component.addFilterEmptyTarget;
|
||||
rule.invert = component.addFilterEmptyInvert;
|
||||
},
|
||||
apply: (rule, data, sources, columns) => {
|
||||
const target = rule.target_column;
|
||||
const invert = rule.invert;
|
||||
const filterFunction = function(el, index) {
|
||||
const row = data[parseInt(index)];
|
||||
return row[target].length ? !invert : invert;
|
||||
};
|
||||
sources = sources.filter(filterFunction);
|
||||
data = data.filter(filterFunction);
|
||||
return { data, sources };
|
||||
}
|
||||
},
|
||||
add_filter_matches: {
|
||||
title: _l("Matching a Supplied Value"),
|
||||
display: (rule, colHeaders) => {
|
||||
return `Filter rows with value ${rule.value} for column ${colHeaders[rule.target_column]}`;
|
||||
},
|
||||
init: (component, rule) => {
|
||||
if (!rule) {
|
||||
component.addFilterMatchesTarget = 0;
|
||||
component.addFilterMatchesValue = "";
|
||||
component.addFilterMatchesInvert = false;
|
||||
} else {
|
||||
component.addFilterMatchesTarget = rule.target_column;
|
||||
component.addFilterMatchesValue = rule.value;
|
||||
component.addFilterMatchesInvert = rule.invert;
|
||||
}
|
||||
},
|
||||
save: (component, rule) => {
|
||||
rule.target_column = component.addFilterMatchesTarget;
|
||||
rule.value = component.addFilterMatchesValue;
|
||||
rule.invert = component.addFilterMatchesInvert;
|
||||
},
|
||||
apply: (rule, data, sources, columns) => {
|
||||
const target = rule.target_column;
|
||||
const invert = rule.invert;
|
||||
const value = rule.value;
|
||||
const filterFunction = function(el, index) {
|
||||
const row = data[parseInt(index)];
|
||||
return row[target] == value ? !invert : invert;
|
||||
};
|
||||
sources = sources.filter(filterFunction);
|
||||
data = data.filter(filterFunction);
|
||||
return { data, sources };
|
||||
}
|
||||
},
|
||||
add_filter_compare: {
|
||||
title: _l("By Comparing to a Numeric Value"),
|
||||
display: (rule, colHeaders) => {
|
||||
return `Filter rows with value ${rule.compare_type} ${rule.value} for column ${
|
||||
colHeaders[rule.target_column]
|
||||
}`;
|
||||
},
|
||||
init: (component, rule) => {
|
||||
if (!rule) {
|
||||
component.addFilterCompareTarget = 0;
|
||||
component.addFilterCompareValue = 0;
|
||||
component.addFilterCompareType = "less_than";
|
||||
} else {
|
||||
component.addFilterCompareTarget = rule.target_column;
|
||||
component.addFilterCompareValue = rule.value;
|
||||
component.addFilterCompareType = rule.compare_type;
|
||||
}
|
||||
},
|
||||
save: (component, rule) => {
|
||||
rule.target_column = component.addFilterCompareTarget;
|
||||
rule.value = component.addFilterCompareValue;
|
||||
rule.compare_type = component.addFilterCompareType;
|
||||
},
|
||||
apply: (rule, data, sources, columns) => {
|
||||
const target = rule.target_column;
|
||||
const compare_type = rule.compare_type;
|
||||
const value = rule.value;
|
||||
const filterFunction = function(el, index) {
|
||||
const row = data[parseInt(index)];
|
||||
const targetValue = parseFloat(row[target]);
|
||||
let matches;
|
||||
if (compare_type == "less_than") {
|
||||
matches = targetValue < value;
|
||||
} else if (compare_type == "less_than_equal") {
|
||||
matches = targetValue <= value;
|
||||
} else if (compare_type == "greater_than") {
|
||||
matches = targetValue > value;
|
||||
} else if (compare_type == "greater_than_equal") {
|
||||
matches = targetValue >= value;
|
||||
}
|
||||
return matches;
|
||||
};
|
||||
sources = sources.filter(filterFunction);
|
||||
data = data.filter(filterFunction);
|
||||
return { data, sources };
|
||||
}
|
||||
},
|
||||
sort: {
|
||||
title: _l("Sort"),
|
||||
display: (rule, colHeaders) => {
|
||||
return `Sort on column ${colHeaders[rule.target_column]}`;
|
||||
},
|
||||
init: (component, rule) => {
|
||||
if (!rule) {
|
||||
component.addSortingTarget = 0;
|
||||
component.addSortingNumeric = false;
|
||||
} else {
|
||||
component.addSortingTarget = rule.target_column;
|
||||
component.addSortingNumeric = rule.numeric;
|
||||
}
|
||||
},
|
||||
save: (component, rule) => {
|
||||
rule.target_column = component.addSortingTarget;
|
||||
rule.numeric = component.addSortingNumeric;
|
||||
},
|
||||
apply: (rule, data, sources, columns) => {
|
||||
const target = rule.target_column;
|
||||
const numeric = rule.numeric;
|
||||
|
||||
const sortable = _.zip(data, sources);
|
||||
|
||||
const sortFunc = (a, b) => {
|
||||
let aVal = a[0][target];
|
||||
let bVal = b[0][target];
|
||||
if (numeric) {
|
||||
aVal = parseFloat(aVal);
|
||||
bVal = parseFloat(bVal);
|
||||
}
|
||||
if (aVal < bVal) {
|
||||
return -1;
|
||||
} else if (bVal < aVal) {
|
||||
return 1;
|
||||
} else {
|
||||
return 0;
|
||||
}
|
||||
};
|
||||
|
||||
sortable.sort(sortFunc);
|
||||
|
||||
const newData = [];
|
||||
const newSources = [];
|
||||
|
||||
sortable.map(zipped => {
|
||||
newData.push(zipped[0]);
|
||||
newSources.push(zipped[1]);
|
||||
});
|
||||
|
||||
return { data: newData, sources: newSources };
|
||||
}
|
||||
},
|
||||
swap_columns: {
|
||||
title: _l("Swap Column(s)"),
|
||||
display: (rule, colHeaders) => {
|
||||
return `Swap ${multiColumnsToString([rule.target_column_0, rule.target_column_1], colHeaders)}`;
|
||||
},
|
||||
init: (component, rule) => {
|
||||
if (!rule) {
|
||||
component.swapColumnsTarget0 = 0;
|
||||
component.swapColumnsTarget1 = 0;
|
||||
} else {
|
||||
component.swapColumnsTarget0 = rule.target_column_0;
|
||||
component.swapColumnsTarget1 = rule.target_column_1;
|
||||
}
|
||||
},
|
||||
save: (component, rule) => {
|
||||
rule.target_column_0 = component.swapColumnsTarget0;
|
||||
rule.target_column_1 = component.swapColumnsTarget1;
|
||||
},
|
||||
apply: (rule, data, sources, columns) => {
|
||||
const target0 = rule.target_column_0;
|
||||
const target1 = rule.target_column_1;
|
||||
function newRow(row) {
|
||||
const newRow = row.slice();
|
||||
newRow[target0] = row[target1];
|
||||
newRow[target1] = row[target0];
|
||||
return newRow;
|
||||
}
|
||||
data = data.map(newRow);
|
||||
const tempColumn = columns[target0];
|
||||
columns[target0] = columns[target1];
|
||||
columns[target1] = tempColumn;
|
||||
return { data, columns };
|
||||
}
|
||||
},
|
||||
split_columns: {
|
||||
title: _l("Split Column(s)"),
|
||||
display: (rule, colHeaders) => {
|
||||
return `Duplicate each row and split up columns`;
|
||||
},
|
||||
init: (component, rule) => {
|
||||
if (!rule) {
|
||||
component.splitColumnsTargets0 = [];
|
||||
component.splitColumnsTargets1 = [];
|
||||
} else {
|
||||
component.splitColumnsTargets0 = rule.target_columns_0;
|
||||
component.splitColumnsTargets1 = rule.target_columns_1;
|
||||
}
|
||||
},
|
||||
save: (component, rule) => {
|
||||
rule.target_columns_0 = component.splitColumnsTargets0;
|
||||
rule.target_columns_1 = component.splitColumnsTargets1;
|
||||
},
|
||||
apply: (rule, data, sources, columns) => {
|
||||
const targets0 = rule.target_columns_0;
|
||||
const targets1 = rule.target_columns_1;
|
||||
|
||||
const splitRow = function(row) {
|
||||
const newRow0 = [],
|
||||
newRow1 = [];
|
||||
for (let index in row) {
|
||||
index = parseInt(index);
|
||||
if (targets0.indexOf(index) > -1) {
|
||||
newRow0.push(row[index]);
|
||||
} else if (targets1.indexOf(index) > -1) {
|
||||
newRow1.push(row[index]);
|
||||
} else {
|
||||
newRow0.push(row[index]);
|
||||
newRow1.push(row[index]);
|
||||
}
|
||||
}
|
||||
return [newRow0, newRow1];
|
||||
};
|
||||
data = flatMap(splitRow, data);
|
||||
sources = flatMap(src => [src, src], sources);
|
||||
columns = removeColumns(columns, targets0);
|
||||
return { data, sources, columns };
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
const MAPPING_TARGETS = {
|
||||
list_identifiers: {
|
||||
multiple: true,
|
||||
label: _l("List Identifier(s)"),
|
||||
columnHeader: _l("List Identifier"),
|
||||
help: _l(
|
||||
"This should be a short description of the replicate, sample name, condition, etc... that describes each level of the list structure."
|
||||
),
|
||||
importType: "collections"
|
||||
},
|
||||
paired_identifier: {
|
||||
label: _l("Paired-end Indicator"),
|
||||
columnHeader: _l("Paired Indicator"),
|
||||
help: _l(
|
||||
"This should be set to '1', 'R1', 'forward', 'f', or 'F' to indicate forward reads, and '2', 'r', 'reverse', 'R2', 'R', or 'R2' to indicate reverse reads."
|
||||
),
|
||||
importType: "collections"
|
||||
},
|
||||
collection_name: {
|
||||
label: _l("Collection Name"),
|
||||
help: _l(
|
||||
"If this is set, all rows with the same collection name will be joined into a collection and it is possible to create multiple collections at once."
|
||||
),
|
||||
modes: ["raw", "ftp", "datasets", "library_datasets"],
|
||||
importType: "collections"
|
||||
},
|
||||
name: {
|
||||
label: _l("Name"),
|
||||
importType: "datasets"
|
||||
},
|
||||
dbkey: {
|
||||
label: _l("Genome"),
|
||||
modes: ["raw", "ftp"]
|
||||
},
|
||||
file_type: {
|
||||
label: _l("Type"),
|
||||
modes: ["raw", "ftp"],
|
||||
help: _l("This should be the Galaxy file type corresponding to this file.")
|
||||
},
|
||||
url: {
|
||||
label: _l("URL"),
|
||||
modes: ["raw"],
|
||||
help: _l("This should be a URL the file can be downloaded from.")
|
||||
},
|
||||
info: {
|
||||
label: _l("Info"),
|
||||
help: _l(
|
||||
"Unstructured text associated with the dataset that shows up in the history panel, this is optional and can be whatever you would like."
|
||||
),
|
||||
modes: ["raw", "ftp"]
|
||||
},
|
||||
ftp_path: {
|
||||
label: _l("FTP Path"),
|
||||
modes: ["raw", "ftp"],
|
||||
help: _l(
|
||||
"This should be the path to the target file to include relative to your FTP directory on the Galaxy server"
|
||||
),
|
||||
requiresFtp: true
|
||||
}
|
||||
};
|
||||
|
||||
const columnDisplay = function(columns, colHeaders) {
|
||||
let columnNames;
|
||||
if (typeof columns == "object") {
|
||||
columnNames = columns.map(idx => colHeaders[idx]);
|
||||
} else {
|
||||
columnNames = [colHeaders[columns]];
|
||||
}
|
||||
if (columnNames.length == 2) {
|
||||
return "columns " + columnNames[0] + " and " + columnNames[1];
|
||||
} else if (columnNames.length > 2) {
|
||||
return "columns " + columnNames.slice(0, -1).join(", ") + ", and " + columnNames[columnNames.length - 1];
|
||||
} else {
|
||||
return "column " + columnNames[0];
|
||||
}
|
||||
};
|
||||
|
||||
const colHeadersFor = function(data, columns) {
|
||||
if (data.length == 0) {
|
||||
if (columns) {
|
||||
return columns.map((el, i) => String.fromCharCode(65 + i));
|
||||
} else {
|
||||
return [];
|
||||
}
|
||||
} else {
|
||||
return data[0].map((el, i) => String.fromCharCode(65 + i));
|
||||
}
|
||||
};
|
||||
|
||||
const applyRules = function(data, sources, columns, rules, colHeadersPerRule) {
|
||||
var colHeadersPerRule = colHeadersPerRule || [];
|
||||
let hasRuleError = false;
|
||||
for (var ruleIndex in rules) {
|
||||
const ruleHeaders = colHeadersFor(data, columns);
|
||||
colHeadersPerRule[ruleIndex] = ruleHeaders;
|
||||
const rule = rules[ruleIndex];
|
||||
rule.error = null;
|
||||
rule.warn = null;
|
||||
if (hasRuleError) {
|
||||
rule.warn = _l("Skipped due to previous errors.");
|
||||
continue;
|
||||
}
|
||||
var ruleType = rule.type;
|
||||
const ruleDef = RULES[ruleType];
|
||||
const res = ruleDef.apply(rule, data, sources, columns);
|
||||
if (res.error) {
|
||||
hasRuleError = true;
|
||||
rule.error = res.error;
|
||||
} else {
|
||||
if (res.warn) {
|
||||
rule.warn = res.warn;
|
||||
}
|
||||
data = res.data || data;
|
||||
sources = res.sources || sources;
|
||||
columns = res.columns || columns;
|
||||
}
|
||||
}
|
||||
return { data, sources, columns };
|
||||
};
|
||||
|
||||
export default {
|
||||
applyRules: applyRules,
|
||||
columnDisplay: columnDisplay,
|
||||
colHeadersFor: colHeadersFor,
|
||||
RULES: RULES,
|
||||
MAPPING_TARGETS: MAPPING_TARGETS
|
||||
};
|
||||
@@ -107,7 +107,7 @@ var TagsEditor = Backbone.View.extend(baseMVC.LoggableMixin)
|
||||
var renderedArray = [];
|
||||
_.each(tags, tag => {
|
||||
tag = tag.indexOf("name:") == 0 ? tag.slice(5) : tag;
|
||||
var renderString = `<span class="label label-info">${tag}</span>`;
|
||||
var renderString = `<span class="badge badge-primary badge-tags">${tag}</span>`;
|
||||
renderedArray.push(renderString);
|
||||
});
|
||||
if (renderedArray.length === 0) {
|
||||
|
||||
@@ -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 {
|
||||
@@ -245,6 +250,12 @@ export default FormBase.extend({
|
||||
.addClass("ui-form-help")
|
||||
.append(options.help);
|
||||
$tmpl.find("a").attr("target", "_blank");
|
||||
$tmpl.find("img").each(function() {
|
||||
var img_src = $(this).attr("src");
|
||||
if (img_src.indexOf("admin_toolshed") !== -1) {
|
||||
$(this).attr("src", Galaxy.root + img_src);
|
||||
}
|
||||
});
|
||||
return $tmpl;
|
||||
},
|
||||
|
||||
|
||||
@@ -15,6 +15,9 @@ var View = Backbone.View.extend({
|
||||
this.modal = parent.Galaxy.modal || new Modal.View();
|
||||
this.model = (options && options.model) || new Backbone.Model(options);
|
||||
this.deferred = new Deferred();
|
||||
if (options && options.active_tab) {
|
||||
this.active_tab = options.active_tab;
|
||||
}
|
||||
this.setElement(
|
||||
$("<div/>")
|
||||
.addClass("ui-form-composite")
|
||||
@@ -25,18 +28,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 */
|
||||
@@ -201,6 +192,7 @@ var View = Backbone.View.extend({
|
||||
this._renderParameters();
|
||||
this._renderHistory();
|
||||
this._renderUseCachedJob();
|
||||
this._renderResourceParameters();
|
||||
_.each(this.steps, step => {
|
||||
self._renderStep(step);
|
||||
});
|
||||
@@ -312,6 +304,19 @@ var View = Backbone.View.extend({
|
||||
this._append(this.$steps, this.history_form.$el);
|
||||
},
|
||||
|
||||
/** Render Workflow Options */
|
||||
_renderResourceParameters: function() {
|
||||
this.workflow_resource_parameters_form = null;
|
||||
if (!_.isEmpty(this.model.get("workflow_resource_parameters"))) {
|
||||
this.workflow_resource_parameters_form = new Form({
|
||||
cls: "ui-portlet-narrow",
|
||||
title: "<b>Workflow Resource Options</b>",
|
||||
inputs: this.model.get("workflow_resource_parameters")
|
||||
});
|
||||
this._append(this.$steps, this.workflow_resource_parameters_form.$el);
|
||||
}
|
||||
},
|
||||
|
||||
/** Render job caching option */
|
||||
_renderUseCachedJob: function() {
|
||||
var extra_user_preferences = {};
|
||||
@@ -429,10 +434,12 @@ var View = Backbone.View.extend({
|
||||
step
|
||||
)
|
||||
);
|
||||
if (step.step_label) {
|
||||
form.$el.attr("step-label", step.step_label);
|
||||
}
|
||||
}
|
||||
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 &&
|
||||
@@ -534,6 +541,9 @@ var View = Backbone.View.extend({
|
||||
var job_def = {
|
||||
new_history_name: history_form_data["new_history|name"] ? history_form_data["new_history|name"] : null,
|
||||
history_id: !history_form_data["new_history|name"] ? this.model.get("history_id") : null,
|
||||
resource_params: this.workflow_resource_parameters_form
|
||||
? this.workflow_resource_parameters_form.data.create()
|
||||
: {},
|
||||
replacement_params: this.wp_form ? this.wp_form.data.create() : {},
|
||||
parameters: {},
|
||||
// Tool form will submit flat maps for each parameter
|
||||
@@ -598,7 +608,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
|
||||
});
|
||||
|
||||
@@ -138,10 +138,12 @@ var View = Backbone.View.extend({
|
||||
if (options.job_id && options.job_remap) {
|
||||
if (options.job_remap === "job_produced_collection_elements") {
|
||||
var label = "Replace elements in collection ?";
|
||||
var help = "The previous run of this tool failed. Use this option to replace the failed element(s) in the dataset collectio that were produced during the previous tool run.";
|
||||
var help =
|
||||
"The previous run of this tool failed. Use this option to replace the failed element(s) in the dataset collectio that were produced during the previous tool run.";
|
||||
} else {
|
||||
var label = "Resume dependencies from this job ?";
|
||||
var help = "The previous run of this tool failed and other tools were waiting for it to finish successfully. Use this option to resume those tools using the new output(s) of this tool run.";
|
||||
var help =
|
||||
"The previous run of this tool failed and other tools were waiting for it to finish successfully. Use this option to resume those tools using the new output(s) of this tool run.";
|
||||
}
|
||||
options.inputs.push({
|
||||
label: label,
|
||||
@@ -151,7 +153,7 @@ var View = Backbone.View.extend({
|
||||
ignore: "__ignore__",
|
||||
value: "__ignore__",
|
||||
options: [["Yes", options.job_id], ["No", "__ignore__"]],
|
||||
help: help,
|
||||
help: help
|
||||
});
|
||||
}
|
||||
|
||||
@@ -225,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
|
||||
});
|
||||
}
|
||||
@@ -278,6 +280,17 @@ var View = Backbone.View.extend({
|
||||
this.form.highlight(input_id);
|
||||
return false;
|
||||
}
|
||||
if (input_field.validate) {
|
||||
// wish there was a way to just reset this input field
|
||||
const reset = () => {
|
||||
this.form.trigger("reset");
|
||||
};
|
||||
const validateObject = input_field.validate(reset);
|
||||
if (!validateObject.valid) {
|
||||
this.form.highlight(input_id, validateObject.message);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
if (input_value && input_value.batch) {
|
||||
var n = input_value.values.length;
|
||||
var src = n > 0 && input_value.values[0] && input_value.values[0].src;
|
||||
|
||||
@@ -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
|
||||
);
|
||||
|
||||
@@ -830,7 +830,7 @@ var templates = {
|
||||
'<a class="<%- id %> tool-link" href="<%= link %>" target="<%- target %>" minsizehint="<%- min_width %>">',
|
||||
'<span class="labels">',
|
||||
"<% _.each( labels, function( label ){ %>",
|
||||
'<span class="label label-default label-<%- label %>">',
|
||||
'<span class="badge badge-default badge-<%- label %>">',
|
||||
"<%- label %>",
|
||||
"</span>",
|
||||
"<% }); %>",
|
||||
|
||||
@@ -1,29 +1,40 @@
|
||||
import _l from "utils/localization";
|
||||
/**
|
||||
* This is the primary galaxy tours definition, currently only used for
|
||||
* rendering a tour menu.
|
||||
*/
|
||||
import _l from "utils/localization";
|
||||
// The following must remain staged out of libs and not sourced from
|
||||
// node_modules, until TDTs are bundled.
|
||||
import * as Backbone from "libs/backbone";
|
||||
import * as _ from "libs/underscore";
|
||||
import "libs/bootstrap-tour";
|
||||
|
||||
/* global $ */
|
||||
/* global Galaxy */
|
||||
|
||||
// bootstrap-tour configures a window.Tour object; keep a local ref.
|
||||
import "libs/bootstrap-tour";
|
||||
let Tour = window.Tour;
|
||||
|
||||
var gxy_root = typeof Galaxy === "undefined" ? "/" : Galaxy.root;
|
||||
|
||||
var tourpage_template = `<h2>Galaxy Tours</h2>
|
||||
<p>This page presents a list of interactive tours available on this Galaxy server.
|
||||
Select any tour to get started (and remember, you can click 'End Tour' at any time).</p>
|
||||
const TOURPAGE_TEMPLATE = `
|
||||
<h2>Galaxy Tours</h2>
|
||||
<p>This page presents a list of interactive tours available on this Galaxy server.
|
||||
Select any tour to get started (and remember, you can click 'End Tour' at any time).</p>
|
||||
|
||||
<div class="col-12 btn-group" role="group" aria-label="Tag selector">
|
||||
<% _.each(tourtagorder, function(tag) { %>
|
||||
<button class="btn btn-primary tag-selector-button" tag-selector-button="<%- tag %>">
|
||||
<%- tag %>
|
||||
</button>
|
||||
<% }); %>
|
||||
<div class="row mb-3">
|
||||
<div class="col-12 btn-group" role="group" aria-label="Tag selector">
|
||||
<% _.each(tourtagorder, function(tag) { %>
|
||||
<button class="btn btn-primary tag-selector-button" tag-selector-button="<%- tag %>">
|
||||
<%- tag %>
|
||||
</button>
|
||||
<% }); %>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<% _.each(tourtagorder, function(tourtagkey) { %>
|
||||
<div tag="<%- tourtagkey %>" style="display: block;">
|
||||
<div tag="<%- tourtagkey %>" class="row mb-3">
|
||||
<div class="col-12">
|
||||
<% var tourtag = tourtags[tourtagkey]; %>
|
||||
<h4>
|
||||
<%- tourtag.name %>
|
||||
@@ -32,7 +43,7 @@ Select any tour to get started (and remember, you can click 'End Tour' at any ti
|
||||
<% _.each(tourtag.tours, function(tour) { %>
|
||||
<li class="list-group-item">
|
||||
<a href="/tours/<%- tour.id %>" class="tourItem" data-tour.id=<%- tour.id %>>
|
||||
<%- tour.name || tour.id %>
|
||||
<%- tour.attributes.name || tour.id %>
|
||||
</a>
|
||||
- <%- tour.attributes.description || "No description given." %>
|
||||
<% _.each(tour.attributes.tags, function(tag) { %>
|
||||
@@ -43,13 +54,14 @@ Select any tour to get started (and remember, you can click 'End Tour' at any ti
|
||||
</li>
|
||||
<% }); %>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
<% }); %>`;
|
||||
|
||||
var tour_opts = {
|
||||
storage: window.sessionStorage,
|
||||
onEnd: function() {
|
||||
sessionStorage.removeItem("activeGalaxyTour");
|
||||
window.sessionStorage.removeItem("activeGalaxyTour");
|
||||
},
|
||||
delay: 150, // Attempts to make it look natural
|
||||
orphan: true
|
||||
@@ -124,7 +136,7 @@ export var ToursView = Backbone.View.extend({
|
||||
},
|
||||
|
||||
render: function() {
|
||||
var tpl = _.template(tourpage_template);
|
||||
var tpl = _.template(TOURPAGE_TEMPLATE);
|
||||
|
||||
var tourtags = {};
|
||||
_.each(this.model.models, tour => {
|
||||
@@ -175,7 +187,7 @@ export var ToursView = Backbone.View.extend({
|
||||
|
||||
export function giveTourWithData(data) {
|
||||
let hookedTourData = hooked_tour_from_data(data);
|
||||
sessionStorage.setItem("activeGalaxyTour", JSON.stringify(data));
|
||||
window.sessionStorage.setItem("activeGalaxyTour", JSON.stringify(data));
|
||||
// Store tour steps in sessionStorage to easily persist w/o hackery.
|
||||
let tour = new Tour(_.extend({ steps: hookedTourData.steps }, tour_opts));
|
||||
// Always clean restart, since this is a new, explicit execution.
|
||||
@@ -193,7 +205,7 @@ export function giveTourById(tour_id) {
|
||||
}
|
||||
|
||||
export function activeGalaxyTourRunner() {
|
||||
var et = JSON.parse(sessionStorage.getItem("activeGalaxyTour"));
|
||||
var et = JSON.parse(window.sessionStorage.getItem("activeGalaxyTour"));
|
||||
if (et) {
|
||||
et = hooked_tour_from_data(et);
|
||||
if (et && et.steps) {
|
||||
|
||||
@@ -56,7 +56,7 @@ function _errorModal(message, title, details) {
|
||||
.appendTo(Galaxy.modal.$(".modal-content"))
|
||||
.append([$("<p/>").text(DETAILS_MSG), $("<pre/>").text(JSON.stringify(details, null, " "))]);
|
||||
|
||||
$(`<button id="button-1" class="pull-left">${_l("Details")}</button>`)
|
||||
$(`<button id="button-1" class="float-left">${_l("Details")}</button>`)
|
||||
.appendTo(Galaxy.modal.$(".buttons"))
|
||||
.click(() => {
|
||||
Galaxy.modal.$(".error-details").toggle();
|
||||
|
||||
@@ -1,5 +1,9 @@
|
||||
/** This module contains all button views. */
|
||||
import Utils from "utils/utils";
|
||||
import * as Backbone from "backbone";
|
||||
|
||||
/* global $ */
|
||||
|
||||
/** This renders the default button which is used e.g. at the bottom of the upload modal. */
|
||||
var ButtonDefault = Backbone.View.extend({
|
||||
initialize: function(options) {
|
||||
@@ -9,12 +13,13 @@ var ButtonDefault = Backbone.View.extend({
|
||||
id: Utils.uid(),
|
||||
title: "",
|
||||
icon: "",
|
||||
cls: "btn btn-default",
|
||||
cls: "btn btn-secondary",
|
||||
wait: false,
|
||||
wait_text: "Sending...",
|
||||
wait_cls: "btn btn-info",
|
||||
disabled: false,
|
||||
percentage: -1
|
||||
percentage: -1,
|
||||
visible: true
|
||||
}).set(options);
|
||||
this.setElement(
|
||||
$("<button/>")
|
||||
@@ -28,7 +33,6 @@ var ButtonDefault = Backbone.View.extend({
|
||||
},
|
||||
|
||||
render: function() {
|
||||
var self = this;
|
||||
var options = this.model.attributes;
|
||||
this.$el
|
||||
.removeClass()
|
||||
@@ -36,12 +40,13 @@ var ButtonDefault = Backbone.View.extend({
|
||||
.addClass(options.disabled && "disabled")
|
||||
.attr("id", options.id)
|
||||
.attr("disabled", options.disabled)
|
||||
.css("display", options.visible ? "inline-block" : "none")
|
||||
.off("click")
|
||||
.on("click", () => {
|
||||
$(".tooltip").hide();
|
||||
options.onclick && !self.disabled && options.onclick();
|
||||
options.onclick && !this.disabled && options.onclick();
|
||||
})
|
||||
.tooltip({ title: options.tooltip, placement: "bottom" });
|
||||
.tooltip({ title: options.tooltip || "", placement: "bottom" });
|
||||
this.$progress.addClass("progress").css("display", options.percentage !== -1 ? "block" : "none");
|
||||
this.$progress_bar.addClass("progress-bar").css({ width: `${options.percentage}%` });
|
||||
this.$icon.removeClass().addClass("icon fa");
|
||||
@@ -60,12 +65,12 @@ var ButtonDefault = Backbone.View.extend({
|
||||
|
||||
/** Show button */
|
||||
show: function() {
|
||||
this.$el.show();
|
||||
this.model.set("visible", true);
|
||||
},
|
||||
|
||||
/** Hide button */
|
||||
hide: function() {
|
||||
this.$el.hide();
|
||||
this.model.set("visible", false);
|
||||
},
|
||||
|
||||
/** Disable button */
|
||||
@@ -101,6 +106,7 @@ var ButtonLink = ButtonDefault.extend({
|
||||
(options && options.model) ||
|
||||
new Backbone.Model({
|
||||
id: Utils.uid(),
|
||||
visible: true,
|
||||
title: "",
|
||||
icon: "",
|
||||
cls: ""
|
||||
@@ -122,6 +128,7 @@ var ButtonLink = ButtonDefault.extend({
|
||||
target: options.target || "_top",
|
||||
disabled: options.disabled
|
||||
})
|
||||
.css("display", options.visible ? "inline-block" : "none")
|
||||
.tooltip({ placement: "bottom" })
|
||||
.off("click")
|
||||
.on("click", () => {
|
||||
@@ -141,6 +148,7 @@ var ButtonCheck = Backbone.View.extend({
|
||||
title: "Select/Unselect all",
|
||||
icons: ["fa-square-o", "fa-minus-square-o", "fa-check-square-o"],
|
||||
value: 0,
|
||||
visible: true,
|
||||
onchange: function() {}
|
||||
}).set(options);
|
||||
this.setElement(
|
||||
@@ -153,13 +161,13 @@ var ButtonCheck = Backbone.View.extend({
|
||||
},
|
||||
|
||||
render: function(options) {
|
||||
var self = this;
|
||||
var options = this.model.attributes;
|
||||
this.$el
|
||||
.addClass("ui-button-check")
|
||||
.css("display", options.visible ? "inline-block" : "none")
|
||||
.off("click")
|
||||
.on("click", () => {
|
||||
self.model.set("value", (self.model.get("value") === 0 && 2) || 0);
|
||||
this.model.set("value", (this.model.get("value") === 0 && 2) || 0);
|
||||
options.onclick && options.onclick();
|
||||
});
|
||||
this.$title.html(options.title);
|
||||
@@ -196,6 +204,7 @@ var ButtonIcon = ButtonDefault.extend({
|
||||
id: Utils.uid(),
|
||||
title: "",
|
||||
icon: "",
|
||||
visible: true,
|
||||
cls: "ui-button-icon",
|
||||
disabled: false
|
||||
}).set(options);
|
||||
@@ -211,12 +220,12 @@ var ButtonIcon = ButtonDefault.extend({
|
||||
},
|
||||
|
||||
render: function(options) {
|
||||
var self = this;
|
||||
var options = this.model.attributes;
|
||||
this.$el
|
||||
.removeClass()
|
||||
.addClass(options.cls)
|
||||
.addClass(options.disabled && "disabled")
|
||||
.css("display", options.visible ? "inline-block" : "none")
|
||||
.attr("disabled", options.disabled)
|
||||
.attr("id", options.id)
|
||||
.off("click")
|
||||
@@ -224,7 +233,7 @@ var ButtonIcon = ButtonDefault.extend({
|
||||
$(".tooltip").hide();
|
||||
!options.disabled && options.onclick && options.onclick();
|
||||
});
|
||||
this.$button.addClass("button").tooltip({ title: options.tooltip, placement: "bottom" });
|
||||
this.$button.addClass("button").tooltip({ title: options.tooltip || "", placement: "bottom" });
|
||||
this.$icon
|
||||
.removeClass()
|
||||
.addClass("icon fa")
|
||||
@@ -268,7 +277,6 @@ var ButtonMenu = ButtonDefault.extend({
|
||||
},
|
||||
|
||||
render: function() {
|
||||
var self = this;
|
||||
var options = this.model.attributes;
|
||||
this.$el
|
||||
.removeClass()
|
||||
@@ -281,7 +289,7 @@ var ButtonMenu = ButtonDefault.extend({
|
||||
this.$root
|
||||
.addClass("root button dropdown-toggle")
|
||||
.attr("data-toggle", "dropdown")
|
||||
.tooltip({ title: options.tooltip, placement: "bottom" })
|
||||
.tooltip({ title: options.tooltip || "", placement: "bottom" })
|
||||
.off("click")
|
||||
.on("click", e => {
|
||||
$(".tooltip").hide();
|
||||
@@ -301,7 +309,7 @@ var ButtonMenu = ButtonDefault.extend({
|
||||
if (this.collection.length > 0) {
|
||||
this.$menu = $("<ul/>")
|
||||
.addClass("menu dropdown-menu")
|
||||
.addClass(`pull-${self.model.get("pull")}`)
|
||||
.addClass(`pull-${this.model.get("pull")}`)
|
||||
.attr("role", "menu");
|
||||
this.$el.append(this.$menu);
|
||||
}
|
||||
@@ -327,8 +335,8 @@ var ButtonMenu = ButtonDefault.extend({
|
||||
suboptions.onclick();
|
||||
}
|
||||
});
|
||||
self.$menu.append($("<li/>").append($link));
|
||||
suboptions.divider && self.$menu.append($("<li/>").addClass("divider"));
|
||||
this.$menu.append($("<li/>").append($link));
|
||||
suboptions.divider && this.$menu.append($("<li/>").addClass("divider"));
|
||||
}
|
||||
});
|
||||
},
|
||||
|
||||
@@ -1,8 +1,12 @@
|
||||
import _l from "utils/localization";
|
||||
/** Frame view */
|
||||
import _l from "utils/localization";
|
||||
import * as Backbone from "backbone";
|
||||
import * as _ from "underscore";
|
||||
/* global $ */
|
||||
/* global Galaxy */
|
||||
|
||||
var FrameView = Backbone.View.extend({
|
||||
initialize: function(options) {
|
||||
var self = this;
|
||||
this.model = (options && options.model) || new Backbone.Model(options);
|
||||
this.setElement($("<div/>").addClass("corner frame"));
|
||||
this.$el
|
||||
@@ -50,7 +54,7 @@ var FrameView = Backbone.View.extend({
|
||||
option.onclick(self);
|
||||
})
|
||||
.tooltip({
|
||||
title: option.tooltip,
|
||||
title: option.tooltip || "",
|
||||
placement: "bottom"
|
||||
});
|
||||
}
|
||||
@@ -64,7 +68,11 @@ var FrameView = Backbone.View.extend({
|
||||
.attr("src", `${options.url + (options.url.indexOf("?") === -1 ? "?" : "&")}widget=True`)
|
||||
);
|
||||
} else if (options.content) {
|
||||
_.isFunction(options.content) ? options.content(self.$content) : self.$content.html(options.content);
|
||||
if (_.isFunction(options.content)) {
|
||||
options.content(self.$content);
|
||||
} else {
|
||||
self.$content.html(options.content);
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
@@ -120,10 +128,16 @@ var View = Backbone.View.extend({
|
||||
this.frame_list["#frame-shadow"] = this.frame_shadow;
|
||||
|
||||
// initialize panel
|
||||
this.visible ? this.show() : this.hide();
|
||||
if (this.visible) {
|
||||
this.show();
|
||||
} else {
|
||||
this.hide();
|
||||
}
|
||||
this._panelRefresh();
|
||||
$(window).resize(() => {
|
||||
self.visible && self._panelRefresh();
|
||||
if (self.visible) {
|
||||
self._panelRefresh();
|
||||
}
|
||||
});
|
||||
},
|
||||
|
||||
@@ -184,7 +198,9 @@ var View = Backbone.View.extend({
|
||||
height: options.height
|
||||
});
|
||||
this._frameInsert(frame, { top: 0, left: 0 }, true);
|
||||
!this.visible && this.show();
|
||||
if (!this.visible) {
|
||||
this.show();
|
||||
}
|
||||
this.trigger("add");
|
||||
}
|
||||
}
|
||||
@@ -310,7 +326,9 @@ var View = Backbone.View.extend({
|
||||
top: this._toGridCoord("top", p.top),
|
||||
left: this._toGridCoord("left", p.left)
|
||||
};
|
||||
l.left !== 0 && l.left++;
|
||||
if (l.left !== 0) {
|
||||
l.left++;
|
||||
}
|
||||
this._frameInsert(this.frame_shadow, l);
|
||||
}
|
||||
}
|
||||
@@ -334,7 +352,9 @@ var View = Backbone.View.extend({
|
||||
|
||||
/** Hide all frames */
|
||||
_eventHide: function(e) {
|
||||
!this.event.type && this.hide();
|
||||
if (!this.event.type) {
|
||||
this.hide();
|
||||
}
|
||||
},
|
||||
|
||||
/** Fired when scrolling occurs on panel */
|
||||
@@ -411,7 +431,9 @@ var View = Backbone.View.extend({
|
||||
/** Converts a pixel to a grid dimension */
|
||||
_toGridCoord: function(type, px) {
|
||||
var sign = type == "width" || type == "height" ? 1 : -1;
|
||||
type == "top" && (px -= this.top);
|
||||
if (type == "top") {
|
||||
px -= this.top;
|
||||
}
|
||||
return parseInt((px + sign * this.options.margin) / this.options.cell, 10);
|
||||
},
|
||||
|
||||
@@ -419,7 +441,9 @@ var View = Backbone.View.extend({
|
||||
_toPixelCoord: function(type, g) {
|
||||
var sign = type == "width" || type == "height" ? 1 : -1;
|
||||
var px = g * this.options.cell - sign * this.options.margin;
|
||||
type == "top" && (px += this.top);
|
||||
if (type == "top") {
|
||||
px += this.top;
|
||||
}
|
||||
return px;
|
||||
},
|
||||
|
||||
|
||||
@@ -47,6 +47,9 @@ export var Message = Backbone.View.extend({
|
||||
fade: true
|
||||
}).set(options);
|
||||
this.listenTo(this.model, "change", this.render, this);
|
||||
if (options && options.active_tab) {
|
||||
this.active_tab = options.active_tab;
|
||||
}
|
||||
this.render();
|
||||
},
|
||||
update: function(options) {
|
||||
@@ -181,6 +184,46 @@ export var Hidden = Backbone.View.extend({
|
||||
}
|
||||
});
|
||||
|
||||
/** Creates an input element which switches between select and text field */
|
||||
export var TextSelect = Backbone.View.extend({
|
||||
initialize: function(options) {
|
||||
this.select = options.select;
|
||||
this.model = this.select.model;
|
||||
this.text = new Input({
|
||||
onchange: this.model.get("onchange")
|
||||
});
|
||||
this.on("change", () => {
|
||||
if (this.model.get("onchange")) {
|
||||
this.model.get("onchange")(this.value());
|
||||
}
|
||||
});
|
||||
this.setElement(
|
||||
$("<div/>")
|
||||
.append(this.select.$el)
|
||||
.append(this.text.$el)
|
||||
);
|
||||
this.update(this.model.get("data"));
|
||||
},
|
||||
wait: function() {
|
||||
this.select.wait();
|
||||
},
|
||||
unwait: function() {
|
||||
this.select.unwait();
|
||||
},
|
||||
value: function(new_val) {
|
||||
var element = this.textmode ? this.text : this.select;
|
||||
return element.value(new_val);
|
||||
},
|
||||
update: function(options) {
|
||||
var v = this.value();
|
||||
this.textmode = !$.isArray(options) || options.length === 0;
|
||||
this.text.$el[this.textmode ? "show" : "hide"]();
|
||||
this.select.$el[this.textmode ? "hide" : "show"]();
|
||||
this.select.update(options);
|
||||
this.value(v);
|
||||
}
|
||||
});
|
||||
|
||||
/** Creates a upload element input field */
|
||||
export var Upload = Backbone.View.extend({
|
||||
initialize: function(options) {
|
||||
@@ -263,6 +306,7 @@ export default {
|
||||
Checkbox: Options.Checkbox,
|
||||
Radio: Options.Radio,
|
||||
Select: Select,
|
||||
TextSelect: TextSelect,
|
||||
Hidden: Hidden,
|
||||
Slider: Slider,
|
||||
Drilldown: Drilldown
|
||||
|
||||
@@ -10,6 +10,7 @@ export var View = Backbone.View.extend({
|
||||
backdrop: true,
|
||||
height: null,
|
||||
width: null,
|
||||
xlarge: false /* BS4 max-width already expanded in Galaxy, expand even more if true. */,
|
||||
closing_events: false,
|
||||
closing_callback: null,
|
||||
title_separator: true
|
||||
@@ -129,6 +130,9 @@ export var View = Backbone.View.extend({
|
||||
if (this.options.width) {
|
||||
this.$dialog.css("width", this.options.width);
|
||||
}
|
||||
if (this.options.xlarge) {
|
||||
this.$dialog.css("max-width", "3000px");
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
|
||||
@@ -1,6 +1,11 @@
|
||||
/** Base class for options based ui elements **/
|
||||
import * as Backbone from "backbone";
|
||||
import * as _ from "underscore";
|
||||
import Utils from "utils/utils";
|
||||
import Buttons from "mvc/ui/ui-buttons";
|
||||
|
||||
/* global $ */
|
||||
|
||||
var Base = Backbone.View.extend({
|
||||
initialize: function(options) {
|
||||
var self = this;
|
||||
@@ -71,13 +76,12 @@ var Base = Backbone.View.extend({
|
||||
$(self._templateOption(option))
|
||||
.addClass("ui-option")
|
||||
.tooltip({
|
||||
title: option.tooltip,
|
||||
title: option.tooltip || "",
|
||||
placement: "bottom"
|
||||
})
|
||||
);
|
||||
});
|
||||
}
|
||||
var self = this;
|
||||
this.$("input").on("change", () => {
|
||||
self.value(self._getValue());
|
||||
self.trigger("change");
|
||||
@@ -115,13 +119,16 @@ var Base = Backbone.View.extend({
|
||||
if (this._getValue() === null && !this.model.get("multiple") && !this.model.get("optional")) {
|
||||
this._setValue(this.first());
|
||||
}
|
||||
this.all_button &&
|
||||
if (this.all_button) {
|
||||
this.all_button.value($.isArray(this._getValue()) ? this._getValue().length : 0, this.length());
|
||||
}
|
||||
},
|
||||
|
||||
/** Return/Set current selection */
|
||||
value: function(new_value) {
|
||||
new_value !== undefined && this.model.set("value", new_value);
|
||||
if (new_value !== undefined) {
|
||||
this.model.set("value", new_value);
|
||||
}
|
||||
return this._getValue();
|
||||
},
|
||||
|
||||
@@ -253,14 +260,15 @@ RadioButton.View = Base.extend({
|
||||
|
||||
/** Template for a single option */
|
||||
_templateOption: function(pair) {
|
||||
var $el = $("<label/>").addClass("btn btn-default");
|
||||
pair.icon &&
|
||||
var $el = $("<label/>").addClass("btn btn-secondary");
|
||||
if (pair.icon) {
|
||||
$el.append(
|
||||
$("<i/>")
|
||||
.addClass("fa")
|
||||
.addClass(pair.icon)
|
||||
.addClass(!pair.label && "no-padding")
|
||||
);
|
||||
}
|
||||
$el.append(
|
||||
$("<input/>").attr({
|
||||
type: "radio",
|
||||
@@ -268,15 +276,15 @@ RadioButton.View = Base.extend({
|
||||
value: pair.value
|
||||
})
|
||||
);
|
||||
pair.label && $el.append(pair.label);
|
||||
if (pair.label) {
|
||||
$el.append(pair.label);
|
||||
}
|
||||
return $el;
|
||||
},
|
||||
|
||||
/** Main template function */
|
||||
_template: function() {
|
||||
return $("<div/>")
|
||||
.addClass("btn-group ui-radiobutton")
|
||||
.attr("data-toggle", "buttons");
|
||||
return $("<div/>").addClass("btn-group ui-radiobutton");
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
@@ -1,7 +1,12 @@
|
||||
/**
|
||||
* Popover wrapper
|
||||
*/
|
||||
import * as Backbone from "backbone";
|
||||
import * as _ from "underscore";
|
||||
import Utils from "utils/utils";
|
||||
|
||||
/* global $ */
|
||||
|
||||
var View = Backbone.View.extend({
|
||||
optionsDefault: {
|
||||
with_close: true,
|
||||
@@ -21,18 +26,23 @@ var View = Backbone.View.extend({
|
||||
this.$body = this.$(".popover-content");
|
||||
|
||||
// add initial content
|
||||
this.options.body && this.append(this.options.body);
|
||||
if (this.options.body) {
|
||||
this.append(this.options.body);
|
||||
}
|
||||
|
||||
// add event to hide if click is outside of popup and not on container
|
||||
var self = this;
|
||||
$("body").on(`mousedown.${this.uid}`, e => {
|
||||
// the 'is' for buttons that trigger popups
|
||||
// the 'has' for icons within a button that triggers a popup
|
||||
self.visible &&
|
||||
if (
|
||||
self.visible &&
|
||||
!$(self.options.container).is(e.target) &&
|
||||
!$(self.el).is(e.target) &&
|
||||
$(self.el).has(e.target).length === 0 &&
|
||||
$(self.el).has(e.target).length === 0
|
||||
) {
|
||||
self.hide();
|
||||
}
|
||||
});
|
||||
},
|
||||
|
||||
@@ -43,7 +53,7 @@ var View = Backbone.View.extend({
|
||||
this.$title.html(this.options.title);
|
||||
this.$el
|
||||
.removeClass()
|
||||
.addClass("ui-popover popover fade in")
|
||||
.addClass("ui-popover popover in")
|
||||
.addClass(this.options.placement);
|
||||
this.$el.css(this._get_placement(this.options.placement));
|
||||
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user