mirror of
https://github.com/galaxyproject/galaxy.git
synced 2026-09-24 16:30:27 +08:00
(merge dev)
This commit is contained in:
@@ -0,0 +1,17 @@
|
||||
.venv/
|
||||
client/
|
||||
database/
|
||||
doc/patch.py
|
||||
doc/source/conf.py
|
||||
lib/galaxy/util/jstree.py
|
||||
lib/pkg_resources.py
|
||||
scripts/api/
|
||||
scripts/data_libraries/
|
||||
scripts/loc_files/
|
||||
scripts/microbes/
|
||||
scripts/others/
|
||||
scripts/scramble/
|
||||
scripts/tool_shed/
|
||||
scripts/tools/
|
||||
scripts/transfer.py
|
||||
tools/
|
||||
@@ -2,11 +2,7 @@
|
||||
|
||||
set -e
|
||||
|
||||
# Run broad range of checks over specific white-listed files, this uses
|
||||
# the default checks defined in setup.cfg.
|
||||
while read p; do
|
||||
flake8 $(eval echo "$p")
|
||||
done <.ci/pep8_sources.txt
|
||||
flake8 --exclude `paste -sd, .ci/flake8_blacklist.txt` .
|
||||
|
||||
# Look for obviously broken stuff lots more places.
|
||||
flake8 --select=E901,E902,F821,F822,F823,F831 --exclude lib/galaxy/util/pastescript/serve.py,lib/pkg_resources.py lib/ test/{api,unit}
|
||||
flake8 --select=E901,E902,F821,F822,F823,F831 --exclude lib/pkg_resources.py contrib/ lib/
|
||||
|
||||
@@ -1,49 +0,0 @@
|
||||
cron/parse_builds.py
|
||||
lib/galaxy/auth
|
||||
lib/galaxy/config.py
|
||||
lib/galaxy/datatypes/{data,proteomics}.py
|
||||
lib/galaxy/datatypes/converters/{bedgraph_to_array_tree_converter,wiggle_to_array_tree_converter}.py
|
||||
lib/galaxy/exceptions/error_codes.py
|
||||
lib/galaxy/jobs/{__init__,error_level,manager,stock_rules}.py
|
||||
lib/galaxy/main.py
|
||||
lib/galaxy/managers/{api_keys,citations,collections,collections_util,datasets,folders,pages,tags,workflows}.py
|
||||
lib/galaxy/model/orm/{engine_factory,now}.py
|
||||
lib/galaxy/model/{base,custom_types,search,util}.py
|
||||
lib/galaxy/model/tool_shed_install/__init__.py
|
||||
lib/galaxy/objectstore/{__init__,pulsar,rods,s3}.py
|
||||
lib/galaxy/openid/__init__.py
|
||||
lib/galaxy/queues.py
|
||||
lib/galaxy/queue_worker.py
|
||||
lib/galaxy/tags
|
||||
lib/galaxy/tools
|
||||
lib/galaxy/util/{__init__,json,permutations,plugin_config,properties,simplegraph,sockets,sleeper,streamball,submodules,xml_macros}.py
|
||||
lib/galaxy/visualization/data_providers/{__init__,genome,registry}.py
|
||||
lib/galaxy/web/{proxy,security}
|
||||
lib/galaxy/web/base/{__init__,interactive_environments}.py
|
||||
lib/galaxy/web/formatting.py
|
||||
lib/galaxy/webapps/__init__.py
|
||||
lib/galaxy/webapps/galaxy/__init__.py
|
||||
lib/galaxy/webapps/galaxy/api/{__init__,authenticate,dataset_collections,datatypes,folder_contents,folders,lda_datasets,job_files,libraries,library_contents,provenance,remote_files,search,users}.py
|
||||
lib/galaxy/webapps/galaxy/controllers/{__init__,admin,cloudlaunch,workflow}.py
|
||||
lib/galaxy/webapps/reports/app.py
|
||||
lib/galaxy/webapps/reports/controllers/{__init__,jobs,query,workflows}.py
|
||||
lib/galaxy/webapps/tool_shed/app.py
|
||||
lib/galaxy/webapps/tool_shed/controllers/__init__.py
|
||||
lib/galaxy/webapps/tool_shed/framework/__init__.py
|
||||
lib/galaxy/webapps/tool_shed/framework/middleware/__init__.py
|
||||
lib/galaxy/webapps/tool_shed/util/{__init__,shed_statistics}.py
|
||||
lib/galaxy/work
|
||||
lib/galaxy/workflow
|
||||
lib/galaxy/version.py
|
||||
lib/pulsar
|
||||
lib/tool_shed/__init__.py
|
||||
lib/tool_shed/galaxy_install/{install_manager,repair_repository_manager}.py
|
||||
lib/tool_shed/galaxy_install/repository_dependencies
|
||||
lib/tool_shed/galaxy_install/tools/{__init__,tool_panel_manager}.py
|
||||
lib/tool_shed/repository_types/{__init__,registry,unrestricted}.py
|
||||
lib/tool_shed/util/{__init__,repository_content_util,tool_util,web_util,workflow_util}.py
|
||||
test/functional/tools
|
||||
test/unit/jobs/test_job_configuration.py
|
||||
test/unit/tools
|
||||
tools/data_source/upload.py
|
||||
tools/evolution/codingSnps_filter.py
|
||||
@@ -76,6 +76,7 @@ tool-data/shared/igv/igv_build_sites.txt
|
||||
tool-data/shared/rviewer/rviewer_build_sites.txt
|
||||
tool-data/shared/ncbi/builds.txt
|
||||
tool-data/shared/ucsc/builds.txt
|
||||
tool-data/shared/ucsc/manual_builds.txt
|
||||
tool-data/shared/ensembl/builds.txt
|
||||
tool-data/shared/ucsc/publicbuilds.txt
|
||||
tool-data/shared/ucsc/ucsc_build_sites.txt
|
||||
|
||||
@@ -0,0 +1,99 @@
|
||||
Galaxy Project Code of Conduct
|
||||
==============================
|
||||
|
||||
This code of conduct outlines our expectations for participants within the
|
||||
Galaxy community, as well as steps to reporting unacceptable behavior. We are
|
||||
committed to providing a welcoming and inspiring community for all and expect
|
||||
our code of conduct to be honored. Anyone who violates this code of conduct may
|
||||
be banned from the community.
|
||||
|
||||
Our open source community strives to:
|
||||
|
||||
* **Be friendly and patient.**
|
||||
|
||||
* **Be welcoming**: We strive to be a community that welcomes and
|
||||
supports people of all backgrounds and identities. This includes, but is not
|
||||
limited to members of any race, ethnicity, culture, national origin, colour,
|
||||
immigration status, social and economic class, educational level, sex, sexual
|
||||
orientation, gender identity and expression, age, size, family status,
|
||||
political belief, religion, and mental and physical ability.
|
||||
|
||||
* **Be considerate**: Your work will be used by other people, and you in turn
|
||||
will depend on the work of others. Any decision you take will affect users
|
||||
and colleagues, and you should take those consequences into account when
|
||||
making decisions. Remember that we're a world-wide community, so you might
|
||||
not be communicating in someone else's primary language.
|
||||
|
||||
* **Be respectful**: Not all of us will agree all the time, but disagreement is
|
||||
no excuse for poor behavior and poor manners. We might all experience some
|
||||
frustration now and then, but we cannot allow that frustration to turn into a
|
||||
personal attack. It’s important to remember that a community where people
|
||||
feel uncomfortable or threatened is not a productive one.
|
||||
|
||||
* **Be careful in the words that we choose**: We are a community of
|
||||
professionals, and we conduct ourselves professionally. Be kind to others. Do
|
||||
not insult or put down other participants. Harassment and other exclusionary
|
||||
behavior aren't acceptable. This includes, but is not limited to: Violent
|
||||
threats or language directed against another person, Discriminatory jokes and
|
||||
language, Posting sexually explicit or violent material, Posting (or
|
||||
threatening to post) other people’s personally identifying information
|
||||
(“doxing”), Personal insults, especially those using racist or sexist terms,
|
||||
Unwelcome sexual attention, Advocating for, or encouraging, any of the above
|
||||
behavior, Repeated harassment of others. In general, if someone asks you to
|
||||
stop, then stop.
|
||||
|
||||
* **Try to understand why we disagree**: Disagreements, both social and
|
||||
technical, happen all the time. It is important that we resolve disagreements
|
||||
and differing views constructively. Remember that we’re different. Diversity
|
||||
contributes to the strength of our community, which is composed of people
|
||||
from a wide range of backgrounds. Different people have different
|
||||
perspectives on issues. Being unable to understand why someone holds a
|
||||
viewpoint doesn’t mean that they’re wrong. Don’t forget that it is human to
|
||||
err and blaming each other doesn’t get us anywhere. Instead, focus on helping
|
||||
to resolve issues and learning from mistakes.
|
||||
|
||||
### Diversity Statement
|
||||
|
||||
We encourage everyone to participate and are committed to building a community
|
||||
for all. Although we will fail at times, we seek to treat everyone both as
|
||||
fairly and equally as possible. Whenever a participant has made a mistake, we
|
||||
expect them to take responsibility for it. If someone has been harmed or
|
||||
offended, it is our responsibility to listen carefully and respectfully, and do
|
||||
our best to right the wrong.
|
||||
|
||||
Although this list cannot be exhaustive, we explicitly honor diversity in age,
|
||||
gender, gender identity or expression, culture, ethnicity, language, national
|
||||
origin, political beliefs, profession, race, religion, sexual orientation,
|
||||
socioeconomic status, and technical ability. We will not tolerate
|
||||
discrimination based on any of the protected characteristics above, including
|
||||
participants with disabilities.
|
||||
|
||||
### Reporting Issues
|
||||
|
||||
If you experience or witness unacceptable behavior, or have any other concerns,
|
||||
please report it by contacting Dave Clements (clementsgalaxy@gmail.com). To
|
||||
report an issue involving Dave Clements please email James Taylor
|
||||
(james@taylorlab.org). All reports will be handled with discretion. In your
|
||||
report please include:
|
||||
|
||||
- Your contact information.
|
||||
|
||||
- Names (real, nicknames, or pseudonyms) of any individuals involved. If there
|
||||
are additional witnesses, please include them as well. Your account of what
|
||||
occurred, and if you believe the incident is ongoing. If there is a publicly
|
||||
available record (e.g. a mailing list archive or a public IRC logger), please
|
||||
include a link.
|
||||
|
||||
- Any additional information that may be helpful.
|
||||
|
||||
After filing a report, a representative will contact you personally, review the
|
||||
incident, follow up with any additional questions, and make a decision as to
|
||||
how to respond. If the person who is harassing you is part of the response
|
||||
team, they will recuse themselves from handling your incident. If the complaint
|
||||
originates from a member of the response team, it will be handled by a
|
||||
different member of the response team. We will respect confidentiality requests
|
||||
for the purpose of protecting victims of abuse.
|
||||
|
||||
### Attribution & Acknowledgements
|
||||
|
||||
This code of conduct is based on the Open Code of Conduct from the TODOGroup.
|
||||
+40
-94
@@ -1,5 +1,6 @@
|
||||
# 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
|
||||
@@ -13,37 +14,53 @@ deeper discussion of some of these points - please see the
|
||||
|
||||
## Before you Begin
|
||||
|
||||
If you have an idea for a feature to add or an approach for a bugfix -
|
||||
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 venue for this is the [Galaxy Trello
|
||||
board](https://wiki.galaxyproject.org/Issues). Browse through existing
|
||||
cards [here](http://bit.ly/gxytrello) and if one seems related comment
|
||||
on it. If no existing cards seem appropriate, a new issue can be
|
||||
opened using [this form](http://galaxyproject.org/trello). Galaxy
|
||||
developers are also generally available via
|
||||
common venues for this are
|
||||
[GitHub issues](https://github.com/galaxyproject/galaxy/issues) and the
|
||||
[Galaxy and Tool Shed Trello boards](https://wiki.galaxyproject.org/Issues).
|
||||
Browse through existing GitHub issues and Trello cards and if one seems related,
|
||||
comment on it. We also maintain a [card](https://trello.com/c/eFdPIdIB) with
|
||||
links to smaller issues we believe would make the best entry points for new
|
||||
developers.
|
||||
Galaxy developers are generally available via
|
||||
[IRC](https://wiki.galaxyproject.org/GetInvolved#IRC_Channel) and on
|
||||
the [development mailing list](http://dev.list.galaxyproject.org/).
|
||||
|
||||
## 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).
|
||||
|
||||
## 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).
|
||||
|
||||
* If you are new to Git - please check out the very useful tutorials on the [GitHub training hub](https://training.github.com/kit/)
|
||||
* 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 account](https://github.com/)
|
||||
* Make sure you have a free [GitHub](https://github.com/) account.
|
||||
|
||||
* Fork the galaxy repository on
|
||||
[GitHub](https://github.com/galaxyproject/galaxy) to make your changes.
|
||||
* 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/):
|
||||
```
|
||||
$ git remote add upstream https://github.com/galaxyproject/galaxy
|
||||
$ git fetch upstream
|
||||
$ git checkout dev
|
||||
$ git merge upstream/dev
|
||||
```
|
||||
|
||||
* Choose the correct branch to develop your changes against.
|
||||
|
||||
* Additions to the code base should be pushed to the `dev` branch (`git
|
||||
* 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)
|
||||
@@ -51,7 +68,7 @@ the [development mailing list](http://dev.list.galaxyproject.org/).
|
||||
|
||||
* Serious security problems should not be fixed via pull request - please
|
||||
responsibly disclose these by e-mailing them (with or without patches) to
|
||||
galaxy-lab@lists.galaxyproject.org. The Galaxy core development team will
|
||||
galaxy-committers@lists.galaxyproject.org . The Galaxy core development team will
|
||||
issue patches to public servers before announcing the issue to ensure there
|
||||
is time to patch and highlight these fixes widely. We will provide you
|
||||
credit for the discovery when publicly disclosing the issue.
|
||||
@@ -68,7 +85,8 @@ the [development mailing list](http://dev.list.galaxyproject.org/).
|
||||
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.
|
||||
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/).
|
||||
@@ -77,23 +95,22 @@ the [development mailing list](http://dev.list.galaxyproject.org/).
|
||||
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 change should be made.
|
||||
* A description of why the changes should be made.
|
||||
|
||||
* A description of implementation of the change.
|
||||
* A description of the implementation of the changes.
|
||||
|
||||
* A description of how to test the change.
|
||||
* A description of how to test the changes.
|
||||
|
||||
## Ideas
|
||||
* The pull request should pass all the continuous integration tests which are
|
||||
automatically run by GitHub using e.g. Travis CI.
|
||||
|
||||
Galaxy's [Trello board](http://bit.ly/gxytrello) is filled with bugs and ideas
|
||||
for enhancements, but we maintain a [card](https://trello.com/c/eFdPIdIB) with
|
||||
links to smaller issues we believe would make the best entry points for new
|
||||
developers.
|
||||
* 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
|
||||
[ToolShed](https://wiki.galaxyproject.org/ToolShed) and not in this
|
||||
[Tool Shed](https://wiki.galaxyproject.org/ToolShed) and not in this
|
||||
repository directly. If you are looking to supply fixes for migrated
|
||||
core tools that used to exist in this repository - please checkout
|
||||
the [tools-devteam](https://github.com/galaxyproject/tools-devteam)
|
||||
@@ -101,74 +118,3 @@ developers.
|
||||
|
||||
More information about tool development can be found [on the
|
||||
wiki](https://wiki.galaxyproject.org/Develop).
|
||||
|
||||
## Handling Pull Requests
|
||||
|
||||
Everyone is encouraged to express opinions and issue non-binding votes on pull
|
||||
requests, but only members of the *committers* group may issue binding votes
|
||||
on pull requests. Information on the *committers* group can be found in the
|
||||
[organization document](https://github.com/galaxyproject/galaxy/blob/dev/doc/source/project/organization.rst)
|
||||
describing governance of the core Galaxy code base.
|
||||
|
||||
Votes on pull requests should take the form of
|
||||
[+1, 0, -1, and fractions](http://www.apache.org/foundation/voting.html)
|
||||
as outlined by the Apache Foundation.
|
||||
|
||||
Pull requests modifying pre-existing releases should be restricted to bug fixes
|
||||
and require at least 2 *+1* binding votes from someone other than the author of
|
||||
the pull request with no *-1* binding votes.
|
||||
|
||||
Pull requests changing or clarifying the procedures governing this repository:
|
||||
|
||||
- Must be made to the ``dev`` branch of this repository.
|
||||
- Must remain open for at least 192 hours (unless every qualified committer has
|
||||
voted).
|
||||
- Require binding *+1* votes from at least 25% of qualified *committers* with no
|
||||
*-1* binding votes.
|
||||
- Should be titled with the prefix *[PROCEDURES]* and tagged with
|
||||
the *procedures* tag in Github.
|
||||
- Should not be modified once open. If changes are needed, the pull request
|
||||
should be closed, re-opened with modifications, and votes reset.
|
||||
- Should be restricted to just modifying the procedures and generally should not
|
||||
contain code modifications.
|
||||
- If the pull request adds or removes committers, there must be a separate
|
||||
pull request for each person added or removed.
|
||||
|
||||
Any other pull request requires at least 1 *+1* binding vote from someone other
|
||||
than the author of the pull request.
|
||||
|
||||
### Timelines
|
||||
|
||||
Except in the case of pull requests modifying governance procedures, there are
|
||||
generally no objective guidelines defining how long pull requests must remain
|
||||
open for comment. Subjectively speaking though - larger and more potentially
|
||||
controversial pull requests containing enhancements should remain open for a at
|
||||
least a few days to give everyone the opportunity to weigh in.
|
||||
|
||||
### Vetos
|
||||
|
||||
A note on vetos (*-1* votes) taken verbatim from the
|
||||
[Apache Foundation](http://www.apache.org/foundation/voting.html):
|
||||
|
||||
>"A code-modification proposal may be stopped dead in its tracks by a -1 vote
|
||||
by a qualified voter. This constitutes a veto, and it cannot be overruled nor
|
||||
overridden by anyone. Vetos stand until and unless withdrawn by their casters.
|
||||
>
|
||||
>To prevent vetos from being used capriciously, they must be accompanied by a
|
||||
technical justification showing why the change is bad (opens a security
|
||||
exposure, negatively affects performance, etc. ). A veto without a
|
||||
justification is invalid and has no weight."
|
||||
|
||||
For votes regarding non-coding issues such as procedure changes, the requirement
|
||||
that a veto is accompanied by a *technical* justification is relaxed somewhat,
|
||||
though a well reasoned justification must still be included.
|
||||
|
||||
### Direct Commit Access
|
||||
|
||||
The Galaxy *committers* group may only commit directly to Galaxy (i.e. outside
|
||||
of a pull request and not following the procedures described here) the
|
||||
following two categories of patches:
|
||||
|
||||
* Patches for serious security vulnerabilities.
|
||||
* Cherry-picking and/or merging of existing approved commits to other
|
||||
branches.
|
||||
|
||||
+8
-42
@@ -1,20 +1,23 @@
|
||||
.. figure:: https://wiki.galaxyproject.org/Images/GalaxyLogo?action=AttachFile&do=get&target=galaxy_project_logo.jpg
|
||||
:alt: Galaxy Logo
|
||||
|
||||
The latest information about Galaxy is available via `http://galaxyproject.org/ <http://galaxyproject.org/>`__
|
||||
The latest information about Galaxy is available via `https://galaxyproject.org/ <https://galaxyproject.org/>`__
|
||||
|
||||
.. image:: https://img.shields.io/badge/questions-galaxy%20biostar-blue.svg
|
||||
:target: https://biostar.usegalaxy.org
|
||||
:alt: Ask a question
|
||||
|
||||
.. image:: https://img.shields.io/badge/chat-irc.freenode.net%23galaxyproject-blue.svg
|
||||
:target: https://webchat.freenode.net/?channels=galaxyproject
|
||||
:alt: Chat with us
|
||||
|
||||
.. image:: https://readthedocs.org/projects/galaxy/badge/?version=master
|
||||
:target: http://galaxy.readthedocs.org/en/master/
|
||||
:target: https://galaxy.readthedocs.org/en/master/
|
||||
:alt: Documentation Status
|
||||
|
||||
.. image:: https://travis-ci.org/galaxyproject/galaxy.svg?branch=dev
|
||||
:target: https://travis-ci.org/galaxyproject/galaxy
|
||||
:alt: Inspect the test results
|
||||
|
||||
Galaxy Quickstart
|
||||
=================
|
||||
@@ -49,44 +52,7 @@ Not all dependencies are included for the tools provided in the sample
|
||||
|
||||
https://wiki.galaxyproject.org/Admin/Tools/ToolDependencies
|
||||
|
||||
Issues
|
||||
======
|
||||
|
||||
Issues can be submitted to trello via the `galaxyproject
|
||||
website <http://galaxyproject.org/trello/>`__ and viewed on the `Galaxy
|
||||
Trello Board <https://trello.com/b/75c1kASa/galaxy-development>`__.
|
||||
|
||||
|
||||
Galaxy Development
|
||||
==================
|
||||
|
||||
Galaxy welcomes new development! There is extensive documentation on developing
|
||||
with Galaxy on the `wiki <https://wiki.galaxyproject.org/Develop>`__.
|
||||
|
||||
Source Repository
|
||||
-----------------
|
||||
|
||||
Galaxy development has moved to `Github
|
||||
<https://github.com/galaxyproject/galaxy>`__.
|
||||
|
||||
**Developers** should develop against the ``dev`` branch in the Github
|
||||
repository.
|
||||
|
||||
**Deployers** can continue to pull Galaxy changes from Bitbucket without
|
||||
interruption. Alternatively, deployers can track the ``master`` branch in
|
||||
Github.
|
||||
|
||||
Syncing a Fork
|
||||
--------------
|
||||
|
||||
If you fork Galaxy to work on it, you may be interested in keeping your copy
|
||||
up to date with respect to the main repository. Github has `good documentation
|
||||
<https://help.github.com/articles/syncing-a-fork/>`__ on this.
|
||||
|
||||
.. code:: console
|
||||
|
||||
$ git remote add upstream https://github.com/galaxyproject/galaxy
|
||||
$ git fetch upstream
|
||||
$ git checkout dev
|
||||
$ git merge upstream/dev
|
||||
Issues and Galaxy Development
|
||||
=============================
|
||||
|
||||
Please see `CONTRIBUTING.md <CONTRIBUTING.md>`_ .
|
||||
|
||||
+2
-1
@@ -22,5 +22,6 @@ module.exports = function(grunt) {
|
||||
|
||||
// see the sub directory grunt-tasks/ for individual task definitions
|
||||
grunt.loadTasks( 'grunt-tasks' );
|
||||
grunt.registerTask( 'default', [ 'uglify' ] );
|
||||
// note: 'handlebars' *not* 'templates' since handlebars doesn't call uglify
|
||||
grunt.registerTask( 'default', [ 'handlebars', 'uglify' ] );
|
||||
};
|
||||
|
||||
@@ -33,6 +33,19 @@ This will:
|
||||
2. generate source maps and place them in static/maps
|
||||
|
||||
|
||||
Templates
|
||||
=========
|
||||
|
||||
You can change and recompile the templates by using:
|
||||
|
||||
grunt templates
|
||||
|
||||
This will:
|
||||
|
||||
1. recompile the templates in client/galaxy/scripts/templates to client/galaxy/scripts/templates/compiled
|
||||
2. minify and generate source maps for the compiled templates
|
||||
|
||||
|
||||
Changing Styles/CSS
|
||||
===================
|
||||
|
||||
|
||||
+3
-3
@@ -9,7 +9,7 @@
|
||||
"homepage": "usegalaxy.org",
|
||||
"dependencies": {
|
||||
"jquery": "~1.11.1",
|
||||
"traceKit": "*",
|
||||
"tracekit": "*",
|
||||
"ravenjs": "~1.1.16",
|
||||
"underscore": "~1.7.0",
|
||||
"backbone": "~1.1.2",
|
||||
@@ -18,7 +18,7 @@
|
||||
"farbtastic": "~2.0.0-alpha.1",
|
||||
"toastr": "~2.1.0",
|
||||
"jQTouch": "git://github.com/senchalabs/jQTouch#~1.0.0",
|
||||
"bib2json": "git://github.com/mayanklahiri/bib2json",
|
||||
"bib2json": "git://github.com/galaxyproject/bib2json#galaxy",
|
||||
"jquery-form": "~3.46.0",
|
||||
"jquery-autocomplete": "git://github.com/dyve/jquery-autocomplete",
|
||||
"select2": "~3.5.2",
|
||||
@@ -30,7 +30,7 @@
|
||||
"jstree": "~3.0.9",
|
||||
"jquery-ui": "git://github.com/jquery/jquery-ui.git#~1.11.2",
|
||||
"threedubmedia.jquery.event": "*",
|
||||
"handlebars": "~2.0.0",
|
||||
"handlebars": "~3.0.0",
|
||||
"jquery-migrate": "~1.2.1",
|
||||
"requirejs": "~2.1.17"
|
||||
},
|
||||
|
||||
@@ -138,40 +138,9 @@ GalaxyApp.prototype._setUpListeners = function _setUpListeners(){
|
||||
};
|
||||
//TODO:?? we might somehow manage to *retry* ajax using either this hook or Backbone.sync
|
||||
});
|
||||
self._listenToGalaxyMain();
|
||||
return self;
|
||||
};
|
||||
|
||||
/** trigger an event when the main frame loads a new page */
|
||||
GalaxyApp.prototype._listenToGalaxyMain = function _listenToGalaxyMain(){
|
||||
var self = this,
|
||||
selector = 'iframe#galaxy_main',
|
||||
galaxy_main = $( selector );
|
||||
galaxy_main = galaxy_main.size()? galaxy_main.get( 0 ).contentWindow : undefined ;
|
||||
if( galaxy_main ){
|
||||
// TODO: this would be better external to this function so it can be removed
|
||||
var handler = function __galaxyMainOnLoadHandler( ev ){
|
||||
try {
|
||||
var _galaxy_main = $( selector ).get( 0 ).contentWindow,
|
||||
full = _galaxy_main.location.pathname + _galaxy_main.location.search + _galaxy_main.location.hash;
|
||||
self.trigger( 'galaxy_main:load', {
|
||||
fullpath: full,
|
||||
pathname: _galaxy_main.location.pathname,
|
||||
search : _galaxy_main.location.search,
|
||||
hash : _galaxy_main.location.hash
|
||||
});
|
||||
|
||||
} catch( err ){
|
||||
self.debug( 'Error handling main frame load:', err );
|
||||
}
|
||||
};
|
||||
$( selector ).on( 'load', handler );
|
||||
|
||||
} else {
|
||||
self.debug( 'No galaxy_main found' );
|
||||
}
|
||||
};
|
||||
|
||||
/** string rep */
|
||||
GalaxyApp.prototype.toString = function toString(){
|
||||
var userEmail = this.user.get( 'email' ) || '(anonymous)';
|
||||
|
||||
@@ -97,6 +97,9 @@ var GalaxyLibrary = Backbone.View.extend({
|
||||
this.library_router = new LibraryRouter();
|
||||
|
||||
this.library_router.on( 'route:libraries', function() {
|
||||
if ( Galaxy.libraries.libraryToolbarView){
|
||||
Galaxy.libraries.libraryToolbarView.$el.unbind('click');
|
||||
}
|
||||
Galaxy.libraries.libraryToolbarView = new mod_librarytoolbar_view.LibraryToolbarView();
|
||||
Galaxy.libraries.libraryListView = new mod_librarylist_view.LibraryListView();
|
||||
});
|
||||
@@ -106,7 +109,7 @@ var GalaxyLibrary = Backbone.View.extend({
|
||||
Galaxy.libraries.libraryToolbarView = new mod_librarytoolbar_view.LibraryToolbarView();
|
||||
Galaxy.libraries.libraryListView = new mod_librarylist_view.LibraryListView( { show_page: show_page } );
|
||||
} else {
|
||||
Galaxy.libraries.libraryListView.render( { show_page: show_page } )
|
||||
Galaxy.libraries.libraryListView.render( { show_page: show_page } );
|
||||
}
|
||||
});
|
||||
|
||||
@@ -123,7 +126,7 @@ var GalaxyLibrary = Backbone.View.extend({
|
||||
Galaxy.libraries.folderToolbarView = new mod_foldertoolbar_view.FolderToolbarView( {id: id} );
|
||||
Galaxy.libraries.folderListView = new mod_folderlist_view.FolderListView( { id: id, show_page: show_page } );
|
||||
} else {
|
||||
Galaxy.libraries.folderListView.render( { id: id, show_page: parseInt( show_page ) } )
|
||||
Galaxy.libraries.folderListView.render( { id: id, show_page: parseInt( show_page ) } );
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
@@ -1,91 +1,84 @@
|
||||
// dependencies
|
||||
define(["galaxy.masthead"], function(mod_masthead) {
|
||||
define(['galaxy.masthead'], function( Masthead ) {
|
||||
|
||||
/** GalaxyMenu uses the GalaxyMasthead class in order to add menu items and icons to the Masthead **/
|
||||
var GalaxyMenu = Backbone.Model.extend({
|
||||
// options
|
||||
options: null,
|
||||
|
||||
// link masthead class
|
||||
masthead: null,
|
||||
|
||||
// initialize
|
||||
initialize: function(options) {
|
||||
initialize: function( options ) {
|
||||
this.options = options.config;
|
||||
this.masthead = options.masthead;
|
||||
this.create();
|
||||
},
|
||||
|
||||
// default menu
|
||||
create: function(){
|
||||
create: function() {
|
||||
//
|
||||
// Analyze data tab.
|
||||
//
|
||||
var tab_analysis = new mod_masthead.GalaxyMastheadTab({
|
||||
id : "analysis",
|
||||
title : "Analyze Data",
|
||||
content : "",
|
||||
var tab_analysis = new Masthead.GalaxyMastheadTab({
|
||||
id : 'analysis',
|
||||
title : 'Analyze Data',
|
||||
content : '',
|
||||
title_attribute : 'Analysis home view'
|
||||
});
|
||||
this.masthead.append(tab_analysis);
|
||||
this.masthead.append( tab_analysis );
|
||||
|
||||
//
|
||||
// Workflow tab.
|
||||
//
|
||||
|
||||
var workflow_options = {
|
||||
id : "workflow",
|
||||
title : "Workflow",
|
||||
content : "workflow",
|
||||
id : 'workflow',
|
||||
title : 'Workflow',
|
||||
content : 'workflow',
|
||||
title_attribute : 'Chain tools into workflows'
|
||||
}
|
||||
if (!this.options.user.valid){
|
||||
if ( !Galaxy.user.id ) {
|
||||
workflow_options.disabled = true; // disable workflows for anonymous users
|
||||
}
|
||||
|
||||
var tab_workflow = new mod_masthead.GalaxyMastheadTab(workflow_options);
|
||||
this.masthead.append(tab_workflow);
|
||||
var tab_workflow = new Masthead.GalaxyMastheadTab( workflow_options );
|
||||
this.masthead.append( tab_workflow );
|
||||
|
||||
//
|
||||
// 'Shared Items' or Libraries tab.
|
||||
//
|
||||
var tab_shared = new mod_masthead.GalaxyMastheadTab({
|
||||
id : "shared",
|
||||
title : "Shared Data",
|
||||
content : "library/index",
|
||||
var tab_shared = new Masthead.GalaxyMastheadTab({
|
||||
id : 'shared',
|
||||
title : 'Shared Data',
|
||||
content : 'library/index',
|
||||
title_attribute : 'Access published resources'
|
||||
});
|
||||
|
||||
tab_shared.add({
|
||||
title : "Data Libraries",
|
||||
content : "library/index"
|
||||
title : 'Data Libraries',
|
||||
content : 'library/index'
|
||||
});
|
||||
|
||||
tab_shared.add({
|
||||
title : "Data Libraries Beta",
|
||||
content : "library/list",
|
||||
title : 'Data Libraries Beta',
|
||||
content : 'library/list',
|
||||
divider : true
|
||||
});
|
||||
|
||||
tab_shared.add({
|
||||
title : "Published Histories",
|
||||
content : "history/list_published"
|
||||
title : 'Published Histories',
|
||||
content : 'history/list_published'
|
||||
});
|
||||
|
||||
tab_shared.add({
|
||||
title : "Published Workflows",
|
||||
content : "workflow/list_published"
|
||||
title : 'Published Workflows',
|
||||
content : 'workflow/list_published'
|
||||
|
||||
});
|
||||
|
||||
tab_shared.add({
|
||||
title : "Published Visualizations",
|
||||
content : "visualization/list_published"
|
||||
title : 'Published Visualizations',
|
||||
content : 'visualization/list_published'
|
||||
});
|
||||
|
||||
tab_shared.add({
|
||||
title : "Published Pages",
|
||||
content : "page/list_published"
|
||||
title : 'Published Pages',
|
||||
content : 'page/list_published'
|
||||
});
|
||||
|
||||
this.masthead.append(tab_shared);
|
||||
@@ -93,25 +86,24 @@ var GalaxyMenu = Backbone.Model.extend({
|
||||
//
|
||||
// Lab menu.
|
||||
//
|
||||
if (this.options.user.requests)
|
||||
{
|
||||
var tab_lab = new mod_masthead.GalaxyMastheadTab({
|
||||
id : "lab",
|
||||
title : "Lab"
|
||||
if ( this.options.user_requests ) {
|
||||
var tab_lab = new Masthead.GalaxyMastheadTab({
|
||||
id : 'lab',
|
||||
title : 'Lab'
|
||||
});
|
||||
tab_lab.add({
|
||||
title : "Sequencing Requests",
|
||||
content : "requests/index"
|
||||
title : 'Sequencing Requests',
|
||||
content : 'requests/index'
|
||||
});
|
||||
tab_lab.add({
|
||||
title : "Find Samples",
|
||||
content : "requests/find_samples_index"
|
||||
title : 'Find Samples',
|
||||
content : 'requests/find_samples_index'
|
||||
});
|
||||
tab_lab.add({
|
||||
title : "Help",
|
||||
title : 'Help',
|
||||
content : this.options.lims_doc_url
|
||||
});
|
||||
this.masthead.append(tab_lab);
|
||||
this.masthead.append( tab_lab );
|
||||
}
|
||||
|
||||
//
|
||||
@@ -119,219 +111,206 @@ var GalaxyMenu = Backbone.Model.extend({
|
||||
//
|
||||
|
||||
var visualization_options = {
|
||||
id : "visualization",
|
||||
title : "Visualization",
|
||||
content : "visualization/list",
|
||||
id : 'visualization',
|
||||
title : 'Visualization',
|
||||
content : 'visualization/list',
|
||||
title_attribute : 'Visualize datasets'
|
||||
}
|
||||
if (!this.options.user.valid){
|
||||
visualization_options.disabled = true; // disable visualizations for anonymous users
|
||||
|
||||
// disable visualizations for anonymous users
|
||||
if ( !Galaxy.user.id ) {
|
||||
visualization_options.disabled = true;
|
||||
}
|
||||
var tab_visualization = new Masthead.GalaxyMastheadTab( visualization_options );
|
||||
|
||||
var tab_visualization = new mod_masthead.GalaxyMastheadTab(visualization_options);
|
||||
|
||||
if (this.options.user.valid){ //add submenu only when user is logged in
|
||||
// add submenu only when user is logged in
|
||||
if ( Galaxy.user.id ) {
|
||||
tab_visualization.add({
|
||||
title : "New Track Browser",
|
||||
content : "visualization/trackster",
|
||||
target : "_frame"
|
||||
title : 'New Track Browser',
|
||||
content : 'visualization/trackster',
|
||||
target : '_frame'
|
||||
});
|
||||
tab_visualization.add({
|
||||
title : "Saved Visualizations",
|
||||
content : "visualization/list",
|
||||
target : "_frame"
|
||||
title : 'Saved Visualizations',
|
||||
content : 'visualization/list',
|
||||
target : '_frame'
|
||||
});
|
||||
}
|
||||
this.masthead.append(tab_visualization);
|
||||
|
||||
//
|
||||
// Cloud menu.
|
||||
//
|
||||
if (this.options.enable_cloud_launch){
|
||||
var tab_cloud = new mod_masthead.GalaxyMastheadTab({
|
||||
id : "cloud",
|
||||
title : "Cloud",
|
||||
content : "cloudlaunch/index"
|
||||
});
|
||||
tab_cloud.add({
|
||||
title : "New Cloud Cluster",
|
||||
content : "cloudlaunch/index"
|
||||
});
|
||||
this.masthead.append(tab_cloud);
|
||||
}
|
||||
this.masthead.append( tab_visualization );
|
||||
|
||||
//
|
||||
// Admin.
|
||||
//
|
||||
if (this.options.is_admin_user) {
|
||||
var tab_admin = new mod_masthead.GalaxyMastheadTab({
|
||||
id : "admin",
|
||||
title : "Admin",
|
||||
content : "admin",
|
||||
extra_class : "admin-only",
|
||||
if ( Galaxy.user.get( 'is_admin' ) ) {
|
||||
var tab_admin = new Masthead.GalaxyMastheadTab({
|
||||
id : 'admin',
|
||||
title : 'Admin',
|
||||
content : 'admin',
|
||||
extra_class : 'admin-only',
|
||||
title_attribute : 'Administer this Galaxy'
|
||||
});
|
||||
this.masthead.append(tab_admin);
|
||||
this.masthead.append( tab_admin );
|
||||
}
|
||||
|
||||
//
|
||||
// Help tab.
|
||||
//
|
||||
var tab_help = new mod_masthead.GalaxyMastheadTab({
|
||||
id : "help",
|
||||
title : "Help",
|
||||
var tab_help = new Masthead.GalaxyMastheadTab({
|
||||
id : 'help',
|
||||
title : 'Help',
|
||||
title_attribute : 'Support, contact, and community hubs'
|
||||
});
|
||||
if (this.options.biostar_url){
|
||||
if ( this.options.biostar_url ){
|
||||
tab_help.add({
|
||||
title : "Galaxy Biostar",
|
||||
title : 'Galaxy Biostar',
|
||||
content : this.options.biostar_url_redirect,
|
||||
target : "_blank"
|
||||
target : '_blank'
|
||||
});
|
||||
tab_help.add({
|
||||
title : "Ask a question",
|
||||
content : "biostar/biostar_question_redirect",
|
||||
target : "_blank"
|
||||
title : 'Ask a question',
|
||||
content : 'biostar/biostar_question_redirect',
|
||||
target : '_blank'
|
||||
});
|
||||
}
|
||||
tab_help.add({
|
||||
title : "Support",
|
||||
title : 'Support',
|
||||
content : this.options.support_url,
|
||||
target : "_blank"
|
||||
target : '_blank'
|
||||
});
|
||||
tab_help.add({
|
||||
title : "Search",
|
||||
title : 'Search',
|
||||
content : this.options.search_url,
|
||||
target : "_blank"
|
||||
target : '_blank'
|
||||
});
|
||||
tab_help.add({
|
||||
title : "Mailing Lists",
|
||||
title : 'Mailing Lists',
|
||||
content : this.options.mailing_lists,
|
||||
target : "_blank"
|
||||
target : '_blank'
|
||||
});
|
||||
tab_help.add({
|
||||
title : "Videos",
|
||||
title : 'Videos',
|
||||
content : this.options.screencasts_url,
|
||||
target : "_blank"
|
||||
target : '_blank'
|
||||
});
|
||||
tab_help.add({
|
||||
title : "Wiki",
|
||||
title : 'Wiki',
|
||||
content : this.options.wiki_url,
|
||||
target : "_blank"
|
||||
target : '_blank'
|
||||
});
|
||||
tab_help.add({
|
||||
title : "How to Cite Galaxy",
|
||||
title : 'How to Cite Galaxy',
|
||||
content : this.options.citation_url,
|
||||
target : "_blank"
|
||||
target : '_blank'
|
||||
});
|
||||
if (this.options.terms_url){
|
||||
tab_help.add({
|
||||
title : "Terms and Conditions",
|
||||
title : 'Terms and Conditions',
|
||||
content : this.options.terms_url,
|
||||
target : "_blank"
|
||||
target : '_blank'
|
||||
});
|
||||
}
|
||||
this.masthead.append(tab_help);
|
||||
this.masthead.append( tab_help );
|
||||
|
||||
//
|
||||
// User tab.
|
||||
//
|
||||
if (!this.options.user.valid){
|
||||
var tab_user = new mod_masthead.GalaxyMastheadTab({
|
||||
id : "user",
|
||||
title : "User",
|
||||
extra_class : "loggedout-only",
|
||||
if ( !Galaxy.user.id ){
|
||||
var tab_user = new Masthead.GalaxyMastheadTab({
|
||||
id : 'user',
|
||||
title : 'User',
|
||||
extra_class : 'loggedout-only',
|
||||
title_attribute : 'Account registration or login'
|
||||
});
|
||||
|
||||
// login
|
||||
tab_user.add({
|
||||
title : "Login",
|
||||
content : "user/login",
|
||||
target : "galaxy_main"
|
||||
title : 'Login',
|
||||
content : 'user/login',
|
||||
target : 'galaxy_main'
|
||||
});
|
||||
|
||||
// register
|
||||
if (this.options.allow_user_creation){
|
||||
if ( this.options.allow_user_creation ){
|
||||
tab_user.add({
|
||||
title : "Register",
|
||||
content : "user/create",
|
||||
target : "galaxy_main"
|
||||
title : 'Register',
|
||||
content : 'user/create',
|
||||
target : 'galaxy_main'
|
||||
});
|
||||
}
|
||||
|
||||
// add to masthead
|
||||
this.masthead.append(tab_user);
|
||||
this.masthead.append( tab_user );
|
||||
} else {
|
||||
var tab_user = new mod_masthead.GalaxyMastheadTab({
|
||||
id : "user",
|
||||
title : "User",
|
||||
extra_class : "loggedin-only",
|
||||
var tab_user = new Masthead.GalaxyMastheadTab({
|
||||
id : 'user',
|
||||
title : 'User',
|
||||
extra_class : 'loggedin-only',
|
||||
title_attribute : 'Account preferences and saved data'
|
||||
});
|
||||
|
||||
// show user logged in info
|
||||
tab_user.add({
|
||||
title : "Logged in as " + this.options.user.email
|
||||
title : 'Logged in as ' + Galaxy.user.get( 'email' )
|
||||
});
|
||||
|
||||
tab_user.add({
|
||||
title : "Preferences",
|
||||
content : "user?cntrller=user",
|
||||
target : "galaxy_main"
|
||||
title : 'Preferences',
|
||||
content : 'user?cntrller=user',
|
||||
target : 'galaxy_main'
|
||||
});
|
||||
|
||||
tab_user.add({
|
||||
title : "Custom Builds",
|
||||
content : "user/dbkeys",
|
||||
target : "galaxy_main"
|
||||
title : 'Custom Builds',
|
||||
content : 'user/dbkeys',
|
||||
target : 'galaxy_main'
|
||||
});
|
||||
|
||||
tab_user.add({
|
||||
title : "Logout",
|
||||
content : "user/logout",
|
||||
target : "_top",
|
||||
title : 'Logout',
|
||||
content : 'user/logout',
|
||||
target : '_top',
|
||||
divider : true
|
||||
});
|
||||
|
||||
// default tabs
|
||||
tab_user.add({
|
||||
title : "Saved Histories",
|
||||
content : "history/list",
|
||||
target : "galaxy_main"
|
||||
title : 'Saved Histories',
|
||||
content : 'history/list',
|
||||
target : 'galaxy_main'
|
||||
});
|
||||
tab_user.add({
|
||||
title : "Saved Datasets",
|
||||
content : "dataset/list",
|
||||
target : "galaxy_main"
|
||||
title : 'Saved Datasets',
|
||||
content : 'dataset/list',
|
||||
target : 'galaxy_main'
|
||||
});
|
||||
tab_user.add({
|
||||
title : "Saved Pages",
|
||||
content : "page/list",
|
||||
target : "_top"
|
||||
title : 'Saved Pages',
|
||||
content : 'page/list',
|
||||
target : '_top'
|
||||
});
|
||||
|
||||
tab_user.add({
|
||||
title : "API Keys",
|
||||
content : "user/api_keys?cntrller=user",
|
||||
target : "galaxy_main"
|
||||
title : 'API Keys',
|
||||
content : 'user/api_keys?cntrller=user',
|
||||
target : 'galaxy_main'
|
||||
});
|
||||
|
||||
if (this.options.use_remote_user){
|
||||
if ( this.options.use_remote_user ){
|
||||
tab_user.add({
|
||||
title : "Public Name",
|
||||
content : "user/edit_username?cntrller=user",
|
||||
target : "galaxy_main"
|
||||
title : 'Public Name',
|
||||
content : 'user/edit_username?cntrller=user',
|
||||
target : 'galaxy_main'
|
||||
});
|
||||
}
|
||||
|
||||
// add to masthead
|
||||
this.masthead.append(tab_user);
|
||||
this.masthead.append( tab_user );
|
||||
}
|
||||
|
||||
// identify active tab
|
||||
if (this.options.active_view)
|
||||
this.masthead.highlight(this.options.active_view);
|
||||
if ( this.options.active_view ) {
|
||||
this.masthead.highlight( this.options.active_view );
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
@@ -10,7 +10,6 @@ var ensure_dd_helper = function () {
|
||||
}
|
||||
|
||||
// Panels
|
||||
|
||||
var MIN_PANEL_WIDTH = 160,
|
||||
MAX_PANEL_WIDTH = 800;
|
||||
|
||||
@@ -118,9 +117,11 @@ $.extend( Panel.prototype, {
|
||||
$( this ).hide().off( 'mousemove', move );
|
||||
});
|
||||
});
|
||||
window.force_left_panel = function( x ) { self.force_panel( x ) };
|
||||
window.handle_minwidth_hint = function( x ) { self.handle_minwidth_hint( x ) };
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
// Modal dialog boxes
|
||||
var Modal = function( options ) {
|
||||
this.$overlay = options.overlay;
|
||||
|
||||
Vendored
+8
-8
@@ -1,6 +1,6 @@
|
||||
!function() {
|
||||
var d3 = {
|
||||
version: "3.5.5"
|
||||
version: "3.5.6"
|
||||
};
|
||||
var d3_arraySlice = [].slice, d3_array = function(list) {
|
||||
return d3_arraySlice.call(list);
|
||||
@@ -1463,8 +1463,7 @@
|
||||
function zoomended(dispatch) {
|
||||
if (!--zooming) dispatch({
|
||||
type: "zoomend"
|
||||
});
|
||||
center0 = null;
|
||||
}), center0 = null;
|
||||
}
|
||||
function mousedowned() {
|
||||
var that = this, target = d3.event.target, dispatch = event.of(that, arguments), dragged = 0, subject = d3.select(d3_window(that)).on(mousemove, moved).on(mouseup, ended), location0 = location(d3.mouse(that)), dragRestore = d3_event_dragSuppress(that);
|
||||
@@ -1553,8 +1552,8 @@
|
||||
}
|
||||
function mousewheeled() {
|
||||
var dispatch = event.of(this, arguments);
|
||||
if (mousewheelTimer) clearTimeout(mousewheelTimer); else translate0 = location(center0 = center || d3.mouse(this)),
|
||||
d3_selection_interrupt.call(this), zoomstarted(dispatch);
|
||||
if (mousewheelTimer) clearTimeout(mousewheelTimer); else d3_selection_interrupt.call(this),
|
||||
translate0 = location(center0 = center || d3.mouse(this)), zoomstarted(dispatch);
|
||||
mousewheelTimer = setTimeout(function() {
|
||||
mousewheelTimer = null;
|
||||
zoomended(dispatch);
|
||||
@@ -1699,8 +1698,9 @@
|
||||
return v < 16 ? "0" + Math.max(0, v).toString(16) : Math.min(255, v).toString(16);
|
||||
}
|
||||
function d3_rgb_parse(format, rgb, hsl) {
|
||||
format = format.toLowerCase();
|
||||
var r = 0, g = 0, b = 0, m1, m2, color;
|
||||
m1 = /([a-z]+)\((.*)\)/i.exec(format);
|
||||
m1 = /([a-z]+)\((.*)\)/.exec(format);
|
||||
if (m1) {
|
||||
m2 = m1[2].split(",");
|
||||
switch (m1[1]) {
|
||||
@@ -1715,7 +1715,7 @@
|
||||
}
|
||||
}
|
||||
}
|
||||
if (color = d3_rgb_names.get(format.toLowerCase())) {
|
||||
if (color = d3_rgb_names.get(format)) {
|
||||
return rgb(color.r, color.g, color.b);
|
||||
}
|
||||
if (format != null && format.charAt(0) === "#" && !isNaN(color = parseInt(format.slice(1), 16))) {
|
||||
@@ -5785,7 +5785,7 @@
|
||||
}
|
||||
d3.interpolators = [ function(a, b) {
|
||||
var t = typeof b;
|
||||
return (t === "string" ? d3_rgb_names.has(b) || /^(#|rgb\(|hsl\()/.test(b) ? d3_interpolateRgb : d3_interpolateString : b instanceof d3_color ? d3_interpolateRgb : Array.isArray(b) ? d3_interpolateArray : t === "object" && isNaN(b) ? d3_interpolateObject : d3_interpolateNumber)(a, b);
|
||||
return (t === "string" ? d3_rgb_names.has(b.toLowerCase()) || /^(#|rgb\(|hsl\()/i.test(b) ? d3_interpolateRgb : d3_interpolateString : b instanceof d3_color ? d3_interpolateRgb : Array.isArray(b) ? d3_interpolateArray : t === "object" && isNaN(b) ? d3_interpolateObject : d3_interpolateNumber)(a, b);
|
||||
} ];
|
||||
d3.interpolateArray = d3_interpolateArray;
|
||||
function d3_interpolateArray(a, b) {
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
// Farbtastic 2.0 alpha
|
||||
// Farbtastic 2.0.0-alpha.1
|
||||
(function ($) {
|
||||
|
||||
|
||||
var __debug = false;
|
||||
|
||||
$.fn.farbtastic = function (options) {
|
||||
@@ -15,7 +15,7 @@ $.farbtastic = function (container, options) {
|
||||
|
||||
$._farbtastic = function (container, options) {
|
||||
var fb = this;
|
||||
|
||||
|
||||
/////////////////////////////////////////////////////
|
||||
|
||||
/**
|
||||
@@ -98,7 +98,7 @@ $._farbtastic = function (container, options) {
|
||||
.find('div>*').css('position', 'absolute');
|
||||
|
||||
// IE Fix: Recreate canvas elements with doc.createElement and excanvas.
|
||||
navigator.userAgent.match(/msie/i) && $('canvas', container).each(function () {
|
||||
$.browser.msie || false && $('canvas', container).each(function () {
|
||||
// Fetch info.
|
||||
var attr = { 'class': $(this).attr('class'), style: this.getAttribute('style') },
|
||||
e = document.createElement('canvas');
|
||||
@@ -130,7 +130,7 @@ $._farbtastic = function (container, options) {
|
||||
fb.ctxOverlay = fb.cnvOverlay[0].getContext('2d');
|
||||
fb.ctxMask.translate(fb.mid, fb.mid);
|
||||
fb.ctxOverlay.translate(fb.mid, fb.mid);
|
||||
|
||||
|
||||
// Draw widget base layers.
|
||||
fb.drawCircle();
|
||||
fb.drawMask();
|
||||
@@ -166,7 +166,7 @@ $._farbtastic = function (container, options) {
|
||||
// New color
|
||||
color2 = fb.pack(fb.HSLToRGB([d2, 1, 0.5]));
|
||||
if (i > 0) {
|
||||
if (navigator.userAgent.match(/msie/i)) {
|
||||
if ($.browser.msie || false) {
|
||||
// IE's gradient calculations mess up the colors. Correct along the diagonals.
|
||||
var corr = (1 + Math.min(Math.abs(Math.tan(angle1)), Math.abs(Math.tan(Math.PI / 2 - angle1)))) / n;
|
||||
color1 = fb.pack(fb.HSLToRGB([d1 - 0.15 * corr, 1, 0.5]));
|
||||
@@ -204,7 +204,7 @@ $._farbtastic = function (container, options) {
|
||||
m.restore();
|
||||
__debug && $('body').append('<div>drawCircle '+ (+(new Date()) - tm) +'ms');
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Draw the saturation/luminance mask.
|
||||
*/
|
||||
@@ -224,9 +224,9 @@ $._farbtastic = function (container, options) {
|
||||
var c = (a > 0) ? ((2 * l - 1 + a) * .5 / a) : 0;
|
||||
outputPixel(x, y, c, a);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// Method #1: direct pixel access (new Canvas).
|
||||
if (fb.ctxMask.getImageData) {
|
||||
// Create half-resolution buffer.
|
||||
@@ -246,7 +246,7 @@ $._farbtastic = function (container, options) {
|
||||
fb.ctxMask.drawImage(buffer, 0, 0, sz + 1, sz + 1, -sq, -sq, sq * 2, sq * 2);
|
||||
}
|
||||
// Method #2: drawing commands (old Canvas).
|
||||
else if (!navigator.userAgent.match(/msie/i)) {
|
||||
else if (!($.browser.msie || false)) {
|
||||
// Render directly at half-resolution
|
||||
var sz = Math.floor(size / 2);
|
||||
calculateMask(sz, sz, function (x, y, c, a) {
|
||||
@@ -287,7 +287,7 @@ $._farbtastic = function (container, options) {
|
||||
}
|
||||
cache.push([c, a]);
|
||||
});
|
||||
}
|
||||
}
|
||||
__debug && $('body').append('<div>drawMask '+ (+(new Date()) - tm) +'ms');
|
||||
}
|
||||
|
||||
@@ -313,7 +313,7 @@ $._farbtastic = function (container, options) {
|
||||
|
||||
// Update the overlay canvas.
|
||||
fb.ctxOverlay.clearRect(-fb.mid, -fb.mid, sz, sz);
|
||||
for (i in circles) {
|
||||
for (var i = 0; i < circles.length; i++) {
|
||||
var c = circles[i];
|
||||
fb.ctxOverlay.lineWidth = c.lw;
|
||||
fb.ctxOverlay.strokeStyle = c.c;
|
||||
@@ -335,7 +335,7 @@ $._farbtastic = function (container, options) {
|
||||
|
||||
// Draw markers
|
||||
fb.drawMarkers();
|
||||
|
||||
|
||||
// Linked elements or callback
|
||||
if (typeof fb.callback == 'object') {
|
||||
// Set background/foreground color
|
||||
@@ -349,21 +349,21 @@ $._farbtastic = function (container, options) {
|
||||
if ((typeof this.value == 'string') && this.value != fb.color) {
|
||||
this.value = fb.color;
|
||||
}
|
||||
});
|
||||
}).change();
|
||||
}
|
||||
else if (typeof fb.callback == 'function') {
|
||||
fb.callback.call(fb, fb.color);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Helper for returning coordinates relative to the center.
|
||||
*/
|
||||
fb.widgetCoords = function (event) {
|
||||
return {
|
||||
x: event.pageX - fb.offset.left - fb.mid,
|
||||
x: event.pageX - fb.offset.left - fb.mid,
|
||||
y: event.pageY - fb.offset.top - fb.mid
|
||||
};
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -426,7 +426,7 @@ $._farbtastic = function (container, options) {
|
||||
fb.packDX = function (c, a) {
|
||||
return '#' + fb.dec2hex(a) + fb.dec2hex(c) + fb.dec2hex(c) + fb.dec2hex(c);
|
||||
};
|
||||
|
||||
|
||||
fb.pack = function (rgb) {
|
||||
var r = Math.round(rgb[0] * 255);
|
||||
var g = Math.round(rgb[1] * 255);
|
||||
@@ -496,8 +496,7 @@ $._farbtastic = function (container, options) {
|
||||
options = $.extend({
|
||||
width: 300,
|
||||
wheelWidth: (options.width || 300) / 10,
|
||||
callback: null,
|
||||
color: "#808080"
|
||||
callback: null
|
||||
}, options);
|
||||
|
||||
// Initialize.
|
||||
@@ -511,8 +510,7 @@ $._farbtastic = function (container, options) {
|
||||
fb.linkTo(options.callback);
|
||||
}
|
||||
// Set to gray.
|
||||
fb.setColor( "#808080" );
|
||||
fb.setColor( options.color );
|
||||
if (!fb.color) fb.setColor('#808080');
|
||||
}
|
||||
|
||||
})(jQuery);
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
+848
-385
File diff suppressed because it is too large
Load Diff
Regular → Executable
+3457
-2034
File diff suppressed because it is too large
Load Diff
@@ -1,4 +1,4 @@
|
||||
/*! Raven.js 1.1.18 (8ad15bc) | github.com/getsentry/raven-js */
|
||||
/*! Raven.js 1.1.19 (b51bc89) | github.com/getsentry/raven-js */
|
||||
|
||||
/*
|
||||
* Includes TraceKit
|
||||
@@ -1093,7 +1093,7 @@ var _Raven = window.Raven,
|
||||
* @this {Raven}
|
||||
*/
|
||||
var Raven = {
|
||||
VERSION: '1.1.18',
|
||||
VERSION: '1.1.19',
|
||||
|
||||
debug: true,
|
||||
|
||||
@@ -1385,6 +1385,32 @@ var Raven = {
|
||||
return Raven;
|
||||
},
|
||||
|
||||
/*
|
||||
* Set the dataCallback option
|
||||
*
|
||||
* @param {function} callback The callback to run which allows the
|
||||
* data blob to be mutated before sending
|
||||
* @return {Raven}
|
||||
*/
|
||||
setDataCallback: function(callback) {
|
||||
globalOptions.dataCallback = callback;
|
||||
|
||||
return Raven;
|
||||
},
|
||||
|
||||
/*
|
||||
* Set the shouldSendCallback option
|
||||
*
|
||||
* @param {function} callback The callback to run which allows
|
||||
* introspecting the blob before sending
|
||||
* @return {Raven}
|
||||
*/
|
||||
setShouldSendCallback: function(callback) {
|
||||
globalOptions.shouldSendCallback = callback;
|
||||
|
||||
return Raven;
|
||||
},
|
||||
|
||||
/*
|
||||
* Get the latest raw exception that was captured by Raven.
|
||||
*
|
||||
@@ -1475,7 +1501,7 @@ function parseDSN(str) {
|
||||
}
|
||||
|
||||
function isUndefined(what) {
|
||||
return typeof what === 'undefined';
|
||||
return what === void 0;
|
||||
}
|
||||
|
||||
function isFunction(what) {
|
||||
@@ -1483,7 +1509,7 @@ function isFunction(what) {
|
||||
}
|
||||
|
||||
function isString(what) {
|
||||
return typeof what === 'string';
|
||||
return objectPrototype.toString.call(what) === '[object String]';
|
||||
}
|
||||
|
||||
function isObject(what) {
|
||||
@@ -1758,7 +1784,12 @@ function send(data) {
|
||||
if (globalOptions.release) data.release = globalOptions.release;
|
||||
|
||||
if (isFunction(globalOptions.dataCallback)) {
|
||||
data = globalOptions.dataCallback(data);
|
||||
data = globalOptions.dataCallback(data) || data;
|
||||
}
|
||||
|
||||
// Why??????????
|
||||
if (!data || isEmptyObject(data)) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Check if the request should be filtered or not
|
||||
@@ -1776,7 +1807,7 @@ function send(data) {
|
||||
|
||||
|
||||
function makeRequest(data) {
|
||||
var img = new Image(),
|
||||
var img = newImage(),
|
||||
src = globalServer + authQueryString + '&sentry_data=' + encodeURIComponent(JSON.stringify(data));
|
||||
|
||||
img.crossOrigin = 'anonymous';
|
||||
@@ -1795,6 +1826,13 @@ function makeRequest(data) {
|
||||
img.src = src;
|
||||
}
|
||||
|
||||
// Note: this is shitty, but I can't figure out how to get
|
||||
// sinon to stub document.createElement without breaking everything
|
||||
// so this wrapper is just so I can stub it for tests.
|
||||
function newImage() {
|
||||
return document.createElement('img');
|
||||
}
|
||||
|
||||
function isSetup() {
|
||||
if (!hasJSON) return false; // needs JSON support
|
||||
if (!globalServer) {
|
||||
@@ -1868,4 +1906,4 @@ if (typeof define === 'function' && define.amd) {
|
||||
window.Raven = Raven;
|
||||
}
|
||||
|
||||
})(window);
|
||||
})(typeof window !== 'undefined' ? window : this);
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/** vim: et:ts=4:sw=4:sts=4
|
||||
* @license RequireJS 2.1.17 Copyright (c) 2010-2015, The Dojo Foundation All Rights Reserved.
|
||||
* @license RequireJS 2.1.20 Copyright (c) 2010-2015, The Dojo Foundation All Rights Reserved.
|
||||
* Available via the MIT or new BSD license.
|
||||
* see: http://github.com/jrburke/requirejs for details
|
||||
*/
|
||||
@@ -12,7 +12,7 @@ var requirejs, require, define;
|
||||
(function (global) {
|
||||
var req, s, head, baseElement, dataMain, src,
|
||||
interactiveScript, currentlyAddingScript, mainScript, subPath,
|
||||
version = '2.1.17',
|
||||
version = '2.1.20',
|
||||
commentRegExp = /(\/\*([\s\S]*?)\*\/|([^:]|^)\/\/(.*)$)/mg,
|
||||
cjsRequireRegExp = /[^.]\s*require\s*\(\s*["']([^'"\s]+)["']\s*\)/g,
|
||||
jsSuffixRegExp = /\.js$/,
|
||||
@@ -21,7 +21,6 @@ var requirejs, require, define;
|
||||
ostring = op.toString,
|
||||
hasOwn = op.hasOwnProperty,
|
||||
ap = Array.prototype,
|
||||
apsp = ap.splice,
|
||||
isBrowser = !!(typeof window !== 'undefined' && typeof navigator !== 'undefined' && window.document),
|
||||
isWebWorker = !isBrowser && typeof importScripts !== 'undefined',
|
||||
//PS3 indicates loaded and complete, but need to wait for complete
|
||||
@@ -554,11 +553,13 @@ var requirejs, require, define;
|
||||
function takeGlobalQueue() {
|
||||
//Push all the globalDefQueue items into the context's defQueue
|
||||
if (globalDefQueue.length) {
|
||||
//Array splice in the values since the context code has a
|
||||
//local var ref to defQueue, so cannot just reassign the one
|
||||
//on context.
|
||||
apsp.apply(defQueue,
|
||||
[defQueue.length, 0].concat(globalDefQueue));
|
||||
each(globalDefQueue, function(queueItem) {
|
||||
var id = queueItem[0];
|
||||
if (typeof id === 'string') {
|
||||
context.defQueueMap[id] = true;
|
||||
}
|
||||
defQueue.push(queueItem);
|
||||
});
|
||||
globalDefQueue = [];
|
||||
}
|
||||
}
|
||||
@@ -589,7 +590,7 @@ var requirejs, require, define;
|
||||
id: mod.map.id,
|
||||
uri: mod.map.url,
|
||||
config: function () {
|
||||
return getOwn(config.config, mod.map.id) || {};
|
||||
return getOwn(config.config, mod.map.id) || {};
|
||||
},
|
||||
exports: mod.exports || (mod.exports = {})
|
||||
});
|
||||
@@ -845,7 +846,10 @@ var requirejs, require, define;
|
||||
factory = this.factory;
|
||||
|
||||
if (!this.inited) {
|
||||
this.fetch();
|
||||
// Only fetch if not already in the defQueue.
|
||||
if (!hasProp(context.defQueueMap, id)) {
|
||||
this.fetch();
|
||||
}
|
||||
} else if (this.error) {
|
||||
this.emit('error', this.error);
|
||||
} else if (!this.defining) {
|
||||
@@ -1117,6 +1121,9 @@ var requirejs, require, define;
|
||||
this.depCount += 1;
|
||||
|
||||
on(depMap, 'defined', bind(this, function (depExports) {
|
||||
if (this.undefed) {
|
||||
return;
|
||||
}
|
||||
this.defineDep(i, depExports);
|
||||
this.check();
|
||||
}));
|
||||
@@ -1233,13 +1240,15 @@ var requirejs, require, define;
|
||||
while (defQueue.length) {
|
||||
args = defQueue.shift();
|
||||
if (args[0] === null) {
|
||||
return onError(makeError('mismatch', 'Mismatched anonymous define() module: ' + args[args.length - 1]));
|
||||
return onError(makeError('mismatch', 'Mismatched anonymous define() module: ' +
|
||||
args[args.length - 1]));
|
||||
} else {
|
||||
//args are id, deps, factory. Should be normalized by the
|
||||
//define() function.
|
||||
callGetModule(args);
|
||||
}
|
||||
}
|
||||
context.defQueueMap = {};
|
||||
}
|
||||
|
||||
context = {
|
||||
@@ -1249,6 +1258,7 @@ var requirejs, require, define;
|
||||
defined: defined,
|
||||
urlFetched: urlFetched,
|
||||
defQueue: defQueue,
|
||||
defQueueMap: {},
|
||||
Module: Module,
|
||||
makeModuleMap: makeModuleMap,
|
||||
nextTick: req.nextTick,
|
||||
@@ -1320,7 +1330,7 @@ var requirejs, require, define;
|
||||
each(cfg.packages, function (pkgObj) {
|
||||
var location, name;
|
||||
|
||||
pkgObj = typeof pkgObj === 'string' ? { name: pkgObj } : pkgObj;
|
||||
pkgObj = typeof pkgObj === 'string' ? {name: pkgObj} : pkgObj;
|
||||
|
||||
name = pkgObj.name;
|
||||
location = pkgObj.location;
|
||||
@@ -1347,7 +1357,7 @@ var requirejs, require, define;
|
||||
//late to modify them, and ignore unnormalized ones
|
||||
//since they are transient.
|
||||
if (!mod.inited && !mod.map.unnormalized) {
|
||||
mod.map = makeModuleMap(id);
|
||||
mod.map = makeModuleMap(id, null, true);
|
||||
}
|
||||
});
|
||||
|
||||
@@ -1483,6 +1493,7 @@ var requirejs, require, define;
|
||||
var map = makeModuleMap(id, relMap, true),
|
||||
mod = getOwn(registry, id);
|
||||
|
||||
mod.undefed = true;
|
||||
removeScript(id);
|
||||
|
||||
delete defined[id];
|
||||
@@ -1493,10 +1504,11 @@ var requirejs, require, define;
|
||||
//in array so that the splices do not
|
||||
//mess up the iteration.
|
||||
eachReverse(defQueue, function(args, i) {
|
||||
if(args[0] === id) {
|
||||
if (args[0] === id) {
|
||||
defQueue.splice(i, 1);
|
||||
}
|
||||
});
|
||||
delete context.defQueueMap[id];
|
||||
|
||||
if (mod) {
|
||||
//Hold on to listeners in case the
|
||||
@@ -1558,6 +1570,7 @@ var requirejs, require, define;
|
||||
|
||||
callGetModule(args);
|
||||
}
|
||||
context.defQueueMap = {};
|
||||
|
||||
//Do this after the cycle of callGetModule in case the result
|
||||
//of those calls/init calls changes the registry.
|
||||
@@ -1852,6 +1865,9 @@ var requirejs, require, define;
|
||||
if (isBrowser) {
|
||||
//In the browser so use a script tag
|
||||
node = req.createNode(config, moduleName, url);
|
||||
if (config.onNodeCreated) {
|
||||
config.onNodeCreated(node, config, moduleName, url);
|
||||
}
|
||||
|
||||
node.setAttribute('data-requirecontext', context.contextName);
|
||||
node.setAttribute('data-requiremodule', moduleName);
|
||||
@@ -1980,7 +1996,7 @@ var requirejs, require, define;
|
||||
//like a module name.
|
||||
mainScript = mainScript.replace(jsSuffixRegExp, '');
|
||||
|
||||
//If mainScript is still a path, fall back to dataMain
|
||||
//If mainScript is still a path, fall back to dataMain
|
||||
if (req.jsExtRegExp.test(mainScript)) {
|
||||
mainScript = dataMain;
|
||||
}
|
||||
@@ -2059,14 +2075,18 @@ var requirejs, require, define;
|
||||
//where the module name is not known until the script onload event
|
||||
//occurs. If no context, use the global queue, and get it processed
|
||||
//in the onscript load callback.
|
||||
(context ? context.defQueue : globalDefQueue).push([name, deps, callback]);
|
||||
if (context) {
|
||||
context.defQueue.push([name, deps, callback]);
|
||||
context.defQueueMap[name] = true;
|
||||
} else {
|
||||
globalDefQueue.push([name, deps, callback]);
|
||||
}
|
||||
};
|
||||
|
||||
define.amd = {
|
||||
jQuery: true
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Executes the text. Normally just uses eval, but can be modified
|
||||
* to use a better, environment-specific call. Only used for transpiling
|
||||
|
||||
@@ -1,10 +1,12 @@
|
||||
/*
|
||||
TraceKit - Cross brower stack traces - github.com/occ/TraceKit
|
||||
TraceKit - Cross brower stack traces - github.com/csnover/TraceKit
|
||||
MIT license
|
||||
*/
|
||||
|
||||
(function(window, undefined) {
|
||||
|
||||
if (!window) {
|
||||
return;
|
||||
}
|
||||
|
||||
var TraceKit = {};
|
||||
var _oldTraceKit = window.TraceKit;
|
||||
@@ -18,7 +20,7 @@ var UNKNOWN_FUNCTION = '?';
|
||||
* _has, a better form of hasOwnProperty
|
||||
* Example: _has(MainHostObject, property) === true/false
|
||||
*
|
||||
* @param {Object} host object to check property
|
||||
* @param {Object} object to check property
|
||||
* @param {string} key to check
|
||||
*/
|
||||
function _has(object, key) {
|
||||
@@ -126,9 +128,9 @@ TraceKit.report = (function reportModuleWrapper() {
|
||||
* Dispatch stack information to all handlers.
|
||||
* @param {Object.<string, *>} stack
|
||||
*/
|
||||
function notifyHandlers(stack, windowError) {
|
||||
function notifyHandlers(stack, isWindowError) {
|
||||
var exception = null;
|
||||
if (windowError && !TraceKit.collectWindowErrors) {
|
||||
if (isWindowError && !TraceKit.collectWindowErrors) {
|
||||
return;
|
||||
}
|
||||
for (var i in handlers) {
|
||||
@@ -155,29 +157,39 @@ TraceKit.report = (function reportModuleWrapper() {
|
||||
* @param {string} url URL of script that generated the exception.
|
||||
* @param {(number|string)} lineNo The line number at which the error
|
||||
* occurred.
|
||||
* @param {?(number|string)} columnNo The column number at which the error
|
||||
* occurred.
|
||||
* @param {?Error} errorObj The actual Error object.
|
||||
*/
|
||||
function traceKitWindowOnError(message, url, lineNo) {
|
||||
function traceKitWindowOnError(message, url, lineNo, columnNo, errorObj) {
|
||||
var stack = null;
|
||||
|
||||
if (lastExceptionStack) {
|
||||
TraceKit.computeStackTrace.augmentStackTraceWithInitialElement(lastExceptionStack, url, lineNo, message);
|
||||
stack = lastExceptionStack;
|
||||
lastExceptionStack = null;
|
||||
lastException = null;
|
||||
} else {
|
||||
var location = {
|
||||
'url': url,
|
||||
'line': lineNo
|
||||
};
|
||||
location.func = TraceKit.computeStackTrace.guessFunctionName(location.url, location.line);
|
||||
location.context = TraceKit.computeStackTrace.gatherContext(location.url, location.line);
|
||||
stack = {
|
||||
'mode': 'onerror',
|
||||
'message': message,
|
||||
'url': document.location.href,
|
||||
'stack': [location],
|
||||
'useragent': navigator.userAgent
|
||||
};
|
||||
if (errorObj) {
|
||||
stack = TraceKit.computeStackTrace(errorObj);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (lastExceptionStack) {
|
||||
TraceKit.computeStackTrace.augmentStackTraceWithInitialElement(lastExceptionStack, url, lineNo, message);
|
||||
stack = lastExceptionStack;
|
||||
lastExceptionStack = null;
|
||||
lastException = null;
|
||||
} else {
|
||||
var location = {
|
||||
'url': url,
|
||||
'line': lineNo,
|
||||
'column': columnNo
|
||||
};
|
||||
location.func = TraceKit.computeStackTrace.guessFunctionName(location.url, location.line);
|
||||
location.context = TraceKit.computeStackTrace.gatherContext(location.url, location.line);
|
||||
stack = {
|
||||
'mode': 'onerror',
|
||||
'message': message,
|
||||
'url': document.location.href,
|
||||
'stack': [location],
|
||||
'useragent': navigator.userAgent
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
notifyHandlers(stack, 'from window.onerror');
|
||||
@@ -346,11 +358,17 @@ TraceKit.computeStackTrace = (function computeStackTraceWrapper() {
|
||||
* @return {Array.<string>} Source contents.
|
||||
*/
|
||||
function getSource(url) {
|
||||
if (typeof url !== 'string') {
|
||||
return [];
|
||||
}
|
||||
|
||||
if (!_has(sourceCache, url)) {
|
||||
// URL needs to be able to fetched within the acceptable domain. Otherwise,
|
||||
// cross-domain errors will be triggered.
|
||||
var source = '';
|
||||
if (url.indexOf(document.domain) !== -1) {
|
||||
|
||||
url = url || '';
|
||||
if (url.indexOf && url.indexOf(document.domain) !== -1) {
|
||||
source = loadSource(url);
|
||||
}
|
||||
sourceCache[url] = source ? source.split('\n') : [];
|
||||
@@ -587,6 +605,7 @@ TraceKit.computeStackTrace = (function computeStackTraceWrapper() {
|
||||
// ex.message = qq is not defined
|
||||
// ex.fileName = http://...
|
||||
// ex.lineNumber = 59
|
||||
// ex.columnNumber = 69
|
||||
// ex.stack = ...stack trace... (see the example below)
|
||||
// ex.name = ReferenceError
|
||||
//
|
||||
@@ -618,8 +637,9 @@ TraceKit.computeStackTrace = (function computeStackTraceWrapper() {
|
||||
return null;
|
||||
}
|
||||
|
||||
var chrome = /^\s*at (?:((?:\[object object\])?\S+(?: \[as \S+\])?) )?\(?((?:file|http|https):.*?):(\d+)(?::(\d+))?\)?\s*$/i,
|
||||
gecko = /^\s*(\S*)(?:\((.*?)\))?@((?:file|http|https).*?):(\d+)(?::(\d+))?\s*$/i,
|
||||
var chrome = /^\s*at (.*?) ?\(?((?:file|https?|chrome-extension):.*?):(\d+)(?::(\d+))?\)?\s*$/i,
|
||||
gecko = /^\s*(.*?)(?:\((.*?)\))?@?((?:file|https?|chrome):.*?):(\d+)(?::(\d+))?\s*$/i,
|
||||
winjs = /^\s*at (?:((?:\[object object\])?.+) )?\(?((?:ms-appx|http|https):.*?):(\d+)(?::(\d+))?\)?\s*$/i,
|
||||
lines = ex.stack.split('\n'),
|
||||
stack = [],
|
||||
parts,
|
||||
@@ -642,6 +662,13 @@ TraceKit.computeStackTrace = (function computeStackTraceWrapper() {
|
||||
'line': +parts[3],
|
||||
'column': parts[4] ? +parts[4] : null
|
||||
};
|
||||
} else if ((parts = winjs.exec(lines[i]))) {
|
||||
element = {
|
||||
'url': parts[2],
|
||||
'func': parts[1] || UNKNOWN_FUNCTION,
|
||||
'line': +parts[3],
|
||||
'column': parts[4] ? +parts[4] : null
|
||||
};
|
||||
} else {
|
||||
continue;
|
||||
}
|
||||
@@ -657,14 +684,19 @@ TraceKit.computeStackTrace = (function computeStackTraceWrapper() {
|
||||
stack.push(element);
|
||||
}
|
||||
|
||||
if (stack[0] && stack[0].line && !stack[0].column && reference) {
|
||||
stack[0].column = findSourceInLine(reference[1], stack[0].url, stack[0].line);
|
||||
}
|
||||
|
||||
if (!stack.length) {
|
||||
return null;
|
||||
}
|
||||
|
||||
if (stack[0] && stack[0].line && !stack[0].column && reference) {
|
||||
stack[0].column = findSourceInLine(reference[1], stack[0].url, stack[0].line);
|
||||
} else if (!stack[0].column && !_isUndefined(ex.columnNumber)) {
|
||||
// FireFox uses this awesome columnNumber property for its top frame
|
||||
// Also note, Firefox's column number is 0-based and everything else expects 1-based,
|
||||
// so adding 1
|
||||
stack[0].column = ex.columnNumber + 1;
|
||||
}
|
||||
|
||||
return {
|
||||
'mode': 'stack',
|
||||
'name': ex.name,
|
||||
@@ -762,8 +794,8 @@ TraceKit.computeStackTrace = (function computeStackTraceWrapper() {
|
||||
return null;
|
||||
}
|
||||
|
||||
var lineRE1 = /^\s*Line (\d+) of linked script ((?:file|http|https)\S+)(?:: in function (\S+))?\s*$/i,
|
||||
lineRE2 = /^\s*Line (\d+) of inline#(\d+) script in ((?:file|http|https)\S+)(?:: in function (\S+))?\s*$/i,
|
||||
var lineRE1 = /^\s*Line (\d+) of linked script ((?:file|https?)\S+)(?:: in function (\S+))?\s*$/i,
|
||||
lineRE2 = /^\s*Line (\d+) of inline#(\d+) script in ((?:file|https?)\S+)(?:: in function (\S+))?\s*$/i,
|
||||
lineRE3 = /^\s*Line (\d+) of function script\s*$/i,
|
||||
stack = [],
|
||||
scripts = document.getElementsByTagName('script'),
|
||||
@@ -939,6 +971,12 @@ TraceKit.computeStackTrace = (function computeStackTraceWrapper() {
|
||||
item.func = parts[1];
|
||||
}
|
||||
|
||||
if (typeof item.func === 'undefined') {
|
||||
try {
|
||||
item.func = parts.input.substring(0, parts.input.indexOf('{'));
|
||||
} catch (e) { }
|
||||
}
|
||||
|
||||
if ((source = findSourceByFunctionBody(curr))) {
|
||||
item.url = source.url;
|
||||
item.line = source.line;
|
||||
@@ -1059,6 +1097,7 @@ TraceKit.computeStackTrace = (function computeStackTraceWrapper() {
|
||||
computeStackTrace.guessFunctionName = guessFunctionName;
|
||||
computeStackTrace.gatherContext = gatherContext;
|
||||
computeStackTrace.ofCaller = computeStackTraceOfCaller;
|
||||
computeStackTrace.getSource = getSource;
|
||||
|
||||
return computeStackTrace;
|
||||
}());
|
||||
@@ -1067,7 +1106,7 @@ TraceKit.computeStackTrace = (function computeStackTraceWrapper() {
|
||||
* Extends support for global error handling for asynchronous browser
|
||||
* functions. Adopted from Closure Library's errorhandler.js
|
||||
*/
|
||||
(function extendToAsynchronousCallbacks() {
|
||||
TraceKit.extendToAsynchronousCallbacks = function () {
|
||||
var _helper = function _helper(fnName) {
|
||||
var originalFn = window[fnName];
|
||||
window[fnName] = function traceKitAsyncExtension() {
|
||||
@@ -1090,7 +1129,7 @@ TraceKit.computeStackTrace = (function computeStackTraceWrapper() {
|
||||
|
||||
_helper('setTimeout');
|
||||
_helper('setInterval');
|
||||
}());
|
||||
};
|
||||
|
||||
//Default options:
|
||||
if (!TraceKit.remoteFetching) {
|
||||
@@ -1109,4 +1148,4 @@ if (!TraceKit.linesOfContext || TraceKit.linesOfContext < 1) {
|
||||
// Export to global object
|
||||
window.TraceKit = TraceKit;
|
||||
|
||||
}(window));
|
||||
}(typeof window !== 'undefined' ? window : global));
|
||||
|
||||
@@ -0,0 +1,223 @@
|
||||
define(['utils/utils', 'mvc/tools', 'mvc/upload/upload-view', 'mvc/ui/ui-misc',
|
||||
'mvc/history/options-menu', 'mvc/history/history-panel-edit-current', 'mvc/tools/tools-form'],
|
||||
function( Utils, Tools, Upload, Ui, optionsMenu, HistoryPanel, ToolsForm ) {
|
||||
|
||||
/* Builds the center panel */
|
||||
var CenterPanel = Backbone.View.extend({
|
||||
initialize: function( options ) {
|
||||
this.options = Utils.merge( options, {} );
|
||||
this.setElement( this._template() );
|
||||
var self = this;
|
||||
this.$( '#galaxy_main' ).on( 'load', function() {
|
||||
var location = this.contentWindow && this.contentWindow.location;
|
||||
if ( location && location.host ) {
|
||||
$( this ).show();
|
||||
self.$( '#center-panel' ).empty().hide();
|
||||
Galaxy.trigger( 'galaxy_main:load', {
|
||||
fullpath: location.pathname + location.search + location.hash,
|
||||
pathname: location.pathname,
|
||||
search : location.search,
|
||||
hash : location.hash
|
||||
});
|
||||
}
|
||||
});
|
||||
var params = $.extend( {}, Galaxy.params );
|
||||
if ( params.tool_id !== 'upload1' && ( params.tool_id || params.job_id ) ) {
|
||||
params.tool_id && ( params.id = params.tool_id );
|
||||
this.display( ( new ToolsForm.View( params ) ).$el );
|
||||
} else {
|
||||
this.$( '#galaxy_main' ).prop( 'src', Galaxy.root + (
|
||||
( params.workflow_id && ( 'workflow/run?id=' + params.workflow_id ) ) ||
|
||||
( params.m_c && ( params.m_c + '/' + params.m_a ) ) ||
|
||||
( Galaxy.config.require_login && !Galaxy.user.id && 'user/login') ||
|
||||
'root/welcome'
|
||||
));
|
||||
}
|
||||
},
|
||||
display: function( $el ) {
|
||||
this.$( '#galaxy_main' ).hide();
|
||||
this.$( '#center-panel' ).empty().scrollTop( 0 ).append( $el ).show();
|
||||
},
|
||||
_template: function() {
|
||||
return '<div style="position: absolute; width: 100%; height: 100%">' +
|
||||
'<iframe name="galaxy_main" id="galaxy_main" frameborder="0" style="position: absolute; width: 100%; height: 100%;"/>' +
|
||||
'<div id="center-panel" style="position: absolute; width: 100%; height: 100%; padding: 10px; overflow: auto;"/>' +
|
||||
'</div>';
|
||||
}
|
||||
});
|
||||
|
||||
/* Builds the tool panel on the left */
|
||||
var LeftPanel = Backbone.View.extend({
|
||||
initialize: function( options ) {
|
||||
this.options = Utils.merge( options, {} );
|
||||
this.setElement( this._template() );
|
||||
// create tool search, tool panel, and tool panel view.
|
||||
if ( Galaxy.user.id || !Galaxy.config.require_login ) {
|
||||
var tool_search = new Tools.ToolSearch({
|
||||
spinner_url : options.spinner_url,
|
||||
search_url : options.search_url,
|
||||
hidden : false
|
||||
});
|
||||
var tools = new Tools.ToolCollection( options.toolbox );
|
||||
var tool_panel = new Tools.ToolPanel({
|
||||
tool_search : tool_search,
|
||||
tools : tools,
|
||||
layout : options.toolbox_in_panel
|
||||
});
|
||||
tool_panel_view = new Tools.ToolPanelView({ model: tool_panel });
|
||||
|
||||
// add tool panel to Galaxy object
|
||||
Galaxy.toolPanel = tool_panel;
|
||||
|
||||
// if there are tools, render panel and display everything
|
||||
if (tool_panel.get( 'layout' ).size() > 0) {
|
||||
tool_panel_view.render();
|
||||
this.$( '.toolMenu' ).show();
|
||||
}
|
||||
this.$el.prepend( tool_panel_view.$el );
|
||||
|
||||
// add internal workflow list
|
||||
this.$( '#internal-workflows' ).append( this._templateTool({
|
||||
title : 'All workflows',
|
||||
href : 'workflow/list_for_run'
|
||||
}) )
|
||||
for ( var i in options.stored_workflow_menu_entries ) {
|
||||
var m = options.stored_workflow_menu_entries[ i ];
|
||||
this.$( '#internal-workflows' ).append( this._templateTool({
|
||||
title : m.stored_workflow.name,
|
||||
href : 'workflow/run?id=' + m.encoded_stored_workflow_id
|
||||
}) );
|
||||
}
|
||||
|
||||
// minsize init hint
|
||||
this.$( 'a[minsizehint]' ).click( function() {
|
||||
if ( parent.handle_minwidth_hint ) {
|
||||
parent.handle_minwidth_hint( $( this ).attr( 'minsizehint' ) );
|
||||
}
|
||||
});
|
||||
|
||||
// add upload plugin
|
||||
Galaxy.upload = new Upload( options );
|
||||
|
||||
// define components (is used in app-view.js)
|
||||
this.components = {
|
||||
header : {
|
||||
title : 'Tools',
|
||||
buttons : [ Galaxy.upload ]
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
_templateTool: function( options ) {
|
||||
return '<div class="toolTitle">' +
|
||||
'<a href="' + Galaxy.root + options.href + '" target="galaxy_main">' + options.title + '</a>' +
|
||||
'</div>';
|
||||
},
|
||||
|
||||
_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>Search did not match any tools.</strong></em>' +
|
||||
'</div>' +
|
||||
'</div>' +
|
||||
'<div class="toolSectionPad"/>' +
|
||||
'<div class="toolSectionPad"/>' +
|
||||
'<div class="toolSectionTitle" id="title_XXinternalXXworkflow">' +
|
||||
'<span>Workflows</span>' +
|
||||
'</div>' +
|
||||
'<div id="internal-workflows" class="toolSectionBody">' +
|
||||
'<div class="toolSectionBg"/>' +
|
||||
'</div>' +
|
||||
'</div>';
|
||||
}
|
||||
});
|
||||
|
||||
/* Builds the history panel on the right */
|
||||
var RightPanel = Backbone.View.extend({
|
||||
initialize: function(options) {
|
||||
this.options = Utils.merge( options, {} );
|
||||
this.setElement( this._template() );
|
||||
|
||||
var headerButtons = [];
|
||||
// this button re-fetches the history and contents and re-renders the history panel
|
||||
var buttonRefresh = new Ui.ButtonLink({
|
||||
id : 'history-refresh-button',
|
||||
title : 'Refresh history',
|
||||
cls : 'panel-header-button',
|
||||
icon : 'fa fa-refresh',
|
||||
onclick : function() {
|
||||
if( top.Galaxy && top.Galaxy.currHistoryPanel ) {
|
||||
top.Galaxy.currHistoryPanel.loadCurrentHistory();
|
||||
}
|
||||
}
|
||||
});
|
||||
headerButtons.push( buttonRefresh );
|
||||
|
||||
// opens a drop down menu with history related functions (like view all, delete, share, etc.)
|
||||
var buttonOptions = new Ui.ButtonLink({
|
||||
id : 'history-options-button',
|
||||
title : 'History options',
|
||||
cls : 'panel-header-button',
|
||||
target : 'galaxy_main',
|
||||
icon : 'fa fa-cog',
|
||||
href : Galaxy.root + 'root/history_options'
|
||||
});
|
||||
headerButtons.push( buttonOptions );
|
||||
|
||||
// goes to a page showing all the users histories in panel form (for logged in users)
|
||||
if( !Galaxy.user.isAnonymous() ){
|
||||
var buttonViewMulti = new Ui.ButtonLink({
|
||||
id : 'history-view-multi-button',
|
||||
title : 'View all histories',
|
||||
cls : 'panel-header-button',
|
||||
icon : 'fa fa-columns',
|
||||
href : Galaxy.root + 'history/view_multiple'
|
||||
});
|
||||
headerButtons.push( buttonViewMulti );
|
||||
}
|
||||
|
||||
// define components (is used in app-view.js)
|
||||
this.components = {
|
||||
header : {
|
||||
title : 'History',
|
||||
cls : 'history-panel-header',
|
||||
buttons : headerButtons
|
||||
},
|
||||
body : {
|
||||
cls : 'unified-panel-body-background',
|
||||
}
|
||||
};
|
||||
|
||||
// build history options menu
|
||||
Galaxy.historyOptionsMenu = optionsMenu( buttonOptions.$el, {
|
||||
anonymous : Galaxy.user.isAnonymous(),
|
||||
purgeAllowed : Galaxy.config.allow_user_dataset_purge,
|
||||
root : Galaxy.root
|
||||
});
|
||||
|
||||
// load current history
|
||||
Galaxy.currHistoryPanel = new HistoryPanel.CurrentHistoryPanel({
|
||||
el : this.$el,
|
||||
purgeAllowed : Galaxy.config.allow_user_dataset_purge,
|
||||
linkTarget : 'galaxy_main',
|
||||
$scrollContainer: function(){ return this.$el.parent(); }
|
||||
});
|
||||
Galaxy.currHistoryPanel.connectToQuotaMeter( Galaxy.quotaMeter );
|
||||
Galaxy.currHistoryPanel.listenToGalaxy( Galaxy );
|
||||
Galaxy.currHistoryPanel.loadCurrentHistory();
|
||||
},
|
||||
|
||||
// body template
|
||||
_template: function() {
|
||||
return '<div id="current-history-panel" class="history-panel"/>';
|
||||
}
|
||||
});
|
||||
|
||||
return {
|
||||
left : LeftPanel,
|
||||
center : CenterPanel,
|
||||
right : RightPanel
|
||||
};
|
||||
});
|
||||
@@ -0,0 +1,139 @@
|
||||
/**
|
||||
This is the entrance point for the Galaxy UI.
|
||||
*/
|
||||
define(['utils/utils', 'galaxy.masthead', 'galaxy.menu', 'galaxy.frame',
|
||||
'mvc/ui/ui-portlet', 'mvc/ui/ui-misc', 'mvc/ui/ui-modal',
|
||||
'mvc/user/user-quotameter', 'mvc/app/app-analysis'],
|
||||
function( Utils, Masthead, Menu, Frame, Portlet, Ui, Modal, QuotaMeter, Analysis ) {
|
||||
return Backbone.View.extend({
|
||||
initialize: function( options ) {
|
||||
this.options = Utils.merge( options, {} );
|
||||
this.setElement( this._template( options ) );
|
||||
|
||||
// register this view
|
||||
Galaxy.app = this;
|
||||
|
||||
// url request parameters
|
||||
Galaxy.params = this.options.params;
|
||||
|
||||
// shared backbone router
|
||||
Galaxy.router = new Backbone.Router();
|
||||
|
||||
// configure body
|
||||
$( 'body' ).append( this.$el );
|
||||
ensure_dd_helper();
|
||||
|
||||
// adjust parent container
|
||||
var $container = $( this.$el.parent() ).attr( 'scroll', 'no' ).addClass( 'full-content' );
|
||||
if ( this.options.message_box_visible ) {
|
||||
$container.addClass( 'has-message-box' );
|
||||
this.$( '#messagebox' ).show();
|
||||
}
|
||||
if ( this.options.show_inactivity_warning ) {
|
||||
$container.addClass( 'has-inactivity-box' );
|
||||
this.$( '#inactivebox' ).show();
|
||||
}
|
||||
|
||||
// load global galaxy objects
|
||||
if ( !Galaxy.masthead ) {
|
||||
Galaxy.masthead = new Masthead.GalaxyMasthead( options );
|
||||
Galaxy.modal = new Modal.View();
|
||||
Galaxy.frame = new Frame.GalaxyFrame();
|
||||
|
||||
// construct default menu options
|
||||
Galaxy.menu = new Menu.GalaxyMenu({
|
||||
masthead : Galaxy.masthead,
|
||||
config : options
|
||||
});
|
||||
|
||||
// set up the quota meter (And fetch the current user data from trans)
|
||||
// add quota meter to masthead
|
||||
Galaxy.quotaMeter = new QuotaMeter.UserQuotaMeter({
|
||||
model : Galaxy.user,
|
||||
el : Galaxy.masthead.$( '.quota-meter-container' )
|
||||
}).render();
|
||||
}
|
||||
|
||||
// build page
|
||||
this._buildPanels( Analysis, options );
|
||||
},
|
||||
|
||||
/** Display content */
|
||||
display: function ( $el, target ) {
|
||||
// TODO: Remove this line after select2 update
|
||||
$( '.select2-hidden-accessible' ).remove();
|
||||
this.panels && this.panels[ target || 'center' ].display( $el );
|
||||
},
|
||||
|
||||
/** Build all panels **/
|
||||
_buildPanels: function( Views, options ) {
|
||||
this.panels = [];
|
||||
var panel_ids = [ 'left', 'center', 'right' ];
|
||||
for ( var i in panel_ids ) {
|
||||
var id = panel_ids[ i ];
|
||||
var view = this.panels[ id ] = new Views[ id ]( options );
|
||||
if ( id == 'center' ) {
|
||||
this.$('#center').append( view.$el );
|
||||
} else {
|
||||
var components = Utils.merge( view.components, {
|
||||
header : {
|
||||
title : '',
|
||||
cls : '',
|
||||
buttons : []
|
||||
},
|
||||
body : {
|
||||
cls : ''
|
||||
}
|
||||
});
|
||||
var $panel = $( this._templatePanel( id ) );
|
||||
$panel.find('.panel-header-text').html( components.header.title );
|
||||
$panel.find('.unified-panel-header-inner').addClass( components.header.cls );
|
||||
for ( var i in components.header.buttons ) {
|
||||
$panel.find('.panel-header-buttons').append( components.header.buttons[ i ].$el );
|
||||
}
|
||||
$panel.find('.unified-panel-body').addClass( components.body.cls ).append( view.$el );
|
||||
var panel = new Panel( {
|
||||
center : this.$( '#center' ),
|
||||
panel : $panel,
|
||||
drag : $panel.find('.unified-panel-footer > .drag' ),
|
||||
toggle : $panel.find('.unified-panel-footer > .panel-collapse' ),
|
||||
right : id == 'right'
|
||||
} );
|
||||
this.$el.append( $panel );
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
/** Template for left/right panel */
|
||||
_templatePanel: function( id ) {
|
||||
return '<div id="' + id + '">' +
|
||||
'<div class="unified-panel-header" unselectable="on">' +
|
||||
'<div class="unified-panel-header-inner">' +
|
||||
'<div class="panel-header-buttons" style="float: right"/>' +
|
||||
'<div class="panel-header-text"/>' +
|
||||
'</div>' +
|
||||
'</div>' +
|
||||
'<div class="unified-panel-body"/>' +
|
||||
'<div class="unified-panel-footer">' +
|
||||
'<div class="panel-collapse ' + id + '"/>' +
|
||||
'<div class="drag"/>' +
|
||||
'</div>' +
|
||||
'</div>';
|
||||
},
|
||||
|
||||
/** Main template **/
|
||||
_template: function() {
|
||||
return '<div id="everything" style="position: absolute; top: 0; left: 0; width: 100%; height: 100%;">' +
|
||||
'<div id="background"/>' +
|
||||
'<div id="messagebox" class="panel-' + Galaxy.config.message_box_class + '-message" style="display: none;">' +
|
||||
Galaxy.config.message_box_content +
|
||||
'</div>' +
|
||||
'<div id="inactivebox" class="panel-warning-message" style="display: none;">' +
|
||||
Galaxy.config.inactivity_box_content +
|
||||
' <a href="' + Galaxy.root + 'user/resend_verification">Resend verification.</a>' +
|
||||
'</div>' +
|
||||
'<div id="center" class="inbound"/>' +
|
||||
'</div>';
|
||||
}
|
||||
});
|
||||
});
|
||||
@@ -2,7 +2,6 @@ define([
|
||||
'utils/add-logging',
|
||||
'utils/localization'
|
||||
], function( addLogging, _l ){
|
||||
//ASSUMES: backbone
|
||||
//==============================================================================
|
||||
/** @class Mixin to add logging capabilities to an object.
|
||||
* Designed to allow switching an objects log output off/on at one central
|
||||
@@ -33,7 +32,7 @@ var LoggableMixin = /** @lends LoggableMixin# */{
|
||||
// replace null with console (if available) to see all logs
|
||||
logger : null,
|
||||
_logNamespace : '?',
|
||||
|
||||
|
||||
/** Output log messages/arguments to logger.
|
||||
* @param {Arguments} ... (this function is variadic)
|
||||
* @returns undefined if not this.logger
|
||||
@@ -446,7 +445,7 @@ var SelectableViewMixin = {
|
||||
/** is the view currently selected? */
|
||||
this.selected = attributes.selected || false;
|
||||
},
|
||||
|
||||
|
||||
/** $el sub-element where the selector is rendered and what can be clicked to select. */
|
||||
$selector : function(){
|
||||
return this.$( '.selector' );
|
||||
|
||||
@@ -56,6 +56,8 @@ var DCEListItemEdit = DCEListItemView.extend(
|
||||
|
||||
|
||||
//==============================================================================
|
||||
// NOTE: this does not inherit from DatasetDCEListItemView as you would expect
|
||||
//TODO: but should - if we can find something simpler than using diamond
|
||||
/** @class Editable view for a DatasetCollectionElement that is also an DatasetAssociation
|
||||
* (a dataset contained in a dataset collection).
|
||||
*/
|
||||
@@ -70,6 +72,24 @@ var DatasetDCEListItemEdit = DATASET_LI_EDIT.DatasetListItemEdit.extend(
|
||||
DATASET_LI_EDIT.DatasetListItemEdit.prototype.initialize.call( this, attributes );
|
||||
},
|
||||
|
||||
// NOTE: this does not inherit from DatasetDCEListItemView - so we duplicate this here
|
||||
//TODO: fix
|
||||
/** In this override, only get details if in the ready state.
|
||||
* Note: fetch with no 'change' event triggering to prevent automatic rendering.
|
||||
*/
|
||||
_fetchModelDetails : function(){
|
||||
var view = this;
|
||||
if( view.model.inReadyState() && !view.model.hasDetails() ){
|
||||
return view.model.fetch({ silent: true });
|
||||
}
|
||||
return jQuery.when();
|
||||
},
|
||||
|
||||
/** Override to remove delete button */
|
||||
_renderDeleteButton : function(){
|
||||
return null;
|
||||
},
|
||||
|
||||
// ......................................................................... misc
|
||||
/** String representation */
|
||||
toString : function(){
|
||||
|
||||
@@ -195,6 +195,17 @@ var DatasetDCEListItemView = DATASET_LI.DatasetListItemView.extend(
|
||||
DATASET_LI.DatasetListItemView.prototype.initialize.call( this, attributes );
|
||||
},
|
||||
|
||||
/** In this override, only get details if in the ready state.
|
||||
* Note: fetch with no 'change' event triggering to prevent automatic rendering.
|
||||
*/
|
||||
_fetchModelDetails : function(){
|
||||
var view = this;
|
||||
if( view.model.inReadyState() && !view.model.hasDetails() ){
|
||||
return view.model.fetch({ silent: true });
|
||||
}
|
||||
return jQuery.when();
|
||||
},
|
||||
|
||||
// ......................................................................... misc
|
||||
/** String representation */
|
||||
toString : function(){
|
||||
|
||||
@@ -126,6 +126,18 @@ var DatasetDCE = DATASET_MODEL.DatasetAssociation.extend( BASE_MVC.mixin( Datase
|
||||
/** logger used to record this.log messages, commonly set to console */
|
||||
//logger : console,
|
||||
|
||||
/** url fn */
|
||||
url : function(){
|
||||
var galaxyRoot = (( window.galaxy_config && galaxy_config.root )?( galaxy_config.root ):( '/' ));
|
||||
// won't always be an hda
|
||||
if( !this.has( 'history_id' ) ){
|
||||
console.warn( 'no endpoint for non-hdas within a collection yet' );
|
||||
// (a little silly since this api endpoint *also* points at hdas)
|
||||
return galaxyRoot + 'api/datasets';
|
||||
}
|
||||
return galaxyRoot + 'api/histories/' + this.get( 'history_id' ) + '/contents/' + this.get( 'id' );
|
||||
},
|
||||
|
||||
defaults : _.extend( {},
|
||||
DATASET_MODEL.DatasetAssociation.prototype.defaults,
|
||||
DatasetCollectionElementMixin.defaults
|
||||
@@ -133,7 +145,7 @@ var DatasetDCE = DATASET_MODEL.DatasetAssociation.extend( BASE_MVC.mixin( Datase
|
||||
|
||||
// because all objects have constructors (as this hashmap would even if this next line wasn't present)
|
||||
// the constructor in hcontentMixin won't be attached by BASE_MVC.mixin to this model
|
||||
// - re-apply manually it now
|
||||
// - re-apply manually for now
|
||||
/** call the mixin constructor */
|
||||
constructor : function( attributes, options ){
|
||||
this.debug( '\t DatasetDCE.constructor:', attributes, options );
|
||||
@@ -148,6 +160,12 @@ var DatasetDCE = DATASET_MODEL.DatasetAssociation.extend( BASE_MVC.mixin( Datase
|
||||
DATASET_MODEL.DatasetAssociation.prototype.initialize.call( this, attributes, options );
|
||||
},
|
||||
|
||||
/** Does this model already contain detailed data (as opposed to just summary level data)? */
|
||||
hasDetails : function(){
|
||||
// dataset collection api does return genome_build but doesn't return annotation
|
||||
return _.has( this.attributes, 'annotation' );
|
||||
},
|
||||
|
||||
/** String representation. */
|
||||
toString : function(){
|
||||
var objStr = this.get( 'element_identifier' );
|
||||
@@ -241,10 +259,12 @@ var DatasetCollection = Backbone.Model
|
||||
return json;
|
||||
},
|
||||
|
||||
/** is the collection done with updates and ready to be used? (finished running, etc.) */
|
||||
/** Is this collection in a 'ready' state no processing (for the collection) is left
|
||||
* to do on the server.
|
||||
*/
|
||||
inReadyState : function(){
|
||||
//TODO: state currenly unimplemented for collections
|
||||
return true;
|
||||
var populated = this.get( 'populated' );
|
||||
return ( this.isDeletedOrPurged() || populated );
|
||||
},
|
||||
|
||||
//TODO:?? the following are the same interface as DatasetAssociation - can we combine?
|
||||
|
||||
@@ -568,7 +568,8 @@ var PairedCollectionCreator = Backbone.View.extend( baseMVC.LoggableMixin ).exte
|
||||
*/
|
||||
createList : function( name ){
|
||||
var creator = this,
|
||||
url = '/api/histories/' + this.historyId + '/contents/dataset_collections';
|
||||
root = ( window.Galaxy && Galaxy.options.root )? Galaxy.options.root : '/',
|
||||
url = root + 'api/histories/' + this.historyId + '/contents/dataset_collections';
|
||||
|
||||
//TODO: use ListPairedCollection.create()
|
||||
var ajaxData = {
|
||||
|
||||
@@ -5,8 +5,9 @@ define([
|
||||
"mvc/annotations",
|
||||
"ui/fa-icon-button",
|
||||
"mvc/base-mvc",
|
||||
"mvc/tools/tools-form",
|
||||
"utils/localization"
|
||||
], function( STATES, DATASET_LI, TAGS, ANNOTATIONS, faIconButton, BASE_MVC, _l ){
|
||||
], function( STATES, DATASET_LI, TAGS, ANNOTATIONS, faIconButton, BASE_MVC, ToolsForm, _l ){
|
||||
//==============================================================================
|
||||
var _super = DATASET_LI.DatasetListItemView;
|
||||
/** @class Editing view for DatasetAssociation.
|
||||
@@ -157,13 +158,23 @@ var DatasetListItemEdit = _super.extend(
|
||||
|
||||
/** Render icon-button to re-run the job that created this dataset. */
|
||||
_renderRerunButton : function(){
|
||||
return faIconButton({
|
||||
title : _l( 'Run this job again' ),
|
||||
href : this.model.urls.rerun,
|
||||
classes : 'rerun-btn',
|
||||
target : this.linkTarget,
|
||||
faIcon : 'fa-refresh'
|
||||
});
|
||||
var creating_job = this.model.get('creating_job');
|
||||
if (this.model.get('rerunnable')){
|
||||
return faIconButton({
|
||||
title : _l( 'Run this job again' ),
|
||||
href : this.model.urls.rerun,
|
||||
classes : 'rerun-btn',
|
||||
target : this.linkTarget,
|
||||
faIcon : 'fa-refresh',
|
||||
onclick : function(ev) {
|
||||
ev.preventDefault();
|
||||
var form = new ToolsForm.View({'job_id' : creating_job});
|
||||
form.deferred.execute(function(){
|
||||
Galaxy.app.display(form.$el);
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
},
|
||||
|
||||
/** Render an icon-button or popupmenu of links based on the applicable visualizations */
|
||||
|
||||
@@ -37,8 +37,6 @@ var DatasetListItemView = _super.extend(
|
||||
|
||||
/** where should pages from links be displayed? (default to new tab/window) */
|
||||
this.linkTarget = attributes.linkTarget || '_blank';
|
||||
|
||||
this._setUpListeners();
|
||||
},
|
||||
|
||||
/** event listeners */
|
||||
@@ -60,7 +58,7 @@ var DatasetListItemView = _super.extend(
|
||||
},
|
||||
|
||||
// ......................................................................... expandable
|
||||
/** In this override, only get details if in the ready state.
|
||||
/** In this override, only get details if in the ready state, get rerunnable if in other states.
|
||||
* Note: fetch with no 'change' event triggering to prevent automatic rendering.
|
||||
*/
|
||||
_fetchModelDetails : function(){
|
||||
|
||||
@@ -100,14 +100,11 @@ define([], function() {
|
||||
/** Main Template
|
||||
*/
|
||||
_template: function(options) {
|
||||
// create table element
|
||||
var tmp = '<div class="ui-table-form-element">' +
|
||||
var tmp = '<div class="ui-table-form-element input-name-' + options.name + '">' +
|
||||
'<div class="ui-table-form-error ui-error">' +
|
||||
'<span class="fa fa-arrow-down"/><span class="ui-table-form-error-text"/>' +
|
||||
'</div>' +
|
||||
'<div class="ui-table-form-title">';
|
||||
|
||||
// is optional
|
||||
if (options.collapsible) {
|
||||
tmp += '<div class="ui-table-form-optional">' +
|
||||
'<i class="icon"/>' + options.label +
|
||||
@@ -115,12 +112,8 @@ define([], function() {
|
||||
} else {
|
||||
tmp += options.label;
|
||||
}
|
||||
|
||||
// finalize title
|
||||
tmp += '</div>' +
|
||||
'<div class="ui-table-form-field">';
|
||||
|
||||
// add help
|
||||
tmp += '<div class="ui-table-form-info">';
|
||||
if (options.help) {
|
||||
tmp += options.help;
|
||||
@@ -129,12 +122,8 @@ define([], function() {
|
||||
}
|
||||
}
|
||||
tmp += '</div>';
|
||||
|
||||
// finalize
|
||||
tmp += '</div>' +
|
||||
'</div>';
|
||||
|
||||
// return input element
|
||||
return tmp;
|
||||
}
|
||||
});
|
||||
|
||||
@@ -29,7 +29,7 @@ define(['utils/utils',
|
||||
|
||||
// create table
|
||||
this.table = new Table.View(options);
|
||||
|
||||
|
||||
// create parameter handler
|
||||
this.parameters = new Parameters(app, options);
|
||||
|
||||
@@ -179,7 +179,7 @@ define(['utils/utils',
|
||||
onnew : function() {
|
||||
// create
|
||||
create(input_def.inputs);
|
||||
|
||||
|
||||
// trigger refresh
|
||||
self.app.trigger('change');
|
||||
}
|
||||
@@ -202,7 +202,7 @@ define(['utils/utils',
|
||||
ondel : function() {
|
||||
// delete repeat block
|
||||
repeat.del(sub_section_id);
|
||||
|
||||
|
||||
// trigger refresh
|
||||
self.app.trigger('change');
|
||||
}
|
||||
@@ -245,7 +245,7 @@ define(['utils/utils',
|
||||
_addSection: function(input_def) {
|
||||
// link this
|
||||
var self = this;
|
||||
|
||||
|
||||
// create sub section
|
||||
var sub_section = new View(self.app, {
|
||||
inputs : input_def.inputs
|
||||
@@ -316,6 +316,7 @@ define(['utils/utils',
|
||||
|
||||
// create input field wrapper
|
||||
var input_element = new InputElement(this.app, {
|
||||
name : input_def.name,
|
||||
label : input_def.label || input_def.name,
|
||||
value : input_def.value,
|
||||
default_value : input_def.default_value,
|
||||
@@ -348,4 +349,4 @@ define(['utils/utils',
|
||||
return {
|
||||
View: View
|
||||
};
|
||||
});
|
||||
});
|
||||
@@ -2,9 +2,6 @@
|
||||
define(['utils/utils', 'mvc/ui/ui-misc', 'mvc/ui/ui-tabs', 'mvc/tools/tools-template'],
|
||||
function(Utils, Ui, Tabs, ToolTemplate) {
|
||||
|
||||
// track current history elements
|
||||
history = {};
|
||||
|
||||
// hda/hdca content selector ui element
|
||||
var View = Backbone.View.extend({
|
||||
// initialize
|
||||
@@ -13,6 +10,10 @@ var View = Backbone.View.extend({
|
||||
this.app = app;
|
||||
this.options = options;
|
||||
|
||||
// track current history elements
|
||||
this.history = {};
|
||||
|
||||
|
||||
// link this
|
||||
var self = this;
|
||||
|
||||
@@ -197,7 +198,7 @@ var View = Backbone.View.extend({
|
||||
value: item.id
|
||||
});
|
||||
// backup to local history
|
||||
history[item.id + '_' + item.src] = item;
|
||||
self.history[item.id + '_' + item.src] = item;
|
||||
}
|
||||
// update field
|
||||
field.update(select_options);
|
||||
@@ -272,7 +273,7 @@ var View = Backbone.View.extend({
|
||||
|
||||
// append to dataset ids
|
||||
for (var i in id_list) {
|
||||
var details = history[id_list[i] + '_' + this.list[this.current].type];
|
||||
var details = this.history[id_list[i] + '_' + this.list[this.current].type];
|
||||
if (details) {
|
||||
result.values.push(details);
|
||||
} else {
|
||||
@@ -315,7 +316,7 @@ var View = Backbone.View.extend({
|
||||
/** Assists in identifying the batch mode */
|
||||
_batch: function() {
|
||||
if (this.current == 'collection') {
|
||||
var hdca = history[this._select().value() + '_hdca'];
|
||||
var hdca = this.history[this._select().value() + '_hdca'];
|
||||
if (hdca && hdca.map_over_type) {
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -91,13 +91,6 @@ define(['utils/utils', 'mvc/ui/ui-portlet', 'mvc/ui/ui-misc',
|
||||
}
|
||||
},
|
||||
|
||||
/** Shows the final message (usually upon successful job submission)
|
||||
*/
|
||||
reciept: function($el) {
|
||||
this.$el.empty();
|
||||
this.$el.append($el);
|
||||
},
|
||||
|
||||
/** Highlight and scroll to input element (currently only used for error notifications)
|
||||
*/
|
||||
highlight: function (input_id, message, silent) {
|
||||
@@ -114,9 +107,14 @@ define(['utils/utils', 'mvc/ui/ui-portlet', 'mvc/ui/ui-misc',
|
||||
|
||||
// scroll to first input element
|
||||
if (!silent) {
|
||||
$('html, body').animate({
|
||||
scrollTop: input_element.$el.offset().top - 20
|
||||
}, 500);
|
||||
if (self==top) {
|
||||
var $panel = this.$el.parents().filter(function() {
|
||||
return $(this).css('overflow') == 'auto';
|
||||
}).first();
|
||||
$panel.animate({ scrollTop : $panel.scrollTop() + input_element.$el.offset().top - 50 }, 500);
|
||||
} else {
|
||||
$('html, body').animate({ scrollTop : input_element.$el.offset().top - 20 }, 500);
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
@@ -201,12 +199,8 @@ define(['utils/utils', 'mvc/ui/ui-portlet', 'mvc/ui/ui-misc',
|
||||
inputs : this.options.inputs
|
||||
});
|
||||
|
||||
// switch to classic tool form mako if the form definition is incompatible
|
||||
if (this.incompatible) {
|
||||
this.$el.hide();
|
||||
$('#tool-form-classic').show();
|
||||
return;
|
||||
}
|
||||
// remove tooltips
|
||||
$( '.tooltip' ).remove();
|
||||
|
||||
// create portlet
|
||||
this.portlet = new Portlet.View({
|
||||
@@ -240,4 +234,4 @@ define(['utils/utils', 'mvc/ui/ui-portlet', 'mvc/ui/ui-misc',
|
||||
console.debug('tools-form-base::initialize() - Completed.');
|
||||
}
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -118,4 +118,4 @@ return Backbone.Model.extend({
|
||||
|
||||
});
|
||||
|
||||
});
|
||||
});
|
||||
|
||||
@@ -14,13 +14,13 @@ return Backbone.View.extend({
|
||||
|
||||
// model
|
||||
grid: null,
|
||||
|
||||
|
||||
// Initialize
|
||||
initialize: function(grid_config)
|
||||
{
|
||||
// set element
|
||||
this.setElement('#grid-container');
|
||||
|
||||
|
||||
// fix padding
|
||||
if (grid_config.use_panels) {
|
||||
$('#center').css ({
|
||||
@@ -28,11 +28,11 @@ return Backbone.View.extend({
|
||||
overflow : 'auto'
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
// initialize controls
|
||||
this.init_grid(grid_config);
|
||||
},
|
||||
|
||||
|
||||
// refresh frames
|
||||
handle_refresh: function (refresh_frames) {
|
||||
if (refresh_frames) {
|
||||
@@ -49,26 +49,26 @@ return Backbone.View.extend({
|
||||
{
|
||||
// link grid model
|
||||
this.grid = new GridModel(grid_config);
|
||||
|
||||
|
||||
// get options
|
||||
var options = this.grid.attributes;
|
||||
|
||||
|
||||
// handle refresh requests
|
||||
this.handle_refresh(options.refresh_frames);
|
||||
|
||||
|
||||
// strip protocol and domain
|
||||
var url = this.grid.get('url_base');
|
||||
url = url.replace(/^.*\/\/[^\/]+/, '');
|
||||
this.grid.set('url_base', url);
|
||||
|
||||
|
||||
// append main template
|
||||
this.$el.html(Templates.grid(options));
|
||||
|
||||
|
||||
// update div contents
|
||||
this.$el.find('#grid-table-header').html(Templates.header(options));
|
||||
this.$el.find('#grid-table-body').html(Templates.body(options));
|
||||
this.$el.find('#grid-table-footer').html(Templates.footer(options));
|
||||
|
||||
|
||||
// update message
|
||||
if (options.message) {
|
||||
this.$el.find('#grid-message').html(Templates.message(options));
|
||||
@@ -77,37 +77,37 @@ return Backbone.View.extend({
|
||||
setTimeout( function() { self.$el.find('#grid-message').html(''); }, 5000);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// configure elements
|
||||
this.init_grid_elements();
|
||||
this.init_grid_controls();
|
||||
|
||||
|
||||
// attach global event handler
|
||||
init_refresh_on_change();
|
||||
},
|
||||
|
||||
|
||||
// Initialize grid controls
|
||||
init_grid_controls: function() {
|
||||
|
||||
|
||||
// link
|
||||
var self = this;
|
||||
|
||||
|
||||
// Initialize grid operation button.
|
||||
this.$el.find('.operation-button').each(function() {
|
||||
$(this).off();
|
||||
$(this).click(function() {
|
||||
self.submit_operation(this, operation.confirm);
|
||||
self.submit_operation(this);
|
||||
return false;
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
// Initialize text filters to select text on click and use normal font when user is typing.
|
||||
this.$el.find('input[type=text]').each(function() {
|
||||
$(this).off();
|
||||
$(this).click(function() { $(this).select(); } )
|
||||
.keyup(function () { $(this).css('font-style', 'normal'); });
|
||||
});
|
||||
|
||||
|
||||
// Initialize sort links.
|
||||
this.$el.find('.sort-link').each( function() {
|
||||
$(this).off();
|
||||
@@ -116,7 +116,7 @@ return Backbone.View.extend({
|
||||
return false;
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
// Initialize text filters.
|
||||
this.$el.find('.text-filter-form').each( function() {
|
||||
$(this).off();
|
||||
@@ -129,14 +129,14 @@ return Backbone.View.extend({
|
||||
return false;
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
// Initialize categorical filters.
|
||||
this.$el.find('.text-filter-val > a').each( function() {
|
||||
$(this).off();
|
||||
$(this).click( function() {
|
||||
// Remove visible element.
|
||||
$(this).parent().remove();
|
||||
|
||||
|
||||
// Remove filter condition.
|
||||
self.remove_filter_condition ($(this).attr('filter_key'), $(this).attr('filter_val'));
|
||||
|
||||
@@ -144,7 +144,7 @@ return Backbone.View.extend({
|
||||
return false;
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
// Initialize categorical filters.
|
||||
this.$el.find('.categorical-filter > a').each( function() {
|
||||
$(this).off();
|
||||
@@ -153,7 +153,7 @@ return Backbone.View.extend({
|
||||
return false;
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
// Initialize autocomplete for text inputs in search UI.
|
||||
var t1 = this.$el.find('#input-tags-filter');
|
||||
if (t1.length) {
|
||||
@@ -165,7 +165,7 @@ return Backbone.View.extend({
|
||||
t2.autocomplete(this.grid.history_name_autocomplete_url,
|
||||
{ selectFirst: false, autoFill: false, highlight: false, mustMatch: false });
|
||||
}
|
||||
|
||||
|
||||
// Initialize standard, advanced search toggles.
|
||||
this.$el.find('.advanced-search-toggle').each( function() {
|
||||
$(this).off();
|
||||
@@ -175,7 +175,7 @@ return Backbone.View.extend({
|
||||
return false;
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
// Add event to check all box
|
||||
this.$el.find('#check_all').off();
|
||||
this.$el.find('#check_all').on('click', function() {
|
||||
@@ -192,13 +192,13 @@ return Backbone.View.extend({
|
||||
var update_checked = function() {
|
||||
check_count.text( $(checkboxes).filter(":checked").length );
|
||||
};
|
||||
|
||||
|
||||
$(checkboxes).each( function() {
|
||||
$(this).change(update_checked);
|
||||
});
|
||||
update_checked();
|
||||
});
|
||||
|
||||
|
||||
// Initialize ratings.
|
||||
if (this.$el.find('.community_rating_star').length !== 0)
|
||||
this.$el.find('.community_rating_star').rating({});
|
||||
@@ -206,7 +206,7 @@ return Backbone.View.extend({
|
||||
// get options
|
||||
var options = this.grid.attributes;
|
||||
var self = this;
|
||||
|
||||
|
||||
//
|
||||
// add page click events
|
||||
//
|
||||
@@ -216,7 +216,7 @@ return Backbone.View.extend({
|
||||
return false;
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
//
|
||||
// add inbound/outbound events
|
||||
//
|
||||
@@ -227,10 +227,10 @@ return Backbone.View.extend({
|
||||
inbound : true
|
||||
});
|
||||
return false;
|
||||
|
||||
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
this.$el.find('.use-outbound').each( function() {
|
||||
$(this).click( function(e) {
|
||||
self.execute({
|
||||
@@ -239,13 +239,13 @@ return Backbone.View.extend({
|
||||
return false;
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
// empty grid?
|
||||
var items_length = options.items.length;
|
||||
if (items_length == 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
//
|
||||
// add operation popup menus
|
||||
//
|
||||
@@ -253,12 +253,12 @@ return Backbone.View.extend({
|
||||
{
|
||||
// get items
|
||||
var item = options.items[i];
|
||||
|
||||
|
||||
// get identifiers
|
||||
var button = this.$el.find('#grid-' + i + '-popup');
|
||||
button.off();
|
||||
var popup = new PopupMenu(button);
|
||||
|
||||
|
||||
// load details
|
||||
for (var j in options['operations'])
|
||||
{
|
||||
@@ -267,7 +267,7 @@ return Backbone.View.extend({
|
||||
var operation_id = operation['label'];
|
||||
var operation_settings = item['operation_config'][operation_id];
|
||||
var encode_id = item['encode_id'];
|
||||
|
||||
|
||||
// check
|
||||
if (operation_settings['allowed'] && operation['allow_popup'])
|
||||
{
|
||||
@@ -280,7 +280,7 @@ return Backbone.View.extend({
|
||||
confirmation_text : operation['confirm'],
|
||||
inbound : operation['inbound']
|
||||
};
|
||||
|
||||
|
||||
// add popup function
|
||||
popupConfig.func = function(e)
|
||||
{
|
||||
@@ -289,7 +289,7 @@ return Backbone.View.extend({
|
||||
var options = this.findItemByHtml(label);
|
||||
self.execute(options);
|
||||
};
|
||||
|
||||
|
||||
// add item
|
||||
popup.addItem(popupConfig);
|
||||
}
|
||||
@@ -303,10 +303,10 @@ return Backbone.View.extend({
|
||||
if (value === "") {
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
// Add condition to grid.
|
||||
this.grid.add_filter(name, value, true);
|
||||
|
||||
|
||||
// Add button that displays filter and provides a button to delete it.
|
||||
var t = $(Templates.filter_element(name, value));
|
||||
var self = this;
|
||||
@@ -321,7 +321,7 @@ return Backbone.View.extend({
|
||||
// append to container
|
||||
var container = this.$el.find('#' + name + '-filtering-criteria');
|
||||
container.append(t);
|
||||
|
||||
|
||||
// execute
|
||||
this.go_page_one();
|
||||
this.execute();
|
||||
@@ -331,12 +331,12 @@ return Backbone.View.extend({
|
||||
remove_filter_condition: function (name, value) {
|
||||
// Remove filter condition.
|
||||
this.grid.remove_filter(name, value);
|
||||
|
||||
|
||||
// Execute
|
||||
this.go_page_one();
|
||||
this.execute();
|
||||
},
|
||||
|
||||
|
||||
// Set sort condition for grid.
|
||||
set_sort_condition: function (col_key) {
|
||||
// Set new sort condition. New sort is col_key if sorting new column; if reversing sort on
|
||||
@@ -349,17 +349,17 @@ return Backbone.View.extend({
|
||||
new_sort = '-' + col_key;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// Remove sort arrows elements.
|
||||
this.$el.find('.sort-arrow').remove();
|
||||
|
||||
|
||||
// Add sort arrow element to new sort column.
|
||||
var sort_arrow = (new_sort.substring(0,1) == '-') ? '↑' : '↓';
|
||||
var t = $('<span>' + sort_arrow + '</span>').addClass('sort-arrow');
|
||||
|
||||
|
||||
// Add to header
|
||||
this.$el.find('#' + col_key + '-header').append(t);
|
||||
|
||||
|
||||
// Update grid.
|
||||
this.grid.set('sort_key', new_sort);
|
||||
this.go_page_one();
|
||||
@@ -394,7 +394,7 @@ return Backbone.View.extend({
|
||||
$(this).append(t);
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
// Update grid.
|
||||
this.grid.add_filter(name, new_value);
|
||||
this.go_page_one();
|
||||
@@ -417,7 +417,7 @@ return Backbone.View.extend({
|
||||
$(this).empty();
|
||||
$(this).addClass('inactive-link');
|
||||
$(this).text(text);
|
||||
}
|
||||
}
|
||||
else if (page_num === cur_page) {
|
||||
// Add hyperlink to this page since grid will no longer be on this page. It is assumed that
|
||||
// this element has a single child, a hyperlink/anchor.
|
||||
@@ -445,26 +445,32 @@ return Backbone.View.extend({
|
||||
{
|
||||
// identify operation
|
||||
var operation_name = $(operation_button).val();
|
||||
|
||||
|
||||
// verify in any item is selected
|
||||
var number_of_checked_ids = this.$el.find('input[name="id"]:checked').length;
|
||||
if (!number_of_checked_ids > 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
// Check to see if there's grid confirmation text for this operation
|
||||
var operation = _.findWhere(this.grid.attributes.operations, {label: operation_name});
|
||||
if (operation && !confirmation_text){
|
||||
confirmation_text = operation.confirm || '';
|
||||
}
|
||||
|
||||
// collect ids
|
||||
var item_ids = [];
|
||||
this.$el.find('input[name=id]:checked').each(function() {
|
||||
item_ids.push( $(this).val() );
|
||||
});
|
||||
|
||||
|
||||
// execute operation
|
||||
this.execute({
|
||||
operation: operation_name,
|
||||
id: item_ids,
|
||||
confirmation_text: confirmation_text
|
||||
});
|
||||
|
||||
|
||||
// return
|
||||
return true;
|
||||
},
|
||||
@@ -492,14 +498,14 @@ return Backbone.View.extend({
|
||||
}
|
||||
this.init_grid_elements();
|
||||
},
|
||||
|
||||
|
||||
// Go back to page one; this is useful when a filter is applied.
|
||||
go_page_one: function () {
|
||||
// Need to go back to page 1 if not showing all.
|
||||
var cur_page = this.grid.get('cur_page');
|
||||
if (cur_page !== null && cur_page !== undefined && cur_page !== 'all') {
|
||||
this.grid.set('cur_page', 1);
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
//
|
||||
@@ -532,7 +538,7 @@ return Backbone.View.extend({
|
||||
var href_parms = href_parms_str.split("&");
|
||||
for (var index = 0; index < href_parms.length; index++) {
|
||||
if (href_parms[index].indexOf('operation') != -1) {
|
||||
// Found operation parm; get operation value.
|
||||
// Found operation parm; get operation value.
|
||||
operation = href_parms[index].split('=')[1];
|
||||
operation = operation.replace (/\+/g, ' ');
|
||||
} else if (href_parms[index].indexOf('id') != -1) {
|
||||
@@ -543,7 +549,7 @@ return Backbone.View.extend({
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// check for operation details
|
||||
if (operation && id) {
|
||||
// show confirmation box
|
||||
@@ -566,11 +572,11 @@ return Backbone.View.extend({
|
||||
} else {
|
||||
this.go_to(inbound, href);
|
||||
}
|
||||
|
||||
|
||||
// done
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
// refresh grid
|
||||
if (href) {
|
||||
this.go_to(inbound, href);
|
||||
@@ -593,23 +599,23 @@ return Backbone.View.extend({
|
||||
// get aysnc status
|
||||
var async = this.grid.get('async');
|
||||
this.grid.set('async', false);
|
||||
|
||||
|
||||
// get slide status
|
||||
advanced_search = this.$el.find('#advanced-search').is(':visible');
|
||||
this.grid.set('advanced_search', advanced_search);
|
||||
|
||||
|
||||
// get default url
|
||||
if(!href) {
|
||||
href = this.grid.get('url_base') + '?' + $.param(this.grid.get_url_data());
|
||||
}
|
||||
|
||||
|
||||
// clear grid of transient request attributes.
|
||||
this.grid.set({
|
||||
operation: undefined,
|
||||
item_ids: undefined,
|
||||
async: async
|
||||
});
|
||||
|
||||
|
||||
if (inbound) {
|
||||
// this currently assumes that there is only a single grid shown at a time
|
||||
var $div = $('.grid-header').closest('.inbound');
|
||||
@@ -618,7 +624,7 @@ return Backbone.View.extend({
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
window.location = href;
|
||||
},
|
||||
|
||||
@@ -626,7 +632,7 @@ return Backbone.View.extend({
|
||||
update_grid: function () {
|
||||
// If there's an operation, do POST; otherwise, do GET.
|
||||
var method = (this.grid.get('operation') ? 'POST' : 'GET' );
|
||||
|
||||
|
||||
// Show overlay to indicate loading and prevent user actions.
|
||||
this.$el.find('.loading-elt-overlay').show();
|
||||
var self = this;
|
||||
@@ -636,21 +642,21 @@ return Backbone.View.extend({
|
||||
data: self.grid.get_url_data(),
|
||||
error: function(response) { alert( 'Grid refresh failed' );},
|
||||
success: function(response_text) {
|
||||
|
||||
|
||||
// backup
|
||||
var embedded = self.grid.get('embedded');
|
||||
var insert = self.grid.get('insert');
|
||||
|
||||
|
||||
// request new configuration
|
||||
var json = $.parseJSON(response_text);
|
||||
|
||||
|
||||
// update
|
||||
json.embedded = embedded;
|
||||
json.insert = insert;
|
||||
|
||||
|
||||
// Initialize new grid config
|
||||
self.init_grid(json);
|
||||
|
||||
|
||||
// Hide loading overlay.
|
||||
self.$el.find('.loading-elt-overlay').hide();
|
||||
},
|
||||
@@ -661,7 +667,7 @@ return Backbone.View.extend({
|
||||
item_ids: undefined
|
||||
});
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
@@ -0,0 +1,100 @@
|
||||
define([
|
||||
"utils/localization"
|
||||
], function( _l ){
|
||||
/* =============================================================================
|
||||
Wrapper function around the global Galaxy.modal for use when copying histories.
|
||||
|
||||
==============================================================================*/
|
||||
function _renderBody( vars ){
|
||||
return [
|
||||
'<form action="">',
|
||||
'<label for="copy-modal-title">',
|
||||
_l( 'Enter a title for the copied history' ), ':',
|
||||
'</label><br />',
|
||||
'<input id="copy-modal-title" class="form-control" style="width: 100%" value="', vars.defaultCopyName, '" />',
|
||||
'<br />',
|
||||
'<p>', _l( 'You can make a copy of the history that includes all datasets in the original history' ),
|
||||
_l( ' or just the active (not deleted) datasets.' ), '</p>',
|
||||
// copy non-deleted is the default
|
||||
'<input name="copy-what" type="radio" id="copy-non-deleted" value="copy-non-deleted" checked />',
|
||||
'<label for="copy-non-deleted">', _l( 'Copy only active (not deleted) datasets' ), '</label><br />',
|
||||
'<input name="copy-what" type="radio" id="copy-all" value="copy-all" />',
|
||||
'<label for="copy-all">', _l( 'Copy all datasets, including deleted ones' ), '</label><br />',
|
||||
'</form>'
|
||||
].join('');
|
||||
}
|
||||
|
||||
function _validateName( name ){
|
||||
if( !name ){
|
||||
if( !Galaxy.modal.$( '#invalid-title' ).size() ){
|
||||
var $invalidTitle = $( '<p/>' ).attr( 'id', 'invalid-title' )
|
||||
.css({ color: 'red', 'margin-top': '8px' })
|
||||
.addClass( 'bg-danger' ).text( _l( 'Please enter a valid history title' ) );
|
||||
Galaxy.modal.$( '.modal-body' ).append( $invalidTitle );
|
||||
}
|
||||
return false;
|
||||
}
|
||||
return name;
|
||||
}
|
||||
|
||||
function _renderCopyIndicator(){
|
||||
return $([
|
||||
'<p>', '<span class="fa fa-spinner fa-spin"></span> ', _l( 'Copying history' ), '...', '</p>'
|
||||
].join( '' ))
|
||||
//TODO: move out of inline
|
||||
.css({ 'margin-top': '8px' });
|
||||
}
|
||||
|
||||
/** show the dialog and handle validation, ajax, and callbacks */
|
||||
function historyCopyDialog( history, options ){
|
||||
options = options || {};
|
||||
// fall back to un-notifying copy
|
||||
if( !( Galaxy && Galaxy.modal ) ){
|
||||
return history.copy();
|
||||
}
|
||||
|
||||
// maybe better as multiselect dialog?
|
||||
var historyName = history.get( 'name' ),
|
||||
defaultCopyName = "Copy of '" + historyName + "'";
|
||||
|
||||
function copyHistory( name ){
|
||||
var copyAllDatasets = Galaxy.modal.$( 'input[name="copy-what"]:checked' ).val() === 'copy-all',
|
||||
$copyIndicator = _renderCopyIndicator();
|
||||
Galaxy.modal.$( '.modal-body' ).children().replaceWith( $copyIndicator );
|
||||
Galaxy.modal.$( 'button' ).prop( 'disabled', true );
|
||||
history.copy( true, name, copyAllDatasets )
|
||||
//TODO: make this unneccessary with pub-sub error or handling via Galaxy
|
||||
.fail( function(){
|
||||
alert( _l( 'History could not be copied. Please contact a Galaxy administrator' ) );
|
||||
})
|
||||
.always( function(){
|
||||
Galaxy.modal.hide();
|
||||
});
|
||||
}
|
||||
function checkNameAndCopy(){
|
||||
var name = Galaxy.modal.$( '#copy-modal-title' ).val();
|
||||
if( !_validateName( name ) ){ return; }
|
||||
copyHistory( name );
|
||||
}
|
||||
|
||||
Galaxy.modal.show( _.extend({
|
||||
title : _l( 'Copying history' ) + ' "' + historyName + '"',
|
||||
body : $( _renderBody({ defaultCopyName: defaultCopyName }) ),
|
||||
buttons : {
|
||||
'Cancel' : function(){ Galaxy.modal.hide(); },
|
||||
'Copy' : checkNameAndCopy
|
||||
},
|
||||
closing_events : true
|
||||
}, options ));
|
||||
$( '#copy-modal-title' ).focus().select();
|
||||
$( '#copy-modal-title' ).on( 'keydown', function( ev ){
|
||||
if( ev.keyCode === 13 ){
|
||||
checkNameAndCopy();
|
||||
}
|
||||
});
|
||||
// TODO: return a promise completed on copy, close, or error
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
return historyCopyDialog;
|
||||
});
|
||||
@@ -16,13 +16,30 @@ var HDAListItemEdit = _super.extend(
|
||||
|
||||
className : _super.prototype.className + " history-content",
|
||||
|
||||
// ......................................................................... events
|
||||
/** In this override, only get details if in the ready state, get rerunnable if in other states.
|
||||
* Note: fetch with no 'change' event triggering to prevent automatic rendering.
|
||||
*/
|
||||
_fetchModelDetails : function(){
|
||||
var view = this;
|
||||
if( view.model.inReadyState() && !view.model.hasDetails() ){
|
||||
return view.model.fetch({ silent: true });
|
||||
|
||||
// special case the need for the rerunnable and creating_job attributes
|
||||
// needed for rendering re-run button on queued, running datasets
|
||||
} else if( !view.model.has( 'rerunnable' ) ){
|
||||
return view.model.fetch({ silent: true, data: {
|
||||
// only fetch rerunnable and creating_job to keep overhead down
|
||||
keys: [ 'rerunnable', 'creating_job' ].join(',')
|
||||
}});
|
||||
}
|
||||
return jQuery.when();
|
||||
},
|
||||
|
||||
/** event map */
|
||||
events : _.extend( _.clone( _super.prototype.events ), {
|
||||
'click .unhide-link' : function( ev ){ this.model.unhide(); return false; }
|
||||
}),
|
||||
|
||||
// ......................................................................... misc
|
||||
/** string rep */
|
||||
toString : function(){
|
||||
var modelString = ( this.model )?( this.model + '' ):( '(no model)' );
|
||||
|
||||
@@ -21,11 +21,9 @@ var HDCAListItemView = _super.extend(
|
||||
/** event listeners */
|
||||
_setUpListeners : function(){
|
||||
_super.prototype._setUpListeners.call( this );
|
||||
// re-rendering on deletion
|
||||
this.model.on( 'change', function( model, options ){
|
||||
if( _.isEqual( _.keys( model.changed ), [ 'visible' ] ) ){
|
||||
this.render();
|
||||
}
|
||||
|
||||
this.model.on({
|
||||
'change:populated change:visible' : function( model, options ){ this.render(); },
|
||||
}, this );
|
||||
},
|
||||
|
||||
@@ -46,7 +44,7 @@ var HDCAListItemView = _super.extend(
|
||||
_swapNewRender : function( $newRender ){
|
||||
_super.prototype._swapNewRender.call( this, $newRender );
|
||||
//TODO: model currently has no state
|
||||
var state = this.model.get( 'state' ) || STATES.OK;
|
||||
var state = !this.model.get( 'populated' ) ? STATES.RUNNING : STATES.OK;
|
||||
//if( this.model.has( 'state' ) ){
|
||||
this.$el.addClass( 'state-' + state );
|
||||
//}
|
||||
|
||||
@@ -25,7 +25,6 @@ var HistoryContents = Backbone.Collection.extend( BASE_MVC.LoggableMixin ).exten
|
||||
|
||||
/** since history content is a mix, override model fn into a factory, creating based on history_content_type */
|
||||
model : function( attrs, options ) {
|
||||
|
||||
//TODO: can we move the type_id stuff here?
|
||||
//attrs.type_id = typeIdStr( attrs );
|
||||
|
||||
@@ -134,13 +133,25 @@ var HistoryContents = Backbone.Collection.extend( BASE_MVC.LoggableMixin ).exten
|
||||
return item.isVisible( show_deleted, show_hidden );
|
||||
}));
|
||||
|
||||
_.each( filters, function( filter_fn ){
|
||||
if( !_.isFunction( filter_fn ) ){ return; }
|
||||
filteredHdas = new HistoryContents( filteredHdas.filter( filter_fn ) );
|
||||
_.each( filters, function( filterFn ){
|
||||
if( !_.isFunction( filterFn ) ){ return; }
|
||||
filteredHdas = new HistoryContents( filteredHdas.filter( filterFn ) );
|
||||
});
|
||||
return filteredHdas;
|
||||
},
|
||||
|
||||
/** return a new contents collection of only hidden items */
|
||||
hidden : function(){
|
||||
function filterFn( c ){ return c.hidden(); }
|
||||
return new HistoryContents( this.filter( filterFn ) );
|
||||
},
|
||||
|
||||
/** return a new contents collection of only hidden items */
|
||||
deleted : function(){
|
||||
function filterFn( c ){ return c.get( 'deleted' ); }
|
||||
return new HistoryContents( this.filter( filterFn ) );
|
||||
},
|
||||
|
||||
/** return true if any contents don't have details */
|
||||
haveDetails : function(){
|
||||
return this.all( function( content ){ return content.hasDetails(); });
|
||||
@@ -244,6 +255,7 @@ var HistoryContents = Backbone.Collection.extend( BASE_MVC.LoggableMixin ).exten
|
||||
// ........................................................................ misc
|
||||
/** override to ensure type id is set */
|
||||
set : function( models, options ){
|
||||
models = _.isArray( models )? models : [ models ];
|
||||
_.each( models, function( model ){
|
||||
if( !model.type_id || !model.get || !model.get( 'type_id' ) ){
|
||||
model.type_id = HISTORY_CONTENT.typeIdStr( model.history_content_type, model.id );
|
||||
|
||||
@@ -1,8 +1,10 @@
|
||||
|
||||
define([
|
||||
"mvc/history/history-contents",
|
||||
"utils/utils",
|
||||
"mvc/base-mvc",
|
||||
"utils/localization"
|
||||
], function( HISTORY_CONTENTS, BASE_MVC, _l ){
|
||||
], function( HISTORY_CONTENTS, UTILS, BASE_MVC, _l ){
|
||||
//==============================================================================
|
||||
/** @class Model for a Galaxy history resource - both a record of user
|
||||
* tool use and a collection of the datasets those tools produced.
|
||||
@@ -96,6 +98,26 @@ var History = Backbone.Model.extend( BASE_MVC.LoggableMixin ).extend(
|
||||
this.clearUpdateTimeout();
|
||||
},
|
||||
|
||||
/** convert size in bytes to a more human readable version */
|
||||
nice_size : function(){
|
||||
return UTILS.bytesToString( this.get( 'size' ), true, 2 );
|
||||
},
|
||||
|
||||
/** override to add nice_size */
|
||||
toJSON : function(){
|
||||
return _.extend( Backbone.Model.prototype.toJSON.call( this ), {
|
||||
nice_size : this.nice_size()
|
||||
});
|
||||
},
|
||||
|
||||
/** override to allow getting nice_size */
|
||||
get : function( key ){
|
||||
if( key === 'nice_size' ){
|
||||
return this.nice_size();
|
||||
}
|
||||
return Backbone.Model.prototype.get.apply( this, arguments );
|
||||
},
|
||||
|
||||
// ........................................................................ common queries
|
||||
/** T/F is this history owned by the current user (Galaxy.currUser)
|
||||
* Note: that this will return false for an anon user even if the history is theirs.
|
||||
@@ -222,7 +244,7 @@ var History = Backbone.Model.extend( BASE_MVC.LoggableMixin ).extend(
|
||||
* @fires copied passed this history and the response JSON from the copy
|
||||
* @returns {xhr}
|
||||
*/
|
||||
copy : function( current, name ){
|
||||
copy : function( current, name, allDatasets ){
|
||||
current = ( current !== undefined )?( current ):( true );
|
||||
if( !this.id ){
|
||||
throw new Error( 'You must set the history ID before copying it.' );
|
||||
@@ -235,8 +257,9 @@ var History = Backbone.Model.extend( BASE_MVC.LoggableMixin ).extend(
|
||||
if( name ){
|
||||
postData.name = name;
|
||||
}
|
||||
|
||||
//TODO:?? all datasets?
|
||||
if( !allDatasets ){
|
||||
postData.all_datasets = false;
|
||||
}
|
||||
|
||||
var history = this,
|
||||
copy = jQuery.post( this.urlRoot, postData );
|
||||
@@ -354,60 +377,265 @@ History.getHistoryData = function getHistoryData( historyId, options ){
|
||||
};
|
||||
|
||||
|
||||
//==============================================================================
|
||||
var ControlledFetchMixin = {
|
||||
|
||||
/** Override to convert certain options keys into API index parameters */
|
||||
fetch : function( options ){
|
||||
options = options || {};
|
||||
options.data = options.data || this._buildFetchData( options );
|
||||
// use repeated params for arrays, e.g. q=1&qv=1&q=2&qv=2
|
||||
options.traditional = true;
|
||||
return Backbone.Collection.prototype.fetch.call( this, options );
|
||||
},
|
||||
|
||||
/** These attribute keys are valid params to fetch/API-index */
|
||||
_fetchOptions : [
|
||||
/** model dependent string to control the order of models returned */
|
||||
'order',
|
||||
/** limit the number of models returned from a fetch */
|
||||
'limit',
|
||||
/** skip this number of models when fetching */
|
||||
'offset',
|
||||
/** what series of attributes to return (model dependent) */
|
||||
'view',
|
||||
/** individual keys to return for the models (see api/histories.index) */
|
||||
'keys'
|
||||
],
|
||||
|
||||
/** Build the data dictionary to send to fetch's XHR as data */
|
||||
_buildFetchData : function( options ){
|
||||
var data = {},
|
||||
fetchDefaults = this._fetchDefaults();
|
||||
options = _.defaults( options || {}, fetchDefaults );
|
||||
data = _.pick( options, this._fetchOptions );
|
||||
|
||||
var filters = _.has( options, 'filters' )? options.filters : ( fetchDefaults.filters || {} );
|
||||
if( !_.isEmpty( filters ) ){
|
||||
_.extend( data, this._buildFetchFilters( filters ) );
|
||||
}
|
||||
return data;
|
||||
},
|
||||
|
||||
/** Override to have defaults for fetch options and filters */
|
||||
_fetchDefaults : function(){
|
||||
// to be overridden
|
||||
return {};
|
||||
},
|
||||
|
||||
/** Convert dictionary filters to qqv style arrays */
|
||||
_buildFetchFilters : function( filters ){
|
||||
var filterMap = {
|
||||
q : [],
|
||||
qv : []
|
||||
};
|
||||
_.each( filters, function( v, k ){
|
||||
if( v === true ){ v = 'True'; }
|
||||
if( v === false ){ v = 'False'; }
|
||||
filterMap.q.push( k );
|
||||
filterMap.qv.push( v );
|
||||
});
|
||||
return filterMap;
|
||||
},
|
||||
};
|
||||
|
||||
//==============================================================================
|
||||
/** @class A collection of histories (per user).
|
||||
* (stub) currently unused.
|
||||
*/
|
||||
var HistoryCollection = Backbone.Collection.extend( BASE_MVC.LoggableMixin ).extend(
|
||||
var HistoryCollection = Backbone.Collection.extend( BASE_MVC.LoggableMixin ).extend( ControlledFetchMixin ).extend(
|
||||
/** @lends HistoryCollection.prototype */{
|
||||
model : History,
|
||||
|
||||
/** logger used to record this.log messages, commonly set to console */
|
||||
//logger : console,
|
||||
|
||||
urlRoot : ( window.galaxy_config? galaxy_config.root : '/' ) + 'api/histories',
|
||||
//url : function(){ return this.urlRoot; },
|
||||
/** @type {String} the default sortOrders key for sorting */
|
||||
DEFAULT_ORDER : 'update_time',
|
||||
|
||||
/** @type {Object} map of collection sorting orders generally containing a getter to return the attribute
|
||||
* sorted by and asc T/F if it is an ascending sort.
|
||||
*/
|
||||
sortOrders : {
|
||||
'update_time' : {
|
||||
getter : function( h ){ return new Date( h.get( 'update_time' ) ); },
|
||||
asc : false
|
||||
},
|
||||
'update_time-asc' : {
|
||||
getter : function( h ){ return new Date( h.get( 'update_time' ) ); },
|
||||
asc : true
|
||||
},
|
||||
'name' : {
|
||||
getter : function( h ){ return h.get( 'name' ); },
|
||||
asc : true
|
||||
},
|
||||
'name-dsc' : {
|
||||
getter : function( h ){ return h.get( 'name' ); },
|
||||
asc : false
|
||||
},
|
||||
'size' : {
|
||||
getter : function( h ){ return h.get( 'size' ); },
|
||||
asc : false
|
||||
},
|
||||
'size-asc' : {
|
||||
getter : function( h ){ return h.get( 'size' ); },
|
||||
asc : true
|
||||
}
|
||||
},
|
||||
|
||||
initialize : function( models, options ){
|
||||
options = options || {};
|
||||
this.log( 'HistoryCollection.initialize', arguments );
|
||||
|
||||
// instance vars
|
||||
/** @type {boolean} should deleted histories be included */
|
||||
this.includeDeleted = options.includeDeleted || false;
|
||||
// set the sort order
|
||||
this.setOrder( options.order || this.DEFAULT_ORDER );
|
||||
/** @type {String} encoded id of the history that's current */
|
||||
this.currentHistoryId = options.currentHistoryId;
|
||||
/** @type {boolean} have all histories been fetched and in the collection? */
|
||||
this.allFetched = options.allFetched || false;
|
||||
|
||||
//this.on( 'all', function(){
|
||||
// this.on( 'all', function(){
|
||||
// console.info( 'event:', arguments );
|
||||
//});
|
||||
|
||||
// });
|
||||
this.setUpListeners();
|
||||
},
|
||||
|
||||
setUpListeners : function setUpListeners(){
|
||||
var collection = this;
|
||||
urlRoot : ( window.galaxy_config? galaxy_config.root : '/' ) + 'api/histories',
|
||||
url : function(){ return this.urlRoot; },
|
||||
|
||||
// when a history is deleted, remove it from the collection (if optionally set to do so)
|
||||
this.on( 'change:deleted', function( history ){
|
||||
this.debug( 'change:deleted', collection.includeDeleted, history.get( 'deleted' ) );
|
||||
if( !collection.includeDeleted && history.get( 'deleted' ) ){
|
||||
collection.remove( history );
|
||||
}
|
||||
});
|
||||
|
||||
// listen for a history copy, adding it to the beginning of the collection
|
||||
this.on( 'copied', function( original, newData ){
|
||||
this.unshift( new History( newData, [] ) );
|
||||
});
|
||||
/** returns map of default filters and settings for fetching from the API */
|
||||
_fetchDefaults : function(){
|
||||
// to be overridden
|
||||
var defaults = {
|
||||
order : this.order,
|
||||
view : 'detailed'
|
||||
};
|
||||
if( !this.includeDeleted ){
|
||||
defaults.filters = {
|
||||
deleted : false,
|
||||
purged : false,
|
||||
};
|
||||
}
|
||||
return defaults;
|
||||
},
|
||||
|
||||
/** set up reflexive event handlers */
|
||||
setUpListeners : function setUpListeners(){
|
||||
this.on({
|
||||
// when a history is deleted, remove it from the collection (if optionally set to do so)
|
||||
'change:deleted' : function( history ){
|
||||
// TODO: this becomes complicated when more filters are used
|
||||
this.debug( 'change:deleted', this.includeDeleted, history.get( 'deleted' ) );
|
||||
if( !this.includeDeleted && history.get( 'deleted' ) ){
|
||||
this.remove( history );
|
||||
}
|
||||
},
|
||||
// listen for a history copy, setting it to current
|
||||
'copied' : function( original, newData ){
|
||||
this.setCurrent( new History( newData, [] ) );
|
||||
},
|
||||
// when a history is made current, track the id in the collection
|
||||
'set-as-current' : function( history ){
|
||||
var oldCurrentId = this.currentHistoryId;
|
||||
this.trigger( 'no-longer-current', oldCurrentId );
|
||||
this.currentHistoryId = history.id;
|
||||
}
|
||||
}, this );
|
||||
},
|
||||
|
||||
/** override to allow passing options.order and setting the sort order to one of sortOrders */
|
||||
sort : function( options ){
|
||||
options = options || {};
|
||||
this.setOrder( options.order );
|
||||
return Backbone.Collection.prototype.sort.call( this, options );
|
||||
},
|
||||
|
||||
/** build the comparator used to sort this collection using the sortOrder map and the given order key
|
||||
* @event 'changed-order' passed the new order and the collection
|
||||
*/
|
||||
setOrder : function( order ){
|
||||
var collection = this,
|
||||
sortOrder = this.sortOrders[ order ];
|
||||
if( _.isUndefined( sortOrder ) ){ return; }
|
||||
|
||||
collection.order = order;
|
||||
collection.comparator = function comparator( a, b ){
|
||||
var currentHistoryId = collection.currentHistoryId;
|
||||
// current always first
|
||||
if( a.id === currentHistoryId ){ return -1; }
|
||||
if( b.id === currentHistoryId ){ return 1; }
|
||||
// then compare by an attribute
|
||||
a = sortOrder.getter( a );
|
||||
b = sortOrder.getter( b );
|
||||
return sortOrder.asc?
|
||||
( ( a === b )?( 0 ):( a > b ? 1 : -1 ) ):
|
||||
( ( a === b )?( 0 ):( a > b ? -1 : 1 ) );
|
||||
};
|
||||
collection.trigger( 'changed-order', collection.order, collection );
|
||||
return collection;
|
||||
},
|
||||
|
||||
/** override to provide order and offsets based on instance vars, set limit if passed,
|
||||
* and set allFetched/fire 'all-fetched' when xhr returns
|
||||
*/
|
||||
fetch : function( options ){
|
||||
options = options || {};
|
||||
if( this.allFetched ){ return jQuery.when({}); }
|
||||
var collection = this,
|
||||
fetchOptions = _.defaults( options, {
|
||||
remove : false,
|
||||
offset : collection.length >= 1? ( collection.length - 1 ) : 0,
|
||||
order : collection.order
|
||||
}),
|
||||
limit = options.limit;
|
||||
if( !_.isUndefined( limit ) ){
|
||||
fetchOptions.limit = limit;
|
||||
}
|
||||
|
||||
return ControlledFetchMixin.fetch.call( this, fetchOptions )
|
||||
.done( function _postFetchMore( fetchData ){
|
||||
var numFetched = _.isArray( fetchData )? fetchData.length : 0;
|
||||
// anything less than a full page means we got all there is to get
|
||||
if( !limit || numFetched < limit ){
|
||||
collection.allFetched = true;
|
||||
collection.trigger( 'all-fetched', collection );
|
||||
}
|
||||
}
|
||||
);
|
||||
},
|
||||
|
||||
/** create a new history and by default set it to be the current history */
|
||||
create : function create( data, hdas, historyOptions, xhrOptions ){
|
||||
//TODO: .create is actually a collection function that's overridden here
|
||||
var collection = this,
|
||||
xhr = jQuery.getJSON( galaxy_config.root + 'history/create_new_current' );
|
||||
return xhr.done( function( newData ){
|
||||
var history = new History( newData, [], historyOptions || {} );
|
||||
// new histories go in the front
|
||||
//TODO: (implicit ordering by update time...)
|
||||
collection.unshift( history );
|
||||
collection.trigger( 'new-current' );
|
||||
collection.setCurrent( new History( newData, [], historyOptions || {} ) );
|
||||
});
|
||||
//TODO: move back to using history.save (via Deferred.then w/ set_as_current)
|
||||
},
|
||||
|
||||
/** set the current history to the given history, placing it first in the collection.
|
||||
* Pass standard bbone options for use in unshift.
|
||||
* @triggers new-current passed history and this collection
|
||||
*/
|
||||
setCurrent : function( history, options ){
|
||||
options = options || {};
|
||||
// new histories go in the front
|
||||
this.unshift( history, options );
|
||||
this.currentHistoryId = history.get( 'id' );
|
||||
if( !options.silent ){
|
||||
this.trigger( 'new-current', history, this );
|
||||
}
|
||||
return this;
|
||||
},
|
||||
|
||||
/** override to reset allFetched flag to false */
|
||||
reset : function( models, options ){
|
||||
this.allFetched = false;
|
||||
return Backbone.Collection.prototype.reset.call( this, models, options );
|
||||
},
|
||||
|
||||
toString: function toString(){
|
||||
|
||||
@@ -87,14 +87,6 @@ var CurrentHistoryPanel = _super.extend(
|
||||
_super.prototype._setUpListeners.call( this );
|
||||
|
||||
var panel = this;
|
||||
// cache the scroll position (only if visible)
|
||||
this.$scrollContainer().on( 'scroll', function( ev ){
|
||||
if( panel.$el.is( ':visible' ) ){
|
||||
panel.preferences.set( 'scrollPosition', $( this ).scrollTop() );
|
||||
}
|
||||
});
|
||||
|
||||
//TODO: doesn't work
|
||||
// reset scroll position when there's a new history
|
||||
this.on( 'new-model', function(){
|
||||
panel.preferences.set( 'scrollPosition', 0 );
|
||||
@@ -191,6 +183,30 @@ var CurrentHistoryPanel = _super.extend(
|
||||
},
|
||||
|
||||
// ------------------------------------------------------------------------ panel rendering
|
||||
/** override to add a handler to capture the scroll position when the parent scrolls */
|
||||
_setUpBehaviors : function( $where ){
|
||||
$where = $where || this.$el;
|
||||
// we need to call this in _setUpBehaviors which is called after render since the $el
|
||||
// may not be attached to $el.parent and $scrollContainer() may not work
|
||||
var panel = this;
|
||||
_super.prototype._setUpBehaviors.call( panel, $where );
|
||||
|
||||
// cache the handler to remove and re-add so we don't pile up the handlers
|
||||
if( !this._debouncedScrollCaptureHandler ){
|
||||
this._debouncedScrollCaptureHandler = _.debounce( function scrollCapture(){
|
||||
// cache the scroll position (only if visible)
|
||||
if( panel.$el.is( ':visible' ) ){
|
||||
panel.preferences.set( 'scrollPosition', $( this ).scrollTop() );
|
||||
}
|
||||
}, 40 );
|
||||
}
|
||||
|
||||
panel.$scrollContainer()
|
||||
.off( 'scroll', this._debouncedScrollCaptureHandler )
|
||||
.on( 'scroll', this._debouncedScrollCaptureHandler );
|
||||
return panel;
|
||||
},
|
||||
|
||||
/** In this override, handle null models and move the search input to the top */
|
||||
_buildNewRender : function(){
|
||||
if( !this.model ){ return $(); }
|
||||
@@ -281,7 +297,7 @@ var CurrentHistoryPanel = _super.extend(
|
||||
_.delay( function(){
|
||||
var pos = panel.preferences.get( 'scrollPosition' );
|
||||
if( pos ){
|
||||
panel.scrollTo( pos );
|
||||
panel.scrollTo( pos, 0 );
|
||||
}
|
||||
}, 10 );
|
||||
//TODO: is this enough of a delay on larger histories?
|
||||
|
||||
@@ -109,7 +109,7 @@ var HistoryPanelEdit = _super.extend(
|
||||
/** listening for history and HDA events */
|
||||
_setUpModelListeners : function(){
|
||||
_super.prototype._setUpModelListeners.call( this );
|
||||
this.model.on( 'change:nice_size', this.updateHistoryDiskSize, this );
|
||||
this.model.on( 'change:size', this.updateHistoryDiskSize, this );
|
||||
return this;
|
||||
},
|
||||
|
||||
|
||||
@@ -143,17 +143,21 @@ var HistoryPanel = _super.extend(
|
||||
*/
|
||||
_setUpListeners : function(){
|
||||
_super.prototype._setUpListeners.call( this );
|
||||
|
||||
this.on( 'error', function( model, xhr, options, msg, details ){
|
||||
this.errorHandler( model, xhr, options, msg, details );
|
||||
});
|
||||
|
||||
this.on( 'loading-done', function(){
|
||||
//TODO:?? if( this.collection.length ){
|
||||
if( !this.views.length ){
|
||||
this.trigger( 'empty-history', this );
|
||||
this.on({
|
||||
error : function( model, xhr, options, msg, details ){
|
||||
this.errorHandler( model, xhr, options, msg, details );
|
||||
},
|
||||
'loading-done' : function(){
|
||||
//TODO:?? if( this.collection.length ){
|
||||
if( !this.views.length ){
|
||||
this.trigger( 'empty-history', this );
|
||||
}
|
||||
},
|
||||
'views:ready view:attached view:removed' : function( view ){
|
||||
this._renderSelectButton();
|
||||
}
|
||||
});
|
||||
// this.on( 'all', function(){ console.debug( arguments ); });
|
||||
},
|
||||
|
||||
// ------------------------------------------------------------------------ loading history/hda models
|
||||
@@ -304,20 +308,34 @@ var HistoryPanel = _super.extend(
|
||||
/** In this override, add a btn to toggle the selectors */
|
||||
_buildNewRender : function(){
|
||||
var $newRender = _super.prototype._buildNewRender.call( this );
|
||||
//if( this.views.length && this.multiselectActions().length ){
|
||||
if( this.multiselectActions().length ){
|
||||
$newRender.find( '.controls .actions' ).prepend( this._renderSelectButton() );
|
||||
}
|
||||
this._renderSelectButton( $newRender );
|
||||
return $newRender;
|
||||
},
|
||||
|
||||
/** button for starting select mode */
|
||||
_renderSelectButton : function( $where ){
|
||||
$where = $where || this.$el;
|
||||
// do not render selector option if no actions
|
||||
if( !this.multiselectActions().length ){
|
||||
return null;
|
||||
}
|
||||
// do not render (and remove even) if nothing to select
|
||||
if( !this.views.length ){
|
||||
this.hideSelectors();
|
||||
$where.find( '.controls .actions .show-selectors-btn' ).remove();
|
||||
return null;
|
||||
}
|
||||
// don't bother rendering if there's one already
|
||||
var $existing = $where.find( '.controls .actions .show-selectors-btn' );
|
||||
if( $existing.size() ){
|
||||
return $existing;
|
||||
}
|
||||
|
||||
return faIconButton({
|
||||
title : _l( 'Operations on multiple datasets' ),
|
||||
classes : 'show-selectors-btn',
|
||||
faIcon : 'fa-check-square-o'
|
||||
});
|
||||
}).prependTo( $where.find( '.controls .actions' ) );
|
||||
},
|
||||
|
||||
// ------------------------------------------------------------------------ sub-views
|
||||
@@ -398,9 +416,9 @@ var HistoryPanel = _super.extend(
|
||||
if( store ){
|
||||
this.storage.set( 'show_deleted', show );
|
||||
}
|
||||
this.trigger( 'show-hidden', show );
|
||||
//TODO:?? to events on storage('change:show_deleted')
|
||||
this.renderItems();
|
||||
this.trigger( 'show-deleted', show );
|
||||
return this.showDeleted;
|
||||
},
|
||||
|
||||
@@ -416,9 +434,9 @@ var HistoryPanel = _super.extend(
|
||||
if( store ){
|
||||
this.storage.set( 'show_hidden', show );
|
||||
}
|
||||
this.trigger( 'show-hidden', show );
|
||||
//TODO:?? to events on storage('change:show_deleted')
|
||||
this.renderItems();
|
||||
this.trigger( 'show-hidden', show );
|
||||
return this.showHidden;
|
||||
},
|
||||
|
||||
@@ -439,7 +457,7 @@ var HistoryPanel = _super.extend(
|
||||
panel.$el.find( inputSelector ).searchInput( 'toggle-loading' );
|
||||
})
|
||||
.done( function(){
|
||||
panel.searchItems( searchFor );
|
||||
panel.searchItems( panel.searchFor );
|
||||
});
|
||||
},
|
||||
|
||||
|
||||
@@ -1,102 +1,29 @@
|
||||
define([
|
||||
"mvc/history/history-model",
|
||||
"mvc/history/history-panel-edit",
|
||||
"mvc/history/copy-dialog",
|
||||
"mvc/base-mvc",
|
||||
"utils/ajax-queue",
|
||||
"ui/mode-button",
|
||||
"ui/search-input"
|
||||
], function( HISTORY_MODEL, HPANEL_EDIT, baseMVC, ajaxQueue ){
|
||||
//==============================================================================
|
||||
/** */
|
||||
function historyCopyDialog( history, options ){
|
||||
options = options || {};
|
||||
// fall back to un-notifying copy
|
||||
if( !( Galaxy && Galaxy.modal ) ){
|
||||
return history.copy();
|
||||
}
|
||||
|
||||
// maybe better as multiselect dialog?
|
||||
var historyName = history.get( 'name' ),
|
||||
defaultCopyName = "Copy of '" + historyName + "'";
|
||||
|
||||
function validateName( name ){
|
||||
if( !name ){
|
||||
if( !Galaxy.modal.$( '#invalid-title' ).size() ){
|
||||
var $invalidTitle = $( '<p/>' ).attr( 'id', 'invalid-title' )
|
||||
.css({ color: 'red', 'margin-top': '8px' })
|
||||
.addClass( 'bg-danger' ).text( _l( 'Please enter a valid history title' ) );
|
||||
Galaxy.modal.$( '.modal-body' ).append( $invalidTitle );
|
||||
}
|
||||
return false;
|
||||
}
|
||||
return name;
|
||||
}
|
||||
|
||||
function copyHistory( name ){
|
||||
var $copyIndicator = $( '<p><span class="fa fa-spinner fa-spin"></span> Copying history...</p>' )
|
||||
.css( 'margin-top', '8px' );
|
||||
Galaxy.modal.$( '.modal-body' ).append( $copyIndicator );
|
||||
history.copy( true, name )
|
||||
//TODO: make this unneccessary with pub-sub error
|
||||
.fail( function(){
|
||||
alert( _l( 'History could not be copied. Please contact a Galaxy administrator' ) );
|
||||
})
|
||||
.always( function(){
|
||||
Galaxy.modal.hide();
|
||||
});
|
||||
}
|
||||
function checkNameAndCopy(){
|
||||
var name = Galaxy.modal.$( '#copy-modal-title' ).val();
|
||||
if( !validateName( name ) ){ return; }
|
||||
copyHistory( name );
|
||||
}
|
||||
|
||||
Galaxy.modal.show( _.extend({
|
||||
title : _l( 'Copying history' ) + ' "' + historyName + '"',
|
||||
body : $([
|
||||
'<label for="copy-modal-title">',
|
||||
_l( 'Enter a title for the copied history' ), ':',
|
||||
'</label><br />',
|
||||
'<input id="copy-modal-title" class="form-control" style="width: 100%" value="',
|
||||
defaultCopyName, '" />'
|
||||
].join('')),
|
||||
buttons : {
|
||||
'Cancel' : function(){ Galaxy.modal.hide(); },
|
||||
'Copy' : checkNameAndCopy
|
||||
}
|
||||
}, options ));
|
||||
$( '#copy-modal-title' ).focus().select();
|
||||
$( '#copy-modal-title' ).on( 'keydown', function( ev ){
|
||||
if( ev.keyCode === 13 ){
|
||||
checkNameAndCopy();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
], function( HISTORY_MODEL, HPANEL_EDIT, historyCopyDialog, baseMVC, ajaxQueue ){
|
||||
/* ==============================================================================
|
||||
TODO:
|
||||
rendering/delayed rendering is a mess
|
||||
may not need to render columns in renderColumns (just moving them/attaching them may be enough)
|
||||
copy places old current in wrong location
|
||||
sort by size is confusing (but correct) - nice_size shows actual disk usage, !== size
|
||||
handle delete current
|
||||
currently manual by user - delete then create new - make one step
|
||||
render
|
||||
move all columns over as one (agg. then html())
|
||||
performance
|
||||
rendering columns could be better
|
||||
lazy load history list (for large numbers)
|
||||
req. API limit/offset
|
||||
move in-view from pubsub
|
||||
|
||||
handle errors
|
||||
handle no histories
|
||||
handle no histories found
|
||||
|
||||
handle anon
|
||||
no button
|
||||
error message on loading url direct
|
||||
include hidden/deleted
|
||||
allow toggling w/o setting in storage
|
||||
reloading with expanded collections doesn't get details of that collection
|
||||
@@ -150,28 +77,38 @@ var HistoryPanelColumn = Backbone.View.extend( baseMVC.LoggableMixin ).extend({
|
||||
return panel;
|
||||
},
|
||||
|
||||
//TODO: needs work
|
||||
//TODO: move to stub-class to avoid monkey-patching
|
||||
/** the function monkey-patched into panels to show contents loading state */
|
||||
__patch_renderEmptyMessage : function( $whereTo ){
|
||||
var panel = this,
|
||||
hdaCount = _.chain( this.model.get( 'state_ids' ) ).values().flatten().value().length,
|
||||
// a more useful attribute, but does not account for collections (for some reason)
|
||||
// hdaCount = _.chain( this.model.get( 'state_ids' ) ).values().flatten().value().length,
|
||||
empty = panel.model.get( 'empty' ),
|
||||
newMsg = panel.emptyMsg,
|
||||
$emptyMsg = panel.$emptyMessage( $whereTo );
|
||||
|
||||
if( !_.isEmpty( panel.hdaViews ) ){
|
||||
$emptyMsg.hide();
|
||||
|
||||
} else if( hdaCount && !this.model.contents.length ){
|
||||
$emptyMsg.empty()
|
||||
.append( $( '<span class="fa fa-spinner fa-spin"></span> <i>loading datasets...</i>' ) ).show();
|
||||
|
||||
} else if( panel.searchFor ){
|
||||
$emptyMsg.text( panel.noneFoundMsg ).show();
|
||||
|
||||
} else {
|
||||
$emptyMsg.text( panel.emptyMsg ).show();
|
||||
return $emptyMsg.hide();
|
||||
}
|
||||
return $emptyMsg;
|
||||
|
||||
if( !empty ){
|
||||
if( !this.model.contents.length ){
|
||||
newMsg = '<span class="fa fa-spinner fa-spin"></span> <i>' + _l( 'loading datasets' ) + '...</i>';
|
||||
|
||||
} if( panel.searchFor ){
|
||||
// this is a hack until HDCAs implement searching and haveDetails entirely
|
||||
var mixed = !!panel.model.contents.find( function( c ){
|
||||
return c.get( 'model_class' ) !== 'HistoryDatasetAssociation';
|
||||
});
|
||||
if( !mixed && !panel.model.contents.haveDetails() ){
|
||||
newMsg = '<span class="fa fa-spinner fa-spin"></span> <i>' + _l( 'searching' ) + '...</i>';
|
||||
|
||||
} else {
|
||||
newMsg = panel.noneFoundMsg;
|
||||
}
|
||||
}
|
||||
}
|
||||
return $emptyMsg.empty().append( newMsg ).show();
|
||||
},
|
||||
|
||||
/** set up reflexive listeners */
|
||||
@@ -384,26 +321,18 @@ var MultiPanelColumns = Backbone.View.extend( baseMVC.LoggableMixin ).extend({
|
||||
this.$el.addClass( this.className );
|
||||
|
||||
// --- instance vars
|
||||
if( !options.currentHistoryId ){
|
||||
throw new Error( this + ' requires a currentHistoryId in the options' );
|
||||
}
|
||||
this.currentHistoryId = options.currentHistoryId;
|
||||
|
||||
//TODO: move these to some defaults
|
||||
this.options = {
|
||||
columnWidth : 312,
|
||||
borderWidth : 1,
|
||||
columnGap : 8,
|
||||
|
||||
headerHeight : 29,
|
||||
footerHeight : 0,
|
||||
|
||||
controlsHeight : 20
|
||||
};
|
||||
|
||||
/** the order that the collection is rendered in */
|
||||
this.order = options.order || 'update';
|
||||
this._initSortOrders();
|
||||
/** how many histories to get when fetching a new batch/page */
|
||||
this.perPage = options.perPage || 10;
|
||||
|
||||
/** named ajax queue for loading hdas */
|
||||
this.hdaQueue = new ajaxQueue.NamedAjaxQueue( [], false );
|
||||
@@ -411,107 +340,107 @@ var MultiPanelColumns = Backbone.View.extend( baseMVC.LoggableMixin ).extend({
|
||||
// --- set up models, sub-views, and listeners
|
||||
/** the original unfiltered and unordered collection of histories */
|
||||
this.collection = null;
|
||||
this.setCollection( options.histories || [] );
|
||||
|
||||
/** model id to column map */
|
||||
this.columnMap = {};
|
||||
//TODO: why create here?
|
||||
this.createColumns( options.columnOptions );
|
||||
/** model id to column map */
|
||||
this.columnOptions = options.columnOptions || {};
|
||||
|
||||
/** what to search for within all histories */
|
||||
this.historySearch = null;
|
||||
/** what to search for within all datasets */
|
||||
this.datasetSearch = null;
|
||||
|
||||
this.setCollection( options.histories );
|
||||
this.setUpListeners();
|
||||
},
|
||||
|
||||
_initSortOrders : function(){
|
||||
function _comparator( getter, options ){
|
||||
return function __comparator( a, b, currentHistoryId ){
|
||||
// current always first
|
||||
if( a.id === currentHistoryId ){ return -1; }
|
||||
if( b.id === currentHistoryId ){ return 1; }
|
||||
// then compare by an attribute
|
||||
a = getter( a );
|
||||
b = getter( b );
|
||||
return options.asc?
|
||||
( ( a === b )?( 0 ):( a > b ? 1 : -1 ) ):
|
||||
( ( a === b )?( 0 ):( a > b ? -1 : 1 ) );
|
||||
};
|
||||
}
|
||||
|
||||
this.sortOrders = {
|
||||
// default
|
||||
update : {
|
||||
text: _l( 'most recent first' ),
|
||||
fn: _comparator( function( h ){ return new Date( h.get( 'update_time' ) ); }, { asc : false })
|
||||
},
|
||||
'name' : {
|
||||
text: _l( 'name, a to z' ),
|
||||
fn: _comparator( function( h ){ return h.get( 'name' ); }, { asc : true })
|
||||
},
|
||||
'name-dsc' : {
|
||||
text: _l( 'name, z to a' ),
|
||||
fn: _comparator( function( h ){ return h.get( 'name' ); }, { asc : false })
|
||||
},
|
||||
'size' : {
|
||||
text: _l( 'size, large to small' ),
|
||||
fn: _comparator( function( h ){ return h.get( 'size' ); }, { asc : false })
|
||||
},
|
||||
'size-dsc' : {
|
||||
text: _l( 'size, small to large' ),
|
||||
fn: _comparator( function( h ){ return h.get( 'size' ); }, { asc : true })
|
||||
}
|
||||
};
|
||||
return this.sortOrders;
|
||||
},
|
||||
|
||||
/** Set up reflexive listeners */
|
||||
setUpListeners : function setUpListeners(){
|
||||
//var multipanel = this;
|
||||
var multipanel = this;
|
||||
//multipanel.log( 'setUpListeners', multipanel );
|
||||
this.on( 'end-of-scroll', function(){
|
||||
multipanel.collection.fetch({ limit : multipanel.perPage });
|
||||
});
|
||||
},
|
||||
|
||||
// ------------------------------------------------------------------------ collection
|
||||
/** Set up a (new) history collection, sorting and adding listeners
|
||||
* @fires 'new-collection' when set with this view as the arg
|
||||
*/
|
||||
setCollection : function setCollection( models ){
|
||||
setCollection : function setCollection( collection ){
|
||||
this.stopListening( this.collection );
|
||||
|
||||
this.collection = collection || new HISTORY_MODEL.HistoryCollection();
|
||||
this.setUpCollectionListeners();
|
||||
|
||||
this.collection.sort({ silent : true });
|
||||
this.createColumns();
|
||||
this.hdaQueue.clear();
|
||||
|
||||
// multipanel.addModels( collection, { silent: true });
|
||||
this.trigger( 'new-collection', this );
|
||||
return this;
|
||||
},
|
||||
|
||||
/** Set up a (new) history collection, sorting and adding listeners
|
||||
* @fires 'new-collection' when set with this view as the arg
|
||||
*/
|
||||
addModels : function addModels( models, collection, options ){
|
||||
options = options || {};
|
||||
var multipanel = this;
|
||||
multipanel.stopListening( multipanel.collection );
|
||||
multipanel.collection = models;
|
||||
multipanel.sortCollection( multipanel.order, { silent: true });
|
||||
multipanel.setUpCollectionListeners();
|
||||
multipanel.trigger( 'new-collection', multipanel );
|
||||
return multipanel;
|
||||
models = _.isArray( models )? models : [ models ];
|
||||
models.forEach( function( model ){
|
||||
multipanel.addColumn( model, false );
|
||||
});
|
||||
return this;
|
||||
},
|
||||
|
||||
/** Set up listeners for the collection - handling: added histories, change of current, deletion, and sorting */
|
||||
setUpCollectionListeners : function(){
|
||||
var multipanel = this,
|
||||
collection = multipanel.collection;
|
||||
multipanel.listenTo( collection, {
|
||||
var multipanel = this;
|
||||
multipanel.listenTo( multipanel.collection, {
|
||||
// handle ajax errors from the collection
|
||||
'error' : multipanel._ajaxErrorHandler,
|
||||
// add multiple models
|
||||
'add' : multipanel.addModels,
|
||||
// when all the histories a user has have been fetched
|
||||
'all-fetched' : multipanel._postFetchAll,
|
||||
// handle addition of histories, triggered by column copy and create new
|
||||
'add': multipanel.addAsCurrentColumn,
|
||||
'new-current' : multipanel.addAsCurrentColumn,
|
||||
// handle setting a history as current, triggered by history.setAsCurrent
|
||||
'set-as-current': multipanel.setCurrentHistory,
|
||||
'set-as-current' : multipanel.setCurrentHistory,
|
||||
// handle deleting a history (depends on whether panels is including deleted or not)
|
||||
'change:deleted change:purged': multipanel.handleDeletedHistory,
|
||||
|
||||
'sort' : function(){ multipanel.renderColumns( 0 ); }
|
||||
'change:deleted change:purged' : multipanel.handleDeletedHistory,
|
||||
// re-render columns after a sort
|
||||
'sort' : function(){
|
||||
multipanel.renderColumns( 0 );
|
||||
},
|
||||
});
|
||||
},
|
||||
|
||||
_postFetchAll : function( fetchData ){
|
||||
this.$( '.histories-loading-indicator' ).remove();
|
||||
// when new histories is fetched and the indicator is not required,
|
||||
// the panel will jump slightly left - totally aesthetic but jarring
|
||||
// TODO: this probably would be best handled elsewhere during a refinement cycle (if any)
|
||||
if( !this.historySearch ){
|
||||
var $scrollContainer = this.$( '.outer-middle' );
|
||||
$scrollContainer.scrollLeft( $scrollContainer.scrollLeft() + 24 );
|
||||
}
|
||||
},
|
||||
|
||||
/** Re-render and set currentHistoryId to reflect a new current history */
|
||||
setCurrentHistory : function setCurrentHistory( history ){
|
||||
var oldCurrentColumn = this.columnMap[ this.currentHistoryId ];
|
||||
var oldCurrentColumn = _.findWhere( this.columnMap, { currentHistory: true });
|
||||
if( oldCurrentColumn ){
|
||||
oldCurrentColumn.currentHistory = false;
|
||||
oldCurrentColumn.$el.height( '' );
|
||||
}
|
||||
|
||||
this.currentHistoryId = history.id;
|
||||
var newCurrentColumn = this.columnMap[ this.currentHistoryId ];
|
||||
var newCurrentColumn = this.columnMap[ this.collection.currentHistoryId ];
|
||||
newCurrentColumn.currentHistory = true;
|
||||
|
||||
this.sortCollection();
|
||||
|
||||
multipanel._recalcFirstColumnHeight();
|
||||
this.collection.sort();
|
||||
this._recalcFirstColumnHeight();
|
||||
return newCurrentColumn;
|
||||
},
|
||||
|
||||
@@ -526,7 +455,7 @@ var MultiPanelColumns = Backbone.View.extend( baseMVC.LoggableMixin ).extend({
|
||||
if( !column ){ return; }
|
||||
|
||||
// if it's the current column, create a new, empty history as the new current
|
||||
if( column.model.id === this.currentHistoryId ){
|
||||
if( column.model.id === this.collection.currentHistoryId ){
|
||||
//TODO: figuring out the order of async here is tricky
|
||||
// - for now let the user handle the two step process
|
||||
//multipanel.collection.create().done( function(){
|
||||
@@ -538,30 +467,15 @@ var MultiPanelColumns = Backbone.View.extend( baseMVC.LoggableMixin ).extend({
|
||||
}
|
||||
},
|
||||
|
||||
/** Sort this collection based on order (defaulting to this.order) a string key
|
||||
* (sorting the collection will re-render the panel)
|
||||
*/
|
||||
sortCollection : function( order, options ){
|
||||
order = !_.isUndefined( order )? order : this.order;
|
||||
if( !( order in this.sortOrders ) ){
|
||||
order = 'update';
|
||||
/** If Galaxy object is available handle error there, otherwise, locally (and crudely) */
|
||||
_ajaxErrorHandler : function(){
|
||||
var errArgs = _.toArray( arguments );
|
||||
if( window.Galaxy && Galaxy.handleErrors ){
|
||||
Galaxy.handleErrors.apply( Galaxy, errArgs );
|
||||
} else {
|
||||
alert( _l( 'There was a problem getting data from the server' ) );
|
||||
console.error( errArgs[0].responseText );
|
||||
}
|
||||
this.order = order;
|
||||
var currentHistoryId = this.currentHistoryId,
|
||||
sortOrder = this.sortOrders[ order ];
|
||||
|
||||
this.collection.comparator = function __comparator( a, b ){
|
||||
return sortOrder.fn( a, b, currentHistoryId );
|
||||
};
|
||||
// set the sort order text
|
||||
this.$( '.current-order' ).text( sortOrder.text );
|
||||
if( this.$( '.more-options' ).is( ':visible' ) ){
|
||||
this.$( '.open-more-options.btn' ).popover( 'show' );
|
||||
}
|
||||
|
||||
//NOTE: auto fires 'sort' from collection
|
||||
this.collection.sort( options );
|
||||
return this.collection;
|
||||
},
|
||||
|
||||
/** create a new history and set it to current */
|
||||
@@ -571,11 +485,12 @@ var MultiPanelColumns = Backbone.View.extend( baseMVC.LoggableMixin ).extend({
|
||||
|
||||
// ------------------------------------------------------------------------ columns
|
||||
/** create columns from collection */
|
||||
createColumns : function createColumns( columnOptions ){
|
||||
columnOptions = columnOptions || {};
|
||||
createColumns : function createColumns( models, columnOptions ){
|
||||
columnOptions = columnOptions || this.options.columnOptions;
|
||||
var multipanel = this;
|
||||
// clear column map
|
||||
this.columnMap = {};
|
||||
// TODO: make cummulative
|
||||
multipanel.columnMap = {};
|
||||
multipanel.collection.each( function( model, i ){
|
||||
var column = multipanel.createColumn( model, columnOptions );
|
||||
multipanel.columnMap[ model.id ] = column;
|
||||
@@ -590,41 +505,19 @@ var MultiPanelColumns = Backbone.View.extend( baseMVC.LoggableMixin ).extend({
|
||||
purgeAllowed: Galaxy.config.allow_user_dataset_purge
|
||||
});
|
||||
var column = new HistoryPanelColumn( options );
|
||||
if( history.id === this.currentHistoryId ){ column.currentHistory = true; }
|
||||
if( history.id === this.collection.currentHistoryId ){ column.currentHistory = true; }
|
||||
this.setUpColumnListeners( column );
|
||||
return column;
|
||||
},
|
||||
|
||||
/** return array of Columns filtered by filters and sorted to match the collection
|
||||
* @param: filters Function[] array of filter fns
|
||||
*/
|
||||
sortedFilteredColumns : function( filters ){
|
||||
filters = filters || this.filters;
|
||||
if( !filters || !filters.length ){
|
||||
return this.sortedColumns();
|
||||
if( this.datasetSearch ){
|
||||
column.panel.searchItems( this.datasetSearch );
|
||||
this.queueHdaFetchDetails( column );
|
||||
}
|
||||
var multipanel = this;
|
||||
return multipanel.sortedColumns().filter( function( column, index ){
|
||||
var filtered = column.currentHistory || _.every( filters.map( function( filter ){
|
||||
return filter.call( column );
|
||||
}));
|
||||
return filtered;
|
||||
});
|
||||
},
|
||||
|
||||
/** return array of Columns sorted to match the collection */
|
||||
sortedColumns : function(){
|
||||
var multipanel = this;
|
||||
var sorted = this.collection.map( function( history, index ){
|
||||
return multipanel.columnMap[ history.id ];
|
||||
});
|
||||
return sorted;
|
||||
return column;
|
||||
},
|
||||
|
||||
/** add a new column for history and render all columns if render is true */
|
||||
addColumn : function add( history, render ){
|
||||
//this.debug( 'adding column for:', history );
|
||||
render = render !== undefined? render: true;
|
||||
render = render !== undefined? render : true;
|
||||
var newColumn = this.createColumn( history );
|
||||
this.columnMap[ history.id ] = newColumn;
|
||||
if( render ){
|
||||
@@ -634,7 +527,7 @@ 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 ){
|
||||
addAsCurrentColumn : function add( history, collection, options ){
|
||||
//this.log( 'adding current column for:', history );
|
||||
var multipanel = this,
|
||||
newColumn = this.addColumn( history, false );
|
||||
@@ -674,7 +567,7 @@ var MultiPanelColumns = Backbone.View.extend( baseMVC.LoggableMixin ).extend({
|
||||
multipanel.listenTo( column, {
|
||||
//'all': function(){ console.info( 'column ' + column + ':', arguments ) },
|
||||
'in-view': multipanel.queueHdaFetch
|
||||
});
|
||||
});
|
||||
|
||||
multipanel.listenTo( column.panel, {
|
||||
//'all': function(){ console.info( 'panel ' + column.panel + ':', arguments ) },
|
||||
@@ -695,7 +588,9 @@ var MultiPanelColumns = Backbone.View.extend( baseMVC.LoggableMixin ).extend({
|
||||
multipanel._dropData = null;
|
||||
|
||||
var queue = new ajaxQueue.NamedAjaxQueue();
|
||||
toCopy.forEach( function( content ){
|
||||
// need to reverse to better match expected order
|
||||
// TODO: reconsider order in list-panel._setUpItemViewListeners, dragstart (instead of here)
|
||||
toCopy.reverse().forEach( function( content ){
|
||||
queue.add({
|
||||
name : 'copy-' + content.id,
|
||||
fn : function(){
|
||||
@@ -716,6 +611,32 @@ var MultiPanelColumns = Backbone.View.extend( baseMVC.LoggableMixin ).extend({
|
||||
return Object.keys( this.columnMap ).length;
|
||||
},
|
||||
|
||||
/** return array of Columns filtered by filters and sorted to match the collection
|
||||
* @param: filters Function[] array of filter fns
|
||||
*/
|
||||
sortedFilteredColumns : function( filters ){
|
||||
filters = filters || this.filters;
|
||||
if( !filters || !filters.length ){
|
||||
return this.sortedColumns();
|
||||
}
|
||||
var multipanel = this;
|
||||
return multipanel.sortedColumns().filter( function( column, index ){
|
||||
var filtered = column.currentHistory || _.every( filters.map( function( filter ){
|
||||
return filter.call( column );
|
||||
}));
|
||||
return filtered;
|
||||
});
|
||||
},
|
||||
|
||||
/** return array of Columns sorted to match the collection */
|
||||
sortedColumns : function(){
|
||||
var multipanel = this;
|
||||
var sorted = this.collection.map( function( history, index ){
|
||||
return multipanel.columnMap[ history.id ];
|
||||
});
|
||||
return sorted;
|
||||
},
|
||||
|
||||
// ------------------------------------------------------------------------ render
|
||||
/** Render this view, columns, and set up view plugins */
|
||||
render : function render( speed ){
|
||||
@@ -744,30 +665,41 @@ var MultiPanelColumns = Backbone.View.extend( baseMVC.LoggableMixin ).extend({
|
||||
//this.log( '\t sortedAndFiltered:', sortedAndFiltered );
|
||||
|
||||
// set up width based on collection size
|
||||
multipanel.$( '.middle' ).width( sortedAndFiltered.length
|
||||
//TODO: magic number 16 === the amount that safely prevents stacking of columns when adding a new one
|
||||
* ( this.options.columnWidth + this.options.columnGap ) + this.options.columnGap + 16 );
|
||||
var $middle = multipanel.$( '.middle' ).empty()
|
||||
.width( multipanel._calcMiddleWidth( sortedAndFiltered.length ) );
|
||||
|
||||
var $middle = multipanel.$( '.middle' );
|
||||
$middle.empty();
|
||||
sortedAndFiltered.forEach( function( column, i ){
|
||||
column.$el.appendTo( $middle );
|
||||
column.delegateEvents();
|
||||
multipanel.renderColumn( column, speed );
|
||||
});
|
||||
//TODO: event columns-rendered
|
||||
if( !multipanel.collection.allFetched ){
|
||||
$middle.append( multipanel.loadingIndicatorTemplate( multipanel ) );
|
||||
}
|
||||
//TODO: sorta - at least their fx queue has started the re-rendering
|
||||
multipanel.trigger( 'columns-rendered', sortedAndFiltered, multipanel );
|
||||
|
||||
if( this.searchFor && sortedAndFiltered.length <= 1 ){
|
||||
if( multipanel.datasetSearch && sortedAndFiltered.length <= 1 ){
|
||||
} else {
|
||||
// check for in-view, hda lazy-loading if so
|
||||
multipanel.checkColumnsInView();
|
||||
// the first, current column has position: fixed and flex css will not apply - adjust height manually
|
||||
this._recalcFirstColumnHeight();
|
||||
multipanel._recalcFirstColumnHeight();
|
||||
}
|
||||
|
||||
return sortedAndFiltered;
|
||||
},
|
||||
|
||||
_calcMiddleWidth : function( numColumns ){
|
||||
var preventStackWidthAdj = 16;
|
||||
return (
|
||||
numColumns * ( this.options.columnWidth + this.options.columnGap ) +
|
||||
// last column gap
|
||||
this.options.columnGap +
|
||||
// the amount that safely prevents stacking of columns when adding a new one
|
||||
preventStackWidthAdj
|
||||
);
|
||||
},
|
||||
|
||||
/** Render a single column using the non-blocking setTimeout( 0 ) pattern */
|
||||
renderColumn : function( column, speed ){
|
||||
speed = speed !== undefined? speed: this.fxSpeed;
|
||||
@@ -833,15 +765,14 @@ var MultiPanelColumns = Backbone.View.extend( baseMVC.LoggableMixin ).extend({
|
||||
/** */
|
||||
events : {
|
||||
// will move to the server root (gen. Analyze data)
|
||||
'click .done.btn' : 'close',
|
||||
'click .done.btn' : 'close',
|
||||
// creates a new empty history and makes it current
|
||||
'click .create-new.btn' : 'create',
|
||||
'click #include-deleted' : '_clickToggleDeletedHistories',
|
||||
'click .create-new.btn' : 'create',
|
||||
'click #include-deleted' : '_clickToggleDeletedHistories',
|
||||
// these change the collection and column sort order
|
||||
'click .order .set-order' : '_chooseOrder',
|
||||
'click #toggle-deleted' : '_clickToggleDeletedDatasets',
|
||||
'click #toggle-hidden' : '_clickToggleHiddenDatasets'
|
||||
|
||||
'click .order .set-order' : '_chooseOrder',
|
||||
'click #toggle-deleted' : '_clickToggleDeletedDatasets',
|
||||
'click #toggle-hidden' : '_clickToggleHiddenDatasets',
|
||||
//'dragstart .list-item .title-bar' : function( e ){ console.debug( 'ok' ); }
|
||||
},
|
||||
|
||||
@@ -857,7 +788,8 @@ var MultiPanelColumns = Backbone.View.extend( baseMVC.LoggableMixin ).extend({
|
||||
},
|
||||
|
||||
_clickToggleDeletedHistories : function( ev ){
|
||||
return this.toggleDeletedHistories( $( ev.currentTarget ).is( ':checked' ) );
|
||||
this.toggleDeletedHistories( $( ev.currentTarget ).is( ':checked' ) );
|
||||
this.toggleOptionsPopover();
|
||||
},
|
||||
/** Include deleted histories in the collection */
|
||||
toggleDeletedHistories : function( show ){
|
||||
@@ -869,7 +801,8 @@ var MultiPanelColumns = Backbone.View.extend( baseMVC.LoggableMixin ).extend({
|
||||
},
|
||||
|
||||
_clickToggleDeletedDatasets : function( ev ){
|
||||
return this.toggleDeletedDatasets( $( ev.currentTarget ).is( ':checked' ) );
|
||||
this.toggleDeletedDatasets( $( ev.currentTarget ).is( ':checked' ) );
|
||||
this.toggleOptionsPopover();
|
||||
},
|
||||
toggleDeletedDatasets : function( show ){
|
||||
show = show !== undefined? show : false;
|
||||
@@ -882,7 +815,8 @@ var MultiPanelColumns = Backbone.View.extend( baseMVC.LoggableMixin ).extend({
|
||||
},
|
||||
|
||||
_clickToggleHiddenDatasets : function( ev ){
|
||||
return this.toggleHiddenDatasets( $( ev.currentTarget ).is( ':checked' ) );
|
||||
this.toggleHiddenDatasets( $( ev.currentTarget ).is( ':checked' ) );
|
||||
this.toggleOptionsPopover();
|
||||
},
|
||||
toggleHiddenDatasets : function( show ){
|
||||
show = show !== undefined? show : false;
|
||||
@@ -894,9 +828,27 @@ 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 orderKey = $( ev.currentTarget ).data( 'order' );
|
||||
this.sortCollection( orderKey );
|
||||
var multipanel = this,
|
||||
collection = multipanel.collection,
|
||||
orderKey = $( ev.currentTarget ).data( 'order' );
|
||||
// set the sort order text also
|
||||
multipanel.$( '.current-order' ).text( multipanel.orderDescriptions[ orderKey ] );
|
||||
multipanel.toggleOptionsPopover();
|
||||
|
||||
// set the order and re-fetch using the new order
|
||||
collection.setOrder( orderKey );
|
||||
collection.reset( collection.slice( 0, 1 ), { silent : true });
|
||||
multipanel.setCollection( collection );
|
||||
collection.fetch({ limit : multipanel.perPage });
|
||||
multipanel.once( 'columns-rendered', multipanel._scrollLeft, this );
|
||||
//TODO: check allFetched and do not reset if so - just sort instead
|
||||
},
|
||||
|
||||
/** scroll the column container right or left */
|
||||
_scrollLeft : function( val ){
|
||||
this.$( '.outer-middle' ).scrollLeft( val || 0 );
|
||||
},
|
||||
|
||||
/** Set up any view plugins */
|
||||
@@ -908,15 +860,25 @@ var MultiPanelColumns = Backbone.View.extend( baseMVC.LoggableMixin ).extend({
|
||||
multipanel.$( '#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.fetch()
|
||||
.done( function(){
|
||||
multipanel.$( '#search-histories' ).searchInput( 'toggle-loading' );
|
||||
multipanel.renderInfo( '' );
|
||||
});
|
||||
},
|
||||
onsearch : function( searchFor ){
|
||||
multipanel.searchFor = searchFor;
|
||||
multipanel.historySearch = searchFor;
|
||||
multipanel.filters = [ function(){
|
||||
return this.model.matchesAll( multipanel.searchFor );
|
||||
return this.model.matchesAll( multipanel.historySearch );
|
||||
}];
|
||||
multipanel.renderColumns( 0 );
|
||||
},
|
||||
onclear : function( searchFor ){
|
||||
multipanel.searchFor = null;
|
||||
multipanel.historySearch = null;
|
||||
//TODO: remove specifically not just reset
|
||||
multipanel.filters = [];
|
||||
multipanel.renderColumns( 0 );
|
||||
@@ -931,7 +893,7 @@ var MultiPanelColumns = Backbone.View.extend( baseMVC.LoggableMixin ).extend({
|
||||
onfirstsearch : function( searchFor ){
|
||||
multipanel.hdaQueue.clear();
|
||||
multipanel.$( '#search-datasets' ).searchInput( 'toggle-loading' );
|
||||
multipanel.searchFor = searchFor;
|
||||
multipanel.datasetSearch = searchFor;
|
||||
multipanel.sortedFilteredColumns().forEach( function( column ){
|
||||
column.panel.searchItems( searchFor );
|
||||
// load details for them that need
|
||||
@@ -948,13 +910,13 @@ var MultiPanelColumns = Backbone.View.extend( baseMVC.LoggableMixin ).extend({
|
||||
});
|
||||
},
|
||||
onsearch : function( searchFor ){
|
||||
multipanel.searchFor = searchFor;
|
||||
multipanel.datasetSearch = searchFor;
|
||||
multipanel.sortedFilteredColumns().forEach( function( column ){
|
||||
column.panel.searchItems( searchFor );
|
||||
});
|
||||
},
|
||||
onclear : function( searchFor ){
|
||||
multipanel.searchFor = null;
|
||||
multipanel.datasetSearch = null;
|
||||
multipanel.sortedFilteredColumns().forEach( function( column ){
|
||||
column.panel.clearSearch();
|
||||
});
|
||||
@@ -969,11 +931,14 @@ var MultiPanelColumns = Backbone.View.extend( baseMVC.LoggableMixin ).extend({
|
||||
// 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(){
|
||||
multipanel.checkColumnsInView();
|
||||
var viewport = multipanel._viewport();
|
||||
multipanel.checkColumnsInView( viewport );
|
||||
multipanel.checkForEndOfScroll( viewport );
|
||||
}, 100 );
|
||||
this.$( '.middle' ).parent().scroll( debouncedInView );
|
||||
},
|
||||
|
||||
/** create the options popover */
|
||||
_moreOptionsPopover : function(){
|
||||
return this.$( '.open-more-options.btn' ).popover({
|
||||
container : '.header',
|
||||
@@ -983,6 +948,12 @@ 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 */
|
||||
toggleOptionsPopover : function( ev ){
|
||||
// hide seems broken in our version
|
||||
this.$( '.open-more-options.btn' ).popover( 'toggle' );
|
||||
},
|
||||
|
||||
/** Adjust the height of the first, current column since flex-boxes won't work with fixed postiion elements */
|
||||
_recalcFirstColumnHeight : function(){
|
||||
var $firstColumn = this.$( '.history-column' ).first(),
|
||||
@@ -994,15 +965,20 @@ var MultiPanelColumns = Backbone.View.extend( baseMVC.LoggableMixin ).extend({
|
||||
|
||||
/** Get the left and right pixel coords of the middle element */
|
||||
_viewport : function(){
|
||||
var viewLeft = this.$( '.middle' ).parent().offset().left;
|
||||
return { left: viewLeft, right: viewLeft + this.$( '.middle' ).parent().width() };
|
||||
var $outerMiddle = this.$( '.middle' ).parent(),
|
||||
viewLeft = $outerMiddle.offset().left,
|
||||
width = $outerMiddle.width();
|
||||
return {
|
||||
left : viewLeft,
|
||||
right : viewLeft + width
|
||||
};
|
||||
},
|
||||
|
||||
/** returns the columns currently in the viewport */
|
||||
columnsInView : function(){
|
||||
columnsInView : function( viewport ){
|
||||
//TODO: uses offset which is render intensive
|
||||
//TODO: 2N - could use arg filter (sortedFilteredColumns( filter )) instead
|
||||
var vp = this._viewport();
|
||||
var vp = viewport || this._viewport();
|
||||
return this.sortedFilteredColumns().filter( function( column ){
|
||||
return column.currentHistory || column.inView( vp.left, vp.right );
|
||||
});
|
||||
@@ -1017,9 +993,20 @@ var MultiPanelColumns = Backbone.View.extend( baseMVC.LoggableMixin ).extend({
|
||||
});
|
||||
},
|
||||
|
||||
/** */
|
||||
checkForEndOfScroll : function( viewport ){
|
||||
viewport = viewport || this._viewport();
|
||||
var END_PADDING = 16,
|
||||
$middle = this.$( '.middle' ),
|
||||
scrollRight = $middle.parent().scrollLeft() + viewport.right;
|
||||
if( scrollRight >= ( $middle.width() - END_PADDING ) ){
|
||||
this.trigger( 'end-of-scroll' );
|
||||
}
|
||||
},
|
||||
|
||||
/** Show and enable the current columns drop target */
|
||||
currentColumnDropTargetOn : function(){
|
||||
var currentColumn = this.columnMap[ this.currentHistoryId ];
|
||||
var currentColumn = this.columnMap[ this.collection.currentHistoryId ];
|
||||
if( !currentColumn ){ return; }
|
||||
//TODO: fix this - shouldn't need monkeypatch
|
||||
currentColumn.panel.dataDropped = function( data ){};
|
||||
@@ -1028,7 +1015,7 @@ var MultiPanelColumns = Backbone.View.extend( baseMVC.LoggableMixin ).extend({
|
||||
|
||||
/** Hide and disable the current columns drop target */
|
||||
currentColumnDropTargetOff : function(){
|
||||
var currentColumn = this.columnMap[ this.currentHistoryId ];
|
||||
var currentColumn = this.columnMap[ this.collection.currentHistoryId ];
|
||||
if( !currentColumn ){ return; }
|
||||
currentColumn.panel.dataDropped = HPANEL_EDIT.HistoryPanelEdit.prototype.dataDrop;
|
||||
// slight override of dropTargetOff to not erase drop-target-help
|
||||
@@ -1066,22 +1053,36 @@ var MultiPanelColumns = Backbone.View.extend( baseMVC.LoggableMixin ).extend({
|
||||
'<div class="middle flex-column-container flex-row"></div>',
|
||||
'</div>',
|
||||
// footer
|
||||
'<div class="footer flex-column-container">',
|
||||
'<div class="footer flex-column-container"></div>'
|
||||
].join(''), { variable: 'view' }),
|
||||
|
||||
loadingIndicatorTemplate : _.template([
|
||||
'<div class="histories-loading-indicator">',
|
||||
'<span class="fa fa-spin fa-spinner"></span>', _l( 'Loading histories' ), '...',
|
||||
'</div>'
|
||||
].join(''), { variable: 'view' }),
|
||||
|
||||
orderDescriptions : {
|
||||
'update_time' : _l( 'most recent first' ),
|
||||
'update_time-asc' : _l( 'least recent first' ),
|
||||
'name' : _l( 'name, a to z' ),
|
||||
'name-dsc' : _l( 'name, z to a' ),
|
||||
'size' : _l( 'size, large to small' ),
|
||||
'size-asc' : _l( 'size, small to large' )
|
||||
},
|
||||
|
||||
optionsPopoverTemplate : _.template([
|
||||
'<div class="more-options">',
|
||||
'<div class="order btn-group">',
|
||||
'<button type="button" class="btn btn-default dropdown-toggle" data-toggle="dropdown">',
|
||||
_l( 'Order histories by' ) + ' ',
|
||||
'<span class="current-order"><%= view.sortOrders[ view.order ].text %></span> ',
|
||||
'<span class="current-order"><%= view.orderDescriptions[ view.collection.order ] %></span> ',
|
||||
'<span class="caret"></span>',
|
||||
'</button>',
|
||||
'<ul class="dropdown-menu" role="menu">',
|
||||
'<% _.each( view.sortOrders, function( order, key ){ %>',
|
||||
'<li><a href="javascript:void(0);" class="set-order" data-order="<%= key %>">',
|
||||
'<%= order.text %>',
|
||||
'<% _.each( view.orderDescriptions, function( text, order ){ %>',
|
||||
'<li><a href="javascript:void(0);" class="set-order" data-order="<%= order %>">',
|
||||
'<%= text %>',
|
||||
'</a></li>',
|
||||
'<% }); %>',
|
||||
'</ul>',
|
||||
|
||||
@@ -66,15 +66,40 @@ var menu = [
|
||||
},
|
||||
{
|
||||
html : _l( 'Unhide Hidden Datasets' ),
|
||||
confirm : _l( 'Really unhide all hidden datasets?' ),
|
||||
href : 'history/unhide_datasets?current=True',
|
||||
anon : true
|
||||
anon : true,
|
||||
func : function() {
|
||||
if( Galaxy && Galaxy.currHistoryPanel && confirm( _l( 'Really unhide all hidden datasets?' ) ) ){
|
||||
var filtered = Galaxy.currHistoryPanel.model.contents.hidden();
|
||||
//TODO: batch
|
||||
filtered.ajaxQueue( Backbone.Model.prototype.save, { visible : true })
|
||||
.done( function(){
|
||||
Galaxy.currHistoryPanel.renderItems();
|
||||
})
|
||||
.fail( function(){
|
||||
alert( 'There was an error unhiding the datasets' );
|
||||
console.error( arguments );
|
||||
});
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
html : _l( 'Delete Hidden Datasets' ),
|
||||
confirm : _l( 'Really delete all hidden datasets?' ),
|
||||
href : 'history/delete_hidden_datasets',
|
||||
anon : true
|
||||
anon : true,
|
||||
func : function() {
|
||||
if( Galaxy && Galaxy.currHistoryPanel && confirm( _l( 'Really delete all hidden datasets?' ) ) ){
|
||||
var filtered = Galaxy.currHistoryPanel.model.contents.hidden();
|
||||
//TODO: batch
|
||||
// both delete *and* unhide them
|
||||
filtered.ajaxQueue( Backbone.Model.prototype.save, { deleted : true, visible: true })
|
||||
.done( function(){
|
||||
Galaxy.currHistoryPanel.renderItems();
|
||||
})
|
||||
.fail( function(){
|
||||
alert( 'There was an error deleting the datasets' );
|
||||
console.error( arguments );
|
||||
});
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
html : _l( 'Purge Deleted Datasets' ),
|
||||
|
||||
@@ -28,6 +28,7 @@ var FolderListView = Backbone.View.extend({
|
||||
events: {
|
||||
'click #select-all-checkboxes' : 'selectAll',
|
||||
'click .dataset_row' : 'selectClickedRow',
|
||||
'click .folder_row' : 'selectClickedRow',
|
||||
'click .sort-folder-link' : 'sortColumnClicked'
|
||||
},
|
||||
|
||||
@@ -175,7 +176,7 @@ var FolderListView = Backbone.View.extend({
|
||||
*/
|
||||
postRender: function(){
|
||||
var fetched_metadata = this.folderContainer.attributes.metadata;
|
||||
fetched_metadata.contains_file = typeof this.collection.findWhere({type: 'file'}) !== 'undefined';
|
||||
fetched_metadata.contains_file_or_folder = typeof this.collection.findWhere({type: 'file'}) !== 'undefined' || typeof this.collection.findWhere({type: 'folder'}) !== 'undefined';
|
||||
Galaxy.libraries.folderToolbarView.configureElements(fetched_metadata);
|
||||
$('.library-row').hover(function() {
|
||||
$(this).find('.show_on_hover').show();
|
||||
@@ -201,10 +202,10 @@ var FolderListView = Backbone.View.extend({
|
||||
* @param {Item or FolderAsModel} model of the view that will be rendered
|
||||
*/
|
||||
renderOne: function(model){
|
||||
if (model.get('type') !== 'folder'){
|
||||
this.options.contains_file = true;
|
||||
this.options.contains_file_or_folder = true;
|
||||
//if (model.get('type') !== 'folder'){
|
||||
// model.set('readable_size', this.size_to_string(model.get('file_size')));
|
||||
}
|
||||
//}
|
||||
model.set('folder_id', this.id);
|
||||
var rowView = new mod_library_folderrow_view.FolderRowView(model);
|
||||
|
||||
@@ -262,6 +263,11 @@ var FolderListView = Backbone.View.extend({
|
||||
* Currently supports only sorting by name.
|
||||
*/
|
||||
sortFolder: function(sort_by, order){
|
||||
console.log('sorting');
|
||||
// default to asc sort by name
|
||||
if (sort_by === 'undefined' && order === 'undefined'){
|
||||
return this.collection.sortByNameAsc();
|
||||
}
|
||||
if (sort_by === 'name'){
|
||||
if (order === 'asc'){
|
||||
return this.collection.sortByNameAsc();
|
||||
@@ -329,12 +335,14 @@ var FolderListView = Backbone.View.extend({
|
||||
$row.removeClass('light').addClass('dark');
|
||||
$row.find('a').removeClass('light').addClass('dark');
|
||||
$row.find('.fa-file-o').removeClass('fa-file-o').addClass('fa-file');
|
||||
$row.find('.fa-folder-o').removeClass('fa-folder-o').addClass('fa-folder');
|
||||
},
|
||||
|
||||
makeWhiteRow: function($row){
|
||||
$row.removeClass('dark').addClass('light');
|
||||
$row.find('a').removeClass('dark').addClass('light');
|
||||
$row.find('.fa-file').removeClass('fa-file').addClass('fa-file-o');
|
||||
$row.find('.fa-folder').removeClass('fa-folder').addClass('fa-folder-o');
|
||||
},
|
||||
|
||||
renderSortIcon: function(){
|
||||
|
||||
@@ -15,7 +15,8 @@ var FolderRowView = Backbone.View.extend({
|
||||
lastSelectedHistory: '',
|
||||
|
||||
events: {
|
||||
'click .undelete_dataset_btn' : 'undelete_dataset'
|
||||
'click .undelete_dataset_btn' : 'undeleteDataset',
|
||||
'click .undelete_folder_btn' : 'undeleteFolder'
|
||||
},
|
||||
|
||||
options: {
|
||||
@@ -29,16 +30,23 @@ var FolderRowView = Backbone.View.extend({
|
||||
|
||||
render: function(folder_item){
|
||||
var template = null;
|
||||
if (folder_item.get('type') === 'folder'){
|
||||
if (folder_item.get('type') === 'folder' || folder_item.get('model_class') === 'LibraryFolder'){
|
||||
this.options.type = 'folder';
|
||||
template = this.templateRowFolder();
|
||||
} else {
|
||||
if (folder_item.get('deleted')){
|
||||
template = this.templateRowDeletedFolder();
|
||||
} else{
|
||||
template = this.templateRowFolder();
|
||||
}
|
||||
} else if (folder_item.get('type') === 'file' || folder_item.get('model_class') === 'LibraryDataset'){
|
||||
this.options.type = 'file';
|
||||
if (folder_item.get('deleted')){
|
||||
template = this.templateRowDeletedFile();
|
||||
} else {
|
||||
template = this.templateRowFile();
|
||||
}
|
||||
} else {
|
||||
console.error('Unknown library item type found.');
|
||||
console.error(folder_item.get('type') || folder_item.get('model_class'));
|
||||
}
|
||||
this.setElement(template({content_item:folder_item}));
|
||||
this.$el.show();
|
||||
@@ -52,7 +60,7 @@ var FolderRowView = Backbone.View.extend({
|
||||
/**
|
||||
* Undeletes the dataset on server and renders the row again.
|
||||
*/
|
||||
undelete_dataset : function(event){
|
||||
undeleteDataset : function(event){
|
||||
$(".tooltip").hide();
|
||||
var that = this;
|
||||
var dataset_id = $(event.target).closest('tr')[0].id;
|
||||
@@ -63,6 +71,7 @@ var FolderRowView = Backbone.View.extend({
|
||||
Galaxy.libraries.folderListView.collection.remove(dataset_id);
|
||||
var updated_dataset = new mod_library_model.Item(response);
|
||||
Galaxy.libraries.folderListView.collection.add(updated_dataset);
|
||||
Galaxy.libraries.folderListView.collection.sortByNameAsc();
|
||||
mod_toastr.success('Dataset undeleted. Click this to see it.', '', {onclick: function() {
|
||||
var folder_id = that.model.get('folder_id');
|
||||
window.location='#folders/' + folder_id + '/datasets/' + that.id;
|
||||
@@ -78,6 +87,33 @@ var FolderRowView = Backbone.View.extend({
|
||||
});
|
||||
},
|
||||
|
||||
/**
|
||||
* Undeletes the folder on server and renders the row again.
|
||||
*/
|
||||
undeleteFolder : function(event){
|
||||
$(".tooltip").hide();
|
||||
var that = this;
|
||||
var folder_id = $(event.target).closest('tr')[0].id;
|
||||
var folder = Galaxy.libraries.folderListView.collection.get(folder_id);
|
||||
folder.url = folder.urlRoot + folder.id + '?undelete=true';
|
||||
folder.destroy({
|
||||
success : function(model, response){
|
||||
Galaxy.libraries.folderListView.collection.remove(folder_id);
|
||||
var updated_folder = new mod_library_model.FolderAsModel(response);
|
||||
Galaxy.libraries.folderListView.collection.add(updated_folder);
|
||||
Galaxy.libraries.folderListView.collection.sortByNameAsc();
|
||||
mod_toastr.success('Folder undeleted.');
|
||||
},
|
||||
error : function(model, response){
|
||||
if (typeof response.responseJSON !== "undefined"){
|
||||
mod_toastr.error('Folder was not undeleted. ' + response.responseJSON.err_msg);
|
||||
} else {
|
||||
mod_toastr.error('An error occured! Folder was not undeleted. Please try again.');
|
||||
}
|
||||
}
|
||||
});
|
||||
},
|
||||
|
||||
templateRowFolder: function() {
|
||||
tmpl_array = [];
|
||||
|
||||
@@ -85,7 +121,7 @@ var FolderRowView = Backbone.View.extend({
|
||||
tmpl_array.push(' <td>');
|
||||
tmpl_array.push(' <span title="Folder" class="fa fa-folder-o"></span>');
|
||||
tmpl_array.push(' </td>');
|
||||
tmpl_array.push(' <td></td>');
|
||||
tmpl_array.push(' <td style="text-align: center; "><input style="margin: 0;" type="checkbox"></td>');
|
||||
tmpl_array.push(' <td>');
|
||||
tmpl_array.push(' <a href="#folders/<%- content_item.id %>"><%- content_item.get("name") %></a>');
|
||||
tmpl_array.push(' </td>');
|
||||
@@ -138,6 +174,26 @@ var FolderRowView = Backbone.View.extend({
|
||||
tmpl_array.push(' <td><span data-toggle="tooltip" data-placement="top" title="Marked deleted" style="color:grey;" class="fa fa-ban fa-lg"> </span><button data-toggle="tooltip" data-placement="top" title="Undelete <%- content_item.get("name") %>" class="primary-button btn-xs undelete_dataset_btn show_on_hover" type="button" style="display:none; margin-left:1em;"><span class="fa fa-unlock"> Undelete</span></button></td>');
|
||||
tmpl_array.push('</tr>');
|
||||
|
||||
return _.template(tmpl_array.join(''));
|
||||
},
|
||||
|
||||
templateRowDeletedFolder: function(){
|
||||
tmpl_array = [];
|
||||
|
||||
tmpl_array.push('<tr class="active folder_row light library-row" id="<%- content_item.id %>">');
|
||||
tmpl_array.push(' <td>');
|
||||
tmpl_array.push(' <span title="Folder" class="fa fa-folder-o"></span>');
|
||||
tmpl_array.push(' </td>');
|
||||
tmpl_array.push(' <td></td>');
|
||||
tmpl_array.push(' <td style="color:grey;">');
|
||||
tmpl_array.push(' <%- content_item.get("name") %>');
|
||||
tmpl_array.push(' </td>');
|
||||
tmpl_array.push(' <td>folder</td>');
|
||||
tmpl_array.push(' <td></td>');
|
||||
tmpl_array.push(' <td><%= _.escape(content_item.get("update_time")) %></td>'); // time updated
|
||||
tmpl_array.push(' <td><span data-toggle="tooltip" data-placement="top" title="Marked deleted" style="color:grey;" class="fa fa-ban fa-lg"> </span><button data-toggle="tooltip" data-placement="top" title="Undelete <%- content_item.get("name") %>" class="primary-button btn-xs undelete_folder_btn show_on_hover" type="button" style="display:none; margin-left:1em;"><span class="fa fa-unlock"> Undelete</span></button></td>');
|
||||
tmpl_array.push('</tr>');
|
||||
|
||||
return _.template(tmpl_array.join(''));
|
||||
}
|
||||
|
||||
|
||||
@@ -26,9 +26,9 @@ var FolderToolbarView = Backbone.View.extend({
|
||||
},
|
||||
|
||||
defaults: {
|
||||
'can_add_library_item' : false,
|
||||
'contains_file' : false,
|
||||
'chain_call_control' : {
|
||||
'can_add_library_item' : false,
|
||||
'contains_file_or_folder' : false,
|
||||
'chain_call_control' : {
|
||||
'total_number' : 0,
|
||||
'failed_number' : 0
|
||||
},
|
||||
@@ -116,7 +116,7 @@ var FolderToolbarView = Backbone.View.extend({
|
||||
} else{
|
||||
$('.add-library-items').hide();
|
||||
}
|
||||
if (this.options.contains_file === true){
|
||||
if (this.options.contains_file_or_folder === true){
|
||||
if (Galaxy.currUser){
|
||||
if (!Galaxy.currUser.isAnonymous()){
|
||||
$('.logged-dataset-manipulation').show();
|
||||
@@ -250,9 +250,12 @@ var FolderToolbarView = Backbone.View.extend({
|
||||
// we can save last used history to pre-select it next time
|
||||
this.options.last_used_history_id = history_id;
|
||||
var dataset_ids = [];
|
||||
var folder_ids = [];
|
||||
$('#folder_table').find(':checked').each(function(){
|
||||
if (this.parentElement.parentElement.id !== '') {
|
||||
if (this.parentElement.parentElement.id !== '' && this.parentElement.parentElement.classList.contains('dataset_row') ) {
|
||||
dataset_ids.push(this.parentElement.parentElement.id);
|
||||
} else if (this.parentElement.parentElement.id !== '' && this.parentElement.parentElement.classList.contains('folder_row') ) {
|
||||
folder_ids.push(this.parentElement.parentElement.id);
|
||||
}
|
||||
});
|
||||
// prepare the dataset objects to be imported
|
||||
@@ -265,6 +268,18 @@ var FolderToolbarView = Backbone.View.extend({
|
||||
historyItem.source = 'library';
|
||||
datasets_to_import.push(historyItem);
|
||||
}
|
||||
|
||||
// prepare the folder objects to be imported
|
||||
var folders_to_import = [];
|
||||
for (var i = folder_ids.length - 1; i >= 0; i--) {
|
||||
var library_folder_id = folder_ids[i];
|
||||
var historyItem = new mod_library_model.HistoryItem();
|
||||
historyItem.url = historyItem.urlRoot + history_id + '/contents';
|
||||
historyItem.content = library_folder_id;
|
||||
historyItem.source = 'library_folder';
|
||||
datasets_to_import.push(historyItem);
|
||||
}
|
||||
|
||||
this.initChainCallControl( { length: datasets_to_import.length, action: 'to_history', history_name: history_name } );
|
||||
// set the used history as current so user will see the last one
|
||||
// that he imported into in the history panel on the 'analysis' page
|
||||
@@ -289,13 +304,16 @@ var FolderToolbarView = Backbone.View.extend({
|
||||
*/
|
||||
download : function( folder_id, format ){
|
||||
var dataset_ids = [];
|
||||
var folder_ids = [];
|
||||
$( '#folder_table' ).find( ':checked' ).each( function(){
|
||||
if ( this.parentElement.parentElement.id !== '' ) {
|
||||
if ( this.parentElement.parentElement.id !== '' && this.parentElement.parentElement.classList.contains('dataset_row') ) {
|
||||
dataset_ids.push( this.parentElement.parentElement.id );
|
||||
} else if ( this.parentElement.parentElement.id !== '' && this.parentElement.parentElement.classList.contains('folder_row') ) {
|
||||
folder_ids.push( this.parentElement.parentElement.id );
|
||||
}
|
||||
} );
|
||||
var url = ( window.galaxy_config ? galaxy_config.root : '/' ) + 'api/libraries/datasets/download/' + format;
|
||||
var data = { 'ld_ids' : dataset_ids };
|
||||
var data = { 'ld_ids' : dataset_ids, 'folder_ids' : folder_ids };
|
||||
this.processDownload( url, data, 'get' );
|
||||
},
|
||||
|
||||
@@ -845,37 +863,45 @@ var FolderToolbarView = Backbone.View.extend({
|
||||
* call them in chain. Update progress bar in between each.
|
||||
* @param {array} lddas_set array of lddas to delete
|
||||
*/
|
||||
chainCallDeletingHdas: function( lddas_set ){
|
||||
chainCallDeletingItems: function( items_to_delete ){
|
||||
var self = this;
|
||||
this.deleted_lddas = new mod_library_model.Folder();
|
||||
var popped_item = lddas_set.pop();
|
||||
this.deleted_items = new mod_library_model.Folder();
|
||||
var popped_item = items_to_delete.pop();
|
||||
if ( typeof popped_item === "undefined" ) {
|
||||
if ( this.options.chain_call_control.failed_number === 0 ){
|
||||
mod_toastr.success( 'Selected datasets were deleted.' );
|
||||
mod_toastr.success( 'Selected items were deleted.' );
|
||||
} else if ( this.options.chain_call_control.failed_number === this.options.chain_call_control.total_number ){
|
||||
mod_toastr.error( 'There was an error and no datasets were deleted. Please make sure you have sufficient permissions.' );
|
||||
mod_toastr.error( 'There was an error and no items were deleted. Please make sure you have sufficient permissions.' );
|
||||
} else if ( this.options.chain_call_control.failed_number < this.options.chain_call_control.total_number ){
|
||||
mod_toastr.warning( 'Some of the datasets could not be deleted. Please make sure you have sufficient permissions.' );
|
||||
mod_toastr.warning( 'Some of the items could not be deleted. Please make sure you have sufficient permissions.' );
|
||||
}
|
||||
Galaxy.modal.hide();
|
||||
return this.deleted_lddas;
|
||||
return this.deleted_items;
|
||||
}
|
||||
var promise = $.when( popped_item.destroy() );
|
||||
|
||||
promise.done( function( dataset ){
|
||||
promise.done( function( item ){
|
||||
Galaxy.libraries.folderListView.collection.remove( popped_item.id );
|
||||
self.updateProgress();
|
||||
// add the deleted dataset to collection, triggers rendering
|
||||
// add the deleted item to collection, triggers rendering
|
||||
if ( Galaxy.libraries.folderListView.options.include_deleted ){
|
||||
var updated_dataset = new mod_library_model.Item( dataset );
|
||||
Galaxy.libraries.folderListView.collection.add( updated_dataset );
|
||||
var updated_item = null;
|
||||
if (item.type === 'folder' || item.model_class === 'LibraryFolder'){
|
||||
updated_item = new mod_library_model.FolderAsModel( item );
|
||||
} else if (item.type === 'file' || item.model_class === 'LibraryDataset'){
|
||||
updated_item = new mod_library_model.Item( item );
|
||||
} else {
|
||||
console.error('Unknown library item type found.');
|
||||
console.error(item.type || item.model_class);
|
||||
}
|
||||
Galaxy.libraries.folderListView.collection.add( updated_item );
|
||||
}
|
||||
self.chainCallDeletingHdas( lddas_set );
|
||||
self.chainCallDeletingItems( items_to_delete );
|
||||
})
|
||||
.fail( function(){
|
||||
self.options.chain_call_control.failed_number += 1;
|
||||
self.updateProgress();
|
||||
self.chainCallDeletingHdas( lddas_set );
|
||||
self.chainCallDeletingItems( items_to_delete );
|
||||
});
|
||||
},
|
||||
|
||||
@@ -913,25 +939,35 @@ var FolderToolbarView = Backbone.View.extend({
|
||||
this.options.chain_call_control.failed_number = 0;
|
||||
|
||||
var dataset_ids = [];
|
||||
var folder_ids = [];
|
||||
checkedValues.each(function(){
|
||||
if (this.parentElement.parentElement.id !== '') {
|
||||
dataset_ids.push(this.parentElement.parentElement.id);
|
||||
if (this.parentElement.parentElement.id.substring(0,1) == 'F'){
|
||||
folder_ids.push(this.parentElement.parentElement.id);
|
||||
} else {
|
||||
dataset_ids.push(this.parentElement.parentElement.id);
|
||||
}
|
||||
}
|
||||
});
|
||||
// init the progress bar
|
||||
this.progressStep = 100 / dataset_ids.length;
|
||||
var items_total = dataset_ids.length + folder_ids.length
|
||||
this.progressStep = 100 / items_total;
|
||||
this.progress = 0;
|
||||
|
||||
// prepare the dataset items to be added
|
||||
var lddas_to_delete = [];
|
||||
var items_to_delete = [];
|
||||
for (var i = dataset_ids.length - 1; i >= 0; i--) {
|
||||
var dataset = new mod_library_model.Item({id:dataset_ids[i]});
|
||||
lddas_to_delete.push(dataset);
|
||||
items_to_delete.push(dataset);
|
||||
}
|
||||
for (var i = folder_ids.length - 1; i >= 0; i--) {
|
||||
var folder = new mod_library_model.FolderAsModel({id:folder_ids[i]});
|
||||
items_to_delete.push(folder);
|
||||
}
|
||||
|
||||
this.options.chain_call_control.total_number = dataset_ids.length;
|
||||
this.options.chain_call_control.total_number = items_total.length;
|
||||
// call the recursive function to call ajax one after each other (request FIFO queue)
|
||||
this.chainCallDeletingHdas(lddas_to_delete);
|
||||
this.chainCallDeletingItems(items_to_delete);
|
||||
}
|
||||
},
|
||||
|
||||
@@ -994,6 +1030,9 @@ var FolderToolbarView = Backbone.View.extend({
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Show user the prompt to change the number of items shown on page.
|
||||
*/
|
||||
showPageSizePrompt: function(){
|
||||
var folder_page_size = prompt( 'How many items per page do you want to see?', Galaxy.libraries.preferences.get( 'folder_page_size' ) );
|
||||
if ( ( folder_page_size != null ) && ( folder_page_size == parseInt( folder_page_size ) ) ) {
|
||||
@@ -1009,8 +1048,16 @@ var FolderToolbarView = Backbone.View.extend({
|
||||
tmpl_array.push('<div class="library_style_container">');
|
||||
// TOOLBAR START
|
||||
tmpl_array.push(' <div id="library_toolbar">');
|
||||
tmpl_array.push('<form class="form-inline" role="form">');
|
||||
tmpl_array.push(' <span><strong>DATA LIBRARIES</strong></span>');
|
||||
tmpl_array.push(' <span data-toggle="tooltip" data-placement="top" class="logged-dataset-manipulation" title="Include deleted datasets" style="display:none;"> | <input id="include_deleted_datasets_chk" style="margin: 0;" type="checkbox"> include deleted | </input></span>');
|
||||
tmpl_array.push(' <span id="folder_paginator" class="library-paginator">');
|
||||
// paginator will append here
|
||||
tmpl_array.push(' </span>');
|
||||
tmpl_array.push('<div class="checkbox toolbar-item logged-dataset-manipulation" style="height: 20px; display:none;">');
|
||||
tmpl_array.push('<label>');
|
||||
tmpl_array.push('<input id="include_deleted_datasets_chk" type="checkbox"> include deleted </input>');
|
||||
tmpl_array.push('</label>');
|
||||
tmpl_array.push('</div>');
|
||||
tmpl_array.push(' <button style="display:none;" data-toggle="tooltip" data-placement="top" title="Create New Folder" id="toolbtn_create_folder" class="btn btn-default primary-button add-library-items" type="button"><span class="fa fa-plus"></span> <span class="fa fa-folder"></span></button>');
|
||||
|
||||
tmpl_array.push('<% if(mutiple_add_dataset_options) { %>');
|
||||
@@ -1056,12 +1103,9 @@ var FolderToolbarView = Backbone.View.extend({
|
||||
tmpl_array.push(' <button data-toggle="tooltip" data-placement="top" title="Mark selected datasets deleted" id="toolbtn_bulk_delete" class="primary-button logged-dataset-manipulation" style="margin-left: 0.5em; display:none; " type="button"><span class="fa fa-times"></span> Delete</button>');
|
||||
tmpl_array.push(' <button data-id="<%- id %>" data-toggle="tooltip" data-placement="top" title="Show library information" id="toolbtn_show_libinfo" class="primary-button" style="margin-left: 0.5em;" type="button"><span class="fa fa-info-circle"></span> Library Info</button>');
|
||||
tmpl_array.push(' <span class="help-button" data-toggle="tooltip" data-placement="top" title="Visit Libraries Wiki"><a href="https://wiki.galaxyproject.org/DataLibraries/screen/FolderContents" target="_blank"><button class="primary-button" type="button"><span class="fa fa-question-circle"></span> Help</button></a></span>');
|
||||
|
||||
tmpl_array.push(' <span id="folder_paginator" class="library-paginator">');
|
||||
// paginator will append here
|
||||
tmpl_array.push(' </span>');
|
||||
|
||||
tmpl_array.push(' </div>');
|
||||
tmpl_array.push('</form>');
|
||||
|
||||
// TOOLBAR END
|
||||
tmpl_array.push(' <div id="folder_items_element">');
|
||||
tmpl_array.push(' </div>');
|
||||
@@ -1081,6 +1125,7 @@ var FolderToolbarView = Backbone.View.extend({
|
||||
tmpl_array.push('<p><%- library.get("description") %></p>');
|
||||
tmpl_array.push('<h3>Library synopsis:</h3>');
|
||||
tmpl_array.push('<p><%- library.get("synopsis") %></p>');
|
||||
tmpl_array.push('<p data-toggle="tooltip" data-placement="top" title="<%- library.get("create_time") %>">created <%- library.get("create_time_pretty") %></p>');
|
||||
|
||||
tmpl_array.push('</div>');
|
||||
|
||||
|
||||
@@ -4,14 +4,16 @@ define([
|
||||
"utils/utils",
|
||||
"libs/toastr",
|
||||
"mvc/library/library-model",
|
||||
"mvc/library/library-libraryrow-view"
|
||||
"mvc/library/library-libraryrow-view",
|
||||
"libs/underscore"
|
||||
], function(
|
||||
mod_masthead,
|
||||
mod_baseMVC,
|
||||
mod_utils,
|
||||
mod_toastr,
|
||||
mod_library_model,
|
||||
mod_library_libraryrow_view
|
||||
mod_library_libraryrow_view,
|
||||
_
|
||||
){
|
||||
|
||||
var LibraryListView = Backbone.View.extend({
|
||||
@@ -32,11 +34,9 @@ var LibraryListView = Backbone.View.extend({
|
||||
* @param {object} options an object with options
|
||||
*/
|
||||
initialize : function( options ){
|
||||
this.options = _.defaults( this.options || {}, this.defaults, options );
|
||||
this.options = _.defaults( this.options || {}, options, this.defaults );
|
||||
var that = this;
|
||||
this.modal = null;
|
||||
// map of library model ids to library views = cache
|
||||
this.rowViews = {};
|
||||
// collection of {Item}s
|
||||
this.collection = new mod_library_model.Libraries();
|
||||
this.collection.fetch({
|
||||
@@ -60,6 +60,7 @@ var LibraryListView = Backbone.View.extend({
|
||||
*/
|
||||
render: function ( options ) {
|
||||
this.options = _.extend( this.options, options );
|
||||
this.setElement('#libraries_element');
|
||||
var template = this.templateLibraryList();
|
||||
var libraries_to_render = null;
|
||||
var models = null;
|
||||
@@ -75,10 +76,16 @@ var LibraryListView = Backbone.View.extend({
|
||||
libraries_to_render = this.collection.where( { deleted: false } );
|
||||
}
|
||||
} else if ( models !== null ){
|
||||
libraries_to_render = models;
|
||||
if ( Galaxy.libraries.preferences.get( 'with_deleted' ) ){
|
||||
libraries_to_render = models;
|
||||
} else {
|
||||
var is_deleted = function(model){ return model.get('deleted') === false; }
|
||||
libraries_to_render = _.filter(models, is_deleted );
|
||||
}
|
||||
} else {
|
||||
libraries_to_render = [];
|
||||
}
|
||||
|
||||
// pagination
|
||||
if ( this.options.show_page === null || this.options.show_page < 1 ){
|
||||
this.options.show_page = 1;
|
||||
@@ -95,14 +102,16 @@ var LibraryListView = Backbone.View.extend({
|
||||
}
|
||||
this.$el.html( template({
|
||||
length: 1,
|
||||
order: Galaxy.libraries.preferences.get( 'sort_order' )
|
||||
order: Galaxy.libraries.preferences.get( 'sort_order' ),
|
||||
search_term: Galaxy.libraries.libraryToolbarView.options.search_term
|
||||
}));
|
||||
Galaxy.libraries.libraryToolbarView.renderPaginator( this.options );
|
||||
this.renderRows( libraries_to_render );
|
||||
} else {
|
||||
this.$el.html( template({
|
||||
length: 0,
|
||||
order: Galaxy.libraries.preferences.get( 'sort_order' )
|
||||
order: Galaxy.libraries.preferences.get( 'sort_order' ),
|
||||
search_term: Galaxy.libraries.libraryToolbarView.options.search_term
|
||||
}));
|
||||
Galaxy.libraries.libraryToolbarView.renderPaginator( this.options );
|
||||
}
|
||||
@@ -117,14 +126,7 @@ var LibraryListView = Backbone.View.extend({
|
||||
renderRows: function( libraries_to_render ){
|
||||
for ( var i = 0; i < libraries_to_render.length; i++ ) {
|
||||
var library = libraries_to_render[i];
|
||||
// search whether we have the item cached
|
||||
var cachedView = _.findWhere( this.rowViews, { id: library.get( 'id' ) } );
|
||||
if ( cachedView !== undefined && this instanceof Backbone.View ){
|
||||
cachedView.delegateEvents();
|
||||
this.$el.find( '#library_list_body' ).append( cachedView.el );
|
||||
} else {
|
||||
this.renderOne( { library: library } )
|
||||
}
|
||||
this.renderOne( { library: library } );
|
||||
}
|
||||
},
|
||||
|
||||
@@ -136,8 +138,6 @@ var LibraryListView = Backbone.View.extend({
|
||||
var library = options.library;
|
||||
var rowView = new mod_library_libraryrow_view.LibraryRowView( library );
|
||||
this.$el.find( '#library_list_body' ).append( rowView.el );
|
||||
// save new rowView to cache
|
||||
this.rowViews[ library.get( 'id' ) ] = rowView;
|
||||
},
|
||||
|
||||
/**
|
||||
@@ -174,6 +174,24 @@ var LibraryListView = Backbone.View.extend({
|
||||
window.location = '/user/login';
|
||||
},
|
||||
|
||||
/**
|
||||
* In case the search_term is not empty perform the search and render
|
||||
* the result. Render all visible libraries otherwise.
|
||||
* @param {string} search_term string to search for
|
||||
*/
|
||||
searchLibraries: function(search_term){
|
||||
var trimmed_term = $.trim(search_term);
|
||||
if (trimmed_term !== ''){
|
||||
var results = null
|
||||
results = this.collection.search( search_term );
|
||||
this.options.searching = true;
|
||||
this.render({'models': results});
|
||||
} else {
|
||||
this.options.searching = false;
|
||||
this.render();
|
||||
}
|
||||
},
|
||||
|
||||
// MMMMMMMMMMMMMMMMMM
|
||||
// === TEMPLATES ====
|
||||
// MMMMMMMMMMMMMMMMMM
|
||||
@@ -183,7 +201,11 @@ var LibraryListView = Backbone.View.extend({
|
||||
|
||||
tmpl_array.push('<div class="library_container table-responsive">');
|
||||
tmpl_array.push('<% if(length === 0) { %>');
|
||||
tmpl_array.push('<% if(search_term.length > 0) { %>');
|
||||
tmpl_array.push('<div>There are no libraries matching your search. Try different keyword.</div>');
|
||||
tmpl_array.push('<% } else{ %>');
|
||||
tmpl_array.push('<div>There are no libraries visible to you here. If you expected some to show up please consult the <a href="https://wiki.galaxyproject.org/Admin/DataLibraries/LibrarySecurity" target="_blank">library security wikipage</a> or visit the <a href="https://biostar.usegalaxy.org/" target="_blank">Galaxy support site</a>.</div>');
|
||||
tmpl_array.push('<% }%>');
|
||||
tmpl_array.push('<% } else{ %>');
|
||||
tmpl_array.push('<table class="grid table table-condensed">');
|
||||
tmpl_array.push(' <thead>');
|
||||
|
||||
@@ -5,19 +5,24 @@ function(mod_toastr,
|
||||
mod_library_model) {
|
||||
/**
|
||||
* This view represents the top part of the library page.
|
||||
* It contains the tool bar with buttons.
|
||||
* It contains the tool bar with controls.
|
||||
*/
|
||||
var LibraryToolbarView = Backbone.View.extend({
|
||||
el: '#center',
|
||||
|
||||
defaults: {
|
||||
search_term: ''
|
||||
},
|
||||
|
||||
events: {
|
||||
'click #create_new_library_btn' : 'showLibraryModal',
|
||||
'click #include_deleted_chk' : 'includeDeletedChecked',
|
||||
'click #page_size_prompt' : 'showPageSizePrompt'
|
||||
'click #lib_page_size_prompt' : 'showPageSizePrompt',
|
||||
'keyup .library-search-input' : 'searchLibraries'
|
||||
},
|
||||
|
||||
initialize: function( options ){
|
||||
this.options = _.defaults( this.options || {}, options );
|
||||
this.options = _.defaults( this.options || {}, options, this.defaults );
|
||||
this.render();
|
||||
},
|
||||
|
||||
@@ -36,93 +41,109 @@ var LibraryToolbarView = Backbone.View.extend({
|
||||
},
|
||||
|
||||
/**
|
||||
* Called from LibraryListView when needed.
|
||||
* @param {object} options common options
|
||||
* Renders the element that shows pages into its div within the toolbar.
|
||||
*/
|
||||
renderPaginator: function( options ){
|
||||
this.options = _.extend( this.options, options );
|
||||
var paginator_template = this.templatePaginator();
|
||||
this.$el.find( '#library_paginator' ).html( paginator_template({
|
||||
show_page: parseInt( this.options.show_page ),
|
||||
page_count: parseInt( this.options.page_count ),
|
||||
total_libraries_count: this.options.total_libraries_count,
|
||||
libraries_shown: this.options.libraries_shown
|
||||
show_page: parseInt( this.options.show_page ),
|
||||
page_count: parseInt( this.options.page_count ),
|
||||
total_libraries_count: this.options.total_libraries_count,
|
||||
libraries_shown: this.options.libraries_shown
|
||||
}));
|
||||
},
|
||||
|
||||
/**
|
||||
* User clicked on 'New library' button. Show modal to
|
||||
* satisfy the wish.
|
||||
*/
|
||||
showLibraryModal : function (event){
|
||||
event.preventDefault();
|
||||
event.stopPropagation();
|
||||
var self = this;
|
||||
this.modal = Galaxy.modal;
|
||||
this.modal.show({
|
||||
closing_events : true,
|
||||
title : 'Create New Library',
|
||||
body : this.templateNewLibraryInModal(),
|
||||
buttons : {
|
||||
'Create' : function() { self.createNewLibrary(); },
|
||||
'Close' : function() { self.modal.hide(); }
|
||||
}
|
||||
});
|
||||
},
|
||||
|
||||
createNewLibrary: function(){
|
||||
var libraryDetails = this.serializeNewLibrary();
|
||||
if (this.valdiateNewLibrary(libraryDetails)){
|
||||
var library = new mod_library_model.Library();
|
||||
var self = this;
|
||||
library.save(libraryDetails, {
|
||||
success: function (library) {
|
||||
Galaxy.libraries.libraryListView.collection.add(library);
|
||||
self.modal.hide();
|
||||
self.clearLibraryModal();
|
||||
Galaxy.libraries.libraryListView.render();
|
||||
mod_toastr.success('Library created.');
|
||||
},
|
||||
error: function(model, response){
|
||||
if (typeof response.responseJSON !== "undefined"){
|
||||
mod_toastr.error(response.responseJSON.err_msg);
|
||||
} else {
|
||||
mod_toastr.error('An error occured.');
|
||||
}
|
||||
}
|
||||
});
|
||||
} else {
|
||||
mod_toastr.error('Library\'s name is missing.');
|
||||
event.preventDefault();
|
||||
event.stopPropagation();
|
||||
var self = this;
|
||||
this.modal = Galaxy.modal;
|
||||
this.modal.show({
|
||||
closing_events : true,
|
||||
title : 'Create New Library',
|
||||
body : this.templateNewLibraryInModal(),
|
||||
buttons : {
|
||||
'Create' : function() { self.createNewLibrary(); },
|
||||
'Close' : function() { self.modal.hide(); }
|
||||
}
|
||||
return false;
|
||||
});
|
||||
},
|
||||
|
||||
/**
|
||||
* Create the new library using the API asynchronously.
|
||||
*/
|
||||
createNewLibrary: function(){
|
||||
var libraryDetails = this.serializeNewLibrary();
|
||||
if (this.validateNewLibrary(libraryDetails)){
|
||||
var library = new mod_library_model.Library();
|
||||
var self = this;
|
||||
library.save(libraryDetails, {
|
||||
success: function (library) {
|
||||
Galaxy.libraries.libraryListView.collection.add(library);
|
||||
self.modal.hide();
|
||||
self.clearLibraryModal();
|
||||
Galaxy.libraries.libraryListView.render();
|
||||
mod_toastr.success('Library created.');
|
||||
},
|
||||
error: function(model, response){
|
||||
if (typeof response.responseJSON !== "undefined"){
|
||||
mod_toastr.error(response.responseJSON.err_msg);
|
||||
} else {
|
||||
mod_toastr.error('An error occured.');
|
||||
}
|
||||
}
|
||||
});
|
||||
} else {
|
||||
mod_toastr.error('Library\'s name is missing.');
|
||||
}
|
||||
return false;
|
||||
},
|
||||
|
||||
/**
|
||||
* Show user the propmpt to change the number of libs shown on page.
|
||||
*/
|
||||
showPageSizePrompt: function(){
|
||||
var library_page_size = prompt( 'How many libraries per page do you want to see?', Galaxy.libraries.preferences.get( 'library_page_size' ) );
|
||||
if ( ( library_page_size != null ) && ( library_page_size == parseInt( library_page_size ) ) ) {
|
||||
Galaxy.libraries.preferences.set( { 'library_page_size': parseInt( library_page_size ) } );
|
||||
Galaxy.libraries.libraryListView.render( { show_page: 1 } );
|
||||
Galaxy.libraries.preferences.set( { 'library_page_size': parseInt( library_page_size ) } );
|
||||
Galaxy.libraries.libraryListView.render( { show_page: 1 } );
|
||||
}
|
||||
},
|
||||
|
||||
// clear the library modal once saved
|
||||
/**
|
||||
* Clear the library modal once it is saved.
|
||||
*/
|
||||
clearLibraryModal : function(){
|
||||
$("input[name='Name']").val('');
|
||||
$("input[name='Description']").val('');
|
||||
$("input[name='Synopsis']").val('');
|
||||
$("input[name='Name']").val('');
|
||||
$("input[name='Description']").val('');
|
||||
$("input[name='Synopsis']").val('');
|
||||
},
|
||||
|
||||
/**
|
||||
* Prepare new library variables to be submitted to API.
|
||||
*/
|
||||
serializeNewLibrary : function(){
|
||||
return {
|
||||
name: $("input[name='Name']").val(),
|
||||
description: $("input[name='Description']").val(),
|
||||
synopsis: $("input[name='Synopsis']").val()
|
||||
};
|
||||
return {
|
||||
name: $("input[name='Name']").val(),
|
||||
description: $("input[name='Description']").val(),
|
||||
synopsis: $("input[name='Synopsis']").val()
|
||||
};
|
||||
},
|
||||
|
||||
valdiateNewLibrary: function( libraryDetails ){
|
||||
/**
|
||||
* Check whether entered values are valid.
|
||||
*/
|
||||
validateNewLibrary: function( libraryDetails ){
|
||||
return libraryDetails.name !== '';
|
||||
},
|
||||
|
||||
/**
|
||||
* Include or exclude deleted libraries in the view.
|
||||
* @param {object} event common event
|
||||
*/
|
||||
includeDeletedChecked: function( event ){
|
||||
if (event.target.checked){
|
||||
@@ -134,89 +155,102 @@ var LibraryToolbarView = Backbone.View.extend({
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Take the contents of the search field and send it to the list view
|
||||
* to query the collection of libraries.
|
||||
*/
|
||||
searchLibraries: function(event){
|
||||
var search_term = $(".library-search-input").val();
|
||||
this.options.search_term = search_term;
|
||||
Galaxy.libraries.libraryListView.searchLibraries(search_term);
|
||||
},
|
||||
|
||||
templateToolBar: function(){
|
||||
tmpl_array = [];
|
||||
|
||||
|
||||
tmpl_array.push('<div class="library_style_container">');
|
||||
tmpl_array.push(' <div id="toolbar_form">');
|
||||
tmpl_array.push(' <div id="library_toolbar">');
|
||||
tmpl_array.push(' <form class="form-inline" role="form">');
|
||||
tmpl_array.push(' <span><strong><a href="#" title="Go to first page">DATA LIBRARIES</a></strong></span>');
|
||||
tmpl_array.push(' <% if(admin_user === true) { %>');
|
||||
tmpl_array.push(' <div class="checkbox" style="height: 20px;">');
|
||||
tmpl_array.push(' <label>');
|
||||
tmpl_array.push(' <input id="include_deleted_chk" type="checkbox"> include deleted </input>');
|
||||
tmpl_array.push(' </label>');
|
||||
tmpl_array.push(' </div>');
|
||||
tmpl_array.push(' <span 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>');
|
||||
tmpl_array.push(' <% } %>');
|
||||
tmpl_array.push(' <span class="help-button" data-toggle="tooltip" data-placement="top" title="Visit Libraries Wiki"><a href="https://wiki.galaxyproject.org/DataLibraries/screen/ListOfLibraries" target="_blank"><button class="primary-button" type="button"><span class="fa fa-question-circle"></span> Help</button></a></span>');
|
||||
tmpl_array.push(' <span id="library_paginator" class="library-paginator">');
|
||||
// paginator will append here
|
||||
tmpl_array.push(' </span>');
|
||||
tmpl_array.push(' </form>');
|
||||
tmpl_array.push(' </div>');
|
||||
tmpl_array.push(' </div>');
|
||||
tmpl_array.push(' <div id="libraries_element">');
|
||||
// table with libraries will append here
|
||||
tmpl_array.push(' </div>');
|
||||
tmpl_array.push('</div>');
|
||||
|
||||
return _.template(tmpl_array.join(''));
|
||||
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>',
|
||||
// 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>',
|
||||
'</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="Visit Libraries Wiki">',
|
||||
'<a href="https://wiki.galaxyproject.org/DataLibraries/screen/ListOfLibraries" target="_blank">',
|
||||
'<button class="primary-button" type="button"><span class="fa fa-question-circle"></span> Help</button>',
|
||||
'</a>',
|
||||
'</span>',
|
||||
'</form>',
|
||||
'</div>',
|
||||
'</div>',
|
||||
'<div id="libraries_element">',
|
||||
// table with libraries will append here
|
||||
'</div>',
|
||||
'</div>'
|
||||
].join(''));
|
||||
},
|
||||
|
||||
templatePaginator: function(){
|
||||
tmpl_array = [];
|
||||
|
||||
tmpl_array.push(' <ul class="pagination pagination-sm">');
|
||||
tmpl_array.push(' <% if ( ( show_page - 1 ) > 0 ) { %>');
|
||||
tmpl_array.push(' <% if ( ( show_page - 1 ) > page_count ) { %>'); // we are on higher page than total page count
|
||||
tmpl_array.push(' <li><a href="#page/1"><span class="fa fa-angle-double-left"></span></a></li>');
|
||||
tmpl_array.push(' <li class="disabled"><a href="#page/<% print( show_page ) %>"><% print( show_page - 1 ) %></a></li>');
|
||||
tmpl_array.push(' <% } else { %>');
|
||||
tmpl_array.push(' <li><a href="#page/1"><span class="fa fa-angle-double-left"></span></a></li>');
|
||||
tmpl_array.push(' <li><a href="#page/<% print( show_page - 1 ) %>"><% print( show_page - 1 ) %></a></li>');
|
||||
tmpl_array.push(' <% } %>');
|
||||
tmpl_array.push(' <% } else { %>'); // we are on the first page
|
||||
tmpl_array.push(' <li class="disabled"><a href="#page/1"><span class="fa fa-angle-double-left"></span></a></li>');
|
||||
tmpl_array.push(' <li class="disabled"><a href="#page/<% print( show_page ) %>"><% print( show_page - 1 ) %></a></li>');
|
||||
tmpl_array.push(' <% } %>');
|
||||
tmpl_array.push(' <li class="active">');
|
||||
tmpl_array.push(' <a href="#page/<% print( show_page ) %>"><% print( show_page ) %></a>');
|
||||
tmpl_array.push(' </li>');
|
||||
tmpl_array.push(' <% if ( ( show_page ) < page_count ) { %>');
|
||||
tmpl_array.push(' <li><a href="#page/<% print( show_page + 1 ) %>"><% print( show_page + 1 ) %></a></li>');
|
||||
tmpl_array.push(' <li><a href="#page/<% print( page_count ) %>"><span class="fa fa-angle-double-right"></span></a></li>');
|
||||
tmpl_array.push(' <% } else { %>');
|
||||
tmpl_array.push(' <li class="disabled"><a href="#page/<% print( show_page ) %>"><% print( show_page + 1 ) %></a></li>');
|
||||
tmpl_array.push(' <li class="disabled"><a href="#page/<% print( page_count ) %>"><span class="fa fa-angle-double-right"></span></a></li>');
|
||||
tmpl_array.push(' <% } %>');
|
||||
tmpl_array.push(' </ul>');
|
||||
tmpl_array.push(' <span>');
|
||||
tmpl_array.push(' showing <a data-toggle="tooltip" data-placement="top" title="Click to change the number of libraries on page" id="page_size_prompt"><%- libraries_shown %></a> of <%- total_libraries_count %> libraries');
|
||||
tmpl_array.push(' </span>');
|
||||
|
||||
return _.template(tmpl_array.join(''));
|
||||
return _.template([
|
||||
'<ul class="pagination pagination-sm">',
|
||||
'<% 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>',
|
||||
'<% } 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>',
|
||||
'<% } %>',
|
||||
'<% } 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="active">',
|
||||
'<a 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>',
|
||||
'<% } 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>',
|
||||
'<% } %>',
|
||||
'</ul>',
|
||||
'<span id="lib_page_size_prompt">',
|
||||
' showing <a data-toggle="tooltip" data-placement="top" title="Click to change the number of libraries on page"><%- libraries_shown %></a> of <%- total_libraries_count %> libraries',
|
||||
'</span>'
|
||||
].join(''));
|
||||
},
|
||||
|
||||
templateNewLibraryInModal: function(){
|
||||
tmpl_array = [];
|
||||
|
||||
tmpl_array.push('<div id="new_library_modal">');
|
||||
tmpl_array.push(' <form>');
|
||||
tmpl_array.push(' <input type="text" name="Name" value="" placeholder="Name">');
|
||||
tmpl_array.push(' <input type="text" name="Description" value="" placeholder="Description">');
|
||||
tmpl_array.push(' <input type="text" name="Synopsis" value="" placeholder="Synopsis">');
|
||||
tmpl_array.push(' </form>');
|
||||
tmpl_array.push('</div>');
|
||||
|
||||
return tmpl_array.join('');
|
||||
return _.template([
|
||||
'<div id="new_library_modal">',
|
||||
'<form>',
|
||||
'<input type="text" name="Name" value="" placeholder="Name">',
|
||||
'<input type="text" name="Description" value="" placeholder="Description">',
|
||||
'<input type="text" name="Synopsis" value="" placeholder="Synopsis">',
|
||||
'</form>',
|
||||
'</div>'
|
||||
].join(''));
|
||||
}
|
||||
});
|
||||
|
||||
return {
|
||||
LibraryToolbarView: LibraryToolbarView
|
||||
LibraryToolbarView: LibraryToolbarView
|
||||
};
|
||||
|
||||
});
|
||||
|
||||
@@ -31,6 +31,21 @@ define([], function() {
|
||||
options = options || {};
|
||||
},
|
||||
|
||||
search : function(search_term){
|
||||
/**
|
||||
* Search the collection and return only the models that have
|
||||
* the search term in their names.
|
||||
* [the term to search]
|
||||
* @type {string}
|
||||
*/
|
||||
if (search_term == "") return this;
|
||||
var lowercase_term = search_term.toLowerCase();
|
||||
return this.filter(function(data) {
|
||||
lowercase_name = data.get("name").toLowerCase();
|
||||
return lowercase_name.indexOf(lowercase_term) !== -1;
|
||||
});
|
||||
},
|
||||
|
||||
/** Get every 'shown' library in this collection based on deleted filter
|
||||
* @param {Boolean} show_deleted are we including deleted libraries?
|
||||
* @returns array of library models
|
||||
@@ -83,20 +98,19 @@ define([], function() {
|
||||
// ============================================================================
|
||||
// FOLDER RELATED MODELS
|
||||
|
||||
var Item = Backbone.Model.extend({
|
||||
var LibraryItem = Backbone.Model.extend({
|
||||
});
|
||||
|
||||
var Ldda = LibraryItem.extend({
|
||||
urlRoot : ( window.galaxy_config ? galaxy_config.root : '/' ) + 'api/libraries/datasets/'
|
||||
});
|
||||
|
||||
var Ldda = Backbone.Model.extend({
|
||||
urlRoot : ( window.galaxy_config ? galaxy_config.root : '/' ) + 'api/libraries/datasets/'
|
||||
});
|
||||
|
||||
var FolderAsModel = Backbone.Model.extend({
|
||||
urlRoot: ( window.galaxy_config ? galaxy_config.root : '/' ) + 'api/folders'
|
||||
var FolderAsModel = LibraryItem.extend({
|
||||
urlRoot: ( window.galaxy_config ? galaxy_config.root : '/' ) + 'api/folders/'
|
||||
});
|
||||
|
||||
var Folder = Backbone.Collection.extend({
|
||||
model: Item,
|
||||
model: LibraryItem,
|
||||
|
||||
/** Sort collection by item name (ascending) and return the sorted
|
||||
* collection. Folders go before datasets.
|
||||
@@ -150,16 +164,28 @@ define([], function() {
|
||||
});
|
||||
|
||||
var FolderContainer = Backbone.Model.extend({
|
||||
defaults : {
|
||||
folder : new Folder(),
|
||||
urlRoot : ( window.galaxy_config ? galaxy_config.root : '/' ) + 'api/folders/',
|
||||
id : "unknown"
|
||||
},
|
||||
parse : function(obj) {
|
||||
// response is not a simple array, it contains metadata
|
||||
// this will update the inner collection
|
||||
this.get("folder").reset(obj.folder_contents);
|
||||
return obj;
|
||||
defaults : {
|
||||
folder : new Folder(),
|
||||
urlRoot : ( window.galaxy_config ? galaxy_config.root : '/' ) + 'api/folders/',
|
||||
id : "unknown"
|
||||
},
|
||||
parse : function(obj) {
|
||||
// empty the collection
|
||||
this.get("folder").reset();
|
||||
// response is not a simple array, it contains metadata
|
||||
// this will update the inner collection
|
||||
for (var i = 0; i < obj.folder_contents.length; i++) {
|
||||
if (obj.folder_contents[i].type === 'folder'){
|
||||
var folder_item = new FolderAsModel(obj.folder_contents[i])
|
||||
this.get("folder").add(folder_item);
|
||||
} else if(obj.folder_contents[i].type === 'file'){
|
||||
var file_item = new Ldda(obj.folder_contents[i])
|
||||
this.get("folder").add(file_item);
|
||||
} else{
|
||||
console.error('Unknown folder item type encountered while parsing response.');
|
||||
}
|
||||
};
|
||||
return obj;
|
||||
}
|
||||
});
|
||||
|
||||
@@ -204,10 +230,10 @@ define([], function() {
|
||||
|
||||
return {
|
||||
Library: Library,
|
||||
FolderAsModel : FolderAsModel,
|
||||
Libraries : Libraries,
|
||||
Item : Item,
|
||||
Item : Ldda,
|
||||
Ldda : Ldda,
|
||||
FolderAsModel : FolderAsModel,
|
||||
Folder : Folder,
|
||||
FolderContainer : FolderContainer,
|
||||
HistoryItem : HistoryItem,
|
||||
|
||||
@@ -371,6 +371,7 @@ var ListPanel = Backbone.View.extend( BASE_MVC.LoggableMixin ).extend(
|
||||
} else {
|
||||
panel._renderEmptyMessage( $whereTo ).show();
|
||||
}
|
||||
panel.trigger( 'views:ready', panel.views );
|
||||
|
||||
return panel.views;
|
||||
},
|
||||
@@ -635,8 +636,8 @@ var ListPanel = Backbone.View.extend( BASE_MVC.LoggableMixin ).extend(
|
||||
//this.log( 'onSearchClear', this );
|
||||
this.searchFor = '';
|
||||
this.trigger( 'search:clear', this );
|
||||
this.renderItems();
|
||||
this.$( '> .controls .search-query' ).val( '' );
|
||||
this.renderItems();
|
||||
return this;
|
||||
},
|
||||
|
||||
|
||||
@@ -2,8 +2,8 @@
|
||||
* Model, view, and controller objects for Galaxy tools and tool panel.
|
||||
*/
|
||||
|
||||
define( ["libs/underscore", "viz/trackster/util", "mvc/data" ],
|
||||
function(_, util, data) {
|
||||
define( ["libs/underscore", "viz/trackster/util", "mvc/data", "mvc/tools/tools-form" ],
|
||||
function(_, util, data, ToolsForm) {
|
||||
|
||||
/**
|
||||
* Mixin for tracking model visibility.
|
||||
@@ -262,7 +262,7 @@ var Tool = Backbone.Model.extend({
|
||||
|
||||
// Run job and resolve run_deferred to tool outputs.
|
||||
$.when(ss_deferred.go()).then(function(result) {
|
||||
run_deferred.resolve(new data.DatasetCollection().reset(result));
|
||||
run_deferred.resolve(new data.DatasetCollection(result));
|
||||
});
|
||||
return run_deferred;
|
||||
}
|
||||
@@ -524,6 +524,18 @@ var ToolLinkView = BaseView.extend({
|
||||
});
|
||||
}
|
||||
|
||||
// regular tools
|
||||
if ( this.model.id !== 'upload1' && this.model.get( 'model_class' ) === 'Tool' ) {
|
||||
var self = this;
|
||||
$link.find('a').on('click', function(e) {
|
||||
e.preventDefault();
|
||||
var form = new ToolsForm.View( { id : self.model.id, version : self.model.get('version') } );
|
||||
form.deferred.execute(function() {
|
||||
Galaxy.app.display( form.$el );
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
// add element
|
||||
this.$el.append($link);
|
||||
return this;
|
||||
|
||||
@@ -9,23 +9,19 @@ define(['utils/utils', 'utils/deferred', 'mvc/ui/ui-misc', 'mvc/form/form-view',
|
||||
return Backbone.View.extend({
|
||||
// initialize
|
||||
initialize: function(options) {
|
||||
// check if the user is an admin
|
||||
var galaxy = parent.Galaxy;
|
||||
if (galaxy && galaxy.currUser) {
|
||||
this.is_admin = galaxy.currUser.get('is_admin');
|
||||
} else {
|
||||
this.is_admin = false;
|
||||
}
|
||||
this.options = Utils.merge(options, {});
|
||||
this.setElement('<div/>');
|
||||
|
||||
// create deferred processing queue handler
|
||||
// this handler reduces the number of requests to the api by filtering redundant requests
|
||||
this.deferred = new Deferred();
|
||||
|
||||
// set element
|
||||
this.setElement('<div/>');
|
||||
|
||||
// create form
|
||||
this._buildForm(options);
|
||||
if (options.inputs) {
|
||||
this._buildForm(options);
|
||||
} else {
|
||||
this._buildModel(options, true);
|
||||
}
|
||||
},
|
||||
|
||||
/** Build form */
|
||||
@@ -48,6 +44,9 @@ define(['utils/utils', 'utils/deferred', 'mvc/ui/ui-misc', 'mvc/form/form-view',
|
||||
}
|
||||
}, this.options);
|
||||
|
||||
// allow option customization
|
||||
this.options.customize && this.options.customize( this.options );
|
||||
|
||||
// create form
|
||||
this.form = new Form(this.options);
|
||||
|
||||
@@ -61,28 +60,21 @@ define(['utils/utils', 'utils/deferred', 'mvc/ui/ui-misc', 'mvc/form/form-view',
|
||||
|
||||
/** Builds a new model through api call and recreates the entire form
|
||||
*/
|
||||
_buildModel: function(options) {
|
||||
// link this
|
||||
_buildModel: function(options, hide_message) {
|
||||
var self = this;
|
||||
|
||||
// update current version
|
||||
this.options.id = options.id;
|
||||
this.options.version = options.version;
|
||||
|
||||
// construct url
|
||||
var build_url = galaxy_config.root + 'api/tools/' + options.id + '/build?';
|
||||
if (options.job_id) {
|
||||
build_url += 'job_id=' + options.job_id;
|
||||
// build request url
|
||||
var build_url = '';
|
||||
var build_data = {};
|
||||
if ( options.job_id ) {
|
||||
build_url = Galaxy.root + 'api/jobs/' + options.job_id + '/build_for_rerun';
|
||||
} else {
|
||||
if (options.dataset_id) {
|
||||
build_url += 'dataset_id=' + options.dataset_id;
|
||||
} else {
|
||||
build_url += 'tool_version=' + options.version + '&';
|
||||
var loc = top.location.href;
|
||||
var pos = loc.indexOf('?');
|
||||
if (loc.indexOf('tool_id=') != -1 && pos !== -1) {
|
||||
build_url += loc.slice(pos + 1);
|
||||
}
|
||||
build_url = Galaxy.root + 'api/tools/' + options.id + '/build';
|
||||
if ( Galaxy.params && Galaxy.params.tool_id == options.id ) {
|
||||
build_data = $.extend( {}, Galaxy.params );
|
||||
options.version && ( build_data[ 'tool_version' ] = options.version );
|
||||
}
|
||||
}
|
||||
|
||||
@@ -93,12 +85,13 @@ define(['utils/utils', 'utils/deferred', 'mvc/ui/ui-misc', 'mvc/form/form-view',
|
||||
Utils.request({
|
||||
type : 'GET',
|
||||
url : build_url,
|
||||
data : build_data,
|
||||
success : function(new_model) {
|
||||
// rebuild form
|
||||
self._buildForm(new_model['tool_model'] || new_model);
|
||||
|
||||
// show version message
|
||||
self.form.message.update({
|
||||
!hide_message && self.form.message.update({
|
||||
status : 'success',
|
||||
message : 'Now you are using \'' + self.options.name + '\' version ' + self.options.version + '.',
|
||||
persistent : false
|
||||
@@ -120,16 +113,25 @@ define(['utils/utils', 'utils/deferred', 'mvc/ui/ui-misc', 'mvc/form/form-view',
|
||||
console.debug(response);
|
||||
|
||||
// show error
|
||||
var error_message = response.error || 'Uncaught error.';
|
||||
self.form.modal.show({
|
||||
title : 'Tool cannot be executed',
|
||||
body : error_message,
|
||||
buttons : {
|
||||
'Close' : function() {
|
||||
self.form.modal.hide();
|
||||
var error_message = ( response && response.err_msg ) || 'Uncaught error.';
|
||||
if ( self.$el.is(':empty') ) {
|
||||
self.$el.prepend((new Ui.Message({
|
||||
message : error_message,
|
||||
status : 'danger',
|
||||
persistent : true,
|
||||
large : true
|
||||
})).$el);
|
||||
} else {
|
||||
Galaxy.modal.show({
|
||||
title : 'Tool request failed',
|
||||
body : error_message,
|
||||
buttons : {
|
||||
'Close' : function() {
|
||||
Galaxy.modal.hide();
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
}
|
||||
});
|
||||
},
|
||||
@@ -138,7 +140,7 @@ define(['utils/utils', 'utils/deferred', 'mvc/ui/ui-misc', 'mvc/form/form-view',
|
||||
*/
|
||||
_updateModel: function() {
|
||||
// model url for request
|
||||
var model_url = this.options.update_url || galaxy_config.root + 'api/tools/' + this.options.id + '/build';
|
||||
var model_url = this.options.update_url || Galaxy.root + 'api/tools/' + this.options.id + '/build';
|
||||
|
||||
// link this
|
||||
var self = this;
|
||||
@@ -269,19 +271,19 @@ define(['utils/utils', 'utils/deferred', 'mvc/ui/ui-misc', 'mvc/form/form-view',
|
||||
title : 'Share',
|
||||
tooltip : 'Share this tool',
|
||||
onclick : function() {
|
||||
prompt('Copy to clipboard: Ctrl+C, Enter', window.location.origin + galaxy_config.root + 'root?tool_id=' + options.id);
|
||||
prompt('Copy to clipboard: Ctrl+C, Enter', window.location.origin + Galaxy.root + 'root?tool_id=' + options.id);
|
||||
}
|
||||
});
|
||||
|
||||
// add admin operations
|
||||
if (this.is_admin) {
|
||||
if (Galaxy.user && Galaxy.user.get('is_admin')) {
|
||||
// create download button
|
||||
menu_button.addMenu({
|
||||
icon : 'fa-download',
|
||||
title : 'Download',
|
||||
tooltip : 'Download this tool',
|
||||
onclick : function() {
|
||||
window.location.href = galaxy_config.root + 'api/tools/' + options.id + '/download';
|
||||
window.location.href = Galaxy.root + 'api/tools/' + options.id + '/download';
|
||||
}
|
||||
});
|
||||
}
|
||||
@@ -351,4 +353,4 @@ define(['utils/utils', 'utils/deferred', 'mvc/ui/ui-misc', 'mvc/form/form-view',
|
||||
}
|
||||
}
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -121,6 +121,8 @@ define(['utils/utils', 'mvc/tools/tools-form-base'],
|
||||
_makeSection: function(output_id, datatypes){
|
||||
// format datatypes
|
||||
var extensions = [];
|
||||
var input_terminal_names = [];
|
||||
|
||||
for (key in datatypes) {
|
||||
extensions.push({
|
||||
0 : datatypes[key],
|
||||
@@ -128,6 +130,10 @@ define(['utils/utils', 'mvc/tools/tools-form-base'],
|
||||
});
|
||||
}
|
||||
|
||||
for (key in this.node.input_terminals){
|
||||
input_terminal_names.push(this.node.input_terminals[key].name);
|
||||
}
|
||||
|
||||
// sort extensions
|
||||
extensions.sort(function(a, b) {
|
||||
return a.label > b.label ? 1 : a.label < b.label ? -1 : 0;
|
||||
@@ -154,15 +160,15 @@ define(['utils/utils', 'mvc/tools/tools-form-base'],
|
||||
flat : true,
|
||||
inputs : [{
|
||||
action : 'RenameDatasetAction',
|
||||
argument : 'newname',
|
||||
pja_arg : 'newname',
|
||||
label : 'Rename dataset',
|
||||
type : 'text',
|
||||
value : '',
|
||||
ignore : '',
|
||||
help : 'This action will rename the result dataset. Click <a href="https://wiki.galaxyproject.org/Learn/AdvancedWorkflow/Variables">here</a> for more information.'
|
||||
help : 'This action will rename the output dataset. Click <a href="https://wiki.galaxyproject.org/Learn/AdvancedWorkflow/Variables">here</a> for more information. Valid inputs are: <strong>' + input_terminal_names.join(", ") + '</strong>.'
|
||||
},{
|
||||
action : 'ChangeDatatypeAction',
|
||||
argument : 'newtype',
|
||||
pja_arg : 'newtype',
|
||||
label : 'Change datatype',
|
||||
type : 'select',
|
||||
ignore : '__empty__',
|
||||
@@ -171,7 +177,7 @@ define(['utils/utils', 'mvc/tools/tools-form-base'],
|
||||
help : 'This action will change the datatype of the output to the indicated value.'
|
||||
},{
|
||||
action : 'TagDatasetAction',
|
||||
argument : 'tags',
|
||||
pja_arg : 'tags',
|
||||
label : 'Tags',
|
||||
type : 'text',
|
||||
value : '',
|
||||
@@ -183,35 +189,35 @@ define(['utils/utils', 'mvc/tools/tools-form-base'],
|
||||
flat : true,
|
||||
inputs : [{
|
||||
action : 'ColumnSetAction',
|
||||
argument : 'chromCol',
|
||||
pja_arg : 'chromCol',
|
||||
label : 'Chrom column',
|
||||
type : 'integer',
|
||||
value : '',
|
||||
ignore : ''
|
||||
},{
|
||||
action : 'ColumnSetAction',
|
||||
argument : 'startCol',
|
||||
pja_arg : 'startCol',
|
||||
label : 'Start column',
|
||||
type : 'integer',
|
||||
value : '',
|
||||
ignore : ''
|
||||
},{
|
||||
action : 'ColumnSetAction',
|
||||
argument : 'endCol',
|
||||
pja_arg : 'endCol',
|
||||
label : 'End column',
|
||||
type : 'integer',
|
||||
value : '',
|
||||
ignore : ''
|
||||
},{
|
||||
action : 'ColumnSetAction',
|
||||
argument : 'strandCol',
|
||||
pja_arg : 'strandCol',
|
||||
label : 'Strand column',
|
||||
type : 'integer',
|
||||
value : '',
|
||||
ignore : ''
|
||||
},{
|
||||
action : 'ColumnSetAction',
|
||||
argument : 'nameCol',
|
||||
pja_arg : 'nameCol',
|
||||
label : 'Name column',
|
||||
type : 'integer',
|
||||
value : '',
|
||||
@@ -231,8 +237,8 @@ define(['utils/utils', 'mvc/tools/tools-form-base'],
|
||||
if (input.action) {
|
||||
// construct identifier as expected by backend
|
||||
input.name = 'pja__' + output_id + '__' + input.action;
|
||||
if (input.argument) {
|
||||
input.name += '__' + input.argument;
|
||||
if (input.pja_arg) {
|
||||
input.name += '__' + input.pja_arg;
|
||||
}
|
||||
|
||||
// modify names of payload arguments
|
||||
@@ -253,8 +259,8 @@ define(['utils/utils', 'mvc/tools/tools-form-base'],
|
||||
}
|
||||
|
||||
// update input field value
|
||||
if (input.argument) {
|
||||
input.value = d.action_arguments && d.action_arguments[input.argument] || input.value;
|
||||
if (input.pja_arg) {
|
||||
input.value = d.action_arguments && d.action_arguments[input.pja_arg] || input.value;
|
||||
} else {
|
||||
input.value = 'true';
|
||||
}
|
||||
|
||||
@@ -2,49 +2,46 @@
|
||||
This is the regular tool form.
|
||||
*/
|
||||
define(['utils/utils', 'mvc/ui/ui-misc', 'mvc/tools/tools-form-base', 'mvc/tools/tools-jobs'],
|
||||
function(Utils, Ui, ToolFormBase, ToolJobs) {
|
||||
|
||||
// create form view
|
||||
function( Utils, Ui, ToolFormBase, ToolJobs ) {
|
||||
var View = ToolFormBase.extend({
|
||||
// initialize
|
||||
initialize: function(options) {
|
||||
// execute button
|
||||
initialize: function( options ) {
|
||||
var self = this;
|
||||
var execute = new Ui.Button({
|
||||
icon : 'fa-check',
|
||||
tooltip : 'Execute: ' + options.name,
|
||||
title : 'Execute',
|
||||
cls : 'btn btn-primary',
|
||||
floating : 'clear',
|
||||
onclick : function() {
|
||||
execute.wait();
|
||||
self.form.portlet.disable();
|
||||
ToolJobs.submit(self.form, options, function() {
|
||||
execute.unwait();
|
||||
self.form.portlet.enable();
|
||||
});
|
||||
ToolFormBase.prototype.initialize.call( this, Utils.merge({
|
||||
customize : function( options ) {
|
||||
// build execute button
|
||||
options.buttons = {
|
||||
execute : execute_btn = new Ui.Button({
|
||||
icon : 'fa-check',
|
||||
tooltip : 'Execute: ' + options.name + ' (' + options.version + ')',
|
||||
title : 'Execute',
|
||||
cls : 'btn btn-primary',
|
||||
floating : 'clear',
|
||||
onclick : function() {
|
||||
execute_btn.wait();
|
||||
self.form.portlet.disable();
|
||||
ToolJobs.submit( self.form, options, function() {
|
||||
execute_btn.unwait();
|
||||
self.form.portlet.enable();
|
||||
} );
|
||||
}
|
||||
})
|
||||
};
|
||||
|
||||
// remap feature
|
||||
if ( options.job_id && options.job_remap ) {
|
||||
options.inputs[ 'rerun_remap_job_id' ] = {
|
||||
label : 'Resume dependencies from this job',
|
||||
name : 'rerun_remap_job_id',
|
||||
type : 'select',
|
||||
display : 'radio',
|
||||
ignore : '__ignore__',
|
||||
value : '__ignore__',
|
||||
options : [ [ 'Yes', options.job_id ], [ 'No', '__ignore__' ] ],
|
||||
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.'
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
// add remap button
|
||||
if (options.job_id && options.job_remap) {
|
||||
options.inputs['rerun_remap_job_id'] = {
|
||||
label : 'Resume dependencies from this job',
|
||||
name : 'rerun_remap_job_id',
|
||||
type : 'select',
|
||||
display : 'radio',
|
||||
ignore : '__ignore__',
|
||||
value : '__ignore__',
|
||||
options : [['Yes', options.job_id], ['No', '__ignore__']],
|
||||
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.'
|
||||
}
|
||||
}
|
||||
// create options dictionary
|
||||
options = Utils.merge(options, {
|
||||
buttons : {
|
||||
execute : execute
|
||||
}
|
||||
});
|
||||
ToolFormBase.prototype.initialize.call(this, options);
|
||||
}, options ) );
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
@@ -34,23 +34,23 @@ return {
|
||||
data : job_def,
|
||||
success : function(response) {
|
||||
callback && callback();
|
||||
form.reciept(ToolTemplate.success(response));
|
||||
form.$el.replaceWith( ToolTemplate.success( response ) );
|
||||
// begin checking the history for updates
|
||||
self._refreshHdas();
|
||||
},
|
||||
error : function(response, response_full) {
|
||||
error : function(response) {
|
||||
callback && callback();
|
||||
if (response && response.message && response.message.data) {
|
||||
var error_messages = form.data.matchResponse(response.message.data);
|
||||
console.debug(response);
|
||||
if (response && response.err_data) {
|
||||
var error_messages = form.data.matchResponse(response.err_data);
|
||||
for (var input_id in error_messages) {
|
||||
form.highlight(input_id, error_messages[input_id]);
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
// show error message with details
|
||||
console.debug(response);
|
||||
response && form.modal.show({
|
||||
form.modal.show({
|
||||
title : 'Job submission failed',
|
||||
body : ToolTemplate.error(job_def),
|
||||
body : ( response && response.err_msg ) || ToolTemplate.error( job_def ),
|
||||
buttons : {
|
||||
'Close' : function() {
|
||||
form.modal.hide();
|
||||
@@ -135,11 +135,11 @@ return {
|
||||
|
||||
/** Refreshes the history panel
|
||||
*/
|
||||
_refreshHdas: function() {
|
||||
_refreshHdas: function(detailIds, options) {
|
||||
if (parent.Galaxy && parent.Galaxy.currHistoryPanel) {
|
||||
parent.Galaxy.currHistoryPanel.refreshContents();
|
||||
parent.Galaxy.currHistoryPanel.refreshContents(detailIds, options);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
});
|
||||
});
|
||||
|
||||
@@ -1,74 +0,0 @@
|
||||
// dependencies
|
||||
define(['utils/utils'], function(Utils) {
|
||||
|
||||
// plugin
|
||||
return Backbone.View.extend({
|
||||
|
||||
// default options
|
||||
optionsDefault: {
|
||||
icons : ['fa fa-square-o', 'fa fa-minus-square-o', 'fa fa-check-square-o'],
|
||||
value : 0,
|
||||
title : 'Select/Unselect all'
|
||||
},
|
||||
|
||||
// initialize
|
||||
initialize: function(options) {
|
||||
// configure options
|
||||
this.options = Utils.merge(options, this.optionsDefault);
|
||||
|
||||
// create new element
|
||||
this.setElement(this._template());
|
||||
this.$title = this.$('.title');
|
||||
this.$icon = this.$('.icon');
|
||||
|
||||
// set initial value
|
||||
this.value(this.options.value);
|
||||
|
||||
// set title
|
||||
this.$title.html(this.options.title);
|
||||
|
||||
// add event handler
|
||||
var self = this;
|
||||
this.$el.on('click', function() {
|
||||
self.current = (self.current === 0 && 2) || 0;
|
||||
self.value(self.current);
|
||||
self.options.onclick && self.options.onclick();
|
||||
});
|
||||
},
|
||||
|
||||
/* Sets a new value and/or returns the current value.
|
||||
* @param{Integer} new_val - Set a new value 0=unchecked, 1=partial and 2=checked.
|
||||
* OR:
|
||||
* @param{Integer} new_val - Number of selected options.
|
||||
* @param{Integer} total - Total number of available options.
|
||||
*/
|
||||
value: function (new_val, total) {
|
||||
if (new_val !== undefined) {
|
||||
if (total) {
|
||||
if (new_val !== 0) {
|
||||
if (new_val !== total) {
|
||||
new_val = 1;
|
||||
} else {
|
||||
new_val = 2;
|
||||
}
|
||||
}
|
||||
}
|
||||
this.current = new_val;
|
||||
this.$icon.removeClass()
|
||||
.addClass('icon')
|
||||
.addClass(this.options.icons[new_val]);
|
||||
this.options.onchange && this.options.onchange(new_val);
|
||||
}
|
||||
return this.current;
|
||||
},
|
||||
|
||||
/** Template containing the check button and the title
|
||||
*/
|
||||
_template: function() {
|
||||
return '<div class="ui-button-check" >' +
|
||||
'<span class="icon"/>' +
|
||||
'<span class="title"/>' +
|
||||
'</div>';
|
||||
}
|
||||
});
|
||||
});
|
||||
@@ -1,172 +0,0 @@
|
||||
// dependencies
|
||||
define(['utils/utils'], function(Utils) {
|
||||
|
||||
/**
|
||||
* This class creates a button with dropdown menu. It extends the functionality of the Ui.ButtonIcon class.
|
||||
*/
|
||||
return Backbone.View.extend({
|
||||
// main options
|
||||
optionsDefault: {
|
||||
// same as Ui.ButtonIcon
|
||||
id : '',
|
||||
title : '',
|
||||
floating : 'right',
|
||||
icon : null,
|
||||
onclick : null,
|
||||
cls : 'ui-button-icon ui-button-menu',
|
||||
tooltip : '',
|
||||
|
||||
// additional options
|
||||
target : '',
|
||||
href : '',
|
||||
onunload : null,
|
||||
visible : true,
|
||||
tag : ''
|
||||
},
|
||||
|
||||
// optional sub menu
|
||||
$menu: null,
|
||||
|
||||
// initialize
|
||||
initialize: function (options) {
|
||||
// get options
|
||||
this.options = Utils.merge(options, this.optionsDefault);
|
||||
|
||||
// add template for tab
|
||||
this.setElement($(this._template(this.options)));
|
||||
|
||||
// find root
|
||||
var $root = $(this.el).find('.root');
|
||||
|
||||
// link head
|
||||
var self = this;
|
||||
$root.on('click', function(e) {
|
||||
// hide all tooltips
|
||||
$('.tooltip').hide();
|
||||
|
||||
// prevent default
|
||||
e.preventDefault();
|
||||
|
||||
// add click event
|
||||
if(self.options.onclick) {
|
||||
self.options.onclick();
|
||||
}
|
||||
});
|
||||
|
||||
// visiblity
|
||||
if (!this.options.visible)
|
||||
this.hide();
|
||||
|
||||
// add tooltip
|
||||
$root.tooltip({title: options.tooltip, placement: 'bottom'});
|
||||
},
|
||||
|
||||
// show
|
||||
show: function() {
|
||||
$(this.el).show();
|
||||
},
|
||||
|
||||
// hide
|
||||
hide: function() {
|
||||
$(this.el).hide();
|
||||
},
|
||||
|
||||
// add menu item
|
||||
addMenu: function (options) {
|
||||
// menu option defaults
|
||||
var menuOptions = {
|
||||
title : '',
|
||||
target : '',
|
||||
href : '',
|
||||
onclick : null,
|
||||
divider : false,
|
||||
icon : null,
|
||||
cls : 'button-menu btn-group'
|
||||
}
|
||||
|
||||
// get options
|
||||
menuOptions = Utils.merge(options, menuOptions);
|
||||
|
||||
// check if submenu element is available
|
||||
if (!this.$menu) {
|
||||
// insert submenu element into root
|
||||
$(this.el).append(this._templateMenu());
|
||||
|
||||
// update element link
|
||||
this.$menu = $(this.el).find('.menu');
|
||||
}
|
||||
|
||||
// create
|
||||
var $item = $(this._templateMenuItem(menuOptions));
|
||||
|
||||
// add events
|
||||
$item.on('click', function(e) {
|
||||
// prevent default
|
||||
e.preventDefault();
|
||||
|
||||
// add click event
|
||||
if(menuOptions.onclick) {
|
||||
menuOptions.onclick();
|
||||
}
|
||||
});
|
||||
|
||||
// append menu
|
||||
this.$menu.append($item);
|
||||
|
||||
// append divider
|
||||
if (menuOptions.divider)
|
||||
this.$menu.append($(this._templateDivider()));
|
||||
},
|
||||
|
||||
// fill template header
|
||||
_templateMenuItem: function (options) {
|
||||
var tmpl = '<li>' +
|
||||
'<a class="dropdown-item" href="' + options.href + '" target="' + options.target + '">';
|
||||
|
||||
if (options.icon)
|
||||
tmpl += '<i class="fa ' + options.icon + '"></i>';
|
||||
|
||||
tmpl += ' ' + options.title +
|
||||
'</a>' +
|
||||
'</li>';
|
||||
return tmpl;
|
||||
},
|
||||
|
||||
// fill template header
|
||||
_templateMenu: function () {
|
||||
return '<ul class="menu dropdown-menu pull-right" role="menu"></ul>';
|
||||
},
|
||||
|
||||
_templateDivider: function() {
|
||||
return '<li class="divider"></li>';
|
||||
},
|
||||
|
||||
// element
|
||||
_template: function(options) {
|
||||
// TODO: width/margin should be set in css
|
||||
var width = '';
|
||||
var margin = '';
|
||||
if (options.title) {
|
||||
width = 'width: auto;';
|
||||
} else {
|
||||
margin = 'margin: 0px;';
|
||||
}
|
||||
|
||||
// create base string
|
||||
var str = '<div id="' + options.id + '" style="float: ' + options.floating + '; ' + width + '" class="dropdown ' + options.cls + '">' +
|
||||
'<div class="root button dropdown-toggle" data-toggle="dropdown" style="' + margin + '">' +
|
||||
'<i class="icon fa ' + options.icon + '"/>';
|
||||
|
||||
// title
|
||||
if (options.title) {
|
||||
str += ' <span class="title">' + options.title + '</span>';
|
||||
}
|
||||
|
||||
// finalize
|
||||
str += '</div>' +
|
||||
'</div>';
|
||||
return str;
|
||||
}
|
||||
});
|
||||
|
||||
});
|
||||
@@ -0,0 +1,387 @@
|
||||
/** This class contains all button views.
|
||||
*/
|
||||
define(['utils/utils'], function( Utils ) {
|
||||
/** This renders the default button which is used e.g. at the bottom of the upload modal.
|
||||
*/
|
||||
var ButtonBase = Backbone.View.extend({
|
||||
initialize: function( options ) {
|
||||
this.options = Utils.merge( options, {
|
||||
id : Utils.uid(),
|
||||
title : '',
|
||||
floating : 'right',
|
||||
icon : '',
|
||||
cls : 'ui-button btn btn-default',
|
||||
cls_wait : 'btn btn-info'
|
||||
} );
|
||||
this.setElement( this._template( this.options ) );
|
||||
var self = this;
|
||||
$( this.el ).on( 'click' , function() {
|
||||
$( '.tooltip' ).hide();
|
||||
if ( options.onclick && !self.disabled ) {
|
||||
options.onclick();
|
||||
}
|
||||
} );
|
||||
$( this.el ).tooltip( { title: options.tooltip, placement: 'bottom' } );
|
||||
},
|
||||
|
||||
// disable
|
||||
disable: function() {
|
||||
this.$el.addClass( 'disabled' );
|
||||
this.disabled = true;
|
||||
},
|
||||
|
||||
// enable
|
||||
enable: function() {
|
||||
this.$el.removeClass( 'disabled' );
|
||||
this.disabled = false;
|
||||
},
|
||||
|
||||
// show spinner
|
||||
wait: function() {
|
||||
this.$el.removeClass( this.options.cls ).addClass( this.options.cls_wait ).prop( 'disabled', true );
|
||||
this.$( '.icon' ).removeClass( this.options.icon ).addClass( 'fa-spinner fa-spin' );
|
||||
this.$( '.title' ).html( 'Sending...' );
|
||||
},
|
||||
|
||||
// hide spinner
|
||||
unwait: function() {
|
||||
this.$el.removeClass( this.options.cls_wait ).addClass( this.options.cls ).prop( 'disabled', false );
|
||||
this.$( '.icon' ).removeClass( 'fa-spinner fa-spin' ).addClass( this.options.icon );
|
||||
this.$( '.title' ).html( this.options.title );
|
||||
},
|
||||
|
||||
// template
|
||||
_template: function( options ) {
|
||||
var str = '<button id="' + options.id + '" type="submit" style="float: ' + options.floating + ';" type="button" class="' + options.cls + '">';
|
||||
if (options.icon) {
|
||||
str += '<i class="icon fa ' + options.icon + '"/> ';
|
||||
}
|
||||
str += '<span class="title">' + options.title + '</span>' +
|
||||
'</button>';
|
||||
return str;
|
||||
}
|
||||
});
|
||||
|
||||
/** This button allows the right-click/open-in-new-tab feature, its used e.g. for panel buttons.
|
||||
*/
|
||||
var ButtonLink = ButtonBase.extend({
|
||||
initialize: function( options ) {
|
||||
ButtonBase.prototype.initialize.call( this, options );
|
||||
},
|
||||
_template: function( options ) {
|
||||
return '<a id="' + options.id + '" class="' + options.cls + '" href="' + ( options.href || 'javascript:void(0)' ) + '"' +
|
||||
' title="' + options.title + '" target="' + ( options.target || '_top' ) + '">' + '<span class="' + options.icon + '"/>' +
|
||||
'</a>';
|
||||
}
|
||||
});
|
||||
|
||||
/** The check button is used in the tool form and allows to distinguish between multiple states e.g. all, partially and nothing selected.
|
||||
*/
|
||||
var ButtonCheck = Backbone.View.extend({
|
||||
initialize: function( options ) {
|
||||
// configure options
|
||||
this.options = Utils.merge(options, {
|
||||
title : 'Select/Unselect all',
|
||||
icons : ['fa fa-square-o', 'fa fa-minus-square-o', 'fa fa-check-square-o'],
|
||||
value : 0
|
||||
});
|
||||
|
||||
// create new element
|
||||
this.setElement( this._template() );
|
||||
this.$title = this.$( '.title' );
|
||||
this.$icon = this.$( '.icon' );
|
||||
|
||||
// set initial value
|
||||
this.value( this.options.value );
|
||||
|
||||
// set title
|
||||
this.$title.html( this.options.title );
|
||||
|
||||
// add event handler
|
||||
var self = this;
|
||||
this.$el.on('click', function() {
|
||||
self.current = ( self.current === 0 && 2 ) || 0;
|
||||
self.value( self.current );
|
||||
self.options.onclick && self.options.onclick();
|
||||
});
|
||||
},
|
||||
|
||||
/* Sets a new value and/or returns the current value.
|
||||
* @param{Integer} new_val - Set a new value 0=unchecked, 1=partial and 2=checked.
|
||||
* OR:
|
||||
* @param{Integer} new_val - Number of selected options.
|
||||
* @param{Integer} total - Total number of available options.
|
||||
*/
|
||||
value: function ( new_val, total ) {
|
||||
if ( new_val !== undefined ) {
|
||||
if ( total ) {
|
||||
if ( new_val !== 0 ) {
|
||||
new_val = ( new_val !== total ) && 1 || 2;
|
||||
}
|
||||
}
|
||||
this.current = new_val;
|
||||
this.$icon.removeClass()
|
||||
.addClass( 'icon' )
|
||||
.addClass( this.options.icons[ new_val ] );
|
||||
this.options.onchange && this.options.onchange( new_val );
|
||||
}
|
||||
return this.current;
|
||||
},
|
||||
|
||||
/** Template containing the check button and the title
|
||||
*/
|
||||
_template: function() {
|
||||
return '<div class="ui-button-check" >' +
|
||||
'<span class="icon"/>' +
|
||||
'<span class="title"/>' +
|
||||
'</div>';
|
||||
}
|
||||
});
|
||||
|
||||
/** This renders a differently styled, more compact button version.
|
||||
TODO: Consolidate with icon-button.js and/or button-default.js.
|
||||
*/
|
||||
var ButtonIcon = Backbone.View.extend({
|
||||
initialize : function( options ) {
|
||||
// get options
|
||||
this.options = Utils.merge( options, {
|
||||
id : Utils.uid(),
|
||||
title : '',
|
||||
floating : 'right',
|
||||
cls : 'ui-button-icon',
|
||||
icon : '',
|
||||
tooltip : '',
|
||||
onclick : null
|
||||
});
|
||||
|
||||
// create new element
|
||||
this.setElement( this._template( this.options ) );
|
||||
|
||||
// link button element
|
||||
this.$button = this.$el.find( '.button' );
|
||||
|
||||
// add event
|
||||
var self = this;
|
||||
$(this.el).on('click', function() {
|
||||
// hide all tooltips
|
||||
$( '.tooltip' ).hide();
|
||||
|
||||
// execute onclick callback
|
||||
if ( options.onclick && !self.disabled ) {
|
||||
options.onclick();
|
||||
}
|
||||
});
|
||||
|
||||
// add tooltip
|
||||
this.$button.tooltip( { title: options.tooltip, placement: 'bottom' } );
|
||||
},
|
||||
|
||||
// disable
|
||||
disable: function() {
|
||||
this.$button.addClass( 'disabled' );
|
||||
this.disabled = true;
|
||||
},
|
||||
|
||||
// enable
|
||||
enable: function() {
|
||||
this.$button.removeClass( 'disabled' );
|
||||
this.disabled = false;
|
||||
},
|
||||
|
||||
// change icon
|
||||
setIcon: function(icon_cls) {
|
||||
this.$('i').removeClass( this.options.icon ).addClass( icon_cls );
|
||||
this.options.icon = icon_cls;
|
||||
},
|
||||
|
||||
// template
|
||||
_template: function( options ) {
|
||||
// width
|
||||
var width = '';
|
||||
if ( options.title ) {
|
||||
width = 'width: auto;';
|
||||
}
|
||||
|
||||
// string
|
||||
var str = '<div id="' + options.id + '" style="float: ' + options.floating + '; ' + width + '" class="' + options.cls + '">' +
|
||||
'<div class="button">';
|
||||
if (options.title) {
|
||||
str += '<i class="icon fa ' + options.icon + '"/> ' +
|
||||
'<span class="title">' + options.title + '</span>';
|
||||
} else {
|
||||
str += '<i class="icon fa ' + options.icon + '"/>';
|
||||
}
|
||||
str += '</div>' +
|
||||
'</div>';
|
||||
return str;
|
||||
}
|
||||
});
|
||||
|
||||
/** This class creates a button with dropdown menu. It extends the functionality of the Ui.ButtonIcon class.
|
||||
TODO: Consolidate class, use common base class
|
||||
*/
|
||||
var ButtonMenu = Backbone.View.extend({
|
||||
// optional sub menu
|
||||
$menu: null,
|
||||
|
||||
// initialize
|
||||
initialize: function ( options ) {
|
||||
// get options
|
||||
this.options = Utils.merge( options, {
|
||||
// same as Ui.ButtonIcon
|
||||
id : '',
|
||||
title : '',
|
||||
floating : 'right',
|
||||
pull : 'right',
|
||||
icon : null,
|
||||
onclick : null,
|
||||
cls : 'ui-button-icon ui-button-menu',
|
||||
tooltip : '',
|
||||
|
||||
// additional options
|
||||
target : '',
|
||||
href : '',
|
||||
onunload : null,
|
||||
visible : true,
|
||||
tag : ''
|
||||
} );
|
||||
|
||||
// add template for tab
|
||||
this.setElement( $( this._template( this.options ) ) );
|
||||
|
||||
// find root
|
||||
var $root = $( this.el ).find( '.root' );
|
||||
|
||||
// link head
|
||||
var self = this;
|
||||
$root.on( 'click', function( e ) {
|
||||
// hide all tooltips
|
||||
$( '.tooltip' ).hide();
|
||||
|
||||
// prevent default
|
||||
e.preventDefault();
|
||||
|
||||
// add click event
|
||||
if( self.options.onclick ) {
|
||||
self.options.onclick();
|
||||
}
|
||||
});
|
||||
|
||||
// visiblity
|
||||
if ( !this.options.visible )
|
||||
this.hide();
|
||||
|
||||
// add tooltip
|
||||
$root.tooltip( { title: options.tooltip, placement: 'bottom' } );
|
||||
},
|
||||
|
||||
// show
|
||||
show: function() {
|
||||
$( this.el ).show();
|
||||
},
|
||||
|
||||
// hide
|
||||
hide: function() {
|
||||
$( this.el ).hide();
|
||||
},
|
||||
|
||||
// add menu item
|
||||
addMenu: function ( options ) {
|
||||
// menu option defaults
|
||||
var menuOptions = {
|
||||
title : '',
|
||||
target : '',
|
||||
href : '',
|
||||
onclick : null,
|
||||
divider : false,
|
||||
icon : null,
|
||||
cls : 'button-menu btn-group'
|
||||
}
|
||||
|
||||
// get options
|
||||
menuOptions = Utils.merge( options, menuOptions );
|
||||
|
||||
// check if submenu element is available
|
||||
if ( !this.$menu ) {
|
||||
// insert submenu element into root
|
||||
$( this.el ).append( this._templateMenu() );
|
||||
|
||||
// update element link
|
||||
this.$menu = $( this.el ).find( '.menu' );
|
||||
}
|
||||
|
||||
// create
|
||||
var $item = $( this._templateMenuItem( menuOptions ) );
|
||||
|
||||
// add events
|
||||
$item.on( 'click', function( e ) {
|
||||
// prevent default
|
||||
e.preventDefault();
|
||||
|
||||
// add click event
|
||||
if( menuOptions.onclick ) {
|
||||
menuOptions.onclick ();
|
||||
}
|
||||
});
|
||||
|
||||
// append menu
|
||||
this.$menu.append( $item );
|
||||
|
||||
// append divider
|
||||
if ( menuOptions.divider ) {
|
||||
this.$menu.append( $( this._templateDivider() ) );
|
||||
}
|
||||
},
|
||||
|
||||
// fill template header
|
||||
_templateMenuItem: function ( options ) {
|
||||
var tmpl = '<li>' +
|
||||
'<a class="dropdown-item" href="' + options.href + '" target="' + options.target + '">';
|
||||
if ( options.icon ) {
|
||||
tmpl += '<i class="fa ' + options.icon + '"/>';
|
||||
}
|
||||
tmpl += ' ' + options.title +
|
||||
'</a>' +
|
||||
'</li>';
|
||||
return tmpl;
|
||||
},
|
||||
|
||||
// fill template header
|
||||
_templateMenu: function () {
|
||||
return '<ul class="menu dropdown-menu pull-' + this.options.pull + '" role="menu"/>';
|
||||
},
|
||||
|
||||
_templateDivider: function() {
|
||||
return '<li class="divider"/>';
|
||||
},
|
||||
|
||||
// element
|
||||
_template: function(options) {
|
||||
// TODO: width/margin should be set in css
|
||||
var width = '';
|
||||
var margin = '';
|
||||
if ( options.title ) {
|
||||
width = 'width: auto;';
|
||||
} else {
|
||||
margin = 'margin: 0px;';
|
||||
}
|
||||
var str = '<div id="' + options.id + '" style="float: ' + options.floating + '; ' + width + '" class="dropdown ' + options.cls + '">' +
|
||||
'<div class="root button dropdown-toggle" data-toggle="dropdown" style="' + margin + '">' +
|
||||
'<i class="icon fa ' + options.icon + '"/>';
|
||||
if ( options.title ) {
|
||||
str += ' <span class="title">' + options.title + '</span>';
|
||||
}
|
||||
str += '</div>' +
|
||||
'</div>';
|
||||
return str;
|
||||
}
|
||||
});
|
||||
|
||||
return {
|
||||
ButtonDefault : ButtonBase,
|
||||
ButtonLink : ButtonLink,
|
||||
ButtonIcon : ButtonIcon,
|
||||
ButtonCheck : ButtonCheck,
|
||||
ButtonMenu : ButtonMenu
|
||||
}
|
||||
});
|
||||
@@ -1,199 +1,187 @@
|
||||
// dependencies
|
||||
define(['utils/utils', 'mvc/ui/ui-misc'], function(Utils, Ui) {
|
||||
/** Renders the color picker used e.g. in the tool form **/
|
||||
define(['utils/utils'], function( Utils ) {
|
||||
return Backbone.View.extend({
|
||||
colors: {
|
||||
standard: ['c00000','ff0000','ffc000','ffff00','92d050','00b050','00b0f0','0070c0','002060','7030a0'],
|
||||
base : ['ffffff','000000','eeece1','1f497d','4f81bd','c0504d','9bbb59','8064a2','4bacc6','f79646'],
|
||||
theme :[['f2f2f2','7f7f7f','ddd9c3','c6d9f0','dbe5f1','f2dcdb','ebf1dd','e5e0ec','dbeef3','fdeada'],
|
||||
['d8d8d8','595959','c4bd97','8db3e2','b8cce4','e5b9b7','d7e3bc','ccc1d9','b7dde8','fbd5b5'],
|
||||
['bfbfbf','3f3f3f','938953','548dd4','95b3d7','d99694','c3d69b','b2a2c7','92cddc','fac08f'],
|
||||
['a5a5a5','262626','494429','17365d','366092','953734','76923c','5f497a','31859b','e36c09'],
|
||||
['7f7f7e','0c0c0c','1d1b10','0f243e','244061','632423','4f6128','3f3151','205867','974806']]
|
||||
},
|
||||
initialize : function( options ) {
|
||||
this.options = Utils.merge( options, {} );
|
||||
this.setElement( this._template() );
|
||||
|
||||
// plugin
|
||||
return Backbone.View.extend({
|
||||
colors: {
|
||||
standard: ['c00000','ff0000','ffc000','ffff00','92d050','00b050','00b0f0','0070c0','002060','7030a0'],
|
||||
base : ['ffffff','000000','eeece1','1f497d','4f81bd','c0504d','9bbb59','8064a2','4bacc6','f79646'],
|
||||
theme :[['f2f2f2','7f7f7f','ddd9c3','c6d9f0','dbe5f1','f2dcdb','ebf1dd','e5e0ec','dbeef3','fdeada'],
|
||||
['d8d8d8','595959','c4bd97','8db3e2','b8cce4','e5b9b7','d7e3bc','ccc1d9','b7dde8','fbd5b5'],
|
||||
['bfbfbf','3f3f3f','938953','548dd4','95b3d7','d99694','c3d69b','b2a2c7','92cddc','fac08f'],
|
||||
['a5a5a5','262626','494429','17365d','366092','953734','76923c','5f497a','31859b','e36c09'],
|
||||
['7f7f7e','0c0c0c','1d1b10','0f243e','244061','632423','4f6128','3f3151','205867','974806']]
|
||||
},
|
||||
// link components
|
||||
this.$panel = this.$( '.ui-color-picker-panel' );
|
||||
this.$view = this.$( '.ui-color-picker-view' );
|
||||
this.$value = this.$( '.ui-color-picker-value' );
|
||||
this.$header = this.$( '.ui-color-picker-header' );
|
||||
|
||||
// default options
|
||||
optionsDefault: {
|
||||
},
|
||||
// build panel
|
||||
this._build();
|
||||
|
||||
// initialize
|
||||
initialize : function(options) {
|
||||
// configure options
|
||||
this.options = Utils.merge(options, this.optionsDefault);
|
||||
// hide panel on start up
|
||||
this.visible = false;
|
||||
|
||||
// create new element
|
||||
this.setElement(this._template());
|
||||
this.$panel = this.$('.ui-color-picker-panel');
|
||||
this.$view = this.$('.ui-color-picker-view');
|
||||
this.$value = this.$('.ui-color-picker-value');
|
||||
this.$header = this.$('.ui-color-picker-header');
|
||||
// set initial value
|
||||
this.value( this.options.value );
|
||||
|
||||
// build panel
|
||||
this._build();
|
||||
// link boxes
|
||||
this.$boxes = this.$( '.ui-color-picker-box' );
|
||||
|
||||
// hide panel on start up
|
||||
this.visible = false;
|
||||
// add event handler
|
||||
var self = this;
|
||||
this.$boxes.on( 'click', function() {
|
||||
self.value( $( this ).css( 'background-color' ) );
|
||||
self.$header.trigger( 'click' );
|
||||
} );
|
||||
this.$header.on( 'click', function() {
|
||||
self.visible = !self.visible;
|
||||
if ( self.visible ) {
|
||||
self.$view.fadeIn( 'fast' );
|
||||
} else {
|
||||
self.$view.fadeOut( 'fast' );
|
||||
}
|
||||
} );
|
||||
},
|
||||
|
||||
// set initial value
|
||||
this.value(this.options.value);
|
||||
|
||||
// link boxes
|
||||
this.$boxes = this.$('.ui-color-picker-box');
|
||||
|
||||
// add event handler
|
||||
var self = this;
|
||||
this.$boxes.on('click', function() {
|
||||
self.value($(this).css('background-color'));
|
||||
self.$header.trigger('click');
|
||||
});
|
||||
this.$header.on('click', function() {
|
||||
self.visible = !self.visible;
|
||||
if (self.visible) {
|
||||
self.$view.fadeIn('fast');
|
||||
} else {
|
||||
self.$view.fadeOut('fast');
|
||||
}
|
||||
});
|
||||
},
|
||||
|
||||
// value
|
||||
value : function (new_val) {
|
||||
if (new_val !== undefined) {
|
||||
// set default value
|
||||
if (new_val !== null) {
|
||||
// value
|
||||
value : function ( new_val ) {
|
||||
if ( new_val !== undefined && new_val !== null ) {
|
||||
// update color value
|
||||
this.$value.css('background-color', new_val);
|
||||
this.$value.css( 'background-color', new_val );
|
||||
|
||||
// check selected color in panel
|
||||
this.$('.ui-color-picker-box').empty();
|
||||
this.$(this._getValue()).html(this._templateCheck());
|
||||
this.$( '.ui-color-picker-box' ).empty();
|
||||
this.$( this._getValue() ).html( this._templateCheck() );
|
||||
|
||||
// trigger custom event
|
||||
this.options.onchange && this.options.onchange(new_val);
|
||||
this.options.onchange && this.options.onchange( new_val );
|
||||
}
|
||||
}
|
||||
|
||||
// return current value
|
||||
return this._getValue();
|
||||
},
|
||||
// return current value
|
||||
return this._getValue();
|
||||
},
|
||||
|
||||
// get value from dom
|
||||
_getValue: function() {
|
||||
var rgb = this.$value.css('background-color');
|
||||
rgb = rgb.match(/^rgb\((\d+),\s*(\d+),\s*(\d+)\)$/);
|
||||
if (rgb) {
|
||||
function hex(x) {
|
||||
return ('0' + parseInt(x).toString(16)).slice(-2);
|
||||
}
|
||||
return '#' + hex(rgb[1]) + hex(rgb[2]) + hex(rgb[3]);
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
},
|
||||
|
||||
// build panel
|
||||
_build: function() {
|
||||
var $content = this._content({
|
||||
label : 'Theme Colors',
|
||||
colors : this.colors.base,
|
||||
padding : 10
|
||||
});
|
||||
for (var i in this.colors.theme) {
|
||||
var line_def = {};
|
||||
if (i == 0) {
|
||||
line_def['bottom'] = true;
|
||||
} else {
|
||||
if (i != this.colors.theme.length - 1) {
|
||||
line_def['top'] = true;
|
||||
line_def['bottom'] = true;
|
||||
} else {
|
||||
line_def['top'] = true;
|
||||
line_def['padding'] = 5;
|
||||
// get value from dom
|
||||
_getValue: function() {
|
||||
var rgb = this.$value.css( 'background-color' );
|
||||
rgb = rgb.match(/^rgb\((\d+),\s*(\d+),\s*(\d+)\)$/);
|
||||
if ( rgb ) {
|
||||
function hex( x ) {
|
||||
return ( '0' + parseInt( x ).toString( 16 ) ).slice( -2 );
|
||||
}
|
||||
return '#' + hex( rgb[ 1] ) + hex( rgb[ 2 ] ) + hex( rgb[ 3 ] );
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
line_def['colors'] = this.colors.theme[i];
|
||||
this._content(line_def);
|
||||
}
|
||||
this._content({
|
||||
label : 'Standard Colors',
|
||||
colors : this.colors.standard,
|
||||
padding : 5
|
||||
});
|
||||
},
|
||||
},
|
||||
|
||||
// create content
|
||||
_content: function(options) {
|
||||
// get parameters
|
||||
var label = options.label;
|
||||
var colors = options.colors;
|
||||
var padding = options.padding;
|
||||
var top = options.top;
|
||||
var bottom = options.bottom;
|
||||
|
||||
// create lines
|
||||
var $content = $(this._templateContent());
|
||||
|
||||
// set label
|
||||
var $label = $content.find('.label');
|
||||
if (options.label) {
|
||||
$label.html(options.label);
|
||||
} else {
|
||||
$label.hide();
|
||||
}
|
||||
|
||||
// build line
|
||||
var $line = $content.find('.line');
|
||||
this.$panel.append($content);
|
||||
for (var i in colors) {
|
||||
var $box = $(this._templateBox(colors[i]));
|
||||
if (top) {
|
||||
$box.css('border-top', 'none');
|
||||
$box.css('border-top-left-radius', '0px');
|
||||
$box.css('border-top-right-radius', '0px');
|
||||
// build panel
|
||||
_build: function() {
|
||||
var $content = this._content({
|
||||
label : 'Theme Colors',
|
||||
colors : this.colors.base,
|
||||
padding : 10
|
||||
});
|
||||
for ( var i in this.colors.theme ) {
|
||||
var line_def = {};
|
||||
if ( i == 0 ) {
|
||||
line_def[ 'bottom' ] = true;
|
||||
} else {
|
||||
if ( i != this.colors.theme.length - 1 ) {
|
||||
line_def[ 'top' ] = true;
|
||||
line_def[ 'bottom' ] = true;
|
||||
} else {
|
||||
line_def[ 'top' ] = true;
|
||||
line_def[ 'padding' ] = 5;
|
||||
}
|
||||
}
|
||||
line_def[ 'colors' ] = this.colors.theme[ i ];
|
||||
this._content( line_def );
|
||||
}
|
||||
if (bottom) {
|
||||
$box.css('border-bottom', 'none');
|
||||
$box.css('border-bottom-left-radius', '0px');
|
||||
$box.css('border-bottom-right-radius', '0px');
|
||||
this._content({
|
||||
label : 'Standard Colors',
|
||||
colors : this.colors.standard,
|
||||
padding : 5
|
||||
});
|
||||
},
|
||||
|
||||
// create content
|
||||
_content: function( options ) {
|
||||
// get parameters
|
||||
var label = options.label;
|
||||
var colors = options.colors;
|
||||
var padding = options.padding;
|
||||
var top = options.top;
|
||||
var bottom = options.bottom;
|
||||
|
||||
// create lines
|
||||
var $content = $( this._templateContent() );
|
||||
|
||||
// set label
|
||||
var $label = $content.find( '.label' );
|
||||
if ( options.label ) {
|
||||
$label.html( options.label );
|
||||
} else {
|
||||
$label.hide();
|
||||
}
|
||||
$line.append($box);
|
||||
|
||||
// build line
|
||||
var $line = $content.find( '.line' );
|
||||
this.$panel.append( $content );
|
||||
for ( var i in colors ) {
|
||||
var $box = $( this._templateBox( colors[ i ] ) );
|
||||
if ( top ) {
|
||||
$box.css( 'border-top', 'none' );
|
||||
$box.css( 'border-top-left-radius', '0px' );
|
||||
$box.css( 'border-top-right-radius', '0px' );
|
||||
}
|
||||
if ( bottom ) {
|
||||
$box.css( 'border-bottom', 'none' );
|
||||
$box.css( 'border-bottom-left-radius', '0px' );
|
||||
$box.css( 'border-bottom-right-radius', '0px' );
|
||||
}
|
||||
$line.append( $box );
|
||||
}
|
||||
if (padding) {
|
||||
$line.css( 'padding-bottom', padding );
|
||||
}
|
||||
return $content;
|
||||
},
|
||||
|
||||
// check icon
|
||||
_templateCheck: function() {
|
||||
return '<div class="ui-color-picker-check fa fa-check"/>';
|
||||
},
|
||||
|
||||
// content template
|
||||
_templateContent: function() {
|
||||
return '<div class="ui-color-picker-content">' +
|
||||
'<div class="label"/>' +
|
||||
'<div class="line"/>' +
|
||||
'</div>';
|
||||
},
|
||||
|
||||
// box template
|
||||
_templateBox: function( color ) {
|
||||
return '<div id="' + color + '" class="ui-color-picker-box" style="background-color: #' + color + ';"/>';
|
||||
},
|
||||
|
||||
// template
|
||||
_template: function() {
|
||||
return '<div class="ui-color-picker">' +
|
||||
'<div class="ui-color-picker-header">' +
|
||||
'<div class="ui-color-picker-value"/>' +
|
||||
'<div class="ui-color-picker-label">Select a color</div>' +
|
||||
'</div>' +
|
||||
'<div class="ui-color-picker-view ui-input">' +
|
||||
'<div class="ui-color-picker-panel"/>' +
|
||||
'</div>'
|
||||
'</div>';
|
||||
}
|
||||
if (padding) {
|
||||
$line.css('padding-bottom', padding);
|
||||
}
|
||||
return $content;
|
||||
},
|
||||
|
||||
// check icon
|
||||
_templateCheck: function() {
|
||||
return '<div class="ui-color-picker-check fa fa-check"/>';
|
||||
},
|
||||
|
||||
// content template
|
||||
_templateContent: function() {
|
||||
return '<div class="ui-color-picker-content">' +
|
||||
'<div class="label"/>' +
|
||||
'<div class="line"/>' +
|
||||
'</div>';
|
||||
},
|
||||
|
||||
// box template
|
||||
_templateBox: function(color) {
|
||||
return '<div id="' + color + '" class="ui-color-picker-box" style="background-color: #' + color + ';"/>';
|
||||
},
|
||||
|
||||
// template
|
||||
_template: function() {
|
||||
return '<div class="ui-color-picker">' +
|
||||
'<div class="ui-color-picker-header">' +
|
||||
'<div class="ui-color-picker-value"/>' +
|
||||
'<div class="ui-color-picker-label">Select a color</div>' +
|
||||
'</div>' +
|
||||
'<div class="ui-color-picker-view ui-input">' +
|
||||
'<div class="ui-color-picker-panel"/>' +
|
||||
'</div>'
|
||||
'</div>';
|
||||
}
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -1,493 +1,276 @@
|
||||
// dependencies
|
||||
/**
|
||||
* This class contains backbone wrappers for basic ui elements such as Images, Labels, Buttons, Input fields etc.
|
||||
*/
|
||||
define(['utils/utils',
|
||||
'mvc/ui/ui-select-default',
|
||||
'mvc/ui/ui-slider',
|
||||
'mvc/ui/ui-options',
|
||||
'mvc/ui/ui-drilldown',
|
||||
'mvc/ui/ui-button-menu',
|
||||
'mvc/ui/ui-button-check',
|
||||
'mvc/ui/ui-buttons',
|
||||
'mvc/ui/ui-modal'],
|
||||
function(Utils, Select, Slider, Options, Drilldown, ButtonMenu, ButtonCheck, Modal) {
|
||||
function( Utils, Select, Slider, Options, Drilldown, Buttons, Modal ) {
|
||||
|
||||
/**
|
||||
* This class contains backbone wrappers for basic ui elements such as Images, Labels, Buttons, Input fields etc.
|
||||
*/
|
||||
|
||||
// plugin
|
||||
var Image = Backbone.View.extend({
|
||||
// options
|
||||
optionsDefault: {
|
||||
url : '',
|
||||
cls : ''
|
||||
},
|
||||
|
||||
// initialize
|
||||
initialize : function(options) {
|
||||
// get options
|
||||
this.options = Utils.merge(options, this.optionsDefault);
|
||||
|
||||
// create new element
|
||||
this.setElement(this._template(this.options));
|
||||
},
|
||||
|
||||
// template
|
||||
_template: function(options) {
|
||||
return '<img class="ui-image ' + options.cls + '" src="' + options.url + '"/>';
|
||||
}
|
||||
});
|
||||
|
||||
// plugin
|
||||
var Label = Backbone.View.extend({
|
||||
// options
|
||||
optionsDefault: {
|
||||
title : '',
|
||||
cls : ''
|
||||
},
|
||||
|
||||
// initialize
|
||||
initialize : function(options) {
|
||||
// get options
|
||||
this.options = Utils.merge(options, this.optionsDefault);
|
||||
|
||||
// create new element
|
||||
this.setElement(this._template(this.options));
|
||||
},
|
||||
|
||||
// title
|
||||
title: function(new_title) {
|
||||
this.$el.html(new_title);
|
||||
},
|
||||
|
||||
// template
|
||||
_template: function(options) {
|
||||
return '<label class="ui-label ' + options.cls + '">' + options.title + '</label>';
|
||||
},
|
||||
|
||||
// value
|
||||
value: function() {
|
||||
return options.title;
|
||||
}
|
||||
});
|
||||
|
||||
// plugin
|
||||
var Icon = Backbone.View.extend({
|
||||
// options
|
||||
optionsDefault: {
|
||||
floating : 'right',
|
||||
icon : '',
|
||||
tooltip : '',
|
||||
placement : 'bottom',
|
||||
title : '',
|
||||
cls : ''
|
||||
},
|
||||
|
||||
// initialize
|
||||
initialize : function(options) {
|
||||
// get options
|
||||
this.options = Utils.merge(options, this.optionsDefault);
|
||||
|
||||
// create new element
|
||||
this.setElement(this._template(this.options));
|
||||
|
||||
// add tooltip
|
||||
$(this.el).tooltip({title: options.tooltip, placement: 'bottom'});
|
||||
},
|
||||
|
||||
// element
|
||||
_template: function(options) {
|
||||
return '<div>' +
|
||||
'<span class="fa ' + options.icon + '" class="ui-icon"/> ' +
|
||||
options.title +
|
||||
'</div>';
|
||||
}
|
||||
});
|
||||
|
||||
// plugin
|
||||
var Button = Backbone.View.extend({
|
||||
// options
|
||||
optionsDefault: {
|
||||
id : Utils.uid(),
|
||||
title : '',
|
||||
floating : 'right',
|
||||
cls : 'ui-button btn btn-default',
|
||||
icon : ''
|
||||
},
|
||||
|
||||
// initialize
|
||||
initialize : function(options) {
|
||||
// get options
|
||||
this.options = Utils.merge(options, this.optionsDefault);
|
||||
|
||||
// create new element
|
||||
this.setElement(this._template(this.options));
|
||||
|
||||
// add event
|
||||
$(this.el).on('click', function() {
|
||||
// hide all tooltips
|
||||
$('.tooltip').hide();
|
||||
|
||||
// execute onclick callback
|
||||
if (options.onclick) {
|
||||
options.onclick();
|
||||
}
|
||||
});
|
||||
|
||||
// add tooltip
|
||||
$(this.el).tooltip({title: options.tooltip, placement: 'bottom'});
|
||||
},
|
||||
|
||||
// show spinner
|
||||
wait: function() {
|
||||
this.$el.removeClass(this.options.cls).addClass('btn btn-info').prop('disabled', true);
|
||||
this.$('.icon').removeClass(this.options.icon).addClass('fa-spinner fa-spin');
|
||||
this.$('.title').html('Sending...');
|
||||
},
|
||||
|
||||
// hide spinner
|
||||
unwait: function() {
|
||||
this.$el.removeClass('btn btn-info').addClass(this.options.cls).prop('disabled', false);
|
||||
this.$('.icon').removeClass('fa-spinner fa-spin').addClass(this.options.icon);
|
||||
this.$('.title').html(this.options.title);
|
||||
},
|
||||
|
||||
// element
|
||||
_template: function(options) {
|
||||
var str = '<button id="' + options.id + '" type="submit" style="float: ' + options.floating + ';" type="button" class="' + options.cls + '">';
|
||||
if (options.icon) {
|
||||
str += '<i class="icon fa ' + options.icon + '"></i> ';
|
||||
}
|
||||
str += '<span class="title">' + options.title + '</span>' +
|
||||
'</button>';
|
||||
return str;
|
||||
}
|
||||
});
|
||||
|
||||
// plugin
|
||||
var ButtonIcon = Backbone.View.extend({
|
||||
// options
|
||||
optionsDefault: {
|
||||
id : Utils.uid(),
|
||||
title : '',
|
||||
floating : 'right',
|
||||
cls : 'ui-button-icon',
|
||||
icon : '',
|
||||
tooltip : '',
|
||||
onclick : null
|
||||
},
|
||||
|
||||
// initialize
|
||||
initialize : function(options) {
|
||||
// get options
|
||||
this.options = Utils.merge(options, this.optionsDefault);
|
||||
|
||||
// create new element
|
||||
this.setElement(this._template(this.options));
|
||||
|
||||
// link button element
|
||||
this.$button = this.$el.find('.button');
|
||||
|
||||
// add event
|
||||
var self = this;
|
||||
$(this.el).on('click', function() {
|
||||
// hide all tooltips
|
||||
$('.tooltip').hide();
|
||||
|
||||
// execute onclick callback
|
||||
if (options.onclick && !self.disabled) {
|
||||
options.onclick();
|
||||
}
|
||||
});
|
||||
|
||||
// add tooltip
|
||||
this.$button.tooltip({title: options.tooltip, placement: 'bottom'});
|
||||
},
|
||||
|
||||
// disable
|
||||
disable: function() {
|
||||
this.$button.addClass('disabled');
|
||||
this.disabled = true;
|
||||
},
|
||||
|
||||
// enable
|
||||
enable: function() {
|
||||
this.$button.removeClass('disabled');
|
||||
this.disabled = false;
|
||||
},
|
||||
|
||||
// change icon
|
||||
setIcon: function(icon_cls) {
|
||||
this.$('i').removeClass(this.options.icon).addClass(icon_cls);
|
||||
this.options.icon = icon_cls;
|
||||
},
|
||||
|
||||
// template
|
||||
_template: function(options) {
|
||||
// width
|
||||
var width = '';
|
||||
if (options.title) {
|
||||
width = 'width: auto;';
|
||||
}
|
||||
|
||||
// string
|
||||
var str = '<div id="' + options.id + '" style="float: ' + options.floating + '; ' + width + '" class="' + options.cls + '">' +
|
||||
'<div class="button">';
|
||||
if (options.title) {
|
||||
str += '<i class="icon fa ' + options.icon + '"/> ' +
|
||||
'<span class="title">' + options.title + '</span>';
|
||||
} else {
|
||||
str += '<i class="icon fa ' + options.icon + '"/>';
|
||||
}
|
||||
str += '</div>' +
|
||||
'</div>';
|
||||
return str;
|
||||
}
|
||||
});
|
||||
|
||||
// plugin
|
||||
var Anchor = Backbone.View.extend({
|
||||
// options
|
||||
optionsDefault: {
|
||||
title : '',
|
||||
cls : ''
|
||||
},
|
||||
|
||||
// initialize
|
||||
initialize : function(options) {
|
||||
// get options
|
||||
this.options = Utils.merge(options, this.optionsDefault);
|
||||
|
||||
// create new element
|
||||
this.setElement(this._template(this.options));
|
||||
|
||||
// add event
|
||||
$(this.el).on('click', options.onclick);
|
||||
},
|
||||
|
||||
// element
|
||||
_template: function(options) {
|
||||
return '<div><a href="javascript:void(0)" class="ui-anchor ' + options.cls + '">' + options.title + '</a></div>';
|
||||
}
|
||||
});
|
||||
|
||||
// plugin
|
||||
var Message = Backbone.View.extend({
|
||||
// options
|
||||
optionsDefault: {
|
||||
message : null,
|
||||
status : 'info',
|
||||
cls : '',
|
||||
persistent : false
|
||||
},
|
||||
|
||||
// initialize
|
||||
initialize : function(options) {
|
||||
// configure options
|
||||
this.options = Utils.merge(options, this.optionsDefault);
|
||||
|
||||
// create new element
|
||||
this.setElement('<div class="' + this.options.cls + '"/>');
|
||||
|
||||
// show initial message
|
||||
if (this.options.message) {
|
||||
this.update(this.options);
|
||||
}
|
||||
},
|
||||
|
||||
// update
|
||||
update : function(options) {
|
||||
// get options
|
||||
this.options = Utils.merge(options, this.optionsDefault);
|
||||
|
||||
// show message
|
||||
if (options.message != '') {
|
||||
this.$el.html(this._template(this.options));
|
||||
this.$el.find('.alert').append(options.message);
|
||||
this.$el.fadeIn();
|
||||
|
||||
// clear previous timeouts
|
||||
if (this.timeout) {
|
||||
window.clearTimeout(this.timeout);
|
||||
}
|
||||
|
||||
// set timeout if message is not persistent
|
||||
if (!options.persistent) {
|
||||
var self = this;
|
||||
this.timeout = window.setTimeout(function() {
|
||||
if (self.$el.is(':visible')) {
|
||||
self.$el.fadeOut();
|
||||
} else {
|
||||
self.$el.hide();
|
||||
}
|
||||
}, 3000);
|
||||
}
|
||||
} else {
|
||||
this.$el.fadeOut();
|
||||
}
|
||||
},
|
||||
|
||||
// element
|
||||
_template: function(options) {
|
||||
return '<div class="ui-message alert alert-' + options.status + '"/>';
|
||||
}
|
||||
});
|
||||
|
||||
// plugin
|
||||
var Searchbox = Backbone.View.extend({
|
||||
// options
|
||||
optionsDefault: {
|
||||
onclick : null,
|
||||
searchword : ''
|
||||
},
|
||||
|
||||
// initialize
|
||||
initialize : function(options) {
|
||||
// configure options
|
||||
this.options = Utils.merge(options, this.optionsDefault);
|
||||
|
||||
// create new element
|
||||
this.setElement(this._template(this.options));
|
||||
|
||||
// add click event
|
||||
var self = this;
|
||||
if (this.options.onclick) {
|
||||
this.$el.on('submit', function(e) {
|
||||
var search_field = self.$el.find('#search');
|
||||
self.options.onclick(search_field.val());
|
||||
/** Image wrapper */
|
||||
var Image = Backbone.View.extend({
|
||||
initialize : function(options) {
|
||||
this.options = Utils.merge(options, {
|
||||
url : '',
|
||||
cls : ''
|
||||
});
|
||||
this.setElement(this._template(this.options));
|
||||
},
|
||||
_template: function(options) {
|
||||
return '<img class="ui-image ' + options.cls + '" src="' + options.url + '"/>';
|
||||
}
|
||||
},
|
||||
});
|
||||
|
||||
// element
|
||||
_template: function(options) {
|
||||
return '<div class="ui-search">' +
|
||||
'<form onsubmit="return false;">' +
|
||||
'<input id="search" class="form-control input-sm" type="text" name="search" placeholder="Search..." value="' + options.searchword + '">' +
|
||||
'<button type="submit" class="btn search-btn">' +
|
||||
'<i class="fa fa-search"></i>' +
|
||||
'</button>' +
|
||||
'</form>' +
|
||||
'</div>';
|
||||
}
|
||||
});
|
||||
|
||||
// plugin
|
||||
var Input = Backbone.View.extend({
|
||||
// options
|
||||
optionsDefault: {
|
||||
type : 'text',
|
||||
placeholder : '',
|
||||
disabled : false,
|
||||
visible : true,
|
||||
cls : '',
|
||||
area : false
|
||||
},
|
||||
|
||||
// initialize
|
||||
initialize : function(options) {
|
||||
// configure options
|
||||
this.options = Utils.merge(options, this.optionsDefault);
|
||||
|
||||
// create new element
|
||||
this.setElement(this._template(this.options));
|
||||
|
||||
// set initial value
|
||||
if (this.options.value !== undefined) {
|
||||
this.value(this.options.value);
|
||||
/** Label wrapper */
|
||||
var Label = Backbone.View.extend({
|
||||
initialize : function(options) {
|
||||
this.options = Utils.merge(options, {
|
||||
title : '',
|
||||
cls : ''
|
||||
});
|
||||
this.setElement(this._template(this.options));
|
||||
},
|
||||
title: function(new_title) {
|
||||
this.$el.html(new_title);
|
||||
},
|
||||
value: function() {
|
||||
return options.title;
|
||||
},
|
||||
_template: function(options) {
|
||||
return '<label class="ui-label ' + options.cls + '">' + options.title + '</label>';
|
||||
}
|
||||
});
|
||||
|
||||
// disable input field
|
||||
if (this.options.disabled) {
|
||||
this.$el.prop('disabled', true);
|
||||
}
|
||||
|
||||
// hide input field
|
||||
if (!this.options.visible) {
|
||||
this.$el.hide();
|
||||
}
|
||||
|
||||
// onchange event handler. fires on user activity.
|
||||
var self = this;
|
||||
this.$el.on('input', function() {
|
||||
if (self.options.onchange) {
|
||||
self.options.onchange(self.$el.val());
|
||||
}
|
||||
});
|
||||
},
|
||||
|
||||
// value
|
||||
value : function (new_val) {
|
||||
if (new_val !== undefined) {
|
||||
this.$el.val(new_val);
|
||||
}
|
||||
return this.$el.val();
|
||||
},
|
||||
|
||||
// element
|
||||
_template: function(options) {
|
||||
if (options.area) {
|
||||
return '<textarea id="' + options.id + '" class="ui-textarea ' + options.cls + '"></textarea>';
|
||||
} else {
|
||||
return '<input id="' + options.id + '" type="' + options.type + '" value="' + options.value + '" placeholder="' + options.placeholder + '" class="ui-input ' + options.cls + '">';
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
// plugin
|
||||
var Hidden = Backbone.View.extend({
|
||||
// initialize
|
||||
initialize : function(options) {
|
||||
// configure options
|
||||
this.options = options;
|
||||
|
||||
// create new element
|
||||
this.setElement(this._template(this.options));
|
||||
|
||||
// set initial value
|
||||
if (this.options.value !== undefined) {
|
||||
this.value(this.options.value);
|
||||
}
|
||||
},
|
||||
|
||||
// value
|
||||
value : function (new_val) {
|
||||
if (new_val !== undefined) {
|
||||
this.$('hidden').val(new_val);
|
||||
}
|
||||
return this.$('hidden').val();
|
||||
},
|
||||
|
||||
// element
|
||||
_template: function(options) {
|
||||
var tmpl = '<div id="' + options.id + '" >';
|
||||
if (options.info) {
|
||||
tmpl += '<div>' + options.info + '</div>';
|
||||
}
|
||||
tmpl += '<hidden value="' + options.value + '"/>' +
|
||||
/** Displays an icon with title */
|
||||
var Icon = Backbone.View.extend({
|
||||
initialize : function(options) {
|
||||
this.options = Utils.merge(options, {
|
||||
floating : 'right',
|
||||
icon : '',
|
||||
tooltip : '',
|
||||
placement : 'bottom',
|
||||
title : '',
|
||||
cls : ''
|
||||
});
|
||||
this.setElement(this._template(this.options));
|
||||
$(this.el).tooltip({title: options.tooltip, placement: 'bottom'});
|
||||
},
|
||||
_template: function(options) {
|
||||
return '<div>' +
|
||||
'<span class="fa ' + options.icon + '" class="ui-icon"/> ' +
|
||||
options.title +
|
||||
'</div>';
|
||||
return tmpl;
|
||||
}
|
||||
});
|
||||
|
||||
/** Renders an anchor element */
|
||||
var Anchor = Backbone.View.extend({
|
||||
initialize : function(options) {
|
||||
this.options = Utils.merge(options, {
|
||||
title : '',
|
||||
cls : ''
|
||||
});
|
||||
this.setElement(this._template(this.options));
|
||||
$(this.el).on('click', options.onclick);
|
||||
},
|
||||
_template: function(options) {
|
||||
return '<div><a href="javascript:void(0)" class="ui-anchor ' + options.cls + '">' + options.title + '</a></div>';
|
||||
}
|
||||
});
|
||||
|
||||
/** Displays messages used e.g. in the tool form */
|
||||
var Message = Backbone.View.extend({
|
||||
initialize : function(options) {
|
||||
this.options = Utils.merge(options, {
|
||||
message : null,
|
||||
status : 'info',
|
||||
cls : '',
|
||||
persistent : false
|
||||
});
|
||||
this.setElement('<div class="' + this.options.cls + '"/>');
|
||||
this.options.message && this.update(this.options);
|
||||
},
|
||||
|
||||
// update
|
||||
update: function(options) {
|
||||
// get options
|
||||
this.options = Utils.merge(options, this.options);
|
||||
|
||||
// show message
|
||||
if (options.message != '') {
|
||||
this.$el.html(this._template(this.options));
|
||||
this.$el.fadeIn();
|
||||
|
||||
// clear previous timeouts
|
||||
if (this.timeout) {
|
||||
window.clearTimeout(this.timeout);
|
||||
}
|
||||
|
||||
// set timeout if message is not persistent
|
||||
if (!options.persistent) {
|
||||
var self = this;
|
||||
this.timeout = window.setTimeout(function() {
|
||||
if (self.$el.is(':visible')) {
|
||||
self.$el.fadeOut();
|
||||
} else {
|
||||
self.$el.hide();
|
||||
}
|
||||
}, 3000);
|
||||
}
|
||||
} else {
|
||||
this.$el.fadeOut();
|
||||
}
|
||||
},
|
||||
|
||||
// template
|
||||
_template: function(options) {
|
||||
var cls_status = 'ui-message alert alert-' + options.status;
|
||||
if (options.large) {
|
||||
cls_status = ( ( options.status == 'success' && 'done' ) ||
|
||||
( options.status == 'danger' && 'error' ) ||
|
||||
options.status ) + 'messagelarge';
|
||||
}
|
||||
return '<div class="' + cls_status + '" >' +
|
||||
options.message +
|
||||
'</div>';
|
||||
}
|
||||
});
|
||||
|
||||
/** Render a search box */
|
||||
var Searchbox = Backbone.View.extend({
|
||||
initialize : function(options) {
|
||||
this.options = Utils.merge(options, {
|
||||
onclick : null,
|
||||
searchword : ''
|
||||
});
|
||||
this.setElement(this._template(this.options));
|
||||
var self = this;
|
||||
if (this.options.onclick) {
|
||||
this.$el.on('submit', function(e) {
|
||||
var search_field = self.$el.find('#search');
|
||||
self.options.onclick(search_field.val());
|
||||
});
|
||||
}
|
||||
},
|
||||
_template: function(options) {
|
||||
return '<div class="ui-search">' +
|
||||
'<form onsubmit="return false;">' +
|
||||
'<input id="search" class="form-control input-sm" type="text" name="search" placeholder="Search..." value="' + options.searchword + '">' +
|
||||
'<button type="submit" class="btn search-btn">' +
|
||||
'<i class="fa fa-search"></i>' +
|
||||
'</button>' +
|
||||
'</form>' +
|
||||
'</div>';
|
||||
}
|
||||
});
|
||||
|
||||
/** Renders an input element used e.g. in the tool form */
|
||||
var Input = Backbone.View.extend({
|
||||
initialize : function(options) {
|
||||
// configure options
|
||||
this.options = Utils.merge(options, {
|
||||
type : 'text',
|
||||
placeholder : '',
|
||||
disabled : false,
|
||||
visible : true,
|
||||
cls : '',
|
||||
area : false
|
||||
});
|
||||
|
||||
// create new element
|
||||
this.setElement(this._template(this.options));
|
||||
|
||||
// set initial value
|
||||
if (this.options.value !== undefined) {
|
||||
this.value(this.options.value);
|
||||
}
|
||||
|
||||
// disable input field
|
||||
if (this.options.disabled) {
|
||||
this.$el.prop('disabled', true);
|
||||
}
|
||||
|
||||
// hide input field
|
||||
if (!this.options.visible) {
|
||||
this.$el.hide();
|
||||
}
|
||||
|
||||
// onchange event handler. fires on user activity.
|
||||
var self = this;
|
||||
this.$el.on('input', function() {
|
||||
if (self.options.onchange) {
|
||||
self.options.onchange(self.$el.val());
|
||||
}
|
||||
});
|
||||
},
|
||||
|
||||
// value
|
||||
value : function (new_val) {
|
||||
if (new_val !== undefined) {
|
||||
this.$el.val(new_val);
|
||||
}
|
||||
return this.$el.val();
|
||||
},
|
||||
|
||||
// template
|
||||
_template: function(options) {
|
||||
if (options.area) {
|
||||
return '<textarea id="' + options.id + '" class="ui-textarea ' + options.cls + '"></textarea>';
|
||||
} else {
|
||||
return '<input id="' + options.id + '" type="' + options.type + '" value="' + options.value + '" placeholder="' + options.placeholder + '" class="ui-input ' + options.cls + '">';
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
/** Creates a hidden element input field used e.g. in the tool form */
|
||||
var Hidden = Backbone.View.extend({
|
||||
initialize : function(options) {
|
||||
this.options = options;
|
||||
this.setElement(this._template(this.options));
|
||||
if (this.options.value !== undefined) {
|
||||
this.value(this.options.value);
|
||||
}
|
||||
},
|
||||
value : function (new_val) {
|
||||
if (new_val !== undefined) {
|
||||
this.$('hidden').val(new_val);
|
||||
}
|
||||
return this.$('hidden').val();
|
||||
},
|
||||
_template: function(options) {
|
||||
var tmpl = '<div id="' + options.id + '" >';
|
||||
if (options.info) {
|
||||
tmpl += '<div>' + options.info + '</div>';
|
||||
}
|
||||
tmpl += '<hidden value="' + options.value + '"/>' +
|
||||
'</div>';
|
||||
return tmpl;
|
||||
}
|
||||
});
|
||||
|
||||
return {
|
||||
Anchor : Anchor,
|
||||
Button : Buttons.ButtonDefault,
|
||||
ButtonIcon : Buttons.ButtonIcon,
|
||||
ButtonCheck : Buttons.ButtonCheck,
|
||||
ButtonMenu : Buttons.ButtonMenu,
|
||||
ButtonLink : Buttons.ButtonLink,
|
||||
Icon : Icon,
|
||||
Image : Image,
|
||||
Input : Input,
|
||||
Label : Label,
|
||||
Message : Message,
|
||||
Modal : Modal,
|
||||
RadioButton : Options.RadioButton,
|
||||
Checkbox : Options.Checkbox,
|
||||
Radio : Options.Radio,
|
||||
Searchbox : Searchbox,
|
||||
Select : Select,
|
||||
Hidden : Hidden,
|
||||
Slider : Slider,
|
||||
Drilldown : Drilldown
|
||||
}
|
||||
});
|
||||
|
||||
// return
|
||||
return {
|
||||
Anchor : Anchor,
|
||||
Button : Button,
|
||||
ButtonIcon : ButtonIcon,
|
||||
ButtonCheck : ButtonCheck,
|
||||
ButtonMenu : ButtonMenu,
|
||||
Icon : Icon,
|
||||
Image : Image,
|
||||
Input : Input,
|
||||
Label : Label,
|
||||
Message : Message,
|
||||
Modal : Modal,
|
||||
RadioButton : Options.RadioButton,
|
||||
Checkbox : Options.Checkbox,
|
||||
Radio : Options.Radio,
|
||||
Searchbox : Searchbox,
|
||||
Select : Select,
|
||||
Hidden : Hidden,
|
||||
Slider : Slider,
|
||||
Drilldown : Drilldown
|
||||
}
|
||||
});
|
||||
|
||||
@@ -13,7 +13,8 @@ var View = Backbone.View.extend({
|
||||
height : null,
|
||||
width : null,
|
||||
closing_events : false,
|
||||
closing_callback : null
|
||||
closing_callback : null,
|
||||
title_separator : true
|
||||
},
|
||||
|
||||
// button list
|
||||
@@ -35,12 +36,12 @@ var View = Backbone.View.extend({
|
||||
if (this.options.height){
|
||||
this.$body.css('height', this.options.height);
|
||||
this.$body.css('overflow', 'hidden');
|
||||
} else{
|
||||
} else {
|
||||
this.$body.css('max-height', $(window).height() / 2);
|
||||
}
|
||||
|
||||
// fix width
|
||||
if (this.options.width){
|
||||
if (this.options.width) {
|
||||
this.$dialog.css('width', this.options.width);
|
||||
}
|
||||
|
||||
@@ -169,6 +170,11 @@ var View = Backbone.View.extend({
|
||||
// hide modal if background is clicked
|
||||
this.$el.find('.modal-backdrop').on('click', function() { self.hide(); });
|
||||
}
|
||||
|
||||
// removes the default separator line
|
||||
if (!this.options.title_separator) {
|
||||
this.$('.modal-header').css({ 'border': 'none', 'padding-bottom': '0px' });
|
||||
}
|
||||
},
|
||||
|
||||
// fill regular modal template
|
||||
@@ -180,7 +186,7 @@ var View = Backbone.View.extend({
|
||||
'<div class="modal-header">' +
|
||||
'<button type="button" class="close" style="display: none;">×</button>' +
|
||||
'<h4 class="title">' + title + '</h4>' +
|
||||
'</div>' +
|
||||
'</div>' +
|
||||
'<div class="modal-body" style="position: static;"></div>' +
|
||||
'<div class="modal-footer">' +
|
||||
'<div class="buttons" style="float: right;"></div>' +
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
// dependencies
|
||||
define(['utils/utils', 'mvc/ui/ui-button-check'], function(Utils, ButtonCheck) {
|
||||
define(['utils/utils', 'mvc/ui/ui-buttons'], function(Utils, Buttons) {
|
||||
|
||||
/** Base class for options based ui elements **/
|
||||
var Base = Backbone.View.extend({
|
||||
@@ -8,18 +8,15 @@ var Base = Backbone.View.extend({
|
||||
// link this
|
||||
var self = this;
|
||||
|
||||
// options
|
||||
this.optionsDefault = {
|
||||
// configure options
|
||||
this.options = Utils.merge(options, {
|
||||
visible : true,
|
||||
data : [],
|
||||
id : Utils.uid(),
|
||||
error_text : 'No data available.',
|
||||
error_text : 'No options available.',
|
||||
wait_text : 'Please wait...',
|
||||
multiple : false
|
||||
};
|
||||
|
||||
// configure options
|
||||
this.options = Utils.merge(options, this.optionsDefault);
|
||||
});
|
||||
|
||||
// create new element
|
||||
this.setElement('<div class="ui-options"/>');
|
||||
@@ -36,7 +33,7 @@ var Base = Backbone.View.extend({
|
||||
|
||||
// add select/unselect all button
|
||||
if (this.options.multiple) {
|
||||
this.all_button = new ButtonCheck({
|
||||
this.all_button = new Buttons.ButtonCheck({
|
||||
onclick: function() {
|
||||
self.$('input').prop('checked', self.all_button.value() !== 0);
|
||||
self.trigger('change');
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
define(['utils/utils'], function(Utils) {
|
||||
|
||||
var View = Backbone.View.extend({
|
||||
|
||||
|
||||
// default options
|
||||
optionsDefault: {
|
||||
with_close : true,
|
||||
@@ -10,45 +10,44 @@ var View = Backbone.View.extend({
|
||||
title : null,
|
||||
placement : 'top'
|
||||
},
|
||||
|
||||
|
||||
// visibility flag
|
||||
visible: false,
|
||||
|
||||
|
||||
// initialize
|
||||
initialize: function (options) {
|
||||
|
||||
// link this
|
||||
var self = this;
|
||||
|
||||
|
||||
// update options
|
||||
this.options = _.defaults(options, this.optionsDefault);
|
||||
|
||||
|
||||
// set element
|
||||
this.setElement(this._template(this.options));
|
||||
|
||||
|
||||
// attach popover to parent
|
||||
this.options.container.parent().append(this.$el);
|
||||
|
||||
|
||||
// attach close
|
||||
if (this.options.with_close) {
|
||||
this.$el.find('#close').on('click', function() { self.hide(); });
|
||||
}
|
||||
|
||||
|
||||
// generate unique id
|
||||
this.uid = Utils.uid();
|
||||
|
||||
|
||||
// add event to hide if click is outside of popup
|
||||
var self = this;
|
||||
$('body').on('mousedown.' + this.uid, function(e) { self._hide(e) });
|
||||
},
|
||||
|
||||
|
||||
// title
|
||||
title: function(val) {
|
||||
if (val !== undefined) {
|
||||
this.$el.find('.popover-title-label').html(val);
|
||||
}
|
||||
},
|
||||
|
||||
|
||||
// show
|
||||
show: function () {
|
||||
// show popover
|
||||
@@ -57,107 +56,108 @@ var View = Backbone.View.extend({
|
||||
|
||||
// calculate position
|
||||
var position = this._get_placement(this.options.placement);
|
||||
|
||||
|
||||
// set position
|
||||
this.$el.css(position);
|
||||
},
|
||||
|
||||
// calculate position and error
|
||||
_get_placement: function(placement) {
|
||||
// get popover dimensions
|
||||
var width = this._get_width(this.$el);
|
||||
var height = this.$el.height();
|
||||
|
||||
// get container details
|
||||
var $container = this.options.container;
|
||||
var container_width = this._get_width($container);
|
||||
var container_height = this._get_height($container);
|
||||
var container_position = $container.position();
|
||||
|
||||
// initialize position
|
||||
var top = 0;
|
||||
var left = 0;
|
||||
|
||||
// calculate position
|
||||
if (placement == 'top' || placement == 'bottom') {
|
||||
left = container_position.left - width + (container_width + width) / 2;
|
||||
if (placement == 'top') {
|
||||
top = container_position.top - height - 5;
|
||||
} else {
|
||||
top = container_position.top + container_height + 5;
|
||||
}
|
||||
}
|
||||
|
||||
// return
|
||||
return {top: top, left: left};
|
||||
},
|
||||
|
||||
// width
|
||||
_get_width: function($el) {
|
||||
return $el.width() + parseInt($el.css('padding-left')) + parseInt($el.css('padding-right'))
|
||||
},
|
||||
|
||||
// height
|
||||
_get_height: function($el) {
|
||||
return $el.height() + parseInt($el.css('padding-top')) + parseInt($el.css('padding-bottom'))
|
||||
},
|
||||
|
||||
|
||||
// hide
|
||||
hide: function () {
|
||||
this.$el.hide();
|
||||
this.visible = false;
|
||||
},
|
||||
|
||||
// append
|
||||
|
||||
// append new doms to popover content
|
||||
append: function($el) {
|
||||
this.$el.find('.popover-content').append($el);
|
||||
},
|
||||
|
||||
// empty
|
||||
|
||||
// empty popover content
|
||||
empty: function($el) {
|
||||
this.$el.find('.popover-content').empty();
|
||||
},
|
||||
|
||||
// remove
|
||||
// remove event handler and dom
|
||||
remove: function() {
|
||||
// remove event handler
|
||||
$('body').off('mousedown.' + this.uid);
|
||||
|
||||
// remove element from dom
|
||||
this.$el.remove();
|
||||
},
|
||||
|
||||
|
||||
// calculate position and error
|
||||
_get_placement: function(placement) {
|
||||
// get popover dimensions
|
||||
var width = this._get_width(this.$el);
|
||||
var height = this.$el.height();
|
||||
|
||||
// get container details
|
||||
var $container = this.options.container;
|
||||
var container_width = this._get_width($container);
|
||||
var container_height = this._get_height($container);
|
||||
var container_position = $container.position();
|
||||
|
||||
// initialize position
|
||||
var top = 0;
|
||||
var left = 0;
|
||||
|
||||
// calculate position
|
||||
if (['top', 'bottom'].indexOf(placement) != -1) {
|
||||
left = container_position.left - width + (container_width + width) / 2;
|
||||
switch (placement) {
|
||||
case 'top':
|
||||
top = container_position.top - height - 5;
|
||||
break;
|
||||
case 'bottom':
|
||||
top = container_position.top + container_height + 5;
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
top = container_position.top - height + (container_height + height) / 2;
|
||||
switch (placement) {
|
||||
case 'right':
|
||||
left = container_position.left + container_width;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// return
|
||||
return {top: top, left: left};
|
||||
},
|
||||
|
||||
// width
|
||||
_get_width: function($el) {
|
||||
return $el.width() + parseInt($el.css('padding-left')) + parseInt($el.css('padding-right'))
|
||||
},
|
||||
|
||||
// height
|
||||
_get_height: function($el) {
|
||||
return $el.height() + parseInt($el.css('padding-top')) + parseInt($el.css('padding-bottom'))
|
||||
},
|
||||
|
||||
// remove
|
||||
_hide : function(e) {
|
||||
//the 'is' for buttons that trigger popups
|
||||
//the 'has' for icons within a button that triggers a popup
|
||||
// the 'is' for buttons that trigger popups
|
||||
// the 'has' for icons within a button that triggers a popup
|
||||
if (!$(this.options.container).is(e.target) &&
|
||||
!$(this.el).is(e.target) &&
|
||||
$(this.el).has(e.target).length === 0) {
|
||||
this.hide();
|
||||
}
|
||||
},
|
||||
|
||||
|
||||
// template
|
||||
_template: function(options) {
|
||||
var tmpl = '<div class="ui-popover popover fade ' + options.placement + ' in">' +
|
||||
'<div class="arrow"></div>' +
|
||||
'<div class="arrow"/>' +
|
||||
'<div class="popover-title">' +
|
||||
'<div class="popover-title-label">' +
|
||||
options.title +
|
||||
'</div>';
|
||||
|
||||
// add close icon
|
||||
if (options.with_close) {
|
||||
tmpl += '<div id="close" class="popover-close fa fa-times-circle"></div>';
|
||||
tmpl += '<div id="close" class="popover-close fa fa-times-circle"/>';
|
||||
}
|
||||
|
||||
// finalize
|
||||
tmpl += '</div>' +
|
||||
'<div class="popover-content"></div>' +
|
||||
'<div class="popover-content"/>' +
|
||||
'</div>';
|
||||
|
||||
// return
|
||||
return tmpl;
|
||||
}
|
||||
});
|
||||
|
||||
@@ -1,16 +1,14 @@
|
||||
// dependencies
|
||||
define(['utils/utils', 'mvc/ui/ui-button-check'], function(Utils, ButtonCheck) {
|
||||
|
||||
/**
|
||||
* This class creates/wraps a default html select field as backbone class.
|
||||
*/
|
||||
define(['utils/utils', 'mvc/ui/ui-buttons'], function(Utils, Buttons) {
|
||||
var View = Backbone.View.extend({
|
||||
// options
|
||||
optionsDefault: {
|
||||
id : Utils.uid(),
|
||||
cls : 'ui-select',
|
||||
error_text : 'No data available',
|
||||
empty_text : 'No selection',
|
||||
error_text : 'No options available',
|
||||
empty_text : 'Nothing selected',
|
||||
visible : true,
|
||||
wait : false,
|
||||
multiple : false,
|
||||
@@ -38,7 +36,7 @@ var View = Backbone.View.extend({
|
||||
if (this.options.multiple) {
|
||||
// create select all button
|
||||
if (this.options.searchable) {
|
||||
this.all_button = new ButtonCheck({
|
||||
this.all_button = new Buttons.ButtonCheck({
|
||||
onclick: function() {
|
||||
var new_value = [];
|
||||
if (self.all_button.value() !== 0) {
|
||||
|
||||
@@ -4,7 +4,7 @@ define(['utils/utils', 'mvc/ui/ui-misc', 'mvc/ui/ui-table', 'mvc/ui/ui-list'],
|
||||
|
||||
// collection of libraries
|
||||
var Libraries = Backbone.Collection.extend({
|
||||
url: galaxy_config.root + 'api/libraries'
|
||||
url: galaxy_config.root + 'api/libraries?deleted=false'
|
||||
});
|
||||
|
||||
// collection of dataset
|
||||
|
||||
@@ -68,9 +68,9 @@ var View = Backbone.View.extend({
|
||||
this.$text.on('keydown', function (e) {
|
||||
var v = e.which;
|
||||
pressed[v] = true;
|
||||
if (!(v == 8 || v == 9 || v == 13 || v == 37 || v == 39 || (v >= 48 && v <= 57)
|
||||
|| (v == 190 && $(this).val().indexOf('.') == -1 && self.options.precise)
|
||||
|| (v == 189 && $(this).val().indexOf('-') == -1)
|
||||
if (!(v == 8 || v == 9 || v == 13 || v == 37 || v == 39 || (v >= 48 && v <= 57) || (v >= 96 && v <= 105)
|
||||
|| ((v == 190 || v == 110) && $(this).val().indexOf('.') == -1 && self.options.precise)
|
||||
|| ((v == 189 || v == 109) && $(this).val().indexOf('-') == -1)
|
||||
|| pressed[91] || pressed[17])) {
|
||||
event.preventDefault();
|
||||
}
|
||||
|
||||
@@ -0,0 +1,354 @@
|
||||
// dependencies
|
||||
define(['utils/utils',
|
||||
'mvc/upload/upload-settings',
|
||||
'mvc/upload/upload-ftp',
|
||||
'mvc/ui/ui-popover',
|
||||
'mvc/ui/ui-misc',
|
||||
'mvc/ui/ui-select',
|
||||
'utils/uploadbox'],
|
||||
|
||||
function( Utils,
|
||||
UploadSettings,
|
||||
UploadFtp,
|
||||
Popover,
|
||||
Ui,
|
||||
Select
|
||||
) {
|
||||
|
||||
// renders the composite upload row view
|
||||
return Backbone.View.extend({
|
||||
// states
|
||||
status_classes : {
|
||||
init : 'upload-mode fa fa-exclamation text-primary',
|
||||
ready : 'upload-mode fa fa-check text-success',
|
||||
running : 'upload-mode fa fa-spinner fa-spin',
|
||||
success : 'upload-mode fa fa-check',
|
||||
error : 'upload-mode fa fa-exclamation-triangle'
|
||||
},
|
||||
|
||||
// initialize
|
||||
initialize: function(app, options) {
|
||||
// link app
|
||||
this.app = app;
|
||||
|
||||
// link this
|
||||
var self = this;
|
||||
|
||||
// create model
|
||||
this.model = options.model;
|
||||
|
||||
// add upload row
|
||||
this.setElement(this._template(options.model));
|
||||
|
||||
// build upload functions
|
||||
this.uploadinput = this.$el.uploadinput({
|
||||
ondragover: function() {
|
||||
if (self.model.get('enabled')) {
|
||||
self.$el.addClass('warning');
|
||||
}
|
||||
},
|
||||
ondragleave: function() {
|
||||
self.$el.removeClass('warning');
|
||||
},
|
||||
onchange: function(files) {
|
||||
if (self.model.get('status') != 'running' && files && files.length > 0) {
|
||||
self.model.reset({
|
||||
'file_data': files[0],
|
||||
'file_name': files[0].name,
|
||||
'file_size': files[0].size,
|
||||
'file_mode': files[0].mode || 'local'
|
||||
});
|
||||
self._refreshReady();
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
// source selection popup
|
||||
this.button_menu = new Ui.ButtonMenu({
|
||||
icon : 'fa-caret-down',
|
||||
title : 'Select',
|
||||
pull : 'left'
|
||||
});
|
||||
this.$('#source').append(this.button_menu.$el);
|
||||
this.button_menu.addMenu({
|
||||
icon : 'fa-laptop',
|
||||
title : 'Choose local file',
|
||||
onclick : function() {
|
||||
self.uploadinput.dialog();
|
||||
}
|
||||
});
|
||||
if (this.app.ftp_upload_site) {
|
||||
this.button_menu.addMenu({
|
||||
icon : 'fa-folder-open-o',
|
||||
title : 'Choose FTP file',
|
||||
onclick : function() {
|
||||
self._showFtp();
|
||||
}
|
||||
});
|
||||
}
|
||||
this.button_menu.addMenu({
|
||||
icon : 'fa-edit',
|
||||
title : 'Paste/Fetch data',
|
||||
onclick : function() {
|
||||
self.model.reset({
|
||||
'file_mode': 'new',
|
||||
'file_name': 'New File'
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
// add ftp file viewer
|
||||
this.ftp = new Popover.View({
|
||||
title : 'Choose FTP file:',
|
||||
container : this.$('#source').find('.ui-button-menu'),
|
||||
placement : 'right'
|
||||
});
|
||||
|
||||
// append popup to settings icon
|
||||
this.settings = new Popover.View({
|
||||
title : 'Upload configuration',
|
||||
container : this.$('#settings'),
|
||||
placement : 'bottom'
|
||||
});
|
||||
|
||||
//
|
||||
// ui events
|
||||
//
|
||||
|
||||
// handle text editing event
|
||||
this.$('#text-content').on('change input', function(e) {
|
||||
self.model.set('url_paste', $(e.target).val());
|
||||
self.model.set('file_size', $(e.target).val().length);
|
||||
self._refreshReady();
|
||||
});
|
||||
|
||||
// handle settings popover
|
||||
this.$('#settings').on('click' , function(e) { self._showSettings(); })
|
||||
.on('mousedown', function(e) { e.preventDefault(); });
|
||||
|
||||
//
|
||||
// model events
|
||||
//
|
||||
this.model.on('change:percentage', function() {
|
||||
self._refreshPercentage();
|
||||
});
|
||||
this.model.on('change:status', function() {
|
||||
self._refreshStatus();
|
||||
});
|
||||
this.model.on('change:info', function() {
|
||||
self._refreshInfo();
|
||||
});
|
||||
this.model.on('change:file_name', function() {
|
||||
self._refreshFileName();
|
||||
});
|
||||
this.model.on('change:file_mode', function() {
|
||||
self._refreshMode();
|
||||
});
|
||||
this.model.on('change:file_size', function() {
|
||||
self._refreshFileSize();
|
||||
});
|
||||
this.model.on('remove', function() {
|
||||
self.remove();
|
||||
});
|
||||
this.app.collection.on('reset', function() {
|
||||
self.remove();
|
||||
});
|
||||
},
|
||||
|
||||
// render
|
||||
render: function() {
|
||||
this.$('#file_name').html(this.model.get('file_name') || '-');
|
||||
this.$('#file_desc').html(this.model.get('file_desc') || 'Unavailable');
|
||||
this.$('#file_size').html(Utils.bytesToString (this.model.get('file_size')));
|
||||
this.$('#status').removeClass().addClass(this.status_classes.init);
|
||||
},
|
||||
|
||||
// remove
|
||||
remove: function() {
|
||||
// call the base class remove method
|
||||
Backbone.View.prototype.remove.apply(this);
|
||||
},
|
||||
|
||||
//
|
||||
// handle model events
|
||||
//
|
||||
|
||||
// refresh ready or not states
|
||||
_refreshReady: function() {
|
||||
this.app.collection.each(function(model) {
|
||||
model.set('status', (model.get('file_size') > 0) && 'ready' || 'init');
|
||||
});
|
||||
},
|
||||
|
||||
// refresh mode and e.g. show/hide textarea field
|
||||
_refreshMode: function() {
|
||||
var file_mode = this.model.get('file_mode');
|
||||
if (file_mode == 'new') {
|
||||
this.height = this.$el.height();
|
||||
this.$('#text').css({
|
||||
'width' : this.$el.width() - 16 + 'px',
|
||||
'top' : this.$el.height() - 8 + 'px'
|
||||
}).show();
|
||||
this.$el.height(this.$el.height() - 8 + this.$('#text').height() + 16);
|
||||
this.$('#text-content').val('').trigger('keyup');
|
||||
} else {
|
||||
this.$el.height(this.height);
|
||||
this.$('#text').hide();
|
||||
}
|
||||
},
|
||||
|
||||
// information
|
||||
_refreshInfo: function() {
|
||||
var info = this.model.get('info');
|
||||
if (info) {
|
||||
this.$('#info-text').html('<strong>Failed: </strong>' + info).show();
|
||||
} else {
|
||||
this.$('#info-text').hide();
|
||||
}
|
||||
},
|
||||
|
||||
// percentage
|
||||
_refreshPercentage : function() {
|
||||
var percentage = parseInt(this.model.get('percentage'));
|
||||
if (percentage != 0) {
|
||||
this.$('.progress-bar').css({ width : percentage + '%' });
|
||||
} else {
|
||||
this.$('.progress-bar').addClass('no-transition');
|
||||
this.$('.progress-bar').css({ width : '0%' });
|
||||
this.$('.progress-bar')[0].offsetHeight;
|
||||
this.$('.progress-bar').removeClass('no-transition');
|
||||
}
|
||||
if (percentage != 100) {
|
||||
this.$('#percentage').html(percentage + '%');
|
||||
} else {
|
||||
this.$('#percentage').html('Adding to history...');
|
||||
}
|
||||
},
|
||||
|
||||
// status
|
||||
_refreshStatus : function() {
|
||||
// identify new status
|
||||
var status = this.model.get('status');
|
||||
|
||||
// identify symbol and reset classes
|
||||
this.$('#status').removeClass().addClass(this.status_classes[status]);
|
||||
|
||||
// enable/disable model flag
|
||||
this.model.set('enabled', status != 'running');
|
||||
|
||||
// enable/disable row fields
|
||||
this.$('#text-content').attr('disabled', !this.model.get('enabled'));
|
||||
|
||||
// remove status classes
|
||||
this.$el.removeClass('success danger warning');
|
||||
|
||||
// set status classes
|
||||
if (status == 'running' || status == 'ready') {
|
||||
this.model.set('percentage', 0);
|
||||
}
|
||||
if (status == 'running') {
|
||||
this.$('#source').find('.button').addClass('disabled');
|
||||
} else {
|
||||
this.$('#source').find('.button').removeClass('disabled');
|
||||
}
|
||||
if (status == 'success') {
|
||||
this.$el.addClass('success');
|
||||
this.model.set('percentage', 100);
|
||||
this.$('#percentage').html('100%');
|
||||
}
|
||||
if (status == 'error') {
|
||||
this.$el.addClass('danger');
|
||||
this.model.set('percentage', 0);
|
||||
this.$('#info-progress').hide();
|
||||
this.$('#info-text').show();
|
||||
} else {
|
||||
this.$('#info-progress').show();
|
||||
this.$('#info-text').hide();
|
||||
}
|
||||
},
|
||||
|
||||
// file name
|
||||
_refreshFileName: function() {
|
||||
this.$('#file_name').html(this.model.get('file_name') || '-');
|
||||
},
|
||||
|
||||
// file size
|
||||
_refreshFileSize: function() {
|
||||
this.$('#file_size').html(Utils.bytesToString (this.model.get('file_size')));
|
||||
},
|
||||
|
||||
// show/hide ftp popup
|
||||
_showFtp: function() {
|
||||
if (!this.ftp.visible) {
|
||||
this.ftp.empty();
|
||||
var self = this;
|
||||
this.ftp.append((new UploadFtp({
|
||||
ftp_upload_site: this.app.ftp_upload_site,
|
||||
onchange: function(ftp_file) {
|
||||
self.ftp.hide();
|
||||
if (self.model.get('status') != 'running' && ftp_file) {
|
||||
self.model.reset({
|
||||
'file_mode': 'ftp',
|
||||
'file_name': ftp_file.path,
|
||||
'file_size': ftp_file.size,
|
||||
'file_path': ftp_file.path
|
||||
});
|
||||
self._refreshReady();
|
||||
}
|
||||
}
|
||||
})).$el);
|
||||
this.ftp.show();
|
||||
} else {
|
||||
this.ftp.hide();
|
||||
}
|
||||
},
|
||||
|
||||
// show/hide settings popup
|
||||
_showSettings : function() {
|
||||
if (!this.settings.visible) {
|
||||
this.settings.empty();
|
||||
this.settings.append((new UploadSettings(this)).$el);
|
||||
this.settings.show();
|
||||
} else {
|
||||
this.settings.hide();
|
||||
}
|
||||
},
|
||||
|
||||
// template
|
||||
_template: function(options) {
|
||||
return '<tr id="upload-row-' + options.id + '" class="upload-row">' +
|
||||
'<td>' +
|
||||
'<div id="source"/>' +
|
||||
'<div class="upload-text-column">' +
|
||||
'<div id="text" class="text">' +
|
||||
'<div class="text-info">You can tell Galaxy to download data from web by entering URL in this box (one per line). You can also directly paste the contents of a file.</div>' +
|
||||
'<textarea id="text-content" class="text-content form-control"/>' +
|
||||
'</div>' +
|
||||
'</div>' +
|
||||
'</td>' +
|
||||
'<td>' +
|
||||
'<div id="status"/>' +
|
||||
'</td>' +
|
||||
'<td>' +
|
||||
'<div id="file_desc" class="upload-title"/>' +
|
||||
'</td>' +
|
||||
'<td>' +
|
||||
'<div id="file_name" class="upload-title"/>' +
|
||||
'</td>' +
|
||||
'<td>' +
|
||||
'<div id="file_size" class="upload-size"/>' +
|
||||
'</td>' +
|
||||
'<td><div id="settings" class="upload-icon-button fa fa-gear"/></td>' +
|
||||
'<td>' +
|
||||
'<div id="info" class="upload-info">' +
|
||||
'<div id="info-text"/>' +
|
||||
'<div id="info-progress" class="progress">' +
|
||||
'<div class="progress-bar progress-bar-success"/>' +
|
||||
'<div id="percentage" class="percentage">0%</div>' +
|
||||
'</div>' +
|
||||
'</div>' +
|
||||
'</td>' +
|
||||
'</tr>';
|
||||
}
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,257 @@
|
||||
/** Renders contents of the composite uploader */
|
||||
define(['utils/utils',
|
||||
'mvc/upload/upload-model',
|
||||
'mvc/upload/composite/composite-row',
|
||||
'mvc/ui/ui-popover',
|
||||
'mvc/ui/ui-select',
|
||||
'mvc/ui/ui-misc'],
|
||||
|
||||
function( Utils,
|
||||
UploadModel,
|
||||
UploadRow,
|
||||
Popover,
|
||||
Select,
|
||||
Ui
|
||||
) {
|
||||
|
||||
return Backbone.View.extend({
|
||||
// extension selector
|
||||
select_extension: null,
|
||||
|
||||
// genome selector
|
||||
select_genome: null,
|
||||
|
||||
// collection
|
||||
collection: new UploadModel.Collection(),
|
||||
|
||||
// initialize
|
||||
initialize: function(app) {
|
||||
// link app
|
||||
this.app = app;
|
||||
this.options = app.options;
|
||||
this.list_extensions = app.list_extensions;
|
||||
this.list_genomes = app.list_genomes;
|
||||
this.ftp_upload_site = app.currentFtp();
|
||||
|
||||
// link this
|
||||
var self = this;
|
||||
|
||||
// set element
|
||||
this.setElement(this._template());
|
||||
|
||||
// create button section
|
||||
this.btnStart = new Ui.Button({ title: 'Start', onclick: function() { self._eventStart(); } });
|
||||
this.btnClose = new Ui.Button({ title: 'Close', onclick: function() { self.app.modal.hide(); } });
|
||||
|
||||
// append buttons to dom
|
||||
var buttons = [ this.btnStart, this.btnClose ];
|
||||
for (var i in buttons) {
|
||||
this.$('#upload-buttons').prepend(buttons[i].$el);
|
||||
}
|
||||
|
||||
// select extension
|
||||
this.select_extension = new Select.View({
|
||||
css : 'footer-selection',
|
||||
container : this.$('#footer-extension'),
|
||||
data : _.filter(this.list_extensions, function(ext) { return ext.composite_files }),
|
||||
onchange : function(extension) {
|
||||
self.collection.reset();
|
||||
var details = _.findWhere(self.list_extensions, { id : extension });
|
||||
if (details && details.composite_files) {
|
||||
for (var i in details.composite_files) {
|
||||
var item = details.composite_files[i];
|
||||
self.collection.add({
|
||||
id : self.collection.size(),
|
||||
file_desc : item['description'] || item['name']
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
// handle extension info popover
|
||||
this.$('#footer-extension-info').on('click', function(e) {
|
||||
self._showExtensionInfo({
|
||||
$el : $(e.target),
|
||||
title : self.select_extension.text(),
|
||||
extension : self.select_extension.value(),
|
||||
placement : 'top'
|
||||
});
|
||||
}).on('mousedown', function(e) { e.preventDefault(); });
|
||||
|
||||
// genome extension
|
||||
this.select_genome = new Select.View({
|
||||
css : 'footer-selection',
|
||||
container : this.$('#footer-genome'),
|
||||
data : this.list_genomes,
|
||||
value : this.options.default_genome
|
||||
});
|
||||
|
||||
// listener for collection triggers on change in composite datatype
|
||||
this.collection.on('add', function (model) {
|
||||
self._eventAnnounce(model);
|
||||
});
|
||||
this.collection.on('change add', function() {
|
||||
self._updateScreen();
|
||||
}).trigger('change');
|
||||
|
||||
// trigger initial onchange event
|
||||
this.select_extension.options.onchange(this.select_extension.value());
|
||||
},
|
||||
|
||||
//
|
||||
// upload events / process pipeline
|
||||
//
|
||||
|
||||
// builds the basic ui with placeholder rows for each composite data type file
|
||||
_eventAnnounce: function(model) {
|
||||
// create view/model
|
||||
var upload_row = new UploadRow(this, { model : model });
|
||||
|
||||
// add upload row element to table
|
||||
this.$('#upload-table > tbody:first').append(upload_row.$el);
|
||||
|
||||
// render
|
||||
upload_row.render();
|
||||
|
||||
// table visibility
|
||||
if (this.collection.length > 0) {
|
||||
this.$('#upload-table').show();
|
||||
} else {
|
||||
this.$('#upload-table').hide();
|
||||
}
|
||||
},
|
||||
|
||||
// start upload process
|
||||
_eventStart: function() {
|
||||
var self = this;
|
||||
this.collection.each(function(model) {
|
||||
model.set('genome', self.select_genome.value());
|
||||
model.set('extension', self.select_extension.value());
|
||||
});
|
||||
$.uploadpost({
|
||||
url : this.app.options.nginx_upload_path,
|
||||
data : this.app.toData(this.collection.filter()),
|
||||
success : function(message) { self._eventSuccess(message); },
|
||||
error : function(message) { self._eventError(message); },
|
||||
progress : function(percentage) { self._eventProgress(percentage); }
|
||||
});
|
||||
},
|
||||
|
||||
// progress
|
||||
_eventProgress: function(percentage) {
|
||||
this.collection.each(function(it) { it.set('percentage', percentage); });
|
||||
},
|
||||
|
||||
// success
|
||||
_eventSuccess: function(message) {
|
||||
this.collection.each(function(it) {
|
||||
it.set('status', 'success');
|
||||
});
|
||||
Galaxy.currHistoryPanel.refreshContents();
|
||||
},
|
||||
|
||||
// error
|
||||
_eventError: function(message) {
|
||||
this.collection.each(function(it) {
|
||||
it.set('status', 'error');
|
||||
it.set('info', message);
|
||||
});
|
||||
},
|
||||
|
||||
// display extension info popup
|
||||
_showExtensionInfo: function(options) {
|
||||
// initialize
|
||||
var self = this;
|
||||
var $el = options.$el;
|
||||
var extension = options.extension;
|
||||
var title = options.title;
|
||||
var description = _.findWhere(this.list_extensions, { id : extension });
|
||||
|
||||
// create popup
|
||||
this.extension_popup && this.extension_popup.remove();
|
||||
this.extension_popup = new Popover.View({
|
||||
placement: options.placement || 'bottom',
|
||||
container: $el,
|
||||
destroy: true
|
||||
});
|
||||
|
||||
// add content and show popup
|
||||
this.extension_popup.title(title);
|
||||
this.extension_popup.empty();
|
||||
this.extension_popup.append(this._templateDescription(description));
|
||||
this.extension_popup.show();
|
||||
},
|
||||
|
||||
// set screen
|
||||
_updateScreen: function () {
|
||||
// show start button if components have been selected
|
||||
var model = this.collection.first();
|
||||
if (model && model.get('status') == 'running') {
|
||||
this.select_genome.disable();
|
||||
this.select_extension.disable();
|
||||
} else {
|
||||
this.select_genome.enable();
|
||||
this.select_extension.enable();
|
||||
}
|
||||
if (this.collection.where({ status : 'ready' }).length == this.collection.length && this.collection.length > 0) {
|
||||
this.btnStart.enable();
|
||||
this.btnStart.$el.addClass('btn-primary');
|
||||
} else {
|
||||
this.btnStart.disable();
|
||||
this.btnStart.$el.removeClass('btn-primary');
|
||||
}
|
||||
|
||||
// table visibility
|
||||
if (this.collection.length > 0) {
|
||||
this.$('#upload-table').show();
|
||||
} else {
|
||||
this.$('#upload-table').hide();
|
||||
}
|
||||
},
|
||||
|
||||
// template for extensions description
|
||||
_templateDescription: function(options) {
|
||||
if (options.description) {
|
||||
var tmpl = options.description;
|
||||
if (options.description_url) {
|
||||
tmpl += ' (<a href="' + options.description_url + '" target="_blank">read more</a>)';
|
||||
}
|
||||
return tmpl;
|
||||
} else {
|
||||
return 'There is no description available for this file extension.';
|
||||
}
|
||||
},
|
||||
|
||||
// load html template
|
||||
_template: function() {
|
||||
return '<div class="upload-view-composite">' +
|
||||
'<div id="upload-footer" class="upload-footer">' +
|
||||
'<span class="footer-title">Composite Type:</span>' +
|
||||
'<span id="footer-extension"/>' +
|
||||
'<span id="footer-extension-info" class="upload-icon-button fa fa-search"/> ' +
|
||||
'<span class="footer-title">Genome/Build:</span>' +
|
||||
'<span id="footer-genome"/>' +
|
||||
'</div>' +
|
||||
'<div id="upload-box" class="upload-box">' +
|
||||
'<table id="upload-table" class="ui-table-striped" style="display: none;">' +
|
||||
'<thead>' +
|
||||
'<tr>' +
|
||||
'<th/>' +
|
||||
'<th/>' +
|
||||
'<th>Description</th>' +
|
||||
'<th>Name</th>' +
|
||||
'<th>Size</th>' +
|
||||
'<th>Settings</th>' +
|
||||
'<th>Status</th>' +
|
||||
'</tr>' +
|
||||
'</thead>' +
|
||||
'<tbody></tbody>' +
|
||||
'</table>' +
|
||||
'</div>' +
|
||||
'<div id="upload-buttons" class="upload-buttons"/>' +
|
||||
'</div>';
|
||||
}
|
||||
});
|
||||
|
||||
});
|
||||
+55
-99
@@ -12,13 +12,8 @@ define(['utils/utils',
|
||||
Select
|
||||
) {
|
||||
|
||||
// item view
|
||||
// row view
|
||||
return Backbone.View.extend({
|
||||
// options
|
||||
options: {
|
||||
padding : 8
|
||||
},
|
||||
|
||||
// states
|
||||
status_classes : {
|
||||
init : 'upload-icon-button fa fa-trash-o',
|
||||
@@ -46,18 +41,15 @@ return Backbone.View.extend({
|
||||
var self = this;
|
||||
|
||||
// create model
|
||||
this.model = new UploadModel.Model(options);
|
||||
this.model = options.model;
|
||||
|
||||
// add upload item
|
||||
this.setElement(this._template(options));
|
||||
|
||||
// link item
|
||||
var it = this.$el;
|
||||
// add upload row
|
||||
this.setElement(this._template(options.model));
|
||||
|
||||
// append popup to settings icon
|
||||
this.settings = new Popover.View({
|
||||
title : 'Upload configuration',
|
||||
container : it.find('#settings'),
|
||||
container : this.$('#settings'),
|
||||
placement : 'bottom'
|
||||
});
|
||||
|
||||
@@ -66,13 +58,13 @@ return Backbone.View.extend({
|
||||
|
||||
// select genomes
|
||||
this.select_genome = new Select.View({
|
||||
css: 'genome',
|
||||
css: 'upload-genome',
|
||||
onchange : function(genome) {
|
||||
self.model.set('genome', genome);
|
||||
self.app.updateGenome(genome, true);
|
||||
},
|
||||
data: self.app.list_genomes,
|
||||
container: it.find('#genome'),
|
||||
container: this.$('#genome'),
|
||||
value: default_genome
|
||||
});
|
||||
|
||||
@@ -84,13 +76,13 @@ return Backbone.View.extend({
|
||||
|
||||
// select extension
|
||||
this.select_extension = new Select.View({
|
||||
css: 'extension',
|
||||
css: 'upload-extension',
|
||||
onchange : function(extension) {
|
||||
self.model.set('extension', extension);
|
||||
self.app.updateExtension(extension, true);
|
||||
},
|
||||
data: self.app.list_extensions,
|
||||
container: it.find('#extension'),
|
||||
container: this.$('#extension'),
|
||||
value: default_extension
|
||||
});
|
||||
|
||||
@@ -102,10 +94,10 @@ return Backbone.View.extend({
|
||||
//
|
||||
|
||||
// handle click event
|
||||
it.find('#symbol').on('click', function() { self._removeRow(); });
|
||||
this.$('#symbol').on('click', function() { self._removeRow(); });
|
||||
|
||||
// handle extension info popover
|
||||
it.find('#extension-info').on('click' , function(e) {
|
||||
this.$('#extension-info').on('click' , function(e) {
|
||||
self.app.showExtensionInfo({
|
||||
$el : $(e.target),
|
||||
title : self.select_extension.text(),
|
||||
@@ -114,20 +106,15 @@ return Backbone.View.extend({
|
||||
}).on('mousedown', function(e) { e.preventDefault(); });
|
||||
|
||||
// handle settings popover
|
||||
it.find('#settings').on('click' , function(e) { self._showSettings(); })
|
||||
this.$('#settings').on('click' , function(e) { self._showSettings(); })
|
||||
.on('mousedown', function(e) { e.preventDefault(); });
|
||||
|
||||
// handle text editing event
|
||||
it.find('#text-content').on('keyup', function(e) {
|
||||
this.$('#text-content').on('change input', function(e) {
|
||||
self.model.set('url_paste', $(e.target).val());
|
||||
self.model.set('file_size', $(e.target).val().length);
|
||||
});
|
||||
|
||||
// handle space to tabs button
|
||||
it.find('#space_to_tabs').on('change', function(e) {
|
||||
self.model.set('space_to_tabs', $(e.target).prop('checked'));
|
||||
});
|
||||
|
||||
//
|
||||
// model events
|
||||
//
|
||||
@@ -164,54 +151,36 @@ return Backbone.View.extend({
|
||||
var file_size = this.model.get('file_size');
|
||||
var file_mode = this.model.get('file_mode');
|
||||
|
||||
// link item
|
||||
var it = this.$el;
|
||||
|
||||
// update title
|
||||
it.find('#title').html(file_name);
|
||||
this.$('#title').html(file_name);
|
||||
|
||||
// update info
|
||||
it.find('#size').html(Utils.bytesToString (file_size));
|
||||
this.$('#size').html(Utils.bytesToString (file_size));
|
||||
|
||||
// remove mode class
|
||||
it.find('#mode')
|
||||
this.$('#mode')
|
||||
.removeClass()
|
||||
.addClass('mode');
|
||||
.addClass('upload-mode')
|
||||
.addClass('text-primary');
|
||||
|
||||
// activate text field if file is new
|
||||
if (file_mode == 'new') {
|
||||
// get text component
|
||||
var text = it.find('#text');
|
||||
|
||||
// get padding
|
||||
var padding = this.options.padding;
|
||||
|
||||
// get dimensions
|
||||
var width = it.width() - 2 * padding;
|
||||
var height = it.height() - padding;
|
||||
|
||||
// set dimensions
|
||||
text.css('width', width + 'px');
|
||||
text.css('top', height + 'px');
|
||||
it.height(height + text.height() + 2 * padding);
|
||||
|
||||
// show text field
|
||||
text.show();
|
||||
|
||||
// update icon
|
||||
it.find('#mode').addClass('fa fa-pencil');
|
||||
this.$('#text').css({
|
||||
'width' : this.$el.width() - 16 + 'px',
|
||||
'top' : this.$el.height() - 8 + 'px'
|
||||
}).show();
|
||||
this.$el.height(this.$el.height() - 8 + this.$('#text').height() + 16);
|
||||
this.$('#mode').addClass('fa fa-edit');
|
||||
}
|
||||
|
||||
// file from local disk
|
||||
if (file_mode == 'local') {
|
||||
// update icon
|
||||
it.find('#mode').addClass('fa fa-laptop');
|
||||
this.$('#mode').addClass('fa fa-laptop');
|
||||
}
|
||||
|
||||
// file from ftp
|
||||
if (file_mode == 'ftp') {
|
||||
// update icon
|
||||
it.find('#mode').addClass('fa fa-code-fork');
|
||||
this.$('#mode').addClass('fa fa-folder-open-o');
|
||||
}
|
||||
},
|
||||
|
||||
@@ -244,74 +213,61 @@ return Backbone.View.extend({
|
||||
// write error message
|
||||
var info = this.model.get('info');
|
||||
if (info) {
|
||||
this.$el.find('#info').html('<strong>Failed: </strong>' + info).show();
|
||||
this.$('#info').html('<strong>Failed: </strong>' + info).show();
|
||||
} else {
|
||||
this.$el.find('#info').hide();
|
||||
this.$('#info').hide();
|
||||
}
|
||||
},
|
||||
|
||||
// progress
|
||||
_refreshPercentage : function() {
|
||||
var percentage = parseInt(this.model.get('percentage'));
|
||||
this.$el.find('.progress-bar').css({ width : percentage + '%' });
|
||||
this.$('.progress-bar').css({ width : percentage + '%' });
|
||||
if (percentage != 100)
|
||||
this.$el.find('#percentage').html(percentage + '%');
|
||||
this.$('#percentage').html(percentage + '%');
|
||||
else
|
||||
this.$el.find('#percentage').html('Adding to history...');
|
||||
this.$('#percentage').html('Adding to history...');
|
||||
},
|
||||
|
||||
// status
|
||||
_refreshStatus : function() {
|
||||
// get element
|
||||
var it = this.$el;
|
||||
|
||||
// identify new status
|
||||
var status = this.model.get('status');
|
||||
var status_class = this.status_classes[status];
|
||||
|
||||
// identify symbol and reset classes
|
||||
var sy = this.$el.find('#symbol');
|
||||
sy.removeClass();
|
||||
this.$('#symbol').removeClass().addClass(this.status_classes[status]);
|
||||
|
||||
// set new status class
|
||||
sy.addClass(status_class);
|
||||
// enable/disable model flag
|
||||
this.model.set('enabled', status == 'init');
|
||||
|
||||
// enable form fields
|
||||
if (status == 'init') {
|
||||
// select fields
|
||||
// enable/disable row fields
|
||||
var enabled = this.model.get('enabled');
|
||||
this.$('#text-content').attr('disabled', !enabled);
|
||||
if (enabled) {
|
||||
this.select_genome.enable();
|
||||
this.select_extension.enable();
|
||||
|
||||
// default fields
|
||||
it.find('#text-content').attr('disabled', false);
|
||||
it.find('#space_to_tabs').attr('disabled', false);
|
||||
} else {
|
||||
// select fields
|
||||
this.select_genome.disable();
|
||||
this.select_extension.disable();
|
||||
|
||||
// default fields
|
||||
it.find('#text-content').attr('disabled', true);
|
||||
it.find('#space_to_tabs').attr('disabled', true);
|
||||
}
|
||||
|
||||
// success
|
||||
if (status == 'success') {
|
||||
it.addClass('success');
|
||||
it.find('#percentage').html('100%');
|
||||
this.$el.addClass('success');
|
||||
this.$('#percentage').html('100%');
|
||||
}
|
||||
|
||||
// error
|
||||
if (status == 'error') {
|
||||
it.addClass('danger');
|
||||
it.find('.progress').remove();
|
||||
this.$el.addClass('danger');
|
||||
this.$('.progress').remove();
|
||||
}
|
||||
},
|
||||
|
||||
// refresh size
|
||||
_refreshFileSize: function() {
|
||||
var count = this.model.get('file_size');
|
||||
this.$el.find('#size').html(Utils.bytesToString (count));
|
||||
this.$('#size').html(Utils.bytesToString (count));
|
||||
},
|
||||
|
||||
//
|
||||
@@ -345,38 +301,38 @@ return Backbone.View.extend({
|
||||
|
||||
// template
|
||||
_template: function(options) {
|
||||
return '<tr id="upload-item-' + options.id + '" class="upload-item">' +
|
||||
return '<tr id="upload-row-' + options.id + '" class="upload-row">' +
|
||||
'<td>' +
|
||||
'<div class="name-column">' +
|
||||
'<div id="mode"></div>' +
|
||||
'<div id="title" class="title"></div>' +
|
||||
'<div class="upload-text-column">' +
|
||||
'<div id="mode"/>' +
|
||||
'<div id="title" class="upload-title"/>' +
|
||||
'<div id="text" class="text">' +
|
||||
'<div class="text-info">You can tell Galaxy to download data from web by entering URL in this box (one per line). You can also directly paste the contents of a file.</div>' +
|
||||
'<textarea id="text-content" class="text-content form-control"></textarea>' +
|
||||
'<textarea id="text-content" class="text-content form-control"/>' +
|
||||
'</div>' +
|
||||
'</div>' +
|
||||
'</td>' +
|
||||
'<td>' +
|
||||
'<div id="size" class="size"></div>' +
|
||||
'<div id="size" class="upload-size"/>' +
|
||||
'</td>' +
|
||||
'<td>' +
|
||||
'<div id="extension" class="extension" style="float: left;"/>  ' +
|
||||
'<div id="extension" class="upload-extension" style="float: left;"/>  ' +
|
||||
'<div id="extension-info" class="upload-icon-button fa fa-search"/>' +
|
||||
'</td>' +
|
||||
'<td>' +
|
||||
'<div id="genome" class="genome" />' +
|
||||
'<div id="genome" class="upload-genome"/>' +
|
||||
'</td>' +
|
||||
'<td><div id="settings" class="upload-icon-button fa fa-gear"></div>' +
|
||||
'<td><div id="settings" class="upload-icon-button fa fa-gear"/></td>' +
|
||||
'<td>' +
|
||||
'<div id="info" class="info">' +
|
||||
'<div id="info" class="upload-info">' +
|
||||
'<div class="progress">' +
|
||||
'<div class="progress-bar progress-bar-success"></div>' +
|
||||
'<div class="progress-bar progress-bar-success"/>' +
|
||||
'<div id="percentage" class="percentage">0%</div>' +
|
||||
'</div>' +
|
||||
'</div>' +
|
||||
'</td>' +
|
||||
'<td>' +
|
||||
'<div id="symbol" class="' + this.status_classes.init + '"></div>' +
|
||||
'<div id="symbol" class="' + this.status_classes.init + '"/>' +
|
||||
'</td>' +
|
||||
'</tr>';
|
||||
}
|
||||
@@ -0,0 +1,568 @@
|
||||
/** Renders contents of the default uploader */
|
||||
define(['utils/utils',
|
||||
'mvc/upload/upload-model',
|
||||
'mvc/upload/default/default-row',
|
||||
'mvc/upload/upload-ftp',
|
||||
'mvc/ui/ui-popover',
|
||||
'mvc/ui/ui-select',
|
||||
'mvc/ui/ui-misc',
|
||||
'utils/uploadbox'],
|
||||
|
||||
function( Utils,
|
||||
UploadModel,
|
||||
UploadRow,
|
||||
UploadFtp,
|
||||
Popover,
|
||||
Select,
|
||||
Ui
|
||||
) {
|
||||
|
||||
return Backbone.View.extend({
|
||||
// extension selector
|
||||
select_extension : null,
|
||||
|
||||
// genome selector
|
||||
select_genome: null,
|
||||
|
||||
// jquery uploadbox plugin
|
||||
uploadbox: null,
|
||||
|
||||
// current upload size in bytes
|
||||
upload_size: 0,
|
||||
|
||||
// contains upload row models
|
||||
collection : new UploadModel.Collection(),
|
||||
|
||||
// ftp file viewer
|
||||
ftp : null,
|
||||
|
||||
// keeps track of the current uploader state
|
||||
counter : {
|
||||
announce : 0,
|
||||
success : 0,
|
||||
error : 0,
|
||||
running : 0,
|
||||
reset : function() {
|
||||
this.announce = this.success = this.error = this.running = 0;
|
||||
}
|
||||
},
|
||||
|
||||
// initialize
|
||||
initialize : function(app) {
|
||||
// link app
|
||||
this.app = app;
|
||||
this.options = app.options;
|
||||
this.list_extensions = app.list_extensions;
|
||||
this.list_genomes = app.list_genomes;
|
||||
this.ui_button = app.ui_button;
|
||||
this.ftp_upload_site = app.currentFtp();
|
||||
|
||||
// link this
|
||||
var self = this;
|
||||
|
||||
// set element
|
||||
this.setElement(this._template());
|
||||
|
||||
// create button section
|
||||
this.btnLocal = new Ui.Button({ id: 'btn-local', title: 'Choose local file', onclick: function() { self.uploadbox.select(); }, icon: 'fa fa-laptop' });
|
||||
this.btnFtp = new Ui.Button({ id: 'btn-ftp', title: 'Choose FTP file', onclick: function() { self._eventFtp(); }, icon: 'fa fa-folder-open-o' });
|
||||
this.btnCreate = new Ui.Button({ id: 'btn-new', title: 'Paste/Fetch data', onclick: function() { self._eventCreate(); }, icon: 'fa fa-edit' });
|
||||
this.btnStart = new Ui.Button({ id: 'btn-start', title: 'Start', onclick: function() { self._eventStart(); } });
|
||||
this.btnStop = new Ui.Button({ id: 'btn-stop', title: 'Pause', onclick: function() { self._eventStop(); } });
|
||||
this.btnReset = new Ui.Button({ id: 'btn-reset', title: 'Reset', onclick: function() { self._eventReset(); } });
|
||||
this.btnClose = new Ui.Button({ id: 'btn-close', title: 'Close', onclick: function() { self.app.modal.hide(); } });
|
||||
|
||||
// append buttons to dom
|
||||
var buttons = [ this.btnLocal, this.btnFtp, this.btnCreate, this.btnStop, this.btnReset, this.btnStart, this.btnClose ];
|
||||
for (var i in buttons) {
|
||||
this.$('#upload-buttons').prepend(buttons[i].$el);
|
||||
}
|
||||
|
||||
// file upload
|
||||
var self = this;
|
||||
this.uploadbox = this.$('#upload-box').uploadbox({
|
||||
url : this.app.options.nginx_upload_path,
|
||||
announce : function(index, file) { self._eventAnnounce(index, file) },
|
||||
initialize : function(index) { return self.app.toData([ self.collection.get(index) ], self.history_id) },
|
||||
progress : function(index, percentage) { self._eventProgress(index, percentage) },
|
||||
success : function(index, message) { self._eventSuccess(index, message) },
|
||||
error : function(index, message) { self._eventError(index, message) },
|
||||
complete : function() { self._eventComplete() },
|
||||
ondragover : function() { self.$('.upload-box').addClass('highlight'); },
|
||||
ondragleave : function() { self.$('.upload-box').removeClass('highlight'); }
|
||||
});
|
||||
|
||||
// add ftp file viewer
|
||||
this.ftp = new Popover.View({
|
||||
title : 'FTP files',
|
||||
container : this.btnFtp.$el
|
||||
});
|
||||
|
||||
// select extension
|
||||
this.select_extension = new Select.View({
|
||||
css : 'footer-selection',
|
||||
container : this.$('#footer-extension'),
|
||||
data : _.filter(this.list_extensions, function(ext) { return !ext.composite_files }),
|
||||
value : this.options.default_extension,
|
||||
onchange : function(extension) {
|
||||
self.updateExtension(extension);
|
||||
}
|
||||
});
|
||||
|
||||
// handle extension info popover
|
||||
self.$('#footer-extension-info').on('click', function(e) {
|
||||
self.showExtensionInfo({
|
||||
$el : $(e.target),
|
||||
title : self.select_extension.text(),
|
||||
extension : self.select_extension.value(),
|
||||
placement : 'top'
|
||||
});
|
||||
}).on('mousedown', function(e) { e.preventDefault(); });
|
||||
|
||||
// genome extension
|
||||
this.select_genome = new Select.View({
|
||||
css : 'footer-selection',
|
||||
container : this.$('#footer-genome'),
|
||||
data : this.list_genomes,
|
||||
value : this.options.default_genome,
|
||||
onchange : function(genome) {
|
||||
self.updateGenome(genome);
|
||||
}
|
||||
});
|
||||
|
||||
// events
|
||||
this.collection.on('remove', function(model) {
|
||||
self._eventRemove(model);
|
||||
});
|
||||
|
||||
// setup info
|
||||
this._updateScreen();
|
||||
},
|
||||
|
||||
//
|
||||
// events triggered by the upload box plugin
|
||||
//
|
||||
|
||||
// a new file has been dropped/selected through the uploadbox plugin
|
||||
_eventAnnounce: function(index, file) {
|
||||
// update counter
|
||||
this.counter.announce++;
|
||||
|
||||
// create if model has not been created yet
|
||||
var new_model = new UploadModel.Model({
|
||||
id : index,
|
||||
file_name : file.name,
|
||||
file_size : file.size,
|
||||
file_mode : file.mode || 'local',
|
||||
file_path : file.path,
|
||||
file_data : file
|
||||
});
|
||||
|
||||
// add model to collection
|
||||
this.collection.add(new_model);
|
||||
|
||||
// create view/model
|
||||
var upload_row = new UploadRow(this, { model: new_model });
|
||||
|
||||
// add upload row element to table
|
||||
this.$('#upload-table > tbody:first').append(upload_row.$el);
|
||||
|
||||
// show on screen info
|
||||
this._updateScreen();
|
||||
|
||||
// render
|
||||
upload_row.render();
|
||||
},
|
||||
|
||||
// progress
|
||||
_eventProgress: function(index, percentage) {
|
||||
// set progress for row
|
||||
var it = this.collection.get(index);
|
||||
it.set('percentage', percentage);
|
||||
|
||||
// update ui button
|
||||
this.ui_button.set('percentage', this._uploadPercentage(percentage, it.get('file_size')));
|
||||
},
|
||||
|
||||
// success
|
||||
_eventSuccess: function(index, message) {
|
||||
// update status
|
||||
var it = this.collection.get(index);
|
||||
it.set('percentage', 100);
|
||||
it.set('status', 'success');
|
||||
|
||||
// update ui button
|
||||
this.ui_button.set('percentage', this._uploadPercentage(100, it.get('file_size')));
|
||||
|
||||
// update completed
|
||||
this.upload_completed += it.get('file_size') * 100;
|
||||
|
||||
// update counter
|
||||
this.counter.announce--;
|
||||
this.counter.success++;
|
||||
|
||||
// update on screen info
|
||||
this._updateScreen();
|
||||
|
||||
// update galaxy history
|
||||
Galaxy.currHistoryPanel.refreshContents();
|
||||
},
|
||||
|
||||
// error
|
||||
_eventError: function(index, message) {
|
||||
// get element
|
||||
var it = this.collection.get(index);
|
||||
|
||||
// update status
|
||||
it.set('percentage', 100);
|
||||
it.set('status', 'error');
|
||||
it.set('info', message);
|
||||
|
||||
// update ui button
|
||||
this.ui_button.set('percentage', this._uploadPercentage(100, it.get('file_size')));
|
||||
this.ui_button.set('status', 'danger');
|
||||
|
||||
// update completed
|
||||
this.upload_completed += it.get('file_size') * 100;
|
||||
|
||||
// update counter
|
||||
this.counter.announce--;
|
||||
this.counter.error++;
|
||||
|
||||
// update on screen info
|
||||
this._updateScreen();
|
||||
},
|
||||
|
||||
// queue is done
|
||||
_eventComplete: function() {
|
||||
// reset queued upload to initial status
|
||||
this.collection.each(function(model) {
|
||||
if(model.get('status') == 'queued') {
|
||||
model.set('status', 'init');
|
||||
}
|
||||
});
|
||||
|
||||
// update running
|
||||
this.counter.running = 0;
|
||||
|
||||
// update on screen info
|
||||
this._updateScreen();
|
||||
},
|
||||
|
||||
//
|
||||
// events triggered by collection
|
||||
//
|
||||
|
||||
// remove model from upload list
|
||||
_eventRemove: function(model) {
|
||||
// update status
|
||||
var status = model.get('status');
|
||||
|
||||
// reduce counter
|
||||
if (status == 'success') {
|
||||
this.counter.success--;
|
||||
} else if (status == 'error') {
|
||||
this.counter.error--;
|
||||
} else {
|
||||
this.counter.announce--;
|
||||
}
|
||||
|
||||
// remove from queue
|
||||
this.uploadbox.remove(model.id);
|
||||
|
||||
// show on screen info
|
||||
this._updateScreen();
|
||||
},
|
||||
|
||||
//
|
||||
// events triggered by this view
|
||||
//
|
||||
|
||||
// [public] display extension info popup
|
||||
showExtensionInfo: function(options) {
|
||||
// initialize
|
||||
var self = this;
|
||||
var $el = options.$el;
|
||||
var extension = options.extension;
|
||||
var title = options.title;
|
||||
var description = _.findWhere(self.list_extensions, {'id': extension});
|
||||
|
||||
// create popup
|
||||
this.extension_popup && this.extension_popup.remove();
|
||||
this.extension_popup = new Popover.View({
|
||||
placement: options.placement || 'bottom',
|
||||
container: $el,
|
||||
destroy: true
|
||||
});
|
||||
|
||||
// add content and show popup
|
||||
this.extension_popup.title(title);
|
||||
this.extension_popup.empty();
|
||||
this.extension_popup.append(this._templateDescription(description));
|
||||
this.extension_popup.show();
|
||||
},
|
||||
|
||||
// show/hide ftp popup
|
||||
_eventFtp: function() {
|
||||
if (!this.ftp.visible) {
|
||||
this.ftp.empty();
|
||||
var self = this;
|
||||
this.ftp.append((new UploadFtp({
|
||||
collection : this.collection,
|
||||
ftp_upload_site : this.ftp_upload_site,
|
||||
onadd: function(ftp_file) {
|
||||
self.uploadbox.add([{
|
||||
mode: 'ftp',
|
||||
name: ftp_file.path,
|
||||
size: ftp_file.size,
|
||||
path: ftp_file.path
|
||||
}]);
|
||||
},
|
||||
onremove: function(model_index) {
|
||||
self.collection.remove(model_index);
|
||||
}
|
||||
})).$el);
|
||||
this.ftp.show();
|
||||
} else {
|
||||
this.ftp.hide();
|
||||
}
|
||||
},
|
||||
|
||||
// create a new file
|
||||
_eventCreate: function (){
|
||||
this.uploadbox.add([{
|
||||
name : 'New File',
|
||||
size : 0,
|
||||
mode : 'new'
|
||||
}]);
|
||||
},
|
||||
|
||||
// start upload process
|
||||
_eventStart: function() {
|
||||
// check
|
||||
if (this.counter.announce == 0 || this.counter.running > 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
// reset current size
|
||||
var self = this;
|
||||
this.upload_size = 0;
|
||||
this.upload_completed = 0;
|
||||
// switch icons for new uploads
|
||||
this.collection.each(function(model) {
|
||||
if(model.get('status') == 'init') {
|
||||
model.set('status', 'queued');
|
||||
self.upload_size += model.get('file_size');
|
||||
}
|
||||
});
|
||||
|
||||
// reset progress
|
||||
this.ui_button.set('percentage', 0);
|
||||
this.ui_button.set('status', 'success');
|
||||
|
||||
// update running
|
||||
this.counter.running = this.counter.announce;
|
||||
|
||||
// set current history id
|
||||
this.history_id = this.app.currentHistory();
|
||||
|
||||
// initiate upload procedure in plugin
|
||||
this.uploadbox.start();
|
||||
|
||||
// update on screen info
|
||||
this._updateScreen();
|
||||
},
|
||||
|
||||
// pause upload process
|
||||
_eventStop: function() {
|
||||
// check
|
||||
if (this.counter.running > 0) {
|
||||
// show upload has paused
|
||||
this.ui_button.set('status', 'info');
|
||||
|
||||
// set html content
|
||||
$('#upload-info').html('Queue will pause after completing the current file...');
|
||||
|
||||
// request pause
|
||||
this.uploadbox.stop();
|
||||
}
|
||||
},
|
||||
|
||||
// remove all
|
||||
_eventReset: function() {
|
||||
// make sure queue is not running
|
||||
if (this.counter.running == 0){
|
||||
// reset collection
|
||||
this.collection.reset();
|
||||
|
||||
// reset counter
|
||||
this.counter.reset();
|
||||
|
||||
// remove from queue
|
||||
this.uploadbox.reset();
|
||||
|
||||
// reset value for universal type drop-down
|
||||
this.select_extension.value(this.options.default_extension);
|
||||
this.select_genome.value(this.options.default_genome);
|
||||
|
||||
// reset button
|
||||
this.ui_button.set('percentage', 0);
|
||||
|
||||
// show on screen info
|
||||
this._updateScreen();
|
||||
}
|
||||
},
|
||||
|
||||
// update extension for all models
|
||||
updateExtension: function(extension, defaults_only) {
|
||||
var self = this;
|
||||
this.collection.each(function(model) {
|
||||
if (model.get('status') == 'init' && (model.get('extension') == self.options.default_extension || !defaults_only)) {
|
||||
model.set('extension', extension);
|
||||
}
|
||||
});
|
||||
},
|
||||
|
||||
// update genome for all models
|
||||
updateGenome: function(genome, defaults_only) {
|
||||
var self = this;
|
||||
this.collection.each(function(model) {
|
||||
if (model.get('status') == 'init' && (model.get('genome') == self.options.default_genome || !defaults_only)) {
|
||||
model.set('genome', genome);
|
||||
}
|
||||
});
|
||||
},
|
||||
|
||||
// set screen
|
||||
_updateScreen: function () {
|
||||
/*
|
||||
update on screen info
|
||||
*/
|
||||
|
||||
// check default message
|
||||
if(this.counter.announce == 0) {
|
||||
if (this.uploadbox.compatible()) {
|
||||
message = ' ';
|
||||
} else {
|
||||
message = 'Browser does not support Drag & Drop. Try Firefox 4+, Chrome 7+, IE 10+, Opera 12+ or Safari 6+.';
|
||||
}
|
||||
} else {
|
||||
if (this.counter.running == 0) {
|
||||
message = 'You added ' + this.counter.announce + ' file(s) to the queue. Add more files or click \'Start\' to proceed.';
|
||||
} else {
|
||||
message = 'Please wait...' + this.counter.announce + ' out of ' + this.counter.running + ' remaining.';
|
||||
}
|
||||
}
|
||||
|
||||
// set html content
|
||||
this.$('#upload-info').html(message);
|
||||
|
||||
/*
|
||||
update button status
|
||||
*/
|
||||
|
||||
// update reset button
|
||||
if (this.counter.running == 0 && this.counter.announce + this.counter.success + this.counter.error > 0) {
|
||||
this.btnReset.enable();
|
||||
} else {
|
||||
this.btnReset.disable();
|
||||
}
|
||||
|
||||
// update upload button
|
||||
if (this.counter.running == 0 && this.counter.announce > 0) {
|
||||
this.btnStart.enable();
|
||||
this.btnStart.$el.addClass('btn-primary');
|
||||
} else {
|
||||
this.btnStart.disable();
|
||||
this.btnStart.$el.removeClass('btn-primary');
|
||||
}
|
||||
|
||||
// pause upload button
|
||||
if (this.counter.running > 0) {
|
||||
this.btnStop.enable();
|
||||
} else {
|
||||
this.btnStop.disable();
|
||||
}
|
||||
|
||||
// select upload button
|
||||
if (this.counter.running == 0) {
|
||||
this.btnLocal.enable();
|
||||
this.btnFtp.enable();
|
||||
this.btnCreate.enable();
|
||||
} else {
|
||||
this.btnLocal.disable();
|
||||
this.btnFtp.disable();
|
||||
this.btnCreate.disable();
|
||||
}
|
||||
|
||||
// ftp button
|
||||
if (this.ftp_upload_site) {
|
||||
this.btnFtp.$el.show();
|
||||
} else {
|
||||
this.btnFtp.$el.hide();
|
||||
}
|
||||
|
||||
// table visibility
|
||||
if (this.counter.announce + this.counter.success + this.counter.error > 0) {
|
||||
this.$('#upload-table').show();
|
||||
this.$('.upload-helper').hide();
|
||||
} else {
|
||||
this.$('#upload-table').hide();
|
||||
this.$('.upload-helper').show();
|
||||
}
|
||||
},
|
||||
|
||||
// calculate percentage of all queued uploads
|
||||
_uploadPercentage: function(percentage, size) {
|
||||
return (this.upload_completed + (percentage * size)) / this.upload_size;
|
||||
},
|
||||
|
||||
// template for extensions description
|
||||
_templateDescription: function(options) {
|
||||
if (options.description) {
|
||||
var tmpl = options.description;
|
||||
if (options.description_url) {
|
||||
tmpl += ' (<a href="' + options.description_url + '" target="_blank">read more</a>)';
|
||||
}
|
||||
return tmpl;
|
||||
} else {
|
||||
return 'There is no description available for this file extension.';
|
||||
}
|
||||
},
|
||||
|
||||
// load html template
|
||||
_template: function() {
|
||||
return '<div class="upload-view-default">' +
|
||||
'<div class="upload-top">' +
|
||||
'<h6 id="upload-info" class="upload-info"/>' +
|
||||
'</div>' +
|
||||
'<div id="upload-box" class="upload-box">' +
|
||||
'<div class="upload-helper"><i class="fa fa-files-o"/>Drop files here</div>' +
|
||||
'<table id="upload-table" class="ui-table-striped" style="display: none;">' +
|
||||
'<thead>' +
|
||||
'<tr>' +
|
||||
'<th>Name</th>' +
|
||||
'<th>Size</th>' +
|
||||
'<th>Type</th>' +
|
||||
'<th>Genome</th>' +
|
||||
'<th>Settings</th>' +
|
||||
'<th>Status</th>' +
|
||||
'<th/>' +
|
||||
'</tr>' +
|
||||
'</thead>' +
|
||||
'<tbody/>' +
|
||||
'</table>' +
|
||||
'</div>' +
|
||||
'<div id="upload-footer" class="upload-footer">' +
|
||||
'<span class="footer-title">Type (set all):</span>' +
|
||||
'<span id="footer-extension"/>' +
|
||||
'<span id="footer-extension-info" class="upload-icon-button fa fa-search"/> ' +
|
||||
'<span class="footer-title">Genome (set all):</span>' +
|
||||
'<span id="footer-genome"/>' +
|
||||
'</div>' +
|
||||
'<div id="upload-buttons" class="upload-buttons"/>' +
|
||||
'</div>';
|
||||
}
|
||||
});
|
||||
|
||||
});
|
||||
@@ -1,101 +1,95 @@
|
||||
// dependencies
|
||||
define([], function() {
|
||||
|
||||
// create model
|
||||
// model for upload/progress bar button
|
||||
var Model = Backbone.Model.extend({
|
||||
defaults: {
|
||||
icon : 'fa-upload',
|
||||
tooltip : 'Download from URL or upload files from disk',
|
||||
label : 'Load Data',
|
||||
percentage : 0,
|
||||
icon : 'fa-circle',
|
||||
label : '',
|
||||
status : ''
|
||||
}
|
||||
});
|
||||
|
||||
// progress bar button on ui
|
||||
// view for upload/progress bar button
|
||||
var View = Backbone.View.extend({
|
||||
// model
|
||||
model : null,
|
||||
|
||||
// initialize
|
||||
initialize : function(model) {
|
||||
initialize : function( options ) {
|
||||
// link this
|
||||
var self = this;
|
||||
|
||||
// create model
|
||||
this.model = model;
|
||||
|
||||
// get options
|
||||
this.options = this.model.attributes;
|
||||
var model = options.model;
|
||||
|
||||
// create new element
|
||||
this.setElement(this._template(this.options));
|
||||
this.setElement( this._template() );
|
||||
|
||||
// add event
|
||||
$(this.el).on('click', this.options.onclick);
|
||||
this.$el.on( 'click', function( e ) { options.onclick( e ); });
|
||||
|
||||
// add tooltip
|
||||
if (this.options.tooltip) {
|
||||
$(this.el).tooltip({title: this.options.tooltip, placement: 'bottom'});
|
||||
}
|
||||
this.$el.tooltip( { title: model.get('tooltip'), placement: 'bottom' } );
|
||||
|
||||
// events
|
||||
this.model.on('change:percentage', function() {
|
||||
self._percentage(self.model.get('percentage'));
|
||||
model.on( 'change:percentage', function() {
|
||||
self._percentage( model.get( 'percentage' ) );
|
||||
});
|
||||
this.model.on('change:status', function() {
|
||||
self._status(self.model.get('status'));
|
||||
model.on( 'change:status', function() {
|
||||
self._status( model.get( 'status' ) );
|
||||
});
|
||||
|
||||
// unload event
|
||||
var self = this;
|
||||
$(window).on('beforeunload', function() {
|
||||
$( window ).on( 'beforeunload', function() {
|
||||
var text = "";
|
||||
if (self.options.onunload) {
|
||||
text = self.options.onunload();
|
||||
if ( options.onunload ) {
|
||||
text = options.onunload();
|
||||
}
|
||||
if (text != "") {
|
||||
if ( text != "" ) {
|
||||
return text;
|
||||
}
|
||||
});
|
||||
},
|
||||
|
||||
// set status
|
||||
_status: function(value) {
|
||||
var $el = this.$el.find('.progress-bar');
|
||||
_status: function( value ) {
|
||||
var $el = this.$el.find( '.progress-bar' );
|
||||
$el.removeClass();
|
||||
$el.addClass('progress-bar');
|
||||
$el.addClass('progress-bar-notransition');
|
||||
if (value != '') {
|
||||
$el.addClass('progress-bar-' + value);
|
||||
$el.addClass( 'progress-bar' );
|
||||
$el.addClass( 'progress-bar-notransition' );
|
||||
if ( value != '' ) {
|
||||
$el.addClass( 'progress-bar-' + value );
|
||||
}
|
||||
},
|
||||
|
||||
// set percentage
|
||||
_percentage: function(value) {
|
||||
var $el = this.$el.find('.progress-bar');
|
||||
$el.css({ width : value + '%' });
|
||||
_percentage: function( value ) {
|
||||
var $el = this.$el.find( '.progress-bar' );
|
||||
$el.css( { width : value + '%' } );
|
||||
},
|
||||
|
||||
// template
|
||||
_template: function(options) {
|
||||
return '<div style="float: right">' +
|
||||
'<div class="upload-button">' +
|
||||
'<div class="progress">' +
|
||||
'<div class="progress-bar"></div>' +
|
||||
'</div>' +
|
||||
'<div id="label" class="label">' +
|
||||
'<a class="panel-header-button" href="javascript:void(0)">' +
|
||||
'<span class="fa fa-upload"></span>' +
|
||||
'</a>' +
|
||||
'</div>' +
|
||||
_template: function() {
|
||||
return '<div class="upload-button">' +
|
||||
'<div class="progress">' +
|
||||
'<div class="progress-bar"></div>' +
|
||||
'</div>' +
|
||||
'<div id="label" class="label">' +
|
||||
'<a class="panel-header-button" href="javascript:void(0)">' +
|
||||
'<span class="fa fa-upload"></span>' +
|
||||
'</a>' +
|
||||
'</div>' +
|
||||
'</div>';
|
||||
}
|
||||
});
|
||||
|
||||
return {
|
||||
Model: Model,
|
||||
View: View
|
||||
Model : Model,
|
||||
View : View
|
||||
};
|
||||
|
||||
});
|
||||
|
||||
@@ -1,23 +1,25 @@
|
||||
// dependencies
|
||||
/** This renders the content of the ftp popup **/
|
||||
define(['utils/utils'], function(Utils) {
|
||||
|
||||
// item view
|
||||
return Backbone.View.extend({
|
||||
// options
|
||||
options: {
|
||||
class_add : 'upload-icon-button fa fa-square-o',
|
||||
class_remove : 'upload-icon-button fa fa-check-square-o',
|
||||
class_partial : 'upload-icon-button fa fa-minus-square-o'
|
||||
},
|
||||
|
||||
// render
|
||||
initialize: function(app) {
|
||||
// link app
|
||||
this.app = app;
|
||||
initialize: function(options) {
|
||||
// link options
|
||||
this.options = Utils.merge(options, {
|
||||
class_add : 'upload-icon-button fa fa-square-o',
|
||||
class_remove : 'upload-icon-button fa fa-check-square-o',
|
||||
class_partial : 'upload-icon-button fa fa-minus-square-o',
|
||||
collection : null,
|
||||
onchange : function() {},
|
||||
onadd : function() {},
|
||||
onremove : function() {}
|
||||
});
|
||||
|
||||
// link this
|
||||
var self = this;
|
||||
|
||||
// link app
|
||||
this.collection = this.options.collection;
|
||||
|
||||
// set template
|
||||
this.setElement(this._template());
|
||||
|
||||
@@ -34,7 +36,7 @@ return Backbone.View.extend({
|
||||
|
||||
// events
|
||||
events: {
|
||||
'mousedown' : function(e) { e.preventDefault(); }
|
||||
'mousedown': function(e) { e.preventDefault(); }
|
||||
},
|
||||
|
||||
// fill table
|
||||
@@ -45,9 +47,9 @@ return Backbone.View.extend({
|
||||
|
||||
// add files to table
|
||||
var size = 0;
|
||||
for (key in ftp_files) {
|
||||
this.rows.push(this._add(ftp_files[key]));
|
||||
size += ftp_files[key].size;
|
||||
for (index in ftp_files) {
|
||||
this.rows.push(this._add(ftp_files[index]));
|
||||
size += ftp_files[index].size;
|
||||
}
|
||||
|
||||
// update stats
|
||||
@@ -55,28 +57,26 @@ return Backbone.View.extend({
|
||||
this.$el.find('#upload-ftp-disk').html(Utils.bytesToString (size, true));
|
||||
|
||||
// add event handler to select/unselect all
|
||||
this.$select_all = $('#upload-selectall');
|
||||
this.$select_all.addClass(this.options.class_add);
|
||||
var self = this;
|
||||
this.$select_all.on('click', function() {
|
||||
var add = self.$select_all.hasClass(self.options.class_add);
|
||||
for (key in ftp_files) {
|
||||
var ftp_file = ftp_files[key];
|
||||
var model_index = self._find(ftp_file);
|
||||
if(!model_index && add || model_index && !add) {
|
||||
self.rows[key].trigger('click');
|
||||
if (this.collection) {
|
||||
var self = this;
|
||||
this.$('._has_collection').show();
|
||||
this.$select_all = $('#upload-selectall');
|
||||
this.$select_all.addClass(this.options.class_add);
|
||||
this.$select_all.on('click', function() {
|
||||
var add = self.$select_all.hasClass(self.options.class_add);
|
||||
for (index in ftp_files) {
|
||||
var ftp_file = ftp_files[index];
|
||||
var model_index = self._find(ftp_file);
|
||||
if(!model_index && add || model_index && !add) {
|
||||
self.rows[index].trigger('click');
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
// refresh
|
||||
self._refresh();
|
||||
});
|
||||
this._refresh();
|
||||
}
|
||||
} else {
|
||||
// add info
|
||||
this.$el.find('#upload-ftp-content').html($(this._templateInfo()));
|
||||
}
|
||||
|
||||
// hide spinner
|
||||
this.$el.find('#upload-ftp-wait').hide();
|
||||
},
|
||||
|
||||
@@ -94,48 +94,36 @@ return Backbone.View.extend({
|
||||
// append to table
|
||||
$(this.el).find('tbody').append($it);
|
||||
|
||||
// find model and set initial 'add' icon class
|
||||
var icon_class = '';
|
||||
if (this._find(ftp_file)) {
|
||||
icon_class = this.options.class_remove;
|
||||
} else {
|
||||
icon_class = this.options.class_add;
|
||||
}
|
||||
|
||||
// add icon class
|
||||
$icon.addClass(icon_class);
|
||||
|
||||
// click to add ftp files
|
||||
$it.on('click', function() {
|
||||
// find model
|
||||
var model_index = self._find(ftp_file);
|
||||
|
||||
// update icon
|
||||
$icon.removeClass();
|
||||
|
||||
// add model
|
||||
if (!model_index) {
|
||||
// add to uploadbox
|
||||
self.app.uploadbox.add([{
|
||||
mode : 'ftp',
|
||||
name : ftp_file.path,
|
||||
size : ftp_file.size,
|
||||
path : ftp_file.path
|
||||
}]);
|
||||
|
||||
// add new icon class
|
||||
$icon.addClass(self.options.class_remove);
|
||||
// collection mode with add/remove triggers
|
||||
if (this.collection) {
|
||||
// find model and set initial 'add' icon class
|
||||
var icon_class = '';
|
||||
if (this._find(ftp_file)) {
|
||||
icon_class = this.options.class_remove;
|
||||
} else {
|
||||
// remove
|
||||
self.app.collection.remove(model_index);
|
||||
|
||||
// add new icon class
|
||||
$icon.addClass(self.options.class_add);
|
||||
icon_class = this.options.class_add;
|
||||
}
|
||||
$icon.addClass(icon_class);
|
||||
|
||||
// update select all checkbox
|
||||
self._refresh();
|
||||
});
|
||||
// click triggers add/remove events
|
||||
$it.on('click', function() {
|
||||
var model_index = self._find(ftp_file);
|
||||
$icon.removeClass();
|
||||
if (!model_index) {
|
||||
self.options.onadd(ftp_file);
|
||||
$icon.addClass(self.options.class_remove);
|
||||
} else {
|
||||
self.options.onremove(model_index);
|
||||
$icon.addClass(self.options.class_add);
|
||||
}
|
||||
self._refresh();
|
||||
});
|
||||
} else {
|
||||
// click triggers only change
|
||||
$it.on('click', function() {
|
||||
self.options.onchange(ftp_file);
|
||||
});
|
||||
}
|
||||
|
||||
// return dom handler
|
||||
return $it;
|
||||
@@ -143,7 +131,7 @@ return Backbone.View.extend({
|
||||
|
||||
// refresh
|
||||
_refresh: function() {
|
||||
var filtered = this.app.collection.where({file_mode : 'ftp'});
|
||||
var filtered = this.collection.where({file_mode: 'ftp', enabled: true});
|
||||
this.$select_all.removeClass();
|
||||
if (filtered.length == 0) {
|
||||
this.$select_all.addClass(this.options.class_add);
|
||||
@@ -158,24 +146,21 @@ return Backbone.View.extend({
|
||||
|
||||
// get model index
|
||||
_find: function(ftp_file) {
|
||||
var item = this.app.collection.findWhere({
|
||||
var item = this.collection.findWhere({
|
||||
file_path : ftp_file.path,
|
||||
status : 'init',
|
||||
file_mode : 'ftp'
|
||||
file_mode : 'ftp',
|
||||
enabled : true
|
||||
});
|
||||
if (item) {
|
||||
return item.get('id');
|
||||
}
|
||||
return null;
|
||||
return item && item.get('id');
|
||||
},
|
||||
|
||||
// template row
|
||||
_templateRow: function(options) {
|
||||
return '<tr class="upload-ftp-row">' +
|
||||
'<td><div class="icon"/></td>' +
|
||||
'<td class="label"><p>' + options.path + '</p></td>' +
|
||||
'<td class="nonlabel">' + Utils.bytesToString(options.size) + '</td>' +
|
||||
'<td class="nonlabel">' + options.ctime + '</td>' +
|
||||
'<td class="_has_collection" style="display: none;"><div class="icon"/></td>' +
|
||||
'<td class="ftp-name">' + options.path + '</td>' +
|
||||
'<td class="ftp-size">' + Utils.bytesToString(options.size) + '</td>' +
|
||||
'<td class="ftp-time">' + options.ctime + '</td>' +
|
||||
'</tr>';
|
||||
},
|
||||
|
||||
@@ -191,13 +176,13 @@ return Backbone.View.extend({
|
||||
'<table class="grid" style="float: left;">' +
|
||||
'<thead>' +
|
||||
'<tr>' +
|
||||
'<th><div id="upload-selectall"></th>' +
|
||||
'<th class="_has_collection" style="display: none;"><div id="upload-selectall"></th>' +
|
||||
'<th>Name</th>' +
|
||||
'<th>Size</th>' +
|
||||
'<th>Created</th>' +
|
||||
'</tr>' +
|
||||
'</thead>' +
|
||||
'<tbody></tbody>' +
|
||||
'<tbody/>' +
|
||||
'</table>';
|
||||
},
|
||||
|
||||
@@ -212,8 +197,8 @@ return Backbone.View.extend({
|
||||
_template: function() {
|
||||
return '<div class="upload-ftp">' +
|
||||
'<div id="upload-ftp-wait" class="upload-ftp-wait fa fa-spinner fa-spin"/>' +
|
||||
'<div class="upload-ftp-help">This Galaxy server allows you to upload files via FTP. To upload some files, log in to the FTP server at <strong>' + this.app.options.ftp_upload_site + '</strong> using your Galaxy credentials (email address and password).</div>' +
|
||||
'<div id="upload-ftp-content"></div>' +
|
||||
'<div class="upload-ftp-help">This Galaxy server allows you to upload files via FTP. To upload some files, log in to the FTP server at <strong>' + this.options.ftp_upload_site + '</strong> using your Galaxy credentials (email address and password).</div>' +
|
||||
'<div id="upload-ftp-content"/>' +
|
||||
'<div>';
|
||||
}
|
||||
});
|
||||
|
||||
@@ -9,13 +9,19 @@ var Model = Backbone.Model.extend({
|
||||
url_paste : '',
|
||||
status : 'init',
|
||||
info : null,
|
||||
file_mode : 'local',
|
||||
file_name : '',
|
||||
file_mode : '',
|
||||
file_size : 0,
|
||||
file_type : null,
|
||||
file_path : '',
|
||||
file_data : null,
|
||||
percentage : 0,
|
||||
space_to_tabs : false,
|
||||
to_posix_lines : true
|
||||
space_to_tab : false,
|
||||
to_posix_lines : true,
|
||||
enabled : true
|
||||
},
|
||||
reset: function(attr) {
|
||||
this.clear().set(this.defaults).set(attr);
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
@@ -1,7 +1,5 @@
|
||||
// dependencies
|
||||
/** This renders the content of the settings popup, allowing users to specify flags i.e. for space-to-tab conversion **/
|
||||
define(['utils/utils'], function(Utils) {
|
||||
|
||||
// item view
|
||||
return Backbone.View.extend({
|
||||
// options
|
||||
options: {
|
||||
@@ -9,7 +7,7 @@ return Backbone.View.extend({
|
||||
class_uncheck : 'upload-icon-button fa fa-square-o'
|
||||
},
|
||||
|
||||
// render
|
||||
// initialize
|
||||
initialize: function(app) {
|
||||
// link app
|
||||
this.app = app;
|
||||
@@ -23,13 +21,13 @@ return Backbone.View.extend({
|
||||
// link model
|
||||
this.model = this.app.model;
|
||||
|
||||
// ui event: space to tabs
|
||||
this.$el.find('#upload-space-to-tabs').on('click', function() {
|
||||
self._switchState('#upload-space-to-tabs', 'space_to_tabs');
|
||||
// ui event: space-to-tab
|
||||
this.$('#upload-space-to-tab').on('click', function() {
|
||||
self._switchState('#upload-space-to-tab', 'space_to_tab');
|
||||
});
|
||||
|
||||
// ui event: to posix
|
||||
this.$el.find('#upload-to-posix-lines').on('click', function() {
|
||||
// ui event: to-posix-lines
|
||||
this.$('#upload-to-posix-lines').on('click', function() {
|
||||
self._switchState('#upload-to-posix-lines', 'to_posix_lines');
|
||||
});
|
||||
|
||||
@@ -45,12 +43,12 @@ return Backbone.View.extend({
|
||||
// render
|
||||
render: function() {
|
||||
// render states
|
||||
this._renderState('#upload-space-to-tabs', this.model.get('space_to_tabs'));
|
||||
this._renderState('#upload-space-to-tab', this.model.get('space_to_tab'));
|
||||
this._renderState('#upload-to-posix-lines', this.model.get('to_posix_lines'));
|
||||
|
||||
// disable options
|
||||
var $cover = this.$el.find('#upload-settings-cover');
|
||||
if (this.model.get('status') != 'init') {
|
||||
var $cover = this.$('#upload-settings-cover');
|
||||
if (!this.model.get('enabled')) {
|
||||
$cover.show();
|
||||
} else {
|
||||
$cover.hide();
|
||||
@@ -59,7 +57,7 @@ return Backbone.View.extend({
|
||||
|
||||
// switch state
|
||||
_switchState: function (element_id, parameter_id) {
|
||||
if (this.model.get('status') == 'init') {
|
||||
if (this.model.get('enabled')) {
|
||||
var checked = !this.model.get(parameter_id);
|
||||
this.model.set(parameter_id, checked);
|
||||
this._renderState(element_id, checked);
|
||||
@@ -68,8 +66,7 @@ return Backbone.View.extend({
|
||||
|
||||
// render state
|
||||
_renderState: function (element_id, checked) {
|
||||
// swith icon class
|
||||
var $it = this.$el.find(element_id);
|
||||
var $it = this.$(element_id);
|
||||
$it.removeClass();
|
||||
if (checked) {
|
||||
$it.addClass(this.options.class_check);
|
||||
@@ -82,10 +79,10 @@ return Backbone.View.extend({
|
||||
_template: function() {
|
||||
return '<div class="upload-settings" style="position: relative;">' +
|
||||
'<div id="upload-settings-cover" class="upload-settings-cover"/>' +
|
||||
'<table class="table table-striped">' +
|
||||
'<table class="ui-table-striped">' +
|
||||
'<tbody>' +
|
||||
'<tr>' +
|
||||
'<td><div id="upload-space-to-tabs"/></td>' +
|
||||
'<td><div id="upload-space-to-tab"/></td>' +
|
||||
'<td>Convert spaces to tabs</td>' +
|
||||
'</tr>' +
|
||||
'<tr>' +
|
||||
|
||||
@@ -1,173 +1,118 @@
|
||||
// dependencies
|
||||
define(["utils/utils",
|
||||
"mvc/upload/upload-button",
|
||||
"mvc/upload/upload-model",
|
||||
"mvc/upload/upload-row",
|
||||
"mvc/upload/upload-ftp",
|
||||
"mvc/ui/ui-popover",
|
||||
"mvc/ui/ui-modal",
|
||||
"mvc/ui/ui-select",
|
||||
"utils/uploadbox"],
|
||||
|
||||
/** Upload app contains the upload progress button and upload modal, compiles model data for API request **/
|
||||
define(['utils/utils',
|
||||
'mvc/ui/ui-modal',
|
||||
'mvc/ui/ui-tabs',
|
||||
'mvc/upload/upload-button',
|
||||
'mvc/upload/default/default-view',
|
||||
'mvc/upload/composite/composite-view'],
|
||||
function( Utils,
|
||||
UploadButton,
|
||||
UploadModel,
|
||||
UploadItem,
|
||||
UploadFtp,
|
||||
Popover,
|
||||
Modal,
|
||||
Select
|
||||
) {
|
||||
|
||||
// galaxy upload
|
||||
Tabs,
|
||||
UploadButton,
|
||||
UploadViewDefault,
|
||||
UploadViewComposite ) {
|
||||
return Backbone.View.extend({
|
||||
// options
|
||||
// default options
|
||||
options : {
|
||||
nginx_upload_path : ''
|
||||
},
|
||||
|
||||
// default for selection fields
|
||||
default_extension : 'auto',
|
||||
default_genome : '?',
|
||||
|
||||
// own modal
|
||||
modal : null,
|
||||
|
||||
// button
|
||||
ui_button : null,
|
||||
|
||||
// jquery uploadbox plugin
|
||||
uploadbox: null,
|
||||
|
||||
// current history
|
||||
current_history: null,
|
||||
|
||||
// extension selector
|
||||
select_extension : null,
|
||||
|
||||
// genome selector
|
||||
select_genome: null,
|
||||
|
||||
// current upload size
|
||||
upload_size: 0,
|
||||
|
||||
// extension types
|
||||
list_extensions :[],
|
||||
|
||||
// genomes
|
||||
list_genomes : [],
|
||||
|
||||
// datatype placeholder for auto-detection
|
||||
auto: {
|
||||
id : 'auto',
|
||||
text : 'Auto-detect',
|
||||
description : 'This system will try to detect the file type automatically. If your file is not detected properly as one of the known formats, it most likely means that it has some format problems (e.g., different number of columns on different rows). You can still coerce the system to set your data to the format you think it should be. You can also upload compressed files, which will automatically be decompressed.'
|
||||
},
|
||||
|
||||
// collection
|
||||
collection : new UploadModel.Collection(),
|
||||
|
||||
// ftp file viewer
|
||||
ftp : null,
|
||||
|
||||
// counter
|
||||
counter : {
|
||||
// stats
|
||||
announce : 0,
|
||||
success : 0,
|
||||
error : 0,
|
||||
running : 0,
|
||||
|
||||
// reset stats
|
||||
reset : function() {
|
||||
this.announce = this.success = this.error = this.running = 0;
|
||||
nginx_upload_path : '',
|
||||
ftp_upload_site : 'n/a',
|
||||
default_genome : '?',
|
||||
default_extension : 'auto',
|
||||
height : 500,
|
||||
width : 900,
|
||||
auto : {
|
||||
id : 'auto',
|
||||
text : 'Auto-detect',
|
||||
description : 'This system will try to detect the file type automatically. If your file is not detected properly as one of the known formats, it most likely means that it has some format problems (e.g., different number of columns on different rows). You can still coerce the system to set your data to the format you think it should be. You can also upload compressed files, which will automatically be decompressed.'
|
||||
}
|
||||
},
|
||||
|
||||
// upload modal container
|
||||
modal: null,
|
||||
|
||||
// progress button in panel
|
||||
ui_button: null,
|
||||
|
||||
// current history identifier
|
||||
current_history: null,
|
||||
|
||||
// contains all available dataset extensions/types
|
||||
list_extensions: [],
|
||||
|
||||
// contains all available genomes
|
||||
list_genomes: [],
|
||||
|
||||
// initialize
|
||||
initialize : function(options) {
|
||||
initialize: function( options ) {
|
||||
// link this
|
||||
var self = this;
|
||||
|
||||
// read in options
|
||||
if (options) {
|
||||
this.options = _.defaults(options, this.options);
|
||||
}
|
||||
// merge parsed options
|
||||
this.options = Utils.merge( options, this.options );
|
||||
|
||||
// create model
|
||||
this.ui_button = new UploadButton.Model({
|
||||
icon : 'fa-upload',
|
||||
tooltip : 'Download from URL or upload files from disk',
|
||||
label : 'Load Data',
|
||||
// create model for upload/progress button
|
||||
this.ui_button = new UploadButton.Model();
|
||||
|
||||
// create view for upload/progress button
|
||||
this.ui_button_view = new UploadButton.View({
|
||||
model : this.ui_button,
|
||||
onclick : function(e) {
|
||||
if (e) {
|
||||
// prevent default
|
||||
e.preventDefault();
|
||||
|
||||
// show
|
||||
self.show()
|
||||
}
|
||||
e.preventDefault();
|
||||
self.show()
|
||||
},
|
||||
onunload : function() {
|
||||
if (self.counter.running > 0) {
|
||||
return "Several uploads are still processing.";
|
||||
var percentage = self.ui_button.get('percentage', 0);
|
||||
if (percentage > 0 && percentage < 100) {
|
||||
return 'Several uploads are queued.';
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
// define location
|
||||
$('.with-upload-button').append((new UploadButton.View(this.ui_button)).$el);
|
||||
// set element to button view
|
||||
this.setElement( this.ui_button_view.$el );
|
||||
|
||||
// load extension
|
||||
// load extensions
|
||||
var self = this;
|
||||
Utils.get({
|
||||
url : galaxy_config.root + "api/datatypes?extension_only=False",
|
||||
success : function(datatypes) {
|
||||
for (key in datatypes) {
|
||||
url : galaxy_config.root + 'api/datatypes?extension_only=False',
|
||||
success : function( datatypes ) {
|
||||
for ( key in datatypes ) {
|
||||
self.list_extensions.push({
|
||||
id : datatypes[key].extension,
|
||||
text : datatypes[key].extension,
|
||||
description : datatypes[key].description,
|
||||
description_url : datatypes[key].description_url
|
||||
id : datatypes[ key ].extension,
|
||||
text : datatypes[ key ].extension,
|
||||
description : datatypes[ key ].description,
|
||||
description_url : datatypes[ key ].description_url,
|
||||
composite_files : datatypes[ key ].composite_files
|
||||
});
|
||||
}
|
||||
|
||||
// sort
|
||||
self.list_extensions.sort(function(a, b) {
|
||||
return a.text > b.text ? 1 : a.text < b.text ? -1 : 0;
|
||||
self.list_extensions.sort( function( a, b ) {
|
||||
var a_text = a.text && a.text.toLowerCase();
|
||||
var b_text = b.text && b.text.toLowerCase();
|
||||
return a_text > b_text ? 1 : a_text < b_text ? -1 : 0;
|
||||
});
|
||||
|
||||
// add auto field
|
||||
if (!self.options.datatypes_disable_auto) {
|
||||
self.list_extensions.unshift(self.auto);
|
||||
if ( !self.options.datatypes_disable_auto ) {
|
||||
self.list_extensions.unshift( self.options.auto );
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
// load genomes
|
||||
Utils.get({
|
||||
url : galaxy_config.root + "api/genomes",
|
||||
success : function(genomes) {
|
||||
for (key in genomes) {
|
||||
url : galaxy_config.root + 'api/genomes',
|
||||
success : function( genomes ) {
|
||||
for ( key in genomes ) {
|
||||
self.list_genomes.push({
|
||||
id : genomes[key][1],
|
||||
text : genomes[key][0]
|
||||
id : genomes[ key ][ 1 ],
|
||||
text : genomes[ key ][ 0 ]
|
||||
});
|
||||
}
|
||||
|
||||
// sort
|
||||
self.list_genomes.sort(function(a, b) {
|
||||
if (a.id == self.default_genome) { return -1; }
|
||||
if (b.id == self.default_genome) { return 1; }
|
||||
self.list_genomes.sort( function( a, b ) {
|
||||
if ( a.id == self.options.default_genome ) { return -1; }
|
||||
if ( b.id == self.options.default_genome ) { return 1; }
|
||||
return a.text > b.text ? 1 : a.text < b.text ? -1 : 0;
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
// events
|
||||
this.collection.on('remove', function(item) {
|
||||
self._eventRemove(item);
|
||||
});
|
||||
},
|
||||
|
||||
//
|
||||
@@ -178,558 +123,106 @@ return Backbone.View.extend({
|
||||
show: function () {
|
||||
// wait for galaxy history panel
|
||||
var self = this;
|
||||
if (!Galaxy.currHistoryPanel || !Galaxy.currHistoryPanel.model) {
|
||||
if ( !Galaxy.currHistoryPanel || !Galaxy.currHistoryPanel.model ) {
|
||||
window.setTimeout(function() { self.show() }, 500)
|
||||
return;
|
||||
}
|
||||
|
||||
// set current user
|
||||
this.current_user = Galaxy.user.id;
|
||||
|
||||
// create modal
|
||||
if (!this.modal) {
|
||||
if ( !this.modal ) {
|
||||
// build tabs
|
||||
this.tabs = new Tabs.View();
|
||||
|
||||
// add tabs
|
||||
this.default_view = new UploadViewDefault( this );
|
||||
this.tabs.add({
|
||||
id : 'regular',
|
||||
title : 'Regular',
|
||||
$el : this.default_view.$el
|
||||
});
|
||||
this.composite_view = new UploadViewComposite( this );
|
||||
this.tabs.add({
|
||||
id : 'composite',
|
||||
title : 'Composite',
|
||||
$el : this.composite_view.$el
|
||||
});
|
||||
|
||||
// make modal
|
||||
var self = this;
|
||||
this.modal = new Modal.View({
|
||||
title : 'Download data directly from web or upload files from your disk',
|
||||
body : this._template('upload-box', 'upload-info'),
|
||||
buttons : {
|
||||
'Choose local file' : function() {self.uploadbox.select()},
|
||||
'Choose FTP file' : function() {self._eventFtp()},
|
||||
'Paste/Fetch data' : function() {self._eventCreate()},
|
||||
'Start' : function() {self._eventStart()},
|
||||
'Pause' : function() {self._eventStop()},
|
||||
'Reset' : function() {self._eventReset()},
|
||||
'Close' : function() {self.modal.hide()},
|
||||
},
|
||||
height : '400',
|
||||
width : '900',
|
||||
closing_events : true
|
||||
});
|
||||
|
||||
// set element
|
||||
this.setElement('#upload-box');
|
||||
|
||||
// file upload
|
||||
var self = this;
|
||||
this.uploadbox = this.$el.uploadbox({
|
||||
announce : function(index, file, message) { self._eventAnnounce(index, file, message) },
|
||||
initialize : function(index, file, message) { return self._eventInitialize(index, file, message) },
|
||||
progress : function(index, file, message) { self._eventProgress(index, file, message) },
|
||||
success : function(index, file, message) { self._eventSuccess(index, file, message) },
|
||||
error : function(index, file, message) { self._eventError(index, file, message) },
|
||||
complete : function() { self._eventComplete() }
|
||||
});
|
||||
|
||||
// add ftp file viewer
|
||||
var button = this.modal.getButton('Choose FTP file');
|
||||
this.ftp = new Popover.View({
|
||||
title : 'FTP files',
|
||||
container : button
|
||||
});
|
||||
|
||||
// select extension
|
||||
this.select_extension = new Select.View({
|
||||
css : 'header-selection',
|
||||
data : this.list_extensions,
|
||||
container : this.$el.parent().find('#header-extension'),
|
||||
value : this.default_extension,
|
||||
onchange : function(extension) {
|
||||
self.updateExtension(extension);
|
||||
}
|
||||
});
|
||||
|
||||
// handle extension info popover
|
||||
self.$el.parent().find('#header-extension-info').on('click' , function(e) {
|
||||
self.showExtensionInfo({
|
||||
$el : $(e.target),
|
||||
title : self.select_extension.text(),
|
||||
extension : self.select_extension.value(),
|
||||
placement : 'top'
|
||||
});
|
||||
}).on('mousedown', function(e) { e.preventDefault(); });
|
||||
|
||||
// genome extension
|
||||
this.select_genome = new Select.View({
|
||||
css : 'header-selection',
|
||||
data : this.list_genomes,
|
||||
container : this.$el.parent().find('#header-genome'),
|
||||
value : this.default_genome,
|
||||
onchange : function(genome) {
|
||||
self.updateGenome(genome);
|
||||
}
|
||||
title : 'Download from web or upload from disk',
|
||||
body : this.tabs.$el,
|
||||
height : this.options.height,
|
||||
width : this.options.width,
|
||||
closing_events : true,
|
||||
title_separator : false
|
||||
});
|
||||
}
|
||||
|
||||
// show modal
|
||||
this.modal.show();
|
||||
|
||||
// refresh
|
||||
this._updateUser();
|
||||
|
||||
// setup info
|
||||
this._updateScreen();
|
||||
},
|
||||
|
||||
showExtensionInfo : function(options) {
|
||||
// initialize
|
||||
var self = this;
|
||||
var $el = options.$el;
|
||||
var extension = options.extension;
|
||||
var title = options.title;
|
||||
var description = _.findWhere(self.list_extensions, {'id': extension});
|
||||
|
||||
// create popup
|
||||
this.extension_popup && this.extension_popup.remove();
|
||||
this.extension_popup = new Popover.View({
|
||||
placement: options.placement || 'bottom',
|
||||
container: $el,
|
||||
destroy: true
|
||||
});
|
||||
|
||||
// add content and show popup
|
||||
this.extension_popup.title(title);
|
||||
this.extension_popup.empty();
|
||||
this.extension_popup.append(this._templateDescription(description));
|
||||
this.extension_popup.show();
|
||||
// refresh user and current history
|
||||
currentHistory: function() {
|
||||
return this.current_user && Galaxy.currHistoryPanel.model.get( 'id' );
|
||||
},
|
||||
|
||||
//
|
||||
// events triggered by collection
|
||||
//
|
||||
|
||||
// remove item from upload list
|
||||
_eventRemove : function(item) {
|
||||
// update status
|
||||
var status = item.get('status');
|
||||
|
||||
// reduce counter
|
||||
if (status == 'success') {
|
||||
this.counter.success--;
|
||||
} else if (status == 'error') {
|
||||
this.counter.error--;
|
||||
} else {
|
||||
this.counter.announce--;
|
||||
}
|
||||
|
||||
// show on screen info
|
||||
this._updateScreen();
|
||||
|
||||
// remove from queue
|
||||
this.uploadbox.remove(item.id);
|
||||
// get ftp configuration
|
||||
currentFtp: function() {
|
||||
return this.current_user && this.options.ftp_upload_site;
|
||||
},
|
||||
|
||||
//
|
||||
// events triggered by the upload box plugin
|
||||
//
|
||||
|
||||
// a new file has been dropped/selected through the uploadbox plugin
|
||||
_eventAnnounce : function(index, file, message) {
|
||||
// update counter
|
||||
this.counter.announce++;
|
||||
|
||||
// update screen
|
||||
this._updateScreen();
|
||||
|
||||
// create view/model
|
||||
var upload_item = new UploadItem(this, {
|
||||
id : index,
|
||||
file_name : file.name,
|
||||
file_size : file.size,
|
||||
file_mode : file.mode,
|
||||
file_path : file.path
|
||||
});
|
||||
|
||||
// add to collection
|
||||
this.collection.add(upload_item.model);
|
||||
|
||||
// add upload item element to table
|
||||
$(this.el).find('tbody:first').append(upload_item.$el);
|
||||
|
||||
// render
|
||||
upload_item.render();
|
||||
},
|
||||
|
||||
// the uploadbox plugin is initializing the upload for this file
|
||||
_eventInitialize : function(index, file, message) {
|
||||
// get element
|
||||
var it = this.collection.get(index);
|
||||
|
||||
// update status
|
||||
it.set('status', 'running');
|
||||
|
||||
// get configuration
|
||||
var file_name = it.get('file_name');
|
||||
var file_path = it.get('file_path');
|
||||
var file_mode = it.get('file_mode');
|
||||
var extension = it.get('extension');
|
||||
var genome = it.get('genome');
|
||||
var url_paste = it.get('url_paste');
|
||||
var space_to_tabs = it.get('space_to_tabs');
|
||||
var to_posix_lines = it.get('to_posix_lines');
|
||||
|
||||
// validate
|
||||
if (!url_paste && !(file.size > 0))
|
||||
return null;
|
||||
|
||||
// configure uploadbox
|
||||
this.uploadbox.configure({url : this.options.nginx_upload_path});
|
||||
|
||||
// local files
|
||||
if (file_mode == 'local') {
|
||||
this.uploadbox.configure({paramname : 'files_0|file_data'});
|
||||
} else {
|
||||
this.uploadbox.configure({paramname : null});
|
||||
/**
|
||||
* Package API data from array of models
|
||||
* @param{Array} items - Upload items/rows filtered from a collection
|
||||
*/
|
||||
toData: function( items, history_id ) {
|
||||
// create dictionary for data submission
|
||||
var data = {
|
||||
payload: {
|
||||
'tool_id' : 'upload1',
|
||||
'history_id' : history_id || this.currentHistory(),
|
||||
'inputs' : {}
|
||||
},
|
||||
files: [],
|
||||
error_message: null
|
||||
}
|
||||
|
||||
// configure tool
|
||||
tool_input = {};
|
||||
|
||||
// new files
|
||||
if (file_mode == 'new') {
|
||||
tool_input['files_0|url_paste'] = url_paste;
|
||||
// add upload tools input data
|
||||
if ( items && items.length > 0 ) {
|
||||
var inputs = {};
|
||||
inputs[ 'dbkey' ] = items[0].get( 'genome', null );
|
||||
inputs[ 'file_type' ] = items[0].get( 'extension', null );
|
||||
for ( var index in items ) {
|
||||
var it = items[ index ];
|
||||
it.set( 'status', 'running' );
|
||||
if ( it.get( 'file_size' ) > 0 ) {
|
||||
var prefix = 'files_' + index + '|';
|
||||
inputs[ prefix + 'type' ] = 'upload_dataset';
|
||||
inputs[ prefix + 'space_to_tab' ] = it.get( 'space_to_tab' ) && 'Yes' || null;
|
||||
inputs[ prefix + 'to_posix_lines' ] = it.get( 'to_posix_lines' ) && 'Yes' || null;
|
||||
switch ( it.get( 'file_mode' ) ) {
|
||||
case 'new':
|
||||
inputs[ prefix + 'url_paste' ] = it.get( 'url_paste' );
|
||||
break;
|
||||
case 'ftp':
|
||||
inputs[ prefix + 'ftp_files' ] = it.get( 'file_path' );
|
||||
break;
|
||||
case 'local':
|
||||
data.files.push( { name: prefix + 'file_data', file: it.get( 'file_data' ) } );
|
||||
}
|
||||
} else {
|
||||
data.error_message = 'Upload content incomplete.';
|
||||
it.set( 'status', 'error' );
|
||||
it.set( 'info', data.error_message );
|
||||
break;
|
||||
}
|
||||
}
|
||||
data.payload.inputs = JSON.stringify( inputs );
|
||||
}
|
||||
|
||||
// files from ftp
|
||||
if (file_mode == 'ftp') {
|
||||
tool_input['files_0|ftp_files'] = file_path;
|
||||
}
|
||||
|
||||
// add common configuration
|
||||
tool_input['dbkey'] = genome;
|
||||
tool_input['file_type'] = extension;
|
||||
tool_input['files_0|type'] = 'upload_dataset';
|
||||
tool_input['files_0|space_to_tab'] = space_to_tabs && 'Yes' || null;
|
||||
tool_input['files_0|to_posix_lines'] = to_posix_lines && 'Yes' || null;
|
||||
|
||||
// setup data
|
||||
data = {};
|
||||
data['history_id'] = this.current_history;
|
||||
data['tool_id'] = 'upload1';
|
||||
data['inputs'] = JSON.stringify(tool_input);
|
||||
|
||||
// return additional data to be send with file
|
||||
return data;
|
||||
},
|
||||
|
||||
// progress
|
||||
_eventProgress : function(index, file, percentage) {
|
||||
// set progress for row
|
||||
var it = this.collection.get(index);
|
||||
it.set('percentage', percentage);
|
||||
|
||||
// update ui button
|
||||
this.ui_button.set('percentage', this._uploadPercentage(percentage, file.size));
|
||||
},
|
||||
|
||||
// success
|
||||
_eventSuccess : function(index, file, message) {
|
||||
// update status
|
||||
var it = this.collection.get(index);
|
||||
it.set('percentage', 100);
|
||||
it.set('status', 'success');
|
||||
|
||||
// file size
|
||||
var file_size = it.get('file_size');
|
||||
|
||||
// update ui button
|
||||
this.ui_button.set('percentage', this._uploadPercentage(100, file_size));
|
||||
|
||||
// update completed
|
||||
this.upload_completed += file_size * 100;
|
||||
|
||||
// update counter
|
||||
this.counter.announce--;
|
||||
this.counter.success++;
|
||||
|
||||
// update on screen info
|
||||
this._updateScreen();
|
||||
|
||||
// update galaxy history
|
||||
Galaxy.currHistoryPanel.refreshContents();
|
||||
},
|
||||
|
||||
// error
|
||||
_eventError : function(index, file, message) {
|
||||
// get element
|
||||
var it = this.collection.get(index);
|
||||
|
||||
// update status
|
||||
it.set('percentage', 100);
|
||||
it.set('status', 'error');
|
||||
it.set('info', message);
|
||||
|
||||
// update ui button
|
||||
this.ui_button.set('percentage', this._uploadPercentage(100, file.size));
|
||||
this.ui_button.set('status', 'danger');
|
||||
|
||||
// update completed
|
||||
this.upload_completed += file.size * 100;
|
||||
|
||||
// update counter
|
||||
this.counter.announce--;
|
||||
this.counter.error++;
|
||||
|
||||
// update on screen info
|
||||
this._updateScreen();
|
||||
},
|
||||
|
||||
// queue is done
|
||||
_eventComplete: function() {
|
||||
// reset queued upload to initial status
|
||||
this.collection.each(function(item) {
|
||||
if(item.get('status') == 'queued') {
|
||||
item.set('status', 'init');
|
||||
}
|
||||
});
|
||||
|
||||
// update running
|
||||
this.counter.running = 0;
|
||||
this._updateScreen();
|
||||
},
|
||||
|
||||
//
|
||||
// events triggered by this view
|
||||
//
|
||||
|
||||
_eventFtp: function() {
|
||||
// check if popover is visible
|
||||
if (!this.ftp.visible) {
|
||||
// show popover
|
||||
this.ftp.empty();
|
||||
this.ftp.append((new UploadFtp(this)).$el);
|
||||
this.ftp.show();
|
||||
} else {
|
||||
// hide popover
|
||||
this.ftp.hide();
|
||||
}
|
||||
},
|
||||
|
||||
// create a new file
|
||||
_eventCreate : function (){
|
||||
this.uploadbox.add([{
|
||||
name : 'New File',
|
||||
size : 0,
|
||||
mode : 'new'
|
||||
}]);
|
||||
},
|
||||
|
||||
// start upload process
|
||||
_eventStart : function() {
|
||||
// check
|
||||
if (this.counter.announce == 0 || this.counter.running > 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
// reset current size
|
||||
var self = this;
|
||||
this.upload_size = 0;
|
||||
this.upload_completed = 0;
|
||||
// switch icons for new uploads
|
||||
this.collection.each(function(item) {
|
||||
if(item.get('status') == 'init') {
|
||||
item.set('status', 'queued');
|
||||
self.upload_size += item.get('file_size');
|
||||
}
|
||||
});
|
||||
|
||||
// reset progress
|
||||
this.ui_button.set('percentage', 0);
|
||||
this.ui_button.set('status', 'success');
|
||||
|
||||
// update running
|
||||
this.counter.running = this.counter.announce;
|
||||
this._updateScreen();
|
||||
|
||||
// initiate upload procedure in plugin
|
||||
this.uploadbox.start();
|
||||
},
|
||||
|
||||
// pause upload process
|
||||
_eventStop : function() {
|
||||
// check
|
||||
if (this.counter.running == 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
// show upload has paused
|
||||
this.ui_button.set('status', 'info');
|
||||
|
||||
// request pause
|
||||
this.uploadbox.stop();
|
||||
|
||||
// set html content
|
||||
$('#upload-info').html('Queue will pause after completing the current file...');
|
||||
},
|
||||
|
||||
// remove all
|
||||
_eventReset : function() {
|
||||
// make sure queue is not running
|
||||
if (this.counter.running == 0){
|
||||
// reset collection
|
||||
this.collection.reset();
|
||||
|
||||
// reset counter
|
||||
this.counter.reset();
|
||||
|
||||
// show on screen info
|
||||
this._updateScreen();
|
||||
|
||||
// remove from queue
|
||||
this.uploadbox.reset();
|
||||
|
||||
// reset value for universal type drop-down
|
||||
this.select_extension.value(this.default_extension);
|
||||
this.select_genome.value(this.default_genome);
|
||||
|
||||
// reset button
|
||||
this.ui_button.set('percentage', 0);
|
||||
}
|
||||
},
|
||||
|
||||
// update extension for all models
|
||||
updateExtension: function(extension, defaults_only) {
|
||||
var self = this;
|
||||
this.collection.each(function(item) {
|
||||
if (item.get('status') == 'init' && (item.get('extension') == self.default_extension || !defaults_only)) {
|
||||
item.set('extension', extension);
|
||||
}
|
||||
});
|
||||
},
|
||||
|
||||
// update genome for all models
|
||||
updateGenome: function(genome, defaults_only) {
|
||||
var self = this;
|
||||
this.collection.each(function(item) {
|
||||
if (item.get('status') == 'init' && (item.get('genome') == self.default_genome || !defaults_only)) {
|
||||
item.set('genome', genome);
|
||||
}
|
||||
});
|
||||
},
|
||||
|
||||
// update uset
|
||||
_updateUser: function() {
|
||||
// backup current history
|
||||
this.current_user = Galaxy.currUser.get('id');
|
||||
this.current_history = null;
|
||||
if (this.current_user) {
|
||||
this.current_history = Galaxy.currHistoryPanel.model.get('id');
|
||||
}
|
||||
},
|
||||
|
||||
// set screen
|
||||
_updateScreen: function () {
|
||||
/*
|
||||
update on screen info
|
||||
*/
|
||||
|
||||
// check default message
|
||||
if(this.counter.announce == 0){
|
||||
if (this.uploadbox.compatible())
|
||||
message = 'You can Drag & Drop files into this box.';
|
||||
else
|
||||
message = 'Unfortunately, your browser does not support multiple file uploads or drag&drop.<br>Some supported browsers are: Firefox 4+, Chrome 7+, IE 10+, Opera 12+ or Safari 6+.'
|
||||
} else {
|
||||
if (this.counter.running == 0)
|
||||
message = 'You added ' + this.counter.announce + ' file(s) to the queue. Add more files or click \'Start\' to proceed.';
|
||||
else
|
||||
message = 'Please wait...' + this.counter.announce + ' out of ' + this.counter.running + ' remaining.';
|
||||
}
|
||||
|
||||
// set html content
|
||||
$('#upload-info').html(message);
|
||||
|
||||
/*
|
||||
update button status
|
||||
*/
|
||||
|
||||
// update reset button
|
||||
if (this.counter.running == 0 && this.counter.announce + this.counter.success + this.counter.error > 0)
|
||||
this.modal.enableButton('Reset');
|
||||
else
|
||||
this.modal.disableButton('Reset');
|
||||
|
||||
// update upload button
|
||||
if (this.counter.running == 0 && this.counter.announce > 0)
|
||||
this.modal.enableButton('Start');
|
||||
else
|
||||
this.modal.disableButton('Start');
|
||||
|
||||
// pause upload button
|
||||
if (this.counter.running > 0)
|
||||
this.modal.enableButton('Pause');
|
||||
else
|
||||
this.modal.disableButton('Pause');
|
||||
|
||||
// select upload button
|
||||
if (this.counter.running == 0){
|
||||
this.modal.enableButton('Choose local file');
|
||||
this.modal.enableButton('Choose FTP file');
|
||||
this.modal.enableButton('Paste/Fetch data');
|
||||
} else {
|
||||
this.modal.disableButton('Choose local file');
|
||||
this.modal.disableButton('Choose FTP file');
|
||||
this.modal.disableButton('Paste/Fetch data');
|
||||
}
|
||||
|
||||
// ftp button
|
||||
if (this.current_user && this.options.ftp_upload_dir && this.options.ftp_upload_site) {
|
||||
this.modal.showButton('Choose FTP file');
|
||||
} else {
|
||||
this.modal.hideButton('Choose FTP file');
|
||||
}
|
||||
|
||||
// table visibility
|
||||
if (this.counter.announce + this.counter.success + this.counter.error > 0) {
|
||||
$(this.el).find('#upload-table').show();
|
||||
} else {
|
||||
$(this.el).find('#upload-table').hide();
|
||||
}
|
||||
},
|
||||
|
||||
// calculate percentage of all queued uploads
|
||||
_uploadPercentage: function(percentage, size) {
|
||||
return (this.upload_completed + (percentage * size)) / this.upload_size;
|
||||
},
|
||||
|
||||
// template for extensions description
|
||||
_templateDescription: function(options) {
|
||||
if (options.description) {
|
||||
var tmpl = options.description;
|
||||
if (options.description_url) {
|
||||
tmpl += ' (<a href="' + options.description_url + '" target="_blank">read more</a>)';
|
||||
}
|
||||
return tmpl;
|
||||
} else {
|
||||
return 'There is no description available for this file extension.';
|
||||
}
|
||||
},
|
||||
|
||||
// load html template
|
||||
_template: function(id, idInfo) {
|
||||
return '<div class="upload-top">' +
|
||||
'<h6 id="' + idInfo + '" class="upload-info"></h6>' +
|
||||
'</div>' +
|
||||
'<div id="' + id + '" class="upload-box">' +
|
||||
'<table id="upload-table" class="table table-striped" style="display: none;">' +
|
||||
'<thead>' +
|
||||
'<tr>' +
|
||||
'<th>Name</th>' +
|
||||
'<th>Size</th>' +
|
||||
'<th>Type</th>' +
|
||||
'<th>Genome</th>' +
|
||||
'<th>Settings</th>' +
|
||||
'<th>Status</th>' +
|
||||
'<th></th>' +
|
||||
'</tr>' +
|
||||
'</thead>' +
|
||||
'<tbody></tbody>' +
|
||||
'</table>' +
|
||||
'</div>' +
|
||||
'<div id="upload-header" class="upload-header">' +
|
||||
'<span class="header-title">Type (set all):</span>' +
|
||||
'<span id="header-extension"/>' +
|
||||
'<span id="header-extension-info" class="upload-icon-button fa fa-search"/> ' +
|
||||
'<span class="header-title">Genome (set all):</span>' +
|
||||
'<span id="header-genome"/>' +
|
||||
'</div>';
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
@@ -454,7 +454,7 @@ define(['mvc/workflow/workflow-globals'], function( Globals ) {
|
||||
}
|
||||
});
|
||||
|
||||
var OutputCollectionTerminal = OutputTerminal.extend( {
|
||||
var OutputCollectionTerminal = Terminal.extend( {
|
||||
initialize: function( attr ) {
|
||||
Terminal.prototype.initialize.call( this, attr );
|
||||
this.datatypes = attr.datatypes;
|
||||
|
||||
@@ -0,0 +1,652 @@
|
||||
function days_in_month(month, year) {
|
||||
return new Date(year, month, 0).getDate();
|
||||
}
|
||||
|
||||
function date_by_subtracting_days(date, days) {
|
||||
return new Date(
|
||||
date.getFullYear(),
|
||||
date.getMonth(),
|
||||
date.getDate() - days,
|
||||
date.getHours(),
|
||||
date.getMinutes(),
|
||||
date.getSeconds(),
|
||||
date.getMilliseconds()
|
||||
);
|
||||
}
|
||||
|
||||
function date_by_subtracting_hours(date, hours) {
|
||||
return new Date(
|
||||
date.getFullYear(),
|
||||
date.getMonth(),
|
||||
date.getDate(),
|
||||
date.getHours() - hours,
|
||||
date.getMinutes(),
|
||||
date.getSeconds(),
|
||||
date.getMilliseconds()
|
||||
);
|
||||
}
|
||||
|
||||
// Gets the utc time without minutes and seconds
|
||||
function get_utc_time_hours() {
|
||||
var date = new Date();
|
||||
return new Date(
|
||||
date.getUTCFullYear(),
|
||||
date.getUTCMonth(),
|
||||
date.getUTCDate(),
|
||||
date.getUTCHours(),
|
||||
0, 0
|
||||
);
|
||||
}
|
||||
|
||||
// Refreshes the page for more up to date information
|
||||
function refresh() {
|
||||
window.location.reload(true);
|
||||
}
|
||||
setTimeout(refresh, 60000); //1 minute = 60000 ms
|
||||
|
||||
function create_chart( inp_data, name, time, title ) {
|
||||
// Initialize starting variables
|
||||
var data = inp_data;
|
||||
|
||||
var hours_array = []
|
||||
var now = get_utc_time_hours()
|
||||
for(var i = 0; i < 24; i++) {
|
||||
hours_array.push(date_by_subtracting_hours(now, i));
|
||||
}
|
||||
|
||||
var days_array = []
|
||||
var now = get_utc_time_hours()
|
||||
for(var i = 0; i < 30; i++) {
|
||||
days_array.push(date_by_subtracting_days(now, i));
|
||||
}
|
||||
|
||||
var margin = {top: 60, right: 30, bottom: 50, left: 60};
|
||||
var width = 300;
|
||||
var barWidth = 0;
|
||||
if(time == "hours") {
|
||||
barWidth = width / 24;
|
||||
} else if(time == "days") {
|
||||
barWidth = width / 30;
|
||||
}
|
||||
var chart_width = width + margin.left + margin.right;
|
||||
|
||||
var chart_zoom = 1.75;
|
||||
var height = 150;
|
||||
if(d3.max(data) != 0) {
|
||||
var zoom = height / d3.max(data);
|
||||
} else {
|
||||
var zoom = 1.0;
|
||||
}
|
||||
var chart_height = height + margin.top + margin.bottom;
|
||||
|
||||
// Function for zooming in and out of charts
|
||||
function click() {
|
||||
var classes = d3.select(this).attr("class");
|
||||
classes = classes.split(" ");
|
||||
d3.selectAll("." + classes[0]).filter("." + classes[1])
|
||||
.style("cursor", "zoom-in")
|
||||
.transition()
|
||||
.duration(750)
|
||||
.attr("height", chart_height)
|
||||
.attr("width", chart_width);
|
||||
|
||||
d3.select(this)
|
||||
.style("cursor", "default")
|
||||
.transition()
|
||||
.duration(750)
|
||||
.attr("height", chart_height*chart_zoom)
|
||||
.attr("width", chart_width*chart_zoom);
|
||||
}
|
||||
|
||||
// Initialize all chart containers to have the correct height
|
||||
$(".charts").css("height", chart_height * (chart_zoom));
|
||||
|
||||
// Create the chart object
|
||||
var chart = d3.select("#" + name)
|
||||
.attr("width", chart_width)
|
||||
.attr("height", chart_height)
|
||||
.attr("preserveAspectRatio", "xMidYMin")
|
||||
.attr("viewBox", "0 0 " + chart_width + " " + chart_height)
|
||||
.on("click", click);
|
||||
|
||||
// Create bars on the chart and assosciate data with it
|
||||
var bar = chart.selectAll("g")
|
||||
.data(data)
|
||||
.enter().append("g")
|
||||
.attr("transform", function(d, i) {
|
||||
// Place the bar in the correct place
|
||||
curr_margin = +margin.left;
|
||||
curr_margin += +(i * barWidth);
|
||||
return "translate(" + curr_margin + "," + margin.top + ")";
|
||||
})
|
||||
.on("mouseenter", function(d) {
|
||||
// Show tool tip
|
||||
var i = 1;
|
||||
var size = d;
|
||||
|
||||
while( size >= 10) {
|
||||
size = size / 10;
|
||||
i++;
|
||||
}
|
||||
|
||||
var wdth = (i * 4) + 10;
|
||||
d3.select(d3.event.path[1]).select(".tool_tip")
|
||||
.select("text")
|
||||
.attr("transform", "translate( " + (margin.left - 5) + ", " + ((height - (d * zoom)) + +margin.top + 10) + " )" )
|
||||
.attr("visibility", "visible")
|
||||
.text(d);
|
||||
|
||||
d3.select(d3.event.path[1]).select(".tool_tip")
|
||||
.attr("width", wdth + "px")
|
||||
.attr("height", "15px")
|
||||
.select("rect")
|
||||
.attr("transform", "translate( " + ((+margin.left) - wdth) + ", " + ((height - (d * zoom)) + +margin.top) + " )" )
|
||||
.attr("width", wdth + "px")
|
||||
.attr("height", "15px")
|
||||
.attr("fill","#ebd9b2");
|
||||
})
|
||||
.on("mouseleave", function(d) {
|
||||
// Remove tool tip
|
||||
d3.select(d3.event.path[1]).select(".tool_tip")
|
||||
.select("text")
|
||||
.attr("visibility", "hidden");
|
||||
|
||||
d3.select(d3.event.path[1]).select(".tool_tip")
|
||||
.select("rect")
|
||||
.attr("width", "0")
|
||||
.attr("height", "0")
|
||||
.attr("fill","")
|
||||
.text(d);
|
||||
});
|
||||
|
||||
// Add a title to the chart
|
||||
chart.append("g")
|
||||
.append("text")
|
||||
.attr("class", "title")
|
||||
.attr("text-anchor", "end")
|
||||
.attr("transform", function(e) {
|
||||
return "translate( " + width + ",15 )";
|
||||
})
|
||||
.text(title);
|
||||
|
||||
// Add an x axis line to the chart
|
||||
chart.append("g")
|
||||
.attr("class", "axis")
|
||||
.append("path")
|
||||
.attr("class", "x")
|
||||
.attr("d", function(d) {
|
||||
var m_x = margin.left;
|
||||
var m_y = +margin.top + height;
|
||||
var l_x = m_x + width;
|
||||
var l_y = m_y;
|
||||
|
||||
return "M" + m_x + " " + m_y + " L " + l_x + " " + l_y;
|
||||
});
|
||||
|
||||
// Declare how high the y axis goes
|
||||
var y = d3.scale.linear()
|
||||
.range([height, 0]);
|
||||
|
||||
// Create a yAxis object
|
||||
var yAxis = d3.svg.axis()
|
||||
.scale(y)
|
||||
.orient("left")
|
||||
.tickFormat( function(d) { return d3.round( d*d3.max(data), 0 ) });
|
||||
|
||||
// Put the y axis on the chart
|
||||
chart.append("g")
|
||||
.attr("class", "y axis")
|
||||
.attr("id", ("y_" + name))
|
||||
.attr("text-anchor", "end")
|
||||
.attr("transform", "translate( " + margin.left + "," + margin.top + ")")
|
||||
.call(yAxis)
|
||||
.select(".domain");
|
||||
|
||||
// Put a title for y axis on chart
|
||||
chart.append("g")
|
||||
.append("text")
|
||||
.attr("class", "ax_title")
|
||||
.attr("transform", function(e) {
|
||||
var axis = d3.select("#y_" + name).node()
|
||||
var left_pad = +margin.left - axis.getBoundingClientRect().width - 5;
|
||||
var top_pad = +margin.top + (axis.getBoundingClientRect().height/2) - 30
|
||||
var trans = "translate(" + left_pad + "," + top_pad + ")rotate(-90)";
|
||||
|
||||
return trans;
|
||||
})
|
||||
.text("Number of Jobs");
|
||||
|
||||
// Add color to the chart's bars
|
||||
bar.append("rect")
|
||||
.attr("y", function(d) { return height - (d * zoom); })
|
||||
.attr("height", function(d) { return (d * zoom); })
|
||||
.attr("width", barWidth - 1);
|
||||
|
||||
// Append x axis
|
||||
if(time == "hours") {
|
||||
// Append hour lines
|
||||
bar.append("line")
|
||||
.attr("x1", 0)
|
||||
.attr("y1", 0)
|
||||
.attr("x2", 0)
|
||||
.attr("y2", 3)
|
||||
.attr("stroke", "black")
|
||||
.attr("stroke-width", 1)
|
||||
.attr("pointer-events", "none")
|
||||
.attr("transform", function(d, i) {
|
||||
return "translate( " + (barWidth/2) + ", " + height + ")"
|
||||
});
|
||||
|
||||
// Append hour numbers
|
||||
bar.append("text")
|
||||
.attr("fill", "rgb(0,0,0)")
|
||||
.attr("transform", "translate( 10, " + (height + 10) + " )")
|
||||
.text(function(d, i) {
|
||||
var time = "0000"
|
||||
|
||||
if( hours_array[i].getHours() < 10 ) {
|
||||
time = "0" + String(hours_array[i].getHours());
|
||||
} else {
|
||||
time = hours_array[i].getHours();
|
||||
}
|
||||
|
||||
return time;
|
||||
});
|
||||
|
||||
// Append day lines
|
||||
var curr_day = "";
|
||||
var first = false;
|
||||
bar.append("line")
|
||||
.attr("x1", 0)
|
||||
.attr("y1", 0)
|
||||
.attr("x2", 0)
|
||||
.attr("y2", function(d, i) {
|
||||
var _y2 = 0;
|
||||
|
||||
if(hours_array[i].getDate() != curr_day) {
|
||||
if(!first) {
|
||||
_y2 = 27;
|
||||
first = true;
|
||||
} else {
|
||||
_y2 = 20;
|
||||
}
|
||||
|
||||
curr_day = hours_array[i].getDate();
|
||||
}
|
||||
|
||||
return _y2;
|
||||
})
|
||||
.attr("stroke", "black")
|
||||
.attr("stroke-width", 1)
|
||||
.attr("pointer-events", "none")
|
||||
.attr("transform", function(d, i) {
|
||||
return "translate( 0, " + height + ")";
|
||||
});
|
||||
|
||||
// Append day numbers
|
||||
curr_day = "";
|
||||
curr_day_text = "";
|
||||
first = false;
|
||||
bar.append("text")
|
||||
.attr("fill", "rgb(0,0,0)")
|
||||
.attr("pointer-events", "none")
|
||||
.text(function(d, i) {
|
||||
var time = "";
|
||||
var locale = "en-us";
|
||||
|
||||
if(hours_array[i].getDate() != curr_day_text) {
|
||||
time = String(hours_array[i].toLocaleString(locale, { month: "long" }));
|
||||
time += " " + String(hours_array[i].getDate())
|
||||
|
||||
curr_day_text = hours_array[i].getDate();
|
||||
}
|
||||
|
||||
return time;
|
||||
})
|
||||
.attr("transform", function(d, i) {
|
||||
var text_height = height;
|
||||
var this_width = d3.select(this).node().getBBox().width;
|
||||
|
||||
if(hours_array[i].getDate() != curr_day) {
|
||||
if(!first) {
|
||||
text_height += 26;
|
||||
first = true;
|
||||
} else {
|
||||
text_height += 18;
|
||||
}
|
||||
|
||||
curr_day = hours_array[i].getDate();
|
||||
}
|
||||
|
||||
return "translate( " + (this_width + 2) + ", " + (text_height) + " )"
|
||||
});
|
||||
} else if(time == "days") {
|
||||
// Append day lines
|
||||
bar.append("line")
|
||||
.attr("x1", 0)
|
||||
.attr("y1", 0)
|
||||
.attr("x2", 0)
|
||||
.attr("y2", 3)
|
||||
.attr("stroke", "black")
|
||||
.attr("stroke-width", 1)
|
||||
.attr("pointer-events", "none")
|
||||
.attr("transform", function(d, i) {
|
||||
return "translate( " + (barWidth/2) + ", " + height + ")"
|
||||
});
|
||||
|
||||
// Append day numbers
|
||||
bar.append("text")
|
||||
.attr("fill", "rgb(0,0,0)")
|
||||
.attr("transform", "translate( 9, " + (height + 10) + " )")
|
||||
.text(function(d, i) {
|
||||
var time = "0000"
|
||||
|
||||
if( days_array[i].getDate() < 10 ) {
|
||||
time = "0" + String(days_array[i].getDate());
|
||||
} else {
|
||||
time = days_array[i].getDate();
|
||||
}
|
||||
|
||||
return time;
|
||||
});
|
||||
|
||||
// Append month lines
|
||||
var curr_month = "";
|
||||
var first = false;
|
||||
bar.append("line")
|
||||
.attr("x1", 0)
|
||||
.attr("y1", 0)
|
||||
.attr("x2", 0)
|
||||
.attr("y2", function(d, i) {
|
||||
var _y2 = 0;
|
||||
|
||||
if(days_array[i].getMonth() != curr_month) {
|
||||
if(!first) {
|
||||
_y2 = 27;
|
||||
first = true;
|
||||
} else {
|
||||
_y2 = 20;
|
||||
}
|
||||
|
||||
curr_month = days_array[i].getMonth();
|
||||
}
|
||||
|
||||
return _y2;
|
||||
})
|
||||
.attr("stroke", "black")
|
||||
.attr("stroke-width", 1)
|
||||
.attr("pointer-events", "none")
|
||||
.attr("transform", function(d, i) {
|
||||
return "translate( 0, " + height + ")";
|
||||
});
|
||||
|
||||
// Append month numbers
|
||||
curr_month = "";
|
||||
curr_month_text = "";
|
||||
first = false;
|
||||
bar.append("text")
|
||||
.attr("fill", "rgb(0,100,0)")
|
||||
.attr("pointer-events", "none")
|
||||
.text(function(d, i) {
|
||||
var time = "";
|
||||
var locale = "en-us";
|
||||
|
||||
if(days_array[i].getMonth() != curr_month_text) {
|
||||
time = String(days_array[i].toLocaleString(locale, { month: "long" }));
|
||||
time += " " + String(days_array[i].getFullYear())
|
||||
|
||||
curr_month_text = days_array[i].getMonth();
|
||||
}
|
||||
|
||||
return time;
|
||||
})
|
||||
.attr("transform", function(d, i) {
|
||||
var text_height = height;
|
||||
var this_width = d3.select(this).node().getBBox().width;
|
||||
|
||||
if(days_array[i].getMonth() != curr_month) {
|
||||
if(!first) {
|
||||
text_height += 26;
|
||||
first = true;
|
||||
} else {
|
||||
text_height += 18;
|
||||
}
|
||||
|
||||
curr_month = days_array[i].getMonth();
|
||||
}
|
||||
|
||||
return "translate( " + (this_width + 2) + ", " + (text_height) + " )"
|
||||
});
|
||||
}
|
||||
|
||||
// Put an invisible tool tip on the chart
|
||||
chart.append("g")
|
||||
.attr("class", "tool_tip")
|
||||
.append("rect");
|
||||
chart.select(".tool_tip")
|
||||
.append("text");
|
||||
|
||||
// Initialize initial zoomed charts
|
||||
if(name == "jc_dy_chart" || name == "jc_hr_chart") {
|
||||
d3.select("#" + name)
|
||||
.attr("height", chart_height * chart_zoom)
|
||||
.attr("width", chart_width * chart_zoom)
|
||||
.style("cursor", "default");
|
||||
}
|
||||
}
|
||||
|
||||
//============================================================================================================
|
||||
|
||||
function create_histogram( inp_data, name, title ) {
|
||||
// Initialize initial variables
|
||||
// inp_data is an array of numbers that are the amount of minutes per run
|
||||
var data = inp_data;
|
||||
|
||||
var chart_zoom = 1.75;
|
||||
var margin = {top: 60, right: 30, bottom: 50, left: 60};
|
||||
|
||||
var height = 150;
|
||||
var chart_height = height + margin.top + margin.bottom;
|
||||
|
||||
var width = 300;
|
||||
var chart_width = width + margin.left + margin.right;
|
||||
|
||||
// Cereate x axis metadata
|
||||
// Used for x axis, histogram creation, and bar initialization
|
||||
var x = d3.scale.linear()
|
||||
.domain([0, d3.max(data)])
|
||||
.range([0, width]);
|
||||
|
||||
// Generate a histogram using twenty uniformly-spaced bins.
|
||||
var data = d3.layout.histogram()
|
||||
.bins(x.ticks(20))(data);
|
||||
|
||||
// Create an array of the sizes of the bars
|
||||
var lengths = [];
|
||||
for(var i = 0; i < data.length; i ++) {
|
||||
lengths.push(data[i].length)
|
||||
}
|
||||
|
||||
// Find the amount needed to magnify the bars
|
||||
if(d3.max(data) != 0) {
|
||||
var zoom = height / d3.max(lengths);
|
||||
} else {
|
||||
var zoom = 1.0;
|
||||
}
|
||||
|
||||
// Create y axis metadata
|
||||
// Used for y axis and bar initialization
|
||||
var y = d3.scale.linear()
|
||||
.domain([0, d3.max(data, function(d) { return d.y; })])
|
||||
.range([height, 0]);
|
||||
|
||||
// Function for zooming in and out of charts
|
||||
function click() {
|
||||
var classes = d3.select(this).attr("class");
|
||||
classes = classes.split(" ");
|
||||
d3.selectAll("." + classes[0]).filter("." + classes[1])
|
||||
.style("cursor", "zoom-in")
|
||||
.transition()
|
||||
.duration(750)
|
||||
.attr("height", chart_height)
|
||||
.attr("width", chart_width);
|
||||
|
||||
d3.select(this)
|
||||
.style("cursor", "default")
|
||||
.transition()
|
||||
.duration(750)
|
||||
.attr("height", chart_height*chart_zoom)
|
||||
.attr("width", chart_width*chart_zoom);
|
||||
}
|
||||
|
||||
// Formatter for x axis times (converting minutes to HH:MM).
|
||||
var formatMinutes = function(d) {
|
||||
hours = Math.floor( d / 60 )
|
||||
minutes = Math.floor(d - (hours * 60))
|
||||
|
||||
if(hours < 10) {
|
||||
hours = "0" + hours
|
||||
}
|
||||
if(minutes < 10) {
|
||||
minutes = "0" + minutes
|
||||
}
|
||||
|
||||
return hours + ":" + minutes;
|
||||
};
|
||||
|
||||
// Create a chart object
|
||||
var chart = d3.select("#" + name)
|
||||
.attr("viewBox", "0 0 " + chart_width + " " + chart_height)
|
||||
.attr("width", chart_width)
|
||||
.attr("height", chart_height)
|
||||
.attr("preserveAspectRatio", "xMidYMin")
|
||||
.on("click", click);
|
||||
|
||||
// Put title on chart
|
||||
chart.append("g")
|
||||
.append("text")
|
||||
.attr("class", "title")
|
||||
.attr("transform", function(e) {
|
||||
return "translate( " + width + ",15 )";
|
||||
})
|
||||
.text(title);
|
||||
|
||||
// Put bars on chart
|
||||
var bar = chart.selectAll(".bar")
|
||||
.data(data)
|
||||
.enter().append("g")
|
||||
.attr("class", "bar")
|
||||
.attr("transform", function(d) {
|
||||
return "translate(" + (+x(d.x) + +margin.left) + "," + (+y(d.y) + +margin.top) + ")"; })
|
||||
.on("mouseenter", function(d) {
|
||||
// Show tool tip
|
||||
i = 0;
|
||||
size = d.length;
|
||||
|
||||
while( size >= 1) {
|
||||
size = size / 10;
|
||||
i++;
|
||||
}
|
||||
var wdth = (i * 4) + 10;
|
||||
d3.select(d3.event.path[1]).select(".tool_tip")
|
||||
.select("text")
|
||||
.attr("transform", "translate( " + (margin.left - 5) + ", " + (height - (d.length * zoom) + +margin.top + 10) + " )" )
|
||||
.attr("visibility", "visible")
|
||||
.text(d.length);
|
||||
|
||||
d3.select(d3.event.path[1]).select(".tool_tip")
|
||||
.attr("width", wdth + "px")
|
||||
.attr("height", "15px")
|
||||
.select("rect")
|
||||
.attr("transform", "translate( " + ((+margin.left) - wdth) + ", " + (height - (d.length * zoom) + +margin.top) + " )")
|
||||
.attr("width", wdth + "px")
|
||||
.attr("height", "15px")
|
||||
.attr("fill","#ebd9b2");
|
||||
})
|
||||
.on("mouseleave", function(d) {
|
||||
// Remove tool tip
|
||||
d3.select(d3.event.path[1]).select(".tool_tip")
|
||||
.select("text")
|
||||
.attr("visibility", "hidden");
|
||||
|
||||
d3.select(d3.event.path[1]).select(".tool_tip")
|
||||
.select("rect")
|
||||
.attr("width", "0")
|
||||
.attr("height", "0")
|
||||
.attr("fill","")
|
||||
});
|
||||
|
||||
// Create bar width
|
||||
var bar_x;
|
||||
if(data[0] == undefined) {
|
||||
bar_x = 1;
|
||||
} else {
|
||||
bar_x = x(data[0].dx);
|
||||
}
|
||||
|
||||
// Add color to bar
|
||||
bar.append("rect")
|
||||
.attr("x", 1)
|
||||
.attr("width", bar_x - 1)
|
||||
.attr("height", function(d) { return height - y(d.y); });
|
||||
|
||||
// Create x axis
|
||||
var xAxis = d3.svg.axis()
|
||||
.scale(x)
|
||||
.orient("bottom")
|
||||
.tickFormat(formatMinutes);
|
||||
|
||||
// Add x axis to chart
|
||||
chart.append("g")
|
||||
.attr("class", "x axis")
|
||||
.attr("id", "x_" + name)
|
||||
.attr("transform", "translate( " + margin.left + "," + (+height + +margin.top) + ")")
|
||||
.call(xAxis);
|
||||
|
||||
// Add a title to the x axis
|
||||
chart.append("g")
|
||||
.append("text")
|
||||
.attr("class", "ax_title")
|
||||
.attr("transform", function(e) {
|
||||
var axis = d3.select("#x_" + name).node()
|
||||
var left_pad = +margin.left + (axis.getBoundingClientRect().width/2) + 30;
|
||||
var top_pad = +margin.top + height + axis.getBoundingClientRect().height + 10
|
||||
var trans = "translate(" + left_pad + "," + top_pad + ")";
|
||||
|
||||
return trans;
|
||||
})
|
||||
.text("ETA - hrs:mins");
|
||||
|
||||
// Create y axis
|
||||
var yAxis = d3.svg.axis()
|
||||
.scale(y)
|
||||
.orient("left");
|
||||
|
||||
// Add y axis to chart
|
||||
chart.append("g")
|
||||
.attr("class", "y axis")
|
||||
.attr("id", ("y_" + name))
|
||||
.attr("transform", "translate( " + margin.left + "," + margin.top + ")")
|
||||
.call(yAxis);
|
||||
|
||||
// Add a title to the y axis
|
||||
chart.append("g")
|
||||
.append("text")
|
||||
.attr("class", "ax_title")
|
||||
.attr("transform", function(e) {
|
||||
var axis = d3.select("#y_" + name).node()
|
||||
var left_pad = +margin.left - axis.getBoundingClientRect().width - 5;
|
||||
var top_pad = +margin.top + (axis.getBoundingClientRect().height/2) - 30
|
||||
var trans = "translate(" + left_pad + "," + top_pad + ")rotate(-90)";
|
||||
|
||||
return trans;
|
||||
})
|
||||
.text("Number of Jobs");
|
||||
|
||||
// Put an invisible tool tip on the chart
|
||||
chart.append("g")
|
||||
.attr("class", "tool_tip")
|
||||
.append("rect");
|
||||
chart.select(".tool_tip")
|
||||
.append("text");
|
||||
}
|
||||
@@ -1,237 +0,0 @@
|
||||
#!/usr/bin/env python
|
||||
|
||||
# Script requires handlebars compiler be installed; use node package manager
|
||||
# to install handlebars.
|
||||
|
||||
"""%prog [options] [ specific .handlebars or .html template file to compile ]
|
||||
Compiles handlebars templates into javascript for better load times and performance.
|
||||
|
||||
Handlebars compilation errors will print to stderr.
|
||||
NOTE!: you will need node.js, npm, and handlebars (installed from npm) in order to compile templates
|
||||
See http://handlebarsjs.com/precompilation.html for more info.
|
||||
|
||||
Should be called from the $GLX_ROOT/static/scripts/templates directory.
|
||||
Compiled templates will be written to the compiled/ directory.
|
||||
|
||||
If the --multi-ext option is passed a file extension string, the script will:
|
||||
* search for *.<extension> in the current directory,
|
||||
* extract each '<script type="text/template"> from the file and write them into a new handlebars file
|
||||
named using the id of the script tag (eg. id="undeleteMsg" -> undeleteMsg.handlebars)
|
||||
* these new handlebars scripts will be compiled along with any other handlebars templates in the dir
|
||||
|
||||
additionally for each of those files, the script will:
|
||||
* extract each '<script type="text/javascript"> from the file and write them ALL into a single js file
|
||||
named using the prefix 'helpers-' and the basename of the enclosing file
|
||||
(eg. common-templates.html -> helpers-common-templates.js )
|
||||
* these new handlebars scripts will be compiled along with any other handlebars templates in the dir
|
||||
These files can be included as if they were templates (eg. h.templates( 'helpers-common-templates' ) )
|
||||
|
||||
This hopefully will allow both multiple templates and their associated Handlebars helper functions to be
|
||||
included in one file.
|
||||
|
||||
Currently, this works (best?) if:
|
||||
* the multiple templates are within one html file
|
||||
* each template is enclosed in a <script> tag with type="text/template" and some id attribute
|
||||
* each helper fn is enclosed in a <script> tag with type="text/javascript" and some id attribute
|
||||
e.g. <script type="text/template" id="history-deletedMsg" class="template">Deleted {{ msg }}</script>
|
||||
|
||||
"""
|
||||
# ------------------------------------------------------------------------------
|
||||
|
||||
TODO = """
|
||||
remove intermediate, handlebars step of multi->handlebars->compiled/.js (if handlebars does stdin)
|
||||
"""
|
||||
|
||||
import sys
|
||||
|
||||
from glob import glob
|
||||
from subprocess import call
|
||||
import os
|
||||
from optparse import OptionParser
|
||||
import re
|
||||
|
||||
import logging as log
|
||||
log.basicConfig(
|
||||
#level = log.DEBUG,
|
||||
name = __name__
|
||||
)
|
||||
|
||||
COMPILED_DIR = 'compiled'
|
||||
COMPILED_EXT = '.js'
|
||||
COMPILE_CMD_STR = "handlebars %s -f %s"
|
||||
COMPILE_MINIMIZE_SWITCH = ' -m'
|
||||
|
||||
TEMPLATE_TAG = 'script'
|
||||
TEMPLATE_TYPE = 'text/template'
|
||||
HELPER_TYPE = 'text/javascript'
|
||||
|
||||
# both of these are off by default for backward compat
|
||||
DEFAULT_MINIMIZATION = False
|
||||
DEFAULT_MULTI_EXT = None #'.html'
|
||||
|
||||
# ------------------------------------------------------------------------------
|
||||
def parse_html_tag_attrs( string ):
|
||||
attrs = {}
|
||||
for match in re.finditer( r'(?P<key>\w+?)=[\'|\"](?P<val>.*?)[\'|\"]', string, re.DOTALL | re.MULTILINE ):
|
||||
match = match.groupdict()
|
||||
key = match[ 'key' ]
|
||||
val = match[ 'val' ]
|
||||
attrs[ key ] = val
|
||||
return attrs
|
||||
|
||||
def split_on_html_tag( string, tag ):
|
||||
tag_pattern = r'<%s\s*(?P<attrs>.*?)>(?P<body>.*?)</%s>' % ( tag, tag )
|
||||
log.debug( tag_pattern )
|
||||
tag_pattern = re.compile( tag_pattern, re.MULTILINE | re.DOTALL )
|
||||
|
||||
found_list = re.findall( tag_pattern, string )
|
||||
for attrs, body in found_list:
|
||||
yield ( parse_html_tag_attrs( attrs ), body )
|
||||
|
||||
def filter_on_tag_type( generator, type_attr_to_match ):
|
||||
for attrs, body in generator:
|
||||
log.debug( 'attrs: %s', str( attrs ) )
|
||||
if( ( 'type' in attrs )
|
||||
and ( attrs[ 'type' ] == type_attr_to_match ) ):
|
||||
yield attrs, body
|
||||
|
||||
|
||||
# ------------------------------------------------------------------------------
|
||||
def break_multi_template( multi_template_filename ):
|
||||
"""parse the multi template, writing each template into a new handlebars tmpl and returning their names"""
|
||||
template_filenames = []
|
||||
|
||||
# parse the multi template
|
||||
print "\nBreaking multi-template file %s into individual templates and helpers:" % ( multi_template_filename )
|
||||
with open( multi_template_filename, 'r' ) as multi_template_file:
|
||||
multi_template_file_text = multi_template_file.read()
|
||||
|
||||
# write a template file for each template (name based on id in tag)
|
||||
tag_generator = split_on_html_tag( multi_template_file_text, TEMPLATE_TAG )
|
||||
for attrs, template_text in filter_on_tag_type( tag_generator, TEMPLATE_TYPE ):
|
||||
if( 'id' not in attrs ):
|
||||
log.warning( 'Template has no "id". attrs: %s' %( str( attrs ) ) )
|
||||
continue
|
||||
|
||||
template_id = attrs[ 'id' ]
|
||||
template_text = template_text.strip()
|
||||
handlebar_template_filename = template_id + '.handlebars'
|
||||
with open( handlebar_template_filename, 'w' ) as handlebar_template_file:
|
||||
handlebar_template_file.write( template_text )
|
||||
|
||||
log.debug( "%s\n%s\n", template_id, template_text )
|
||||
template_filenames.append( handlebar_template_filename )
|
||||
|
||||
## write all helpers to a single 'helper-' prefixed js file in the compilation dir
|
||||
helper_fns = []
|
||||
# same tag, different type
|
||||
tag_generator = split_on_html_tag( multi_template_file_text, TEMPLATE_TAG )
|
||||
for attrs, helper_text in filter_on_tag_type( tag_generator, HELPER_TYPE ):
|
||||
helper_text = helper_text.strip()
|
||||
print '(helper):', ( attrs[ 'id' ] if 'id' in attrs else '(No id)' )
|
||||
|
||||
helper_fns.append( helper_text )
|
||||
|
||||
if helper_fns:
|
||||
# prefix original filename (in compiled dir) and write all helper funcs to that file
|
||||
helper_filename = 'helpers-' + os.path.splitext( multi_template_filename )[0] + '.js'
|
||||
helper_filename = os.path.join( COMPILED_DIR, helper_filename )
|
||||
with open( helper_filename, 'w' ) as helper_file:
|
||||
helper_file.write( '\n'.join( helper_fns ) )
|
||||
print '(helper functions written to %s)' % helper_filename
|
||||
|
||||
print '\n'.join( template_filenames )
|
||||
return template_filenames
|
||||
|
||||
|
||||
# ------------------------------------------------------------------------------
|
||||
def compile_template( template_filename, minimize=False ):
|
||||
"""compile the given template file (optionally minimizing the js) using subprocess.
|
||||
|
||||
Use the basename of the template file for the outputed js.
|
||||
"""
|
||||
template_basename = os.path.splitext( os.path.split( template_filename )[1] )[0]
|
||||
compiled_filename = os.path.join( COMPILED_DIR, template_basename + COMPILED_EXT )
|
||||
|
||||
command_string = COMPILE_CMD_STR % ( template_filename, compiled_filename )
|
||||
if minimize:
|
||||
command_string += COMPILE_MINIMIZE_SWITCH
|
||||
print command_string
|
||||
return call( command_string, shell=True )
|
||||
|
||||
|
||||
# ------------------------------------------------------------------------------
|
||||
def main( options, args ):
|
||||
"""Break multi template files into single templates, compile all single templates.
|
||||
|
||||
If args, compile that as a list of specific handlebars and/or multi template files.
|
||||
"""
|
||||
print "(Call this script with the '-h' option for more help)"
|
||||
|
||||
handlebars_templates = []
|
||||
# If specific scripts specified on command line, just compile them,
|
||||
if len( args ) >= 1:
|
||||
handlebars_templates = filter( lambda( x ): x.endswith( '.handlebars' ), args )
|
||||
|
||||
# otherwise compile all in the current dir
|
||||
else:
|
||||
handlebars_templates = glob( '*.handlebars' )
|
||||
|
||||
# if desired, break up any passed-in or found multi template files
|
||||
# adding the names of the new single templates to those needing compilation
|
||||
multi_template_template_filenames = []
|
||||
if options.multi_ext:
|
||||
multi_templates = []
|
||||
if len( args ) >= 1:
|
||||
multi_templates = filter( lambda( x ): x.endswith( options.multi_ext ), args )
|
||||
else:
|
||||
multi_templates = glob( '*' + options.multi_ext )
|
||||
|
||||
for multi_template_filename in multi_templates:
|
||||
multi_template_template_filenames.extend( break_multi_template( multi_template_filename ) )
|
||||
|
||||
# unique filenames only (Q&D)
|
||||
handlebars_templates = list( set( handlebars_templates + multi_template_template_filenames ) )
|
||||
|
||||
# compile the templates
|
||||
print "\nCompiling templates:"
|
||||
filenames_w_possible_errors = []
|
||||
for handlebars_template in handlebars_templates:
|
||||
shell_ret = compile_template( handlebars_template, options.minimize )
|
||||
if shell_ret:
|
||||
filenames_w_possible_errors.append( handlebars_template )
|
||||
|
||||
# report any possible errors
|
||||
if filenames_w_possible_errors:
|
||||
print "\nThere may have been errors on the following files:"
|
||||
print ',\n'.join( filenames_w_possible_errors )
|
||||
print "\nCall this script with the '-h' for more help"
|
||||
|
||||
# delete multi template intermediate files
|
||||
if options.remove_intermediate:
|
||||
print "\nCleaning up intermediate multi-template template files:"
|
||||
for filename in multi_template_template_filenames:
|
||||
try:
|
||||
print 'removing', filename
|
||||
os.remove( filename )
|
||||
except Exception, exc:
|
||||
print exc
|
||||
|
||||
|
||||
# ------------------------------------------------------------------------------
|
||||
if __name__ == '__main__':
|
||||
optparser = OptionParser( usage=__doc__ )
|
||||
optparser.add_option( '-m', '--minimize', dest='minimize', action='store_true', default=DEFAULT_MINIMIZATION,
|
||||
help=( 'minimize compiled template scripts via handlebars '
|
||||
+ '(defaults to %s)' % DEFAULT_MINIMIZATION ) )
|
||||
optparser.add_option( '--multi-ext', dest='multi_ext', metavar="FILE_EXTENSION", default=DEFAULT_MULTI_EXT,
|
||||
help=( 'indicates that files ending with the given string contain multiple '
|
||||
+ 'templates and the script should break those into individual '
|
||||
+ 'handlebars templates (defaults to "%s")' ) % DEFAULT_MULTI_EXT )
|
||||
optparser.add_option( '--no-remove', action='store_false', dest='remove_intermediate', default=True,
|
||||
help=( 'remove intermediate *.handlebars files when using multiple template'
|
||||
+ 'files (defaults to "True")' ) )
|
||||
|
||||
( options, args ) = optparser.parse_args()
|
||||
sys.exit( main( options, args ) )
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
(function() {
|
||||
var template = Handlebars.template, templates = Handlebars.templates = Handlebars.templates || {};
|
||||
templates['panel_section'] = template({"compiler":[6,">= 2.0.0-beta.1"],"main":function(depth0,helpers,partials,data) {
|
||||
this["Handlebars"] = this["Handlebars"] || {};
|
||||
this["Handlebars"]["templates"] = this["Handlebars"]["templates"] || {};
|
||||
|
||||
this["Handlebars"]["templates"]["panel_section"] = Handlebars.template({"compiler":[6,">= 2.0.0-beta.1"],"main":function(depth0,helpers,partials,data) {
|
||||
var helper, alias1=helpers.helperMissing, alias2="function", alias3=this.escapeExpression;
|
||||
|
||||
return "<div class=\"toolSectionTitle\" id=\"title_"
|
||||
@@ -10,5 +11,4 @@ templates['panel_section'] = template({"compiler":[6,">= 2.0.0-beta.1"],"main":f
|
||||
+ "</span></a>\n</div>\n<div id=\""
|
||||
+ alias3(((helper = (helper = helpers.id || (depth0 != null ? depth0.id : depth0)) != null ? helper : alias1),(typeof helper === alias2 ? helper.call(depth0,{"name":"id","hash":{},"data":data}) : helper)))
|
||||
+ "\" class=\"toolSectionBody\" style=\"display: none; \">\n <div class=\"toolSectionBg\"></div>\n<div>";
|
||||
},"useData":true});
|
||||
})();
|
||||
},"useData":true});
|
||||
@@ -1,6 +1,7 @@
|
||||
(function() {
|
||||
var template = Handlebars.template, templates = Handlebars.templates = Handlebars.templates || {};
|
||||
templates['tool_form'] = template({"1":function(depth0,helpers,partials,data) {
|
||||
this["Handlebars"] = this["Handlebars"] || {};
|
||||
this["Handlebars"]["templates"] = this["Handlebars"]["templates"] || {};
|
||||
|
||||
this["Handlebars"]["templates"]["tool_form"] = Handlebars.template({"1":function(depth0,helpers,partials,data) {
|
||||
var stack1, helper, alias1=helpers.helperMissing, alias2="function", alias3=this.escapeExpression;
|
||||
|
||||
return " <div class=\"form-row\">\n <label for=\""
|
||||
@@ -24,5 +25,4 @@ templates['tool_form'] = template({"1":function(depth0,helpers,partials,data) {
|
||||
+ " </div>\n <div class=\"form-row form-actions\">\n <input type=\"submit\" class=\"btn btn-primary\" name=\"runtool_btn\" value=\"Execute\">\n</div>\n<div class=\"toolHelp\">\n <div class=\"toolHelpBody\">"
|
||||
+ alias3(((helper = (helper = helpers.help || (depth0 != null ? depth0.help : depth0)) != null ? helper : alias1),(typeof helper === alias2 ? helper.call(depth0,{"name":"help","hash":{},"data":data}) : helper)))
|
||||
+ "</div>\n</div>";
|
||||
},"useData":true});
|
||||
})();
|
||||
},"useData":true});
|
||||
@@ -1,6 +1,7 @@
|
||||
(function() {
|
||||
var template = Handlebars.template, templates = Handlebars.templates = Handlebars.templates || {};
|
||||
templates['tool_link'] = template({"compiler":[6,">= 2.0.0-beta.1"],"main":function(depth0,helpers,partials,data) {
|
||||
this["Handlebars"] = this["Handlebars"] || {};
|
||||
this["Handlebars"]["templates"] = this["Handlebars"]["templates"] || {};
|
||||
|
||||
this["Handlebars"]["templates"]["tool_link"] = Handlebars.template({"compiler":[6,">= 2.0.0-beta.1"],"main":function(depth0,helpers,partials,data) {
|
||||
var helper, alias1=helpers.helperMissing, alias2="function", alias3=this.escapeExpression;
|
||||
|
||||
return "<a class=\""
|
||||
@@ -15,5 +16,4 @@ templates['tool_link'] = template({"compiler":[6,">= 2.0.0-beta.1"],"main":funct
|
||||
+ alias3(((helper = (helper = helpers.name || (depth0 != null ? depth0.name : depth0)) != null ? helper : alias1),(typeof helper === alias2 ? helper.call(depth0,{"name":"name","hash":{},"data":data}) : helper)))
|
||||
+ "</a> "
|
||||
+ alias3(((helper = (helper = helpers.description || (depth0 != null ? depth0.description : depth0)) != null ? helper : alias1),(typeof helper === alias2 ? helper.call(depth0,{"name":"description","hash":{},"data":data}) : helper)));
|
||||
},"useData":true});
|
||||
})();
|
||||
},"useData":true});
|
||||
@@ -1,6 +1,7 @@
|
||||
(function() {
|
||||
var template = Handlebars.template, templates = Handlebars.templates = Handlebars.templates || {};
|
||||
templates['tool_search'] = template({"compiler":[6,">= 2.0.0-beta.1"],"main":function(depth0,helpers,partials,data) {
|
||||
this["Handlebars"] = this["Handlebars"] || {};
|
||||
this["Handlebars"]["templates"] = this["Handlebars"]["templates"] || {};
|
||||
|
||||
this["Handlebars"]["templates"]["tool_search"] = Handlebars.template({"compiler":[6,">= 2.0.0-beta.1"],"main":function(depth0,helpers,partials,data) {
|
||||
var helper, alias1=helpers.helperMissing, alias2="function", alias3=this.escapeExpression;
|
||||
|
||||
return "<input type=\"text\" name=\"query\" placeholder=\""
|
||||
@@ -8,5 +9,4 @@ templates['tool_search'] = template({"compiler":[6,">= 2.0.0-beta.1"],"main":fun
|
||||
+ "\" id=\"tool-search-query\" autocomplete=\"off\" class=\"search-query parent-width\" />\n<a id=\"search-clear-btn\" title=\"clear search (esc)\"> </a>\n<img src=\""
|
||||
+ alias3(((helper = (helper = helpers.spinner_url || (depth0 != null ? depth0.spinner_url : depth0)) != null ? helper : alias1),(typeof helper === alias2 ? helper.call(depth0,{"name":"spinner_url","hash":{},"data":data}) : helper)))
|
||||
+ "\" id=\"search-spinner\" class=\"search-spinner\"/>\n";
|
||||
},"useData":true});
|
||||
})();
|
||||
},"useData":true});
|
||||
@@ -4,7 +4,7 @@ define(['libs/underscore', 'viz/trackster/util', 'utils/config'], function(_, ut
|
||||
* A configuration setting. Currently key is used as id.
|
||||
*/
|
||||
var ConfigSetting = Backbone.Model.extend({
|
||||
|
||||
|
||||
initialize: function(options) {
|
||||
// Use key as id for now.
|
||||
var key = this.get('key');
|
||||
@@ -16,8 +16,8 @@ var ConfigSetting = Backbone.Model.extend({
|
||||
this.set(_.extend({}, defaults, options));
|
||||
}
|
||||
|
||||
if (this.get('value') === undefined) {
|
||||
// Use default to set value.
|
||||
if (this.get('value') === undefined && this.get('default_value') !== undefined) {
|
||||
// Use default to set value (if present).
|
||||
this.set_value(this.get('default_value'));
|
||||
|
||||
// If no default value for color config, set random color.
|
||||
@@ -134,7 +134,7 @@ var ConfigSettingCollection = Backbone.Collection.extend({
|
||||
return _.extend({}, m, { value: saved_values[m.key] });
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
return new ConfigSettingCollection(models);
|
||||
}
|
||||
});
|
||||
@@ -150,7 +150,7 @@ var ConfigSettingCollectionView = Backbone.View.extend({
|
||||
*/
|
||||
render: function() {
|
||||
var container = this.$el;
|
||||
|
||||
|
||||
this.collection.each(function(param, index) {
|
||||
// Hidden params have no representation in the form
|
||||
if (param.get('hidden')) { return; }
|
||||
@@ -165,7 +165,7 @@ var ConfigSettingCollectionView = Backbone.View.extend({
|
||||
if ( type === 'bool' ) {
|
||||
row.append( $('<input type="checkbox" />').attr("id", id ).attr("name", id ).attr( 'checked', value ) );
|
||||
}
|
||||
// Draw parameter as textbox
|
||||
// Draw parameter as textbox
|
||||
else if ( type === 'text' ) {
|
||||
row.append( $('<input type="text"/>').attr("id", id ).val(value).click( function() { $(this).select(); }));
|
||||
}
|
||||
@@ -177,39 +177,39 @@ var ConfigSettingCollectionView = Backbone.View.extend({
|
||||
});
|
||||
select.val( value );
|
||||
row.append( select );
|
||||
|
||||
|
||||
}
|
||||
// Draw parameter as color picker
|
||||
// Draw parameter as color picker
|
||||
else if ( type === 'color' ) {
|
||||
var
|
||||
var
|
||||
container_div = $("<div/>").appendTo(row),
|
||||
input = $('<input />').attr("id", id ).attr("name", id ).val( value ).css("float", "left")
|
||||
input = $('<input />').attr("id", id ).attr("name", id ).val( value ).css("float", "left")
|
||||
.appendTo(container_div).click(function(e) {
|
||||
// Hide other pickers.
|
||||
$(".tooltip").removeClass( "in" );
|
||||
|
||||
|
||||
// Show input's color picker.
|
||||
var tip = $(this).siblings(".tooltip").addClass( "in" );
|
||||
tip.css( {
|
||||
tip.css( {
|
||||
// left: $(this).position().left + ( $(input).width() / 2 ) - 60,
|
||||
// top: $(this).position().top + $(this.height)
|
||||
// top: $(this).position().top + $(this.height)
|
||||
left: $(this).position().left + $(this).width() + 5,
|
||||
top: $(this).position().top - ( $(tip).height() / 2 ) + ( $(this).height() / 2 )
|
||||
} ).show();
|
||||
|
||||
// Click management:
|
||||
|
||||
|
||||
// Click management:
|
||||
|
||||
// Keep showing tip if clicking in tip.
|
||||
tip.click(function(e) {
|
||||
e.stopPropagation();
|
||||
});
|
||||
|
||||
|
||||
// Hide tip if clicking outside of tip.
|
||||
$(document).bind( "click.color-picker", function() {
|
||||
tip.hide();
|
||||
$(document).unbind( "click.color-picker" );
|
||||
});
|
||||
|
||||
|
||||
// No propagation to avoid triggering document click (and tip hiding) above.
|
||||
e.stopPropagation();
|
||||
}),
|
||||
@@ -222,20 +222,20 @@ var ConfigSettingCollectionView = Backbone.View.extend({
|
||||
tip_inner = $("<div class='tooltip-inner' style='text-align: inherit'></div>").appendTo(tip),
|
||||
tip_arrow = $("<div class='tooltip-arrow'></div>").appendTo(tip),
|
||||
farb_obj = $.farbtastic(tip_inner, { width: 100, height: 100, callback: input, color: value });
|
||||
|
||||
|
||||
// Clear floating.
|
||||
container_div.append( $("<div/>").css("clear", "both"));
|
||||
|
||||
|
||||
// Use function to fix farb_obj value.
|
||||
(function(fixed_farb_obj) {
|
||||
new_color_icon.click(function() {
|
||||
fixed_farb_obj.setColor(util_mod.get_random_color());
|
||||
});
|
||||
});
|
||||
})(farb_obj);
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
else {
|
||||
row.append( $('<input />').attr("id", id ).attr("name", id ).val( value ) );
|
||||
row.append( $('<input />').attr("id", id ).attr("name", id ).val( value ) );
|
||||
}
|
||||
// Help text
|
||||
if ( param.help ) {
|
||||
|
||||
@@ -0,0 +1,21 @@
|
||||
define([], function(){
|
||||
// ============================================================================
|
||||
function get( key, queryString ){
|
||||
queryString = queryString || window.location.search.substr( 1 );
|
||||
var keyRegex = new RegExp( key + '=([^&#$]+)' ),
|
||||
matches = queryString.match( keyRegex );
|
||||
if( !matches || !matches.length ){
|
||||
return undefined;
|
||||
}
|
||||
matches = matches.splice( 1 );
|
||||
if( matches.length === 1 ){
|
||||
return matches[0];
|
||||
}
|
||||
return matches;
|
||||
}
|
||||
|
||||
// ============================================================================
|
||||
return {
|
||||
get : get
|
||||
};
|
||||
});
|
||||
@@ -1,364 +1,295 @@
|
||||
/*
|
||||
galaxy upload lib - uses FileReader, FormData and XMLHttpRequest
|
||||
*/
|
||||
;(function($)
|
||||
{
|
||||
// add event properties
|
||||
jQuery.event.props.push("dataTransfer");
|
||||
|
||||
// default options
|
||||
var default_opts =
|
||||
{
|
||||
url : '',
|
||||
paramname : 'content',
|
||||
maxfilesize : 2048,
|
||||
dragover : function() {},
|
||||
dragleave : function() {},
|
||||
announce : function() {},
|
||||
initialize : function() {},
|
||||
progress : function() {},
|
||||
success : function() {},
|
||||
error : function(index, file, message) { alert(message); },
|
||||
complete : function() {},
|
||||
error_filesize : "File exceeds 2GB. Please use an FTP client.",
|
||||
error_default : "Please make sure the file is available.",
|
||||
error_server : "Upload request failed.",
|
||||
error_login : "Uploads require you to log in.",
|
||||
error_missing : "No upload content available."
|
||||
}
|
||||
|
||||
// options
|
||||
var opts = {};
|
||||
|
||||
// file queue
|
||||
var queue = {};
|
||||
|
||||
// queue index counter
|
||||
var queue_index = 0;
|
||||
|
||||
// queue length
|
||||
var queue_length = 0;
|
||||
|
||||
// indicates if queue is currently running
|
||||
var queue_running = false;
|
||||
var queue_stop = false;
|
||||
|
||||
// element
|
||||
var el = null;
|
||||
|
||||
// xml request element
|
||||
var xhr = null;
|
||||
|
||||
// attach to element
|
||||
$.fn.uploadbox = function(options)
|
||||
{
|
||||
// parse options
|
||||
opts = $.extend({}, default_opts, options);
|
||||
|
||||
// element
|
||||
el = this;
|
||||
|
||||
// append upload button
|
||||
el.append('<input id="uploadbox_input" type="file" style="display: none" multiple>');
|
||||
|
||||
// attach events
|
||||
el.on('drop', drop);
|
||||
el.on('dragover', dragover);
|
||||
el.on('dragleave', dragleave);
|
||||
|
||||
// attach change event
|
||||
$('#uploadbox_input').change(function(e)
|
||||
{
|
||||
// add files to queue
|
||||
add(e.target.files);
|
||||
|
||||
// reset
|
||||
$(this).val('');
|
||||
});
|
||||
|
||||
// drop event
|
||||
function drop(e)
|
||||
{
|
||||
// check if it's a file transfer
|
||||
if(!e.dataTransfer)
|
||||
return;
|
||||
|
||||
// add files to queue
|
||||
add(e.dataTransfer.files);
|
||||
|
||||
// prevent default
|
||||
e.preventDefault();
|
||||
|
||||
// return
|
||||
return false;
|
||||
}
|
||||
|
||||
// drag over
|
||||
function dragover(e)
|
||||
{
|
||||
e.preventDefault();
|
||||
opts.dragover.call(e);
|
||||
}
|
||||
|
||||
// drag leave
|
||||
function dragleave(e)
|
||||
{
|
||||
e.stopPropagation();
|
||||
opts.dragleave.call(e);
|
||||
}
|
||||
|
||||
// progress
|
||||
function progress(e)
|
||||
{
|
||||
// get percentage
|
||||
if (e.lengthComputable)
|
||||
opts.progress(this.index, this.file, Math.round((e.loaded * 100) / e.total));
|
||||
}
|
||||
|
||||
// respond to an upload request
|
||||
function add(files)
|
||||
{
|
||||
// only allow adding file if current batch is complete
|
||||
if (queue_running)
|
||||
return;
|
||||
|
||||
// backup queue index
|
||||
var current_index = queue_index;
|
||||
|
||||
// add files to queue
|
||||
for (var i = 0; i < files.length; i++)
|
||||
{
|
||||
|
||||
// new identifier
|
||||
var index = String(queue_index++);
|
||||
|
||||
// add to queue
|
||||
queue[index] = files[i];
|
||||
|
||||
// announce
|
||||
opts.announce(index, queue[index], '');
|
||||
|
||||
// increase counter
|
||||
queue_length++;
|
||||
}
|
||||
|
||||
// return
|
||||
return current_index;
|
||||
}
|
||||
|
||||
// remove entry from queue
|
||||
function remove(index)
|
||||
{
|
||||
if (queue[index])
|
||||
{
|
||||
// remove from queue
|
||||
delete queue[index];
|
||||
|
||||
// update counter
|
||||
queue_length--;
|
||||
}
|
||||
}
|
||||
|
||||
// process an upload, recursive
|
||||
function process()
|
||||
{
|
||||
// validate
|
||||
if (queue_length == 0 || queue_stop)
|
||||
{
|
||||
queue_stop = false;
|
||||
queue_running = false;
|
||||
opts.complete();
|
||||
return;
|
||||
} else
|
||||
queue_running = true;
|
||||
|
||||
// get an identifier from the queue
|
||||
var index = -1;
|
||||
for (var key in queue)
|
||||
{
|
||||
index = key;
|
||||
break;
|
||||
}
|
||||
|
||||
// get current file from queue
|
||||
var file = queue[index];
|
||||
|
||||
// remove from queue
|
||||
remove(index)
|
||||
|
||||
// collect file details
|
||||
var filesize = file.size;
|
||||
var filemode = file.mode;
|
||||
|
||||
// identify maximum file size
|
||||
var maxfilesize = 1048576 * opts.maxfilesize;
|
||||
|
||||
// check file size, unless it's an ftp file
|
||||
if (filesize < maxfilesize || file.mode == 'ftp')
|
||||
{
|
||||
// get parameters
|
||||
var data = opts.initialize(index, file);
|
||||
|
||||
// validate
|
||||
if (data)
|
||||
send(index, file, data);
|
||||
else
|
||||
error(index, file, opts.error_missing);
|
||||
} else {
|
||||
// skip file
|
||||
error(index, file, opts.error_filesize);
|
||||
}
|
||||
}
|
||||
|
||||
// send file
|
||||
function send (index, file, data)
|
||||
{
|
||||
// construct form data
|
||||
var formData = new FormData();
|
||||
for (var key in data) {
|
||||
formData.append(key, data[key]);
|
||||
}
|
||||
|
||||
// check file size
|
||||
if (file.size > 0 && opts.paramname) {
|
||||
formData.append(opts.paramname, file, file.name);
|
||||
}
|
||||
|
||||
// prepare request
|
||||
xhr = new XMLHttpRequest();
|
||||
xhr.open('POST', opts.url, true);
|
||||
xhr.setRequestHeader('Accept', 'application/json');
|
||||
xhr.setRequestHeader('Cache-Control', 'no-cache');
|
||||
xhr.setRequestHeader('X-Requested-With', 'XMLHttpRequest');
|
||||
|
||||
// captures state changes
|
||||
xhr.onreadystatechange = function()
|
||||
{
|
||||
// check for request completed, server connection closed
|
||||
if (xhr.readyState != xhr.DONE)
|
||||
return;
|
||||
|
||||
// retrieve response
|
||||
var response = null;
|
||||
if (xhr.responseText)
|
||||
{
|
||||
try
|
||||
{
|
||||
response = jQuery.parseJSON(xhr.responseText);
|
||||
} catch (e) {
|
||||
response = xhr.responseText;
|
||||
}
|
||||
}
|
||||
|
||||
// pass any error to the error option
|
||||
if (xhr.status < 200 || xhr.status > 299)
|
||||
{
|
||||
// format status
|
||||
var text = xhr.statusText;
|
||||
if (xhr.status == 403)
|
||||
text = opts.error_login;
|
||||
else if (xhr.status == 0)
|
||||
text = opts.error_server;
|
||||
else if (!text)
|
||||
text = opts.error_default;
|
||||
|
||||
// request error
|
||||
error(index, file, text + " (" + xhr.status + ")");
|
||||
} else
|
||||
// parse response
|
||||
success(index, file, response);
|
||||
}
|
||||
|
||||
// prepare upload progress
|
||||
xhr.upload.index = index;
|
||||
xhr.upload.file = file;
|
||||
xhr.upload.addEventListener('progress', progress, false);
|
||||
|
||||
// send request
|
||||
xhr.send(formData);
|
||||
}
|
||||
|
||||
// success
|
||||
function success (index, file, msg)
|
||||
{
|
||||
// parse message
|
||||
opts.success(index, file, msg);
|
||||
|
||||
// restart process after success
|
||||
process();
|
||||
}
|
||||
|
||||
// error
|
||||
function error (index, file, err)
|
||||
{
|
||||
// parse error
|
||||
opts.error(index, file, err);
|
||||
|
||||
// restart process after error
|
||||
process();
|
||||
}
|
||||
|
||||
/*
|
||||
public interface
|
||||
*/
|
||||
|
||||
// open file browser for selection
|
||||
function select()
|
||||
{
|
||||
$('#uploadbox_input').trigger('click');
|
||||
}
|
||||
|
||||
// remove all entries from queue
|
||||
function reset(index)
|
||||
{
|
||||
for (index in queue)
|
||||
remove(index);
|
||||
}
|
||||
|
||||
// initiate upload process
|
||||
function start()
|
||||
{
|
||||
if (!queue_running)
|
||||
{
|
||||
queue_running = true;
|
||||
process();
|
||||
}
|
||||
}
|
||||
|
||||
// stop upload process
|
||||
function stop()
|
||||
{
|
||||
// request stop
|
||||
queue_stop = true;
|
||||
}
|
||||
|
||||
// set options
|
||||
function configure(options)
|
||||
{
|
||||
// update current configuration
|
||||
opts = $.extend({}, opts, options);
|
||||
|
||||
// return new configuration
|
||||
return opts;
|
||||
}
|
||||
|
||||
// verify browser compatibility
|
||||
function compatible()
|
||||
{
|
||||
return window.File && window.FormData && window.XMLHttpRequest && window.FileList;
|
||||
}
|
||||
|
||||
// export functions
|
||||
return {
|
||||
'select' : select,
|
||||
'add' : add,
|
||||
'remove' : remove,
|
||||
'start' : start,
|
||||
'stop' : stop,
|
||||
'reset' : reset,
|
||||
'configure' : configure,
|
||||
'compatible' : compatible
|
||||
};
|
||||
}
|
||||
})(jQuery);
|
||||
|
||||
/*
|
||||
galaxy upload plugins - requires FormData and XMLHttpRequest
|
||||
*/
|
||||
;(function($){
|
||||
// add event properties
|
||||
jQuery.event.props.push("dataTransfer");
|
||||
|
||||
/**
|
||||
Posts file data to the API
|
||||
*/
|
||||
$.uploadpost = function (config) {
|
||||
// parse options
|
||||
var cnf = $.extend({}, {
|
||||
data : {},
|
||||
success : function() {},
|
||||
error : function() {},
|
||||
progress : function() {},
|
||||
url : null,
|
||||
maxfilesize : 2048,
|
||||
error_filesize : 'File exceeds 2GB. Please use an FTP client.',
|
||||
error_default : 'Please make sure the file is available.',
|
||||
error_server : 'Upload request failed.',
|
||||
error_login : 'Uploads require you to log in.'
|
||||
}, config);
|
||||
|
||||
// link data
|
||||
var data = cnf.data;
|
||||
|
||||
// check errors
|
||||
if (data.error_message) {
|
||||
cnf.error(data.error_message);
|
||||
return;
|
||||
}
|
||||
|
||||
// construct form data
|
||||
var form = new FormData();
|
||||
for (var key in data.payload) {
|
||||
form.append(key, data.payload[key]);
|
||||
}
|
||||
|
||||
// add files to submission
|
||||
var sizes = 0;
|
||||
for (var key in data.files) {
|
||||
var d = data.files[key];
|
||||
form.append(d.name, d.file, d.file.name);
|
||||
sizes += d.file.size;
|
||||
}
|
||||
|
||||
// check file size, unless it's an ftp file
|
||||
if (sizes > 1048576 * cnf.maxfilesize) {
|
||||
cnf.error(cnf.error_filesize);
|
||||
return;
|
||||
}
|
||||
|
||||
// prepare request
|
||||
xhr = new XMLHttpRequest();
|
||||
xhr.open('POST', cnf.url, true);
|
||||
xhr.setRequestHeader('Accept', 'application/json');
|
||||
xhr.setRequestHeader('Cache-Control', 'no-cache');
|
||||
xhr.setRequestHeader('X-Requested-With', 'XMLHttpRequest');
|
||||
|
||||
// captures state changes
|
||||
xhr.onreadystatechange = function() {
|
||||
// check for request completed, server connection closed
|
||||
if (xhr.readyState == xhr.DONE) {
|
||||
// parse response
|
||||
var response = null;
|
||||
if (xhr.responseText) {
|
||||
try {
|
||||
response = jQuery.parseJSON(xhr.responseText);
|
||||
} catch (e) {
|
||||
response = xhr.responseText;
|
||||
}
|
||||
}
|
||||
// pass any error to the error option
|
||||
if (xhr.status < 200 || xhr.status > 299) {
|
||||
var text = xhr.statusText;
|
||||
if (xhr.status == 403) {
|
||||
text = cnf.error_login;
|
||||
} else if (xhr.status == 0) {
|
||||
text = cnf.error_server;
|
||||
} else if (!text) {
|
||||
text = cnf.error_default;
|
||||
}
|
||||
cnf.error(text + ' (' + xhr.status + ')');
|
||||
} else {
|
||||
cnf.success(response);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// prepare upload progress
|
||||
xhr.upload.addEventListener('progress', function(e) {
|
||||
if (e.lengthComputable) {
|
||||
cnf.progress(Math.round((e.loaded * 100) / e.total));
|
||||
}
|
||||
}, false);
|
||||
|
||||
// send request
|
||||
console.debug('uploadbox::uploadpost() - Posting following data:');
|
||||
console.debug(cnf);
|
||||
xhr.send(form);
|
||||
}
|
||||
|
||||
/**
|
||||
Handles the upload events drag/drop etc.
|
||||
*/
|
||||
$.fn.uploadinput = function(options) {
|
||||
// initialize
|
||||
var el = this;
|
||||
var opts = $.extend({}, {
|
||||
ondragover : function() {},
|
||||
ondragleave : function() {},
|
||||
onchange : function() {},
|
||||
multiple : false
|
||||
}, options);
|
||||
|
||||
// append hidden upload field
|
||||
var $input = $('<input type="file" style="display: none" ' + (opts.multiple && 'multiple' || '') + '/>');
|
||||
el.append($input).change(function (e) {
|
||||
opts.onchange(e.target.files);
|
||||
$(this).val('');
|
||||
});
|
||||
|
||||
// drag/drop events
|
||||
el.on('drop', function (e) {
|
||||
opts.ondragleave(e);
|
||||
if(e.dataTransfer) {
|
||||
opts.onchange(e.dataTransfer.files);
|
||||
e.preventDefault();
|
||||
}
|
||||
});
|
||||
el.on('dragover', function (e) {
|
||||
e.preventDefault();
|
||||
opts.ondragover(e);
|
||||
});
|
||||
el.on('dragleave', function (e) {
|
||||
e.stopPropagation();
|
||||
opts.ondragleave(e);
|
||||
});
|
||||
|
||||
// exports
|
||||
return {
|
||||
dialog: function () {
|
||||
$input.trigger('click');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
Handles the upload queue and events such as drag/drop etc.
|
||||
*/
|
||||
$.fn.uploadbox = function(options) {
|
||||
// parse options
|
||||
var opts = $.extend({}, {
|
||||
dragover : function() {},
|
||||
dragleave : function() {},
|
||||
announce : function(d) {},
|
||||
initialize : function(d) {},
|
||||
progress : function(d, m) {},
|
||||
success : function(d, m) {},
|
||||
error : function(d, m) { alert(m); },
|
||||
complete : function() {}
|
||||
}, options);
|
||||
|
||||
// file queue
|
||||
var queue = {};
|
||||
|
||||
// queue index/length counter
|
||||
var queue_index = 0;
|
||||
var queue_length = 0;
|
||||
|
||||
// indicates if queue is currently running
|
||||
var queue_running = false;
|
||||
var queue_stop = false;
|
||||
|
||||
// element
|
||||
var uploadinput = $(this).uploadinput({
|
||||
multiple : true,
|
||||
onchange : function(files) { add(files); },
|
||||
ondragover : options.ondragover,
|
||||
ondragleave : options.ondragleave
|
||||
});
|
||||
|
||||
// add new files to upload queue
|
||||
function add(files) {
|
||||
if (files && files.length && !queue_running) {
|
||||
var current_index = queue_index;
|
||||
for (var i = 0; i < files.length; i++) {
|
||||
var index = String(queue_index++);
|
||||
queue[index] = files[i];
|
||||
opts.announce(index, queue[index]);
|
||||
queue_length++;
|
||||
}
|
||||
return current_index;
|
||||
}
|
||||
}
|
||||
|
||||
// remove file from queue
|
||||
function remove(index) {
|
||||
if (queue[index]) {
|
||||
delete queue[index];
|
||||
queue_length--;
|
||||
}
|
||||
}
|
||||
|
||||
// process an upload, recursive
|
||||
function process() {
|
||||
// validate
|
||||
if (queue_length == 0 || queue_stop) {
|
||||
queue_stop = false;
|
||||
queue_running = false;
|
||||
opts.complete();
|
||||
return;
|
||||
} else {
|
||||
queue_running = true;
|
||||
}
|
||||
|
||||
// get an identifier from the queue
|
||||
var index = -1;
|
||||
for (var key in queue) {
|
||||
index = key;
|
||||
break;
|
||||
}
|
||||
|
||||
// get current file from queue
|
||||
var file = queue[index];
|
||||
|
||||
// remove from queue
|
||||
remove(index)
|
||||
|
||||
// create and submit data
|
||||
$.uploadpost({
|
||||
url : opts.url,
|
||||
data : opts.initialize(index),
|
||||
success : function(message) { opts.success(index, message); process();},
|
||||
error : function(message) { opts.error(index, message); process();},
|
||||
progress : function(percentage) { opts.progress(index, percentage); }
|
||||
});
|
||||
}
|
||||
|
||||
/*
|
||||
public interface
|
||||
*/
|
||||
|
||||
// open file browser for selection
|
||||
function select() {
|
||||
uploadinput.dialog();
|
||||
}
|
||||
|
||||
// remove all entries from queue
|
||||
function reset(index) {
|
||||
for (index in queue) {
|
||||
remove(index);
|
||||
}
|
||||
}
|
||||
|
||||
// initiate upload process
|
||||
function start() {
|
||||
if (!queue_running) {
|
||||
queue_running = true;
|
||||
process();
|
||||
}
|
||||
}
|
||||
|
||||
// stop upload process
|
||||
function stop() {
|
||||
queue_stop = true;
|
||||
}
|
||||
|
||||
// set options
|
||||
function configure(options) {
|
||||
opts = $.extend({}, opts, options);
|
||||
return opts;
|
||||
}
|
||||
|
||||
// verify browser compatibility
|
||||
function compatible() {
|
||||
return window.File && window.FormData && window.XMLHttpRequest && window.FileList;
|
||||
}
|
||||
|
||||
// export functions
|
||||
return {
|
||||
'select' : select,
|
||||
'add' : add,
|
||||
'remove' : remove,
|
||||
'start' : start,
|
||||
'stop' : stop,
|
||||
'reset' : reset,
|
||||
'configure' : configure,
|
||||
'compatible' : compatible
|
||||
};
|
||||
}
|
||||
})(jQuery);
|
||||
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
*/
|
||||
|
||||
// dependencies
|
||||
define(["libs/underscore"], function(_) {
|
||||
define([], function() {
|
||||
|
||||
/** Traverse through json
|
||||
*/
|
||||
@@ -116,7 +116,7 @@ function request (options) {
|
||||
data : options.data || {},
|
||||
url : options.url
|
||||
}
|
||||
|
||||
|
||||
// encode data into url
|
||||
if (ajaxConfig.type == 'GET' || ajaxConfig.type == 'DELETE') {
|
||||
if (ajaxConfig.url.indexOf('?') == -1) {
|
||||
@@ -156,36 +156,27 @@ function request (options) {
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
/**
|
||||
* Read a property value from CSS
|
||||
* @param{String} classname - CSS class
|
||||
* @param{String} name - CSS property
|
||||
*/
|
||||
function cssGetAttribute (classname, name) {
|
||||
// place dummy element
|
||||
var el = $('<div class="' + classname + '"></div>');
|
||||
|
||||
// required append
|
||||
el.appendTo(':eq(0)');
|
||||
|
||||
// get value
|
||||
var value = el.css(name);
|
||||
|
||||
// remove element
|
||||
el.remove();
|
||||
|
||||
// return css value
|
||||
return value;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Load a CSS file
|
||||
* @param{String} url - Url of CSS file
|
||||
*/
|
||||
function cssLoadFile (url) {
|
||||
// check if css is already available
|
||||
if (!$('link[href^="' + url + '"]').length)
|
||||
if (!$('link[href^="' + url + '"]').length) {
|
||||
$('<link href="' + galaxy_config.root + url + '" rel="stylesheet">').appendTo('head');
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
@@ -194,29 +185,49 @@ function cssLoadFile (url) {
|
||||
* @param{Object} optionsDefault - Source dictionary
|
||||
*/
|
||||
function merge (options, optionsDefault) {
|
||||
if (options)
|
||||
if (options) {
|
||||
return _.defaults(options, optionsDefault);
|
||||
else
|
||||
} else {
|
||||
return optionsDefault;
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Round floaing point 'number' to 'numPlaces' number of decimal places.
|
||||
* @param{Object} number a floaing point number
|
||||
* @param{Object} numPlaces number of decimal places
|
||||
*/
|
||||
function roundToDecimalPlaces( number, numPlaces ){
|
||||
var placesMultiplier = 1;
|
||||
for( var i=0; i<numPlaces; i++ ){
|
||||
placesMultiplier *= 10;
|
||||
}
|
||||
return Math.round( number * placesMultiplier ) / placesMultiplier;
|
||||
}
|
||||
|
||||
// calculate on import
|
||||
var kb = 1024,
|
||||
mb = kb * kb,
|
||||
gb = mb * kb,
|
||||
tb = gb * kb;
|
||||
/**
|
||||
* Format byte size to string with units
|
||||
* @param{Integer} size - Size in bytes
|
||||
* @param{Boolean} normal_font - Switches font between normal and bold
|
||||
*/
|
||||
function bytesToString (size, normal_font) {
|
||||
function bytesToString (size, normal_font, numberPlaces) {
|
||||
numberPlaces = numberPlaces !== undefined? numberPlaces: 1;
|
||||
// identify unit
|
||||
var unit = "";
|
||||
if (size >= 100000000000) { size = size / 100000000000; unit = 'TB'; } else
|
||||
if (size >= 100000000) { size = size / 100000000; unit = 'GB'; } else
|
||||
if (size >= 100000) { size = size / 100000; unit = 'MB'; } else
|
||||
if (size >= 100) { size = size / 100; unit = 'KB'; } else
|
||||
if (size > 0) { size = size * 10; unit = 'b'; } else
|
||||
return '<strong>-</strong>';
|
||||
|
||||
if (size >= tb){ size = size / tb; unit = 'TB'; } else
|
||||
if (size >= gb){ size = size / gb; unit = 'GB'; } else
|
||||
if (size >= mb){ size = size / mb; unit = 'MB'; } else
|
||||
if (size >= kb){ size = size / kb; unit = 'KB'; } else
|
||||
if (size > 0){ unit = 'b'; }
|
||||
else { return normal_font? '0 b': '<strong>-</strong>'; }
|
||||
// return formatted string
|
||||
var rounded = (Math.round(size) / 10);
|
||||
var rounded = unit == 'b'? size: roundToDecimalPlaces( size, numberPlaces );
|
||||
if (normal_font) {
|
||||
return rounded + ' ' + unit;
|
||||
} else {
|
||||
@@ -236,27 +247,21 @@ function uid(){
|
||||
* Create a time stamp
|
||||
*/
|
||||
function time() {
|
||||
// get date object
|
||||
var d = new Date();
|
||||
|
||||
// format items
|
||||
var hours = (d.getHours() < 10 ? "0" : "") + d.getHours();
|
||||
var minutes = (d.getMinutes() < 10 ? "0" : "") + d.getMinutes()
|
||||
|
||||
// format final stamp
|
||||
var datetime = d.getDate() + "/"
|
||||
return datetime = d.getDate() + "/"
|
||||
+ (d.getMonth() + 1) + "/"
|
||||
+ d.getFullYear() + ", "
|
||||
+ hours + ":"
|
||||
+ minutes;
|
||||
return datetime;
|
||||
};
|
||||
|
||||
return {
|
||||
cssLoadFile : cssLoadFile,
|
||||
cssGetAttribute : cssGetAttribute,
|
||||
get : get,
|
||||
merge : merge,
|
||||
cssLoadFile: cssLoadFile,
|
||||
cssGetAttribute: cssGetAttribute,
|
||||
get: get,
|
||||
merge: merge,
|
||||
bytesToString: bytesToString,
|
||||
uid: uid,
|
||||
time: time,
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/**
|
||||
* Visualization and components for Sweepster, a visualization for exploring a tool's parameter space via
|
||||
* Visualization and components for Sweepster, a visualization for exploring a tool's parameter space via
|
||||
* genomic visualization.
|
||||
*/
|
||||
|
||||
@@ -7,7 +7,7 @@ define(["libs/underscore", "libs/d3", "viz/trackster/util", "viz/visualization",
|
||||
function(_, d3, util, visualization, tracks, tools, data, config, mod_icon_btn) {
|
||||
|
||||
/**
|
||||
* A collection of tool input settings. Object is useful for keeping a list of settings
|
||||
* A collection of tool input settings. Object is useful for keeping a list of settings
|
||||
* for future use without changing the input's value and for preserving inputs order.
|
||||
*/
|
||||
var ToolInputsSettings = Backbone.Model.extend({
|
||||
@@ -16,7 +16,7 @@ var ToolInputsSettings = Backbone.Model.extend({
|
||||
values: null
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
/**
|
||||
* Tree for a tool's parameters.
|
||||
*/
|
||||
@@ -25,7 +25,7 @@ var ToolParameterTree = Backbone.Model.extend({
|
||||
tool: null,
|
||||
tree_data: null
|
||||
},
|
||||
|
||||
|
||||
initialize: function(options) {
|
||||
// Set up tool parameters to work with tree.
|
||||
var self = this;
|
||||
@@ -109,7 +109,7 @@ var ToolParameterTree = Backbone.Model.extend({
|
||||
};
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
// Recurse to handle other parameters.
|
||||
return _.map(settings, function(setting) {
|
||||
return {
|
||||
@@ -141,7 +141,7 @@ var ToolParameterTree = Backbone.Model.extend({
|
||||
get_num_leaves: function() {
|
||||
return this.get_tree_params().reduce(function(memo, param) { return memo * param.get_samples().length; }, 1);
|
||||
},
|
||||
|
||||
|
||||
/**
|
||||
* Returns array of ToolInputsSettings objects based on a node and its subtree.
|
||||
*/
|
||||
@@ -159,7 +159,7 @@ var ToolParameterTree = Backbone.Model.extend({
|
||||
cur_node = cur_node.parent;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// Walk subtree starting at clicked node to get full list of settings.
|
||||
var self = this,
|
||||
get_settings = function(node, settings) {
|
||||
@@ -168,7 +168,7 @@ var ToolParameterTree = Backbone.Model.extend({
|
||||
if (node.param) {
|
||||
settings[node.param.get('name')] = node.value;
|
||||
}
|
||||
|
||||
|
||||
if (!node.children) {
|
||||
// At leaf node, so return settings.
|
||||
return new ToolInputsSettings({
|
||||
@@ -182,10 +182,10 @@ var ToolParameterTree = Backbone.Model.extend({
|
||||
}
|
||||
},
|
||||
all_settings = get_settings(target_node, fixed_settings);
|
||||
|
||||
|
||||
// If user clicked on leaf, settings is a single dict. Convert to array for simplicity.
|
||||
if (!_.isArray(all_settings)) { all_settings = [ all_settings ]; }
|
||||
|
||||
|
||||
return all_settings;
|
||||
},
|
||||
|
||||
@@ -235,7 +235,7 @@ var ToolParameterTree = Backbone.Model.extend({
|
||||
* Returns a list of parameters used in tree.
|
||||
*/
|
||||
toJSON: function() {
|
||||
// FIXME: returning and jsonifying complete param causes trouble on the server side,
|
||||
// FIXME: returning and jsonifying complete param causes trouble on the server side,
|
||||
// so just use essential attributes for now.
|
||||
return this.get_tree_params().map(function(param) {
|
||||
return {
|
||||
@@ -255,7 +255,7 @@ var SweepsterTrack = Backbone.Model.extend({
|
||||
settings: null,
|
||||
regions: null
|
||||
},
|
||||
|
||||
|
||||
initialize: function(options) {
|
||||
this.set('regions', options.regions);
|
||||
if (options.track) {
|
||||
@@ -272,7 +272,7 @@ var SweepsterTrack = Backbone.Model.extend({
|
||||
var this_settings = this.get('settings'),
|
||||
other_settings = a_track.get('settings');
|
||||
for (var prop in this_settings) {
|
||||
if (!other_settings[prop] ||
|
||||
if (!other_settings[prop] ||
|
||||
this_settings[prop] !== other_settings[prop]) {
|
||||
return false;
|
||||
}
|
||||
@@ -316,8 +316,8 @@ var SweepsterVisualization = visualization.Visualization.extend({
|
||||
this.set('tool_with_samplable_inputs', tool_with_samplable_inputs);
|
||||
// Remove complex parameters for now.
|
||||
tool_with_samplable_inputs.remove_inputs( [ 'data', 'hidden_data', 'conditional', 'text' ] );
|
||||
|
||||
this.set('parameter_tree', new ToolParameterTree({
|
||||
|
||||
this.set('parameter_tree', new ToolParameterTree({
|
||||
tool: tool_with_samplable_inputs,
|
||||
config: options.tree_config
|
||||
}));
|
||||
@@ -445,9 +445,9 @@ var SweepsterTrackView = Backbone.View.extend({
|
||||
var ToolInputValOrSweepView = Backbone.View.extend({
|
||||
|
||||
// Template for rendering sweep inputs:
|
||||
number_input_template: '<div class="form-row-input sweep">' +
|
||||
number_input_template: '<div class="form-row-input sweep">' +
|
||||
'<input class="min" type="text" size="6" value="<%= min %>"> - ' +
|
||||
'<input class="max" type="text" size="6" value="<%= max %>">' +
|
||||
'<input class="max" type="text" size="6" value="<%= max %>">' +
|
||||
' samples: <input class="num_samples" type="text" size="1" value="<%= num_samples %>">' +
|
||||
'</div>',
|
||||
|
||||
@@ -471,16 +471,16 @@ var ToolInputValOrSweepView = Backbone.View.extend({
|
||||
|
||||
// Add row for parameter sweep inputs.
|
||||
if (input instanceof tools.IntegerToolParameter) {
|
||||
sweep_inputs_row = $(_.template(this.number_input_template, this.model.toJSON()));
|
||||
sweep_inputs_row = $( _.template(this.number_input_template)(this.model.toJSON()) );
|
||||
}
|
||||
else if (input instanceof tools.SelectToolParameter) {
|
||||
var options = _.map(this.$el.find('select option'), function(option) {
|
||||
return $(option).val();
|
||||
}),
|
||||
options_text = options.join(', ');
|
||||
sweep_inputs_row = $(_.template(this.select_input_template, {
|
||||
sweep_inputs_row = $( _.template(this.select_input_template)({
|
||||
options: options_text
|
||||
}));
|
||||
}) );
|
||||
}
|
||||
sweep_inputs_row.insertAfter(single_input_row);
|
||||
|
||||
@@ -497,7 +497,7 @@ var ToolInputValOrSweepView = Backbone.View.extend({
|
||||
$(this).hide();
|
||||
self.$el.find('.icon-button.toggle').show();
|
||||
}
|
||||
|
||||
|
||||
},
|
||||
{
|
||||
title: 'Remove parameter from tree',
|
||||
@@ -511,9 +511,9 @@ var ToolInputValOrSweepView = Backbone.View.extend({
|
||||
self.$el.find('.icon-button.plus-button').show();
|
||||
}
|
||||
}
|
||||
],
|
||||
],
|
||||
{
|
||||
|
||||
|
||||
});
|
||||
this.$el.prepend(menu.$el);
|
||||
|
||||
@@ -568,12 +568,12 @@ var ToolParameterTreeDesignView = Backbone.View.extend({
|
||||
*/
|
||||
var ToolParameterTreeView = Backbone.View.extend({
|
||||
className: 'tool-parameter-tree',
|
||||
|
||||
|
||||
initialize: function(options) {
|
||||
// When tree data changes, re-render.
|
||||
this.model.on('change:tree_data', this.render, this);
|
||||
},
|
||||
|
||||
|
||||
render: function() {
|
||||
// Start fresh.
|
||||
this.$el.children().remove();
|
||||
@@ -640,7 +640,7 @@ var ToolParameterTreeView = Backbone.View.extend({
|
||||
.on('mouseout', function() {
|
||||
node.style('fill', '#000');
|
||||
});
|
||||
|
||||
|
||||
node.append("circle")
|
||||
.attr("r", 9);
|
||||
|
||||
@@ -658,13 +658,13 @@ var ToolParameterTreeView = Backbone.View.extend({
|
||||
var SweepsterVisualizationView = Backbone.View.extend({
|
||||
className: 'Sweepster',
|
||||
|
||||
helpText:
|
||||
helpText:
|
||||
'<div><h4>Getting Started</h4>' +
|
||||
'<ol><li>Create a parameter tree by using the icons next to the tool\'s parameter names to add or remove parameters.' +
|
||||
'<li>Adjust the tree by using parameter inputs to select min, max, and number of samples' +
|
||||
'<li>Run the tool with different settings by clicking on tree nodes' +
|
||||
'</ol></div>',
|
||||
|
||||
|
||||
initialize: function(options) {
|
||||
this.canvas_manager = new visualization.CanvasManager(this.$el.parents('body'));
|
||||
this.tool_param_tree_view = new ToolParameterTreeView({ model: this.model.get('parameter_tree') });
|
||||
@@ -680,7 +680,7 @@ var SweepsterVisualizationView = Backbone.View.extend({
|
||||
});
|
||||
|
||||
// Set block, reverse strand block colors; these colors will be used for all tracks.
|
||||
this.config = config.ConfigSettingCollection.from_models_and_saved_values(
|
||||
this.config = config.ConfigSettingCollection.from_models_and_saved_values(
|
||||
[
|
||||
{ key: 'name', label: 'Name', type: 'text', default_value: '' },
|
||||
{ key: 'a_color', label: 'A Color', type: 'color', default_value: "#FF0000" },
|
||||
@@ -693,7 +693,7 @@ var SweepsterVisualizationView = Backbone.View.extend({
|
||||
], {}
|
||||
);
|
||||
},
|
||||
|
||||
|
||||
render: function() {
|
||||
// Render tree design view in left panel.
|
||||
var tree_design_view = new ToolParameterTreeDesignView({
|
||||
@@ -749,7 +749,7 @@ var SweepsterVisualizationView = Backbone.View.extend({
|
||||
[
|
||||
// Save.
|
||||
/*
|
||||
{ icon_class: 'disk--arrow', title: 'Save', on_click: function() {
|
||||
{ icon_class: 'disk--arrow', title: 'Save', on_click: function() {
|
||||
// Show saving dialog box
|
||||
show_modal("Saving...", "progress");
|
||||
|
||||
@@ -760,8 +760,8 @@ var SweepsterVisualizationView = Backbone.View.extend({
|
||||
'has_changes': false
|
||||
});
|
||||
})
|
||||
.error(function() {
|
||||
show_modal( "Could Not Save", "Could not save visualization. Please try again later.",
|
||||
.error(function() {
|
||||
show_modal( "Could Not Save", "Could not save visualization. Please try again later.",
|
||||
{ "Close" : hide_modal } );
|
||||
});
|
||||
} },
|
||||
@@ -772,14 +772,14 @@ var SweepsterVisualizationView = Backbone.View.extend({
|
||||
title: 'Set display mode'
|
||||
},
|
||||
// Close viz.
|
||||
{
|
||||
icon_class: 'cross-circle',
|
||||
title: 'Close',
|
||||
on_click: function() {
|
||||
{
|
||||
icon_class: 'cross-circle',
|
||||
title: 'Close',
|
||||
on_click: function() {
|
||||
window.location = "${h.url_for( controller='visualization', action='list' )}";
|
||||
}
|
||||
}
|
||||
}
|
||||
],
|
||||
],
|
||||
{
|
||||
tooltip_config: {placement: 'bottom'}
|
||||
});
|
||||
@@ -797,13 +797,13 @@ var SweepsterVisualizationView = Backbone.View.extend({
|
||||
});
|
||||
|
||||
make_popupmenu(menu.$el.find('.chevron-expand'), mode_mapping);
|
||||
|
||||
|
||||
menu.$el.attr("style", "float: right");
|
||||
$("#right .unified-panel-header-inner").append(menu.$el);
|
||||
},
|
||||
|
||||
get_base_color: function(base) {
|
||||
return this.config.get_value(base.toLowerCase() + '_color') ||
|
||||
return this.config.get_value(base.toLowerCase() + '_color') ||
|
||||
this.config.get_value('n_color');
|
||||
},
|
||||
|
||||
@@ -857,7 +857,7 @@ var SweepsterVisualizationView = Backbone.View.extend({
|
||||
},
|
||||
|
||||
/**
|
||||
* Sets up handling when tree nodes are clicked. When a node is clicked, the tool is run for each of
|
||||
* Sets up handling when tree nodes are clicked. When a node is clicked, the tool is run for each of
|
||||
* the settings defined by the node's subtree and tracks are added for each run.
|
||||
*/
|
||||
handle_node_clicks: function() {
|
||||
@@ -875,11 +875,11 @@ var SweepsterVisualizationView = Backbone.View.extend({
|
||||
|
||||
// Do not allow 10+ jobs to be run.
|
||||
if (all_settings.length >= 10) {
|
||||
show_modal("Whoa there cowboy!",
|
||||
show_modal("Whoa there cowboy!",
|
||||
"You clicked on a node to try " + self.model.get('tool').get('name') +
|
||||
" with " + all_settings.length +
|
||||
" different combinations of settings. You can only run 10 jobs at a time.",
|
||||
{
|
||||
{
|
||||
"Ok": function() { hide_modal(); run_jobs_deferred.resolve(false); }
|
||||
});
|
||||
}
|
||||
@@ -901,7 +901,7 @@ var SweepsterVisualizationView = Backbone.View.extend({
|
||||
self.add_track(pm_track);
|
||||
return pm_track;
|
||||
});
|
||||
|
||||
|
||||
// For each track, run tool using track's settings and update track.
|
||||
_.each(new_tracks, function(pm_track, index) {
|
||||
setTimeout(function() {
|
||||
@@ -915,7 +915,7 @@ var SweepsterVisualizationView = Backbone.View.extend({
|
||||
track_config = dataset.get('track_config');
|
||||
// Set dataset to be the tool's output.
|
||||
track_config.dataset = dataset;
|
||||
// Set tool to null so that it is not unpacked; unpacking it messes with
|
||||
// Set tool to null so that it is not unpacked; unpacking it messes with
|
||||
// the tool parameters and parameter tree.
|
||||
track_config.tool = null;
|
||||
|
||||
@@ -924,11 +924,11 @@ var SweepsterVisualizationView = Backbone.View.extend({
|
||||
// Create and add track for output dataset.
|
||||
var track_obj = tracks.object_from_template(track_config, self, null);
|
||||
track_obj.init_for_tool_data();
|
||||
|
||||
|
||||
pm_track.set('track', track_obj);
|
||||
});
|
||||
}, index * 10000);
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
@@ -31,8 +31,8 @@ require(
|
||||
});
|
||||
|
||||
// trackster viewer
|
||||
define( ["libs/underscore", "base", "viz/trackster/tracks", "viz/visualization", "mvc/ui/icon-button" ],
|
||||
function(_, base, tracks, visualization, mod_icon_btn) {
|
||||
define( ["libs/underscore", "base", "viz/trackster/tracks", "viz/visualization", "mvc/ui/icon-button", "utils/query-string-parsing" ],
|
||||
function(_, base, tracks, visualization, mod_icon_btn, query_string ) {
|
||||
/**
|
||||
* User interface controls for trackster
|
||||
*/
|
||||
@@ -401,11 +401,84 @@ var TracksterView = Backbone.View.extend(
|
||||
// hide right panel
|
||||
force_right_panel("hide");
|
||||
|
||||
|
||||
// check if id is available
|
||||
if (galaxy_config.app.id)
|
||||
if (galaxy_config.app.id){
|
||||
this.view_existing();
|
||||
else
|
||||
} else if( query_string.get( 'dataset_id' ) ){
|
||||
this.choose_existing_or_new();
|
||||
} else {
|
||||
this.view_new();
|
||||
}
|
||||
},
|
||||
|
||||
choose_existing_or_new : function () {
|
||||
var self = this;
|
||||
var dbkey = query_string.get( 'dbkey' ),
|
||||
listTracksParams = {},
|
||||
dataset_params = {
|
||||
dbkey : dbkey,
|
||||
dataset_id : query_string.get( 'dataset_id' ),
|
||||
hda_ldda : query_string.get( 'hda_ldda' )
|
||||
};
|
||||
if (dbkey){
|
||||
listTracksParams[ 'f-dbkey' ] = dbkey;
|
||||
}
|
||||
$.ajax({
|
||||
url: galaxy_config.root + 'visualization/list_tracks?' + $.param( listTracksParams ),
|
||||
dataType: "html",
|
||||
error: function() { alert( "Could not add this dataset to browser." ); },
|
||||
|
||||
success: function(table_html) {
|
||||
console.debug( 'success' );
|
||||
function view_in_saved(){
|
||||
// Show new modal with saved visualizations.
|
||||
Galaxy.modal.show({
|
||||
title : "Add Data to Saved Visualization",
|
||||
body : table_html,
|
||||
buttons : {
|
||||
"Cancel": function() {
|
||||
// go back to the first choice
|
||||
show_choice();
|
||||
},
|
||||
"Add to visualization": function() {
|
||||
$(parent.document).find('input[name=id]:checked').each(function() {
|
||||
var vis_id = $(this).val();
|
||||
dataset_params.id = vis_id;
|
||||
window.location = galaxy_config.root + "visualization/trackster?" + $.param(dataset_params);
|
||||
});
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
function show_choice(){
|
||||
Galaxy.modal.show({
|
||||
title : "View Data in a New or Saved Visualization?",
|
||||
// either have text in here or have to remove body and the header/footer margins
|
||||
body : (
|
||||
// TODO: yay. inline styles. thanks, galaxy
|
||||
'<p><ul style="list-style: disc inside none">You can add this dataset as:' +
|
||||
"<li>a new track to one of your existing, saved Trackster " +
|
||||
"sessions if they share the genome build: <b>" + dbkey + "</b></li>" +
|
||||
"<li>or create a new session with this dataset as the only track</li>" +
|
||||
"</ul></p>"
|
||||
),
|
||||
buttons : {
|
||||
"Cancel": function() {
|
||||
window.location = galaxy_config.root + "visualization/list";
|
||||
},
|
||||
"View in saved visualization": function() {
|
||||
view_in_saved();
|
||||
},
|
||||
"View in new visualization": function() {
|
||||
self.view_new();
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
show_choice();
|
||||
}
|
||||
});
|
||||
},
|
||||
|
||||
// view
|
||||
@@ -459,7 +532,8 @@ var TracksterView = Backbone.View.extend(
|
||||
});
|
||||
|
||||
// select default
|
||||
if (galaxy_config.app.default_dbkey) {
|
||||
var dbkeys_in_genomes = response.map( function( r ){ return r[1]; });
|
||||
if (galaxy_config.app.default_dbkey && _.contains(dbkeys_in_genomes,galaxy_config.app.default_dbkey)) {
|
||||
$("#new-dbkey").val( galaxy_config.app.default_dbkey );
|
||||
}
|
||||
|
||||
|
||||
@@ -8,7 +8,7 @@ define( ["libs/underscore"], function( _ ) {
|
||||
*/
|
||||
var BEFORE = 1001, CONTAINS = 1002, OVERLAP_START = 1003, OVERLAP_END = 1004, CONTAINED_BY = 1005, AFTER = 1006;
|
||||
var compute_overlap = function(first_region, second_region) {
|
||||
var
|
||||
var
|
||||
first_start = first_region[0], first_end = first_region[1],
|
||||
second_start = second_region[0], second_end = second_region[1],
|
||||
overlap;
|
||||
@@ -34,7 +34,7 @@ var compute_overlap = function(first_region, second_region) {
|
||||
overlap = OVERLAP_END;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
return overlap;
|
||||
};
|
||||
|
||||
@@ -60,7 +60,7 @@ var dashedLine = function(ctx, x1, y1, x2, y2, dashLen) {
|
||||
var dashX = dX / dashes;
|
||||
var dashY = dY / dashes;
|
||||
var q;
|
||||
|
||||
|
||||
for (q = 0; q < dashes; q++, x1 += dashX, y1 += dashY) {
|
||||
if (q % 2 !== 0) {
|
||||
continue;
|
||||
@@ -74,11 +74,11 @@ var dashedLine = function(ctx, x1, y1, x2, y2, dashLen) {
|
||||
*/
|
||||
var drawDownwardEquilateralTriangle = function(ctx, down_vertex_x, down_vertex_y, side_len) {
|
||||
// Compute other two points of triangle.
|
||||
var
|
||||
var
|
||||
x1 = down_vertex_x - side_len/2,
|
||||
x2 = down_vertex_x + side_len/2,
|
||||
y = down_vertex_y - Math.sqrt( side_len*3/2 );
|
||||
|
||||
|
||||
// Draw and fill.
|
||||
ctx.beginPath();
|
||||
ctx.moveTo(x1, y);
|
||||
@@ -198,16 +198,16 @@ LinePainter.prototype.draw = function(ctx, width, height, w_scale) {
|
||||
ctx.fillStyle = "#aaa";
|
||||
ctx.fillRect( 0, y_zero, width, 1 );
|
||||
}
|
||||
|
||||
|
||||
ctx.beginPath();
|
||||
var x_scaled, y, delta_x_px;
|
||||
if (data.length > 1) {
|
||||
delta_x_px = Math.ceil((data[1][0] - data[0][0]) * w_scale);
|
||||
}
|
||||
}
|
||||
else {
|
||||
delta_x_px = 10;
|
||||
}
|
||||
|
||||
|
||||
// Painter color can be in either block_color (FeatureTrack) or color pref (LineTrack).
|
||||
var painter_color = this.prefs.block_color || this.prefs.color,
|
||||
// Extract RGB from preference color.
|
||||
@@ -215,10 +215,10 @@ LinePainter.prototype.draw = function(ctx, width, height, w_scale) {
|
||||
pref_r = (pref_color & 0xff0000) >> 16,
|
||||
pref_g = (pref_color & 0x00ff00) >> 8,
|
||||
pref_b = pref_color & 0x0000ff,
|
||||
top_overflow = false,
|
||||
top_overflow = false,
|
||||
bot_overflow = false;
|
||||
|
||||
|
||||
|
||||
|
||||
// Paint track.
|
||||
for (var i = 0, len = data.length; i < len; i++) {
|
||||
// Reset attributes for next point.
|
||||
@@ -241,20 +241,20 @@ LinePainter.prototype.draw = function(ctx, width, height, w_scale) {
|
||||
if (y < min_value) {
|
||||
bot_overflow = true;
|
||||
y = min_value;
|
||||
}
|
||||
}
|
||||
else if (y > max_value) {
|
||||
top_overflow = true;
|
||||
y = max_value;
|
||||
}
|
||||
|
||||
|
||||
// Draw point.
|
||||
if (mode === "Histogram") {
|
||||
// y becomes the bar height in pixels, which is the negated for canvas coords
|
||||
y = Math.round( y / vertical_range * height_px );
|
||||
ctx.fillRect(x_scaled, y_zero, delta_x_px, - y );
|
||||
}
|
||||
}
|
||||
else if (mode === "Intensity") {
|
||||
var
|
||||
var
|
||||
saturation = (y - min_value) / vertical_range,
|
||||
// Range is [pref_color, 255] where saturation = 0 --> 255 and saturation = 1 --> pref color
|
||||
new_r = Math.round( pref_r + (255 - pref_r) * (1 - saturation) ),
|
||||
@@ -262,23 +262,23 @@ LinePainter.prototype.draw = function(ctx, width, height, w_scale) {
|
||||
new_b = Math.round( pref_b + (255 - pref_b) * (1 - saturation) );
|
||||
ctx.fillStyle = "rgb(" + new_r + "," + new_g + "," + new_b + ")";
|
||||
ctx.fillRect(x_scaled, 0, delta_x_px, height_px);
|
||||
}
|
||||
}
|
||||
else { // mode is Coverage/Line or Filled.
|
||||
|
||||
// Scale Y value.
|
||||
y = Math.round( height_px - (y - min_value) / vertical_range * height_px );
|
||||
if (in_path) {
|
||||
ctx.lineTo(x_scaled, y);
|
||||
}
|
||||
}
|
||||
else {
|
||||
in_path = true;
|
||||
if (mode === "Filled") {
|
||||
ctx.moveTo(x_scaled, height_px);
|
||||
ctx.lineTo(x_scaled, y);
|
||||
}
|
||||
}
|
||||
else {
|
||||
ctx.moveTo(x_scaled, y);
|
||||
// Use this approach (note: same as for filled) to draw line from 0 to
|
||||
// Use this approach (note: same as for filled) to draw line from 0 to
|
||||
// first data point.
|
||||
//ctx.moveTo(x_scaled, height_px);
|
||||
//ctx.lineTo(x_scaled, y);
|
||||
@@ -292,7 +292,7 @@ LinePainter.prototype.draw = function(ctx, width, height, w_scale) {
|
||||
var overflow_x;
|
||||
if (mode === "Histogram" || mode === "Intensity") {
|
||||
overflow_x = delta_x_px;
|
||||
}
|
||||
}
|
||||
else { // Line and Filled, which are points
|
||||
x_scaled -= 2; // Move it over to the left so it's centered on the point
|
||||
overflow_x = 4;
|
||||
@@ -312,11 +312,11 @@ LinePainter.prototype.draw = function(ctx, width, height, w_scale) {
|
||||
ctx.lineTo( 0, y_zero );
|
||||
}
|
||||
ctx.fill();
|
||||
}
|
||||
}
|
||||
else {
|
||||
ctx.stroke();
|
||||
}
|
||||
|
||||
|
||||
ctx.restore();
|
||||
};
|
||||
|
||||
@@ -356,7 +356,7 @@ FeaturePositionMapper.prototype.get_feature_data = function(x, y) {
|
||||
if (!this.feature_positions[slot]) {
|
||||
return null;
|
||||
}
|
||||
|
||||
|
||||
// Find feature using X.
|
||||
x += this.translation;
|
||||
for (var i = 0; i < this.feature_positions[slot].length; i++) {
|
||||
@@ -404,8 +404,8 @@ _.extend(FeaturePainter.prototype, {
|
||||
* a FeaturePositionMapper object with information about where features were drawn.
|
||||
*/
|
||||
draw: function(ctx, width, height, w_scale, slots) {
|
||||
var data = this.data,
|
||||
view_start = this.view_start,
|
||||
var data = this.data,
|
||||
view_start = this.view_start,
|
||||
view_end = this.view_end;
|
||||
|
||||
ctx.save();
|
||||
@@ -423,11 +423,11 @@ _.extend(FeaturePainter.prototype, {
|
||||
feature_uid = feature[0],
|
||||
feature_start = feature[1],
|
||||
feature_end = feature[2],
|
||||
// Slot valid only if features are slotted and this feature is slotted;
|
||||
// Slot valid only if features are slotted and this feature is slotted;
|
||||
// feature may not be due to lack of space.
|
||||
slot = (slots && slots[feature_uid] !== undefined ? slots[feature_uid].slot : null);
|
||||
|
||||
// Draw feature if (a) mode is dense or feature is slotted (as it must be for all non-dense modes) and
|
||||
|
||||
// Draw feature if (a) mode is dense or feature is slotted (as it must be for all non-dense modes) and
|
||||
// (b) there's overlap between the feature and drawing region.
|
||||
if ( (this.mode === "Dense" || slot !== null) && ( feature_start < view_end && feature_end > view_start ) ) {
|
||||
x_draw_coords = this.draw_element(ctx, this.mode, feature, slot, view_start, view_end, w_scale, y_scale, width);
|
||||
@@ -449,7 +449,7 @@ _.extend(FeaturePainter.prototype, {
|
||||
});
|
||||
},
|
||||
|
||||
/**
|
||||
/**
|
||||
* Abstract function for drawing an individual feature.
|
||||
*/
|
||||
draw_element: function(ctx, mode, feature, slot, tile_low, tile_high, w_scale, y_scale, width ) {
|
||||
@@ -486,7 +486,7 @@ _.extend(LinkedFeaturePainter.prototype, FeaturePainter.prototype, {
|
||||
get_row_height: function() {
|
||||
var mode = this.mode, height;
|
||||
if (mode === "Dense") {
|
||||
height = DENSE_TRACK_HEIGHT;
|
||||
height = DENSE_TRACK_HEIGHT;
|
||||
}
|
||||
else if (mode === "no_detail") {
|
||||
height = NO_DETAIL_TRACK_HEIGHT;
|
||||
@@ -520,20 +520,20 @@ _.extend(LinkedFeaturePainter.prototype, FeaturePainter.prototype, {
|
||||
// For now, assume '.' === '+'
|
||||
block_color = (!feature_strand || feature_strand === "+" || feature_strand === "." ? this.prefs.block_color : this.prefs.reverse_strand_color);
|
||||
label_color = this.prefs.label_color;
|
||||
|
||||
|
||||
// Set global alpha.
|
||||
ctx.globalAlpha = this.alpha_scaler.gen_val(feature);
|
||||
|
||||
|
||||
// In dense mode, put all data in top slot.
|
||||
if (mode === "Dense") {
|
||||
slot = 1;
|
||||
}
|
||||
|
||||
|
||||
if (mode === "no_detail") {
|
||||
// No details for feature, so only one way to display.
|
||||
ctx.fillStyle = block_color;
|
||||
ctx.fillRect(f_start, y_start + 5, f_end - f_start, NO_DETAIL_FEATURE_HEIGHT);
|
||||
}
|
||||
}
|
||||
else { // Mode is either Squish or Pack:
|
||||
// Feature details.
|
||||
var feature_ts = feature[5],
|
||||
@@ -541,12 +541,12 @@ _.extend(LinkedFeaturePainter.prototype, FeaturePainter.prototype, {
|
||||
feature_blocks = feature[7],
|
||||
// Whether we are drawing full height or squished features
|
||||
full_height = true;
|
||||
|
||||
|
||||
if (feature_ts && feature_te) {
|
||||
thick_start = Math.floor( Math.max(0, (feature_ts - tile_low) * w_scale) );
|
||||
thick_end = Math.ceil( Math.min(width, Math.max(0, (feature_te - tile_low) * w_scale)) );
|
||||
}
|
||||
|
||||
|
||||
// Set vars that depend on mode.
|
||||
var thin_height, thick_height;
|
||||
if (mode === "Squish" ) {
|
||||
@@ -560,7 +560,7 @@ _.extend(LinkedFeaturePainter.prototype, FeaturePainter.prototype, {
|
||||
thin_height = 5;
|
||||
thick_height = PACK_FEATURE_HEIGHT;
|
||||
}
|
||||
|
||||
|
||||
// Draw feature/feature blocks + connectors.
|
||||
if (!feature_blocks) {
|
||||
// If there are no blocks, treat the feature as one big exon.
|
||||
@@ -575,15 +575,15 @@ _.extend(LinkedFeaturePainter.prototype, FeaturePainter.prototype, {
|
||||
}
|
||||
ctx.fillRect(f_start, y_start + 1, f_end - f_start, thick_height);
|
||||
}
|
||||
} else {
|
||||
} else {
|
||||
//
|
||||
// There are feature blocks and mode is either Squish or Pack.
|
||||
//
|
||||
// Approach: (a) draw whole feature as connector/intron and (b) draw blocks as
|
||||
// Approach: (a) draw whole feature as connector/intron and (b) draw blocks as
|
||||
// needed. This ensures that whole feature, regardless of whether it starts with
|
||||
// a block, is visible.
|
||||
//
|
||||
|
||||
|
||||
// Compute y axis start position and height
|
||||
var cur_y_start, cur_height;
|
||||
if (mode === "Squish" || mode === "Dense") {
|
||||
@@ -620,7 +620,7 @@ _.extend(LinkedFeaturePainter.prototype, FeaturePainter.prototype, {
|
||||
}
|
||||
ctx.fillRect(f_start, cur_y_start, f_end - f_start, cur_height);
|
||||
}
|
||||
|
||||
|
||||
// Draw blocks.
|
||||
var start_and_height;
|
||||
for (var k = 0, k_len = feature_blocks.length; k < k_len; k++) {
|
||||
@@ -630,7 +630,7 @@ _.extend(LinkedFeaturePainter.prototype, FeaturePainter.prototype, {
|
||||
block_end = Math.ceil( Math.min(width, Math.max((block[1] - tile_low - 0.5) * w_scale)) ),
|
||||
last_block_start, last_block_end;
|
||||
|
||||
// Skip drawing if block not on tile.
|
||||
// Skip drawing if block not on tile.
|
||||
if (block_start > block_end) { continue; }
|
||||
|
||||
// Draw thin block.
|
||||
@@ -666,15 +666,15 @@ _.extend(LinkedFeaturePainter.prototype, FeaturePainter.prototype, {
|
||||
last_block_start = block_start;
|
||||
last_block_end = block_end;
|
||||
}
|
||||
|
||||
|
||||
// FIXME: Height scaling only works in Pack mode right now.
|
||||
if (mode === "Pack") {
|
||||
// Reset alpha so height scaling is not impacted by alpha scaling.
|
||||
ctx.globalAlpha = 1;
|
||||
|
||||
|
||||
// Height scaling: draw white lines to reduce height according to height scale factor.
|
||||
ctx.fillStyle = "white"; // TODO: set this to background color.
|
||||
var
|
||||
var
|
||||
hscale_factor = this.height_scaler.gen_val(feature),
|
||||
// Ceil ensures that min height is >= 1.
|
||||
new_height = Math.ceil(thick_height * hscale_factor),
|
||||
@@ -682,13 +682,13 @@ _.extend(LinkedFeaturePainter.prototype, FeaturePainter.prototype, {
|
||||
if (hscale_factor !== 1) {
|
||||
ctx.fillRect(f_start, cur_y_start + 1, f_end - f_start, ws_height);
|
||||
ctx.fillRect(f_start, cur_y_start + thick_height - ws_height + 1, f_end - f_start, ws_height);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// Reset alpha so that label is not transparent.
|
||||
ctx.globalAlpha = 1;
|
||||
|
||||
|
||||
// Draw label for Pack mode.
|
||||
if (feature_name && mode === "Pack" && feature_start > tile_low) {
|
||||
ctx.fillStyle = label_color;
|
||||
@@ -705,10 +705,10 @@ _.extend(LinkedFeaturePainter.prototype, FeaturePainter.prototype, {
|
||||
//ctx.fillStyle = block_color;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// Reset global alpha.
|
||||
ctx.globalAlpha = 1;
|
||||
|
||||
|
||||
return [draw_start, draw_end];
|
||||
}
|
||||
});
|
||||
@@ -726,7 +726,7 @@ _.extend(ReadPainter.prototype, FeaturePainter.prototype, {
|
||||
get_row_height: function() {
|
||||
var height, mode = this.mode;
|
||||
if (mode === "Dense") {
|
||||
height = DENSE_TRACK_HEIGHT;
|
||||
height = DENSE_TRACK_HEIGHT;
|
||||
}
|
||||
else if (mode === "Squish") {
|
||||
height = SQUISH_TRACK_HEIGHT;
|
||||
@@ -821,7 +821,7 @@ _.extend(ReadPainter.prototype, FeaturePainter.prototype, {
|
||||
drawing_blocks = [],
|
||||
s_start,
|
||||
s_end;
|
||||
|
||||
|
||||
// Keep list of items that need to be drawn on top of initial drawing layer.
|
||||
var draw_last = [];
|
||||
|
||||
@@ -837,7 +837,7 @@ _.extend(ReadPainter.prototype, FeaturePainter.prototype, {
|
||||
if (is_overlap([feature_start + block[0], feature_start + block[1]], tile_region)) {
|
||||
s_start = get_draw_coord(feature_start + block[0]);
|
||||
s_end = get_draw_coord(feature_start + block[1]);
|
||||
|
||||
|
||||
// Make sure that block is drawn even if it too small to be rendered officially; in this case,
|
||||
// read is drawn at 1px.
|
||||
// TODO: need to ensure that s_start, s_end are calculated the same for both slotting
|
||||
@@ -861,7 +861,7 @@ _.extend(ReadPainter.prototype, FeaturePainter.prototype, {
|
||||
var seq_start = feature_start + base_offset;
|
||||
s_start = get_draw_coord(seq_start);
|
||||
s_end = get_draw_coord(seq_start + cig_len);
|
||||
|
||||
|
||||
// Skip feature if it's not in tile.
|
||||
if (!is_overlap([seq_start, seq_start + cig_len], tile_region)) {
|
||||
// Update offsets.
|
||||
@@ -869,7 +869,7 @@ _.extend(ReadPainter.prototype, FeaturePainter.prototype, {
|
||||
seq_offset = update_seq_offset(seq_offset, cig_op, cig_len);
|
||||
continue;
|
||||
}
|
||||
|
||||
|
||||
// Make sure that read is drawn even if it too small to be rendered officially; in this case,
|
||||
// read is drawn at 1px.
|
||||
// TODO: need to ensure that s_start, s_end are calculated the same for both slotting
|
||||
@@ -877,8 +877,8 @@ _.extend(ReadPainter.prototype, FeaturePainter.prototype, {
|
||||
if (s_start === s_end) {
|
||||
s_end += 1;
|
||||
}
|
||||
|
||||
// Draw read feature.
|
||||
|
||||
// Draw read feature.
|
||||
switch (cig_op) {
|
||||
case "H": // Hard clipping.
|
||||
case "S": // Soft clipping.
|
||||
@@ -932,7 +932,7 @@ _.extend(ReadPainter.prototype, FeaturePainter.prototype, {
|
||||
// Move forward in sequence only if sequence used to get mismatches.
|
||||
if (cig_op === 'X') { seq_offset += cig_len; }
|
||||
base_offset += cig_len;
|
||||
|
||||
|
||||
break;
|
||||
case "N": // Skipped bases.
|
||||
ctx.fillStyle = CONNECTOR_COLOR;
|
||||
@@ -948,7 +948,7 @@ _.extend(ReadPainter.prototype, FeaturePainter.prototype, {
|
||||
// Check to see if sequence should be drawn at all by looking at the overlap between
|
||||
// the sequence region and the tile region.
|
||||
var insert_x_coord = s_start - gap;
|
||||
|
||||
|
||||
if (is_overlap([seq_start, seq_start + cig_len], tile_region)) {
|
||||
var seq = read_seq.slice(seq_offset, seq_offset + cig_len);
|
||||
// Insertion point is between the sequence start and the previous base: (-gap) moves
|
||||
@@ -992,7 +992,7 @@ _.extend(ReadPainter.prototype, FeaturePainter.prototype, {
|
||||
// Draw block.
|
||||
ctx.fillStyle = "yellow";
|
||||
// TODO: This is a pretty hack-ish way to fill rectangle based on mode.
|
||||
ctx.fillRect(x_center, y_start + (this.mode !== "Dense" ? 2 : 5),
|
||||
ctx.fillRect(x_center, y_start + (this.mode !== "Dense" ? 2 : 5),
|
||||
s_end - s_start, (mode !== "Dense" ? SQUISH_FEATURE_HEIGHT : DENSE_FEATURE_HEIGHT));
|
||||
}
|
||||
}
|
||||
@@ -1011,7 +1011,7 @@ _.extend(ReadPainter.prototype, FeaturePainter.prototype, {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
//
|
||||
// Draw last items.
|
||||
//
|
||||
@@ -1032,7 +1032,7 @@ _.extend(ReadPainter.prototype, FeaturePainter.prototype, {
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
|
||||
/**
|
||||
* Draw a complete read pair
|
||||
*/
|
||||
@@ -1048,7 +1048,7 @@ _.extend(ReadPainter.prototype, FeaturePainter.prototype, {
|
||||
y_start = (mode === "Dense" ? 0 : (0 + slot)) * y_scale,
|
||||
draw_height = (mode === 'Pack' ? PACK_FEATURE_HEIGHT : SQUISH_FEATURE_HEIGHT),
|
||||
label_color = this.prefs.label_color;
|
||||
|
||||
|
||||
// Draw read.
|
||||
if (feature[5] instanceof Array) {
|
||||
// Read is paired.
|
||||
@@ -1097,7 +1097,7 @@ _.extend(ReadPainter.prototype, FeaturePainter.prototype, {
|
||||
ctx.fillText(feature_name, f_start - LABEL_SPACING, y_start + 9, this.max_label_length);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// FIXME: provide actual coordinates for drawn read.
|
||||
return [0,0];
|
||||
}
|
||||
@@ -1122,7 +1122,7 @@ _.extend(ArcLinkedFeaturePainter.prototype, FeaturePainter.prototype, LinkedFeat
|
||||
return longest_feature_length;
|
||||
},
|
||||
|
||||
get_top_padding: function( width ) {
|
||||
get_top_padding: function( width ) {
|
||||
var view_range = this.view_end - this.view_start,
|
||||
w_scale = width / view_range;
|
||||
return Math.min( 128, Math.ceil( ( this.longest_feature_length / 2 ) * w_scale ) );
|
||||
@@ -1131,7 +1131,7 @@ _.extend(ArcLinkedFeaturePainter.prototype, FeaturePainter.prototype, LinkedFeat
|
||||
draw_connector: function( ctx, block1_start, block1_end, block2_start, block2_end, y_start ) {
|
||||
// Arc drawing -- from closest endpoints
|
||||
var x_center = ( block1_end + block2_start ) / 2,
|
||||
radius = block2_start - x_center;
|
||||
radius = block2_start - x_center;
|
||||
// For full half circles
|
||||
var angle1 = Math.PI, angle2 = 0;
|
||||
if ( radius > 0 ) {
|
||||
@@ -1290,32 +1290,33 @@ SplitRamp.prototype.map_value = function( value ) {
|
||||
var DiagonalHeatmapPainter = function(data, view_start, view_end, prefs, mode) {
|
||||
Painter.call( this, data, view_start, view_end, prefs, mode );
|
||||
var i, len;
|
||||
|
||||
if ( this.prefs.min_value === undefined ) {
|
||||
var min_value = Infinity;
|
||||
for (i = 0, len = this.data.length; i < len; i++) {
|
||||
min_value = Math.min( min_value, this.data[i][5] );
|
||||
min_value = Math.min( min_value, this.data[i][6] );
|
||||
}
|
||||
this.prefs.min_value = min_value;
|
||||
}
|
||||
if ( this.prefs.max_value === undefined ) {
|
||||
var max_value = -Infinity;
|
||||
for (i = 0, len = this.data.length; i < len; i++) {
|
||||
max_value = Math.max( max_value, this.data[i][5] );
|
||||
max_value = Math.max( max_value, this.data[i][6] );
|
||||
}
|
||||
this.prefs.max_value = max_value;
|
||||
}
|
||||
};
|
||||
|
||||
DiagonalHeatmapPainter.prototype.default_prefs = {
|
||||
min_value: undefined,
|
||||
max_value: undefined,
|
||||
mode: "Heatmap",
|
||||
DiagonalHeatmapPainter.prototype.default_prefs = {
|
||||
min_value: undefined,
|
||||
max_value: undefined,
|
||||
mode: "Heatmap",
|
||||
pos_color: "#FF8C00",
|
||||
neg_color: "#4169E1"
|
||||
neg_color: "#4169E1"
|
||||
};
|
||||
|
||||
DiagonalHeatmapPainter.prototype.draw = function(ctx, width, height, w_scale) {
|
||||
var
|
||||
var
|
||||
min_value = this.prefs.min_value,
|
||||
max_value = this.prefs.max_value,
|
||||
value_range = max_value - min_value,
|
||||
@@ -1336,15 +1337,11 @@ DiagonalHeatmapPainter.prototype.draw = function(ctx, width, height, w_scale) {
|
||||
// Draw into triangle, then rotate and scale
|
||||
ctx.rotate(-45 * Math.PI / 180);
|
||||
ctx.scale( invsqrt2, invsqrt2 );
|
||||
|
||||
|
||||
// Paint track.
|
||||
for (var i = 0, len = data.length; i < len; i++) {
|
||||
|
||||
d = data[i];
|
||||
|
||||
// Ensure the cell is visible
|
||||
// if ( )
|
||||
|
||||
s1 = scale( d[1] );
|
||||
e1 = scale( d[2] );
|
||||
s2 = scale( d[4] );
|
||||
@@ -1352,10 +1349,9 @@ DiagonalHeatmapPainter.prototype.draw = function(ctx, width, height, w_scale) {
|
||||
value = d[6];
|
||||
|
||||
ctx.fillStyle = ( ramp.map_value( value ) );
|
||||
|
||||
ctx.fillRect( s1, s2, ( e1 - s1 ), ( e2 - s2 ) );
|
||||
}
|
||||
|
||||
|
||||
ctx.restore();
|
||||
};
|
||||
|
||||
@@ -1372,7 +1368,7 @@ var ReadPainterUtils = function(ctx, row_height, px_per_base, mode) {
|
||||
|
||||
_.extend(ReadPainterUtils.prototype, {
|
||||
/**
|
||||
* Draw deletion of base(s).
|
||||
* Draw deletion of base(s).
|
||||
* @param draw_detail if true, drawing in detail and deletion is drawn more subtly
|
||||
*/
|
||||
draw_deletion: function(x, y, len) {
|
||||
@@ -1399,7 +1395,7 @@ _.extend(VariantPainter.prototype, Painter.prototype, {
|
||||
get_row_height: function() {
|
||||
var mode = this.mode, height;
|
||||
if (mode === "Dense") {
|
||||
height = DENSE_TRACK_HEIGHT;
|
||||
height = DENSE_TRACK_HEIGHT;
|
||||
}
|
||||
else if (mode === "Squish") {
|
||||
height = SQUISH_TRACK_HEIGHT;
|
||||
@@ -1431,8 +1427,8 @@ _.extend(VariantPainter.prototype, Painter.prototype, {
|
||||
draw: function(ctx, width, height, w_scale) {
|
||||
ctx.save();
|
||||
|
||||
var
|
||||
/**
|
||||
var
|
||||
/**
|
||||
* Returns dictionary of information about an indel; returns empty if there no indel. Assumes indel is left-aligned.
|
||||
* Dict attributes:
|
||||
* -type: 'insertion' or 'deletion'
|
||||
@@ -1485,7 +1481,7 @@ _.extend(VariantPainter.prototype, Painter.prototype, {
|
||||
row_height = (this.mode === 'Squish' ? SQUISH_TRACK_HEIGHT : PACK_TRACK_HEIGHT),
|
||||
// If zoomed out, fill the whole row with feature to make it easier to read;
|
||||
// when zoomed in, use feature height so that there are gaps in sample rows.
|
||||
feature_height = (w_scale < 0.1 ?
|
||||
feature_height = (w_scale < 0.1 ?
|
||||
row_height :
|
||||
(this.mode === 'Squish' ? SQUISH_FEATURE_HEIGHT : PACK_FEATURE_HEIGHT)
|
||||
),
|
||||
@@ -1495,7 +1491,7 @@ _.extend(VariantPainter.prototype, Painter.prototype, {
|
||||
|
||||
// If there's a single sample, update drawing variables.
|
||||
if (num_samples === 1) {
|
||||
row_height = feature_height =
|
||||
row_height = feature_height =
|
||||
(w_scale < ctx.canvas.manager.char_width_px ? this.prefs.summary_height : row_height);
|
||||
paint_utils.row_height = row_height;
|
||||
// No summary when there's a single sample.
|
||||
@@ -1519,7 +1515,7 @@ _.extend(VariantPainter.prototype, Painter.prototype, {
|
||||
alt = [ locus_data[4].split(',') ];
|
||||
sample_gts = locus_data[7].split(',');
|
||||
allele_counts = locus_data.slice(8);
|
||||
|
||||
|
||||
// Process alterate values to derive information about each alt.
|
||||
alt = _.map(_.flatten(alt), function(a) {
|
||||
var alt_info = {
|
||||
@@ -1528,7 +1524,7 @@ _.extend(VariantPainter.prototype, Painter.prototype, {
|
||||
start: 0
|
||||
},
|
||||
indel_info = get_indel_info(ref, a);
|
||||
|
||||
|
||||
return _.extend(alt_info, indel_info);
|
||||
});
|
||||
|
||||
@@ -1541,7 +1537,7 @@ _.extend(VariantPainter.prototype, Painter.prototype, {
|
||||
if (draw_summary) {
|
||||
ctx.fillStyle = '#999999';
|
||||
ctx.globalAlpha = 1;
|
||||
for (j = 0; j < alt.length; j++) {
|
||||
for (j = 0; j < alt.length; j++) {
|
||||
// Draw background for summary.
|
||||
draw_x_start = this.get_start_draw_pos(pos + alt[j].start, w_scale);
|
||||
ctx.fillRect(draw_x_start, 0, base_px, this.prefs.summary_height);
|
||||
@@ -1564,7 +1560,7 @@ _.extend(VariantPainter.prototype, Painter.prototype, {
|
||||
draw_y_start = (draw_summary ? this.prefs.summary_height + this.divider_height : 0);
|
||||
for (j = 0; j < sample_gts.length; j++, draw_y_start += row_height) {
|
||||
genotype = (sample_gts[j] ? sample_gts[j].split(/\/|\|/) : ['0', '0']);
|
||||
|
||||
|
||||
// Get variant to draw and set drawing properties.
|
||||
variant = null;
|
||||
if (genotype[0] === genotype[1]) {
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -4,6 +4,9 @@
|
||||
// Galaxy specific variables
|
||||
@import "galaxy_variables.less";
|
||||
|
||||
// Galaxy Report Webapp
|
||||
@import "reports";
|
||||
|
||||
@white: #fff;
|
||||
@black: #000;
|
||||
|
||||
@@ -134,14 +137,6 @@ body {
|
||||
#left-border {
|
||||
left: 250px;
|
||||
}
|
||||
.reports-panel-container {
|
||||
position: absolute;
|
||||
top: @panel_header_height;
|
||||
bottom: 10px;
|
||||
overflow: scroll;
|
||||
width: 100%;
|
||||
padding: 10px;
|
||||
}
|
||||
#center {
|
||||
left:250 + @border-default-color_padding;
|
||||
right: 250 + @border-default-color_padding;
|
||||
@@ -295,10 +290,15 @@ div.unified-panel-body {
|
||||
top: @panel_header_height;
|
||||
bottom: 0;
|
||||
width: 100%;
|
||||
padding: 0px;
|
||||
}
|
||||
div.unified-panel-body-background {
|
||||
background: none repeat scroll 0 0 #DFE5F9;
|
||||
}
|
||||
|
||||
#left > div.unified-panel-body, #right > div.unified-panel-body {
|
||||
bottom: @panel_footer_height;
|
||||
overflow: auto;
|
||||
}
|
||||
|
||||
.panel-header-button {
|
||||
@@ -762,7 +762,7 @@ input[type="submit"], button {
|
||||
|
||||
// Messages
|
||||
|
||||
.errormessagelarge, .warningmessagelarge, .donemessagelarge, .infomessagelarge {
|
||||
.errormessagelarge, .warningmessagelarge, .donemessagelarge, .infomessagelarge, .toolHelpBody .error, .toolHelpBody .warning, .toolHelpBody .note {
|
||||
.alert();
|
||||
min-height: 36px;
|
||||
padding-left: 52px;
|
||||
@@ -800,7 +800,7 @@ input[type="submit"], button {
|
||||
background-image: none;
|
||||
}
|
||||
|
||||
.errormessage, .warningmessage, .donemessage, .infomessage, .errormessagesmall, .warningmessagesmall, .donemessagesmall, .infomessagesmall {
|
||||
.errormessage, .warningmessage, .donemessage, .infomessage, .errormessagesmall, .warningmessagesmall, .donemessagesmall, .infomessagesmall, toolHelpBody .error, .toolHelpBody .warning, .toolHelpBody .note {
|
||||
.alert();
|
||||
padding: 5px;
|
||||
padding-left: 25px;
|
||||
@@ -812,11 +812,11 @@ input[type="submit"], button {
|
||||
background-position: 5px 5px;
|
||||
}
|
||||
|
||||
.errormessage, .errormessagesmall {
|
||||
.errormessage, .errormessagesmall, .toolHelpBody .error {
|
||||
.alert-danger();
|
||||
}
|
||||
|
||||
.warningmessage, .warningmessagesmall {
|
||||
.warningmessage, .warningmessagesmall, .toolHelpBody .warning {
|
||||
.alert-warning();
|
||||
background-image: url(warn_small.png);
|
||||
}
|
||||
@@ -826,7 +826,7 @@ input[type="submit"], button {
|
||||
background-image: url(ok_small.png);
|
||||
}
|
||||
|
||||
.infomessage, .infomessagesmall {
|
||||
.infomessage, .infomessagesmall, .toolHelpBody .note {
|
||||
.alert-info();
|
||||
background-image: url(info_small.png);
|
||||
}
|
||||
@@ -1447,9 +1447,6 @@ div.permissionContainer {
|
||||
}
|
||||
|
||||
// ============================================================================ History
|
||||
@import "sprite-history-states.less";
|
||||
@import "sprite-history-buttons.less";
|
||||
|
||||
@import "ui/icon-btn.less";
|
||||
@import "list-item.less";
|
||||
@import "history.less";
|
||||
@@ -1628,7 +1625,7 @@ div.toolTitleNoSection
|
||||
.sprite(@tags);
|
||||
}
|
||||
.icon-button.tag--plus {
|
||||
.sprite(@tag--plus);
|
||||
.sprite(@tag-plus);
|
||||
}
|
||||
.icon-button.toggle-expand {
|
||||
.sprite(@toggle-expand);
|
||||
@@ -1676,10 +1673,10 @@ div.toolTitleNoSection
|
||||
.sprite(@chart);
|
||||
}
|
||||
.icon-button.disk--arrow {
|
||||
.sprite(@disk--arrow-bw);
|
||||
.sprite(@disk-arrow-bw);
|
||||
}
|
||||
.icon-button.disk--arrow:hover {
|
||||
.sprite(@disk--arrow);
|
||||
.sprite(@disk-arrow);
|
||||
}
|
||||
.icon-button.cross-circle {
|
||||
.sprite(@cross-circle-bw);
|
||||
|
||||
@@ -15,7 +15,7 @@
|
||||
// Default border bolor, this is between gray-light and gray-lighter -- classic Galaxy border color
|
||||
@border-default-color: lighten(black, 75%);
|
||||
|
||||
// When making borders for components with other colors, how much to darken
|
||||
// When making borders for components with other colors, how much to darken
|
||||
@border-darken-percent: 40%;
|
||||
|
||||
// For making state colors, how much to darken and lighten
|
||||
@@ -29,16 +29,16 @@
|
||||
|
||||
// Bootstrap default colors for states, will be adjusted based on @base-color
|
||||
|
||||
@bs-primary: #428bca;
|
||||
@bs-info: #5bc0de;
|
||||
@bs-primary: #428bca;
|
||||
@bs-success: #5cb85c;
|
||||
@bs-warning: #f0ad4e;
|
||||
@bs-danger: #d9534f;
|
||||
@bs-info: #5bc0de;
|
||||
|
||||
// Derived variables used below
|
||||
|
||||
@sat: saturation(@base-color);
|
||||
@luma: luma(@base-color);
|
||||
@luma: luminance(@base-color);
|
||||
@light: lightness(@base-color);
|
||||
@tone: desaturate(@base-color,100%);
|
||||
|
||||
@@ -64,13 +64,32 @@
|
||||
// Brand colors
|
||||
// -------------------------
|
||||
|
||||
@brand-primary: @base-color;
|
||||
// @brand-primary: hsl(hue(@bs-primary),(saturation(@bs-info) + @sat + 20)/2,@luma - 10);
|
||||
@brand-primary: hsl(
|
||||
hue(@bs-primary),
|
||||
(saturation(@bs-info) + @sat + 20)/2,
|
||||
@luma - 10
|
||||
);
|
||||
|
||||
@brand-info: hsl(hue(@bs-info),(saturation(@bs-info) + @sat + 20)/2,@luma);
|
||||
@brand-success: hsl(hue(@bs-success),(saturation(@bs-success) + @sat + 20)/2,(luma(@bs-success) + @luma - 20)/2);
|
||||
@brand-warning: hsl(hue(@bs-warning),(saturation(@bs-warning) + @sat + 20)/2,(luma(@bs-warning) + @luma - 20)/2);
|
||||
@brand-danger: hsl(hue(@bs-danger),(saturation(@bs-danger) + @sat + 20)/2,(luma(@bs-danger) + @luma + 10)/2);
|
||||
@brand-info: hsl(
|
||||
hue(@bs-info),
|
||||
(saturation(@bs-info) + @sat + 20)/2,
|
||||
@luma
|
||||
);
|
||||
@brand-success: hsl(
|
||||
hue(@bs-success),
|
||||
(saturation(@bs-success) + @sat + 20)/2,
|
||||
(luminance(@bs-success) + @luma - 20)/2
|
||||
);
|
||||
@brand-warning: hsl(
|
||||
hue(@bs-warning),
|
||||
(saturation(@bs-warning) + @sat + 20)/2,
|
||||
(luminance(@bs-warning) + @luma - 20)/2
|
||||
);
|
||||
@brand-danger: hsl(
|
||||
hue(@bs-danger),
|
||||
(saturation(@bs-danger) + @sat + 20)/2,
|
||||
(luminance(@bs-danger) + @luma + 10)/2
|
||||
);
|
||||
|
||||
// Scaffolding
|
||||
// -------------------------
|
||||
@@ -430,7 +449,7 @@
|
||||
@state-danger-border: darken(@state-danger-bg, @border-darken-percent);
|
||||
|
||||
@state-success-text: darken(@brand-success, @state-text-darken-percent);
|
||||
@state-success-bg: saturate(lighten(@brand-success, @state-bg-lighten-percent),@state-bg-saturate-percent);
|
||||
@state-success-bg: saturate(lighten(@brand-success, @state-bg-lighten-percent, relative),@state-bg-saturate-percent);
|
||||
@state-success-border: darken(@state-success-bg, @border-darken-percent);
|
||||
|
||||
@state-info-text: darken(@brand-info, @state-text-darken-percent);
|
||||
|
||||
@@ -1,3 +1,6 @@
|
||||
@import "sprite-history-states.less";
|
||||
@import "sprite-history-buttons.less";
|
||||
|
||||
// ---------------------------------------------------------------------------- panel header buttons
|
||||
.history-panel-header {
|
||||
.panel-header-button {
|
||||
@@ -637,12 +640,16 @@
|
||||
// popover containing sorting and filters
|
||||
.more-options {
|
||||
.btn {
|
||||
width: 100%;
|
||||
height: 26px;
|
||||
}
|
||||
input[type=checkbox] {
|
||||
margin-top: 1px;
|
||||
}
|
||||
}
|
||||
.popover {
|
||||
min-width: 290px;
|
||||
}
|
||||
}
|
||||
.footer {
|
||||
min-height: @mph-footer_height;
|
||||
@@ -800,183 +807,25 @@
|
||||
overflow: auto;
|
||||
}
|
||||
}
|
||||
|
||||
// loading indicator on far right of multipanel view
|
||||
.histories-loading-indicator {
|
||||
// if we don't bring the width down here, the space is way too large for vertical text
|
||||
width: 8px;
|
||||
transform: rotate(90deg);
|
||||
transform-origin: left top 0;
|
||||
margin-left: 16px;
|
||||
white-space: nowrap;
|
||||
color: grey;
|
||||
span {
|
||||
// space btwn icon and text
|
||||
margin-right: 8px;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// ---------------------------------------------------------------------------- older styles
|
||||
//TODO: these may still apply in mobile display, workflow stuff
|
||||
body.historyPage {
|
||||
background: @side-panel-bg;
|
||||
color: @base-text-color;
|
||||
margin: 5px;
|
||||
border: 0;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
// Control links at top of history
|
||||
div.historyLinks {
|
||||
margin: 5px 5px;
|
||||
}
|
||||
|
||||
// Default history item appearance
|
||||
div.historyItem {
|
||||
margin: 0px -5px;
|
||||
padding: 8px 10px;
|
||||
border-top: solid @border-default-color 1px;
|
||||
// border-left: solid @state-queued-border 5px;
|
||||
border-right: none;
|
||||
word-wrap: break-word;
|
||||
background: @state-default-bg;
|
||||
.state-icon {
|
||||
display: inline-block;
|
||||
vertical-align: middle;
|
||||
width: 16px;
|
||||
height: 16px;
|
||||
background-position: 0 1px;
|
||||
background-repeat: no-repeat;
|
||||
}
|
||||
.historyItemTitle {
|
||||
font-weight: bold;
|
||||
line-height: 16px;
|
||||
}
|
||||
}
|
||||
|
||||
.historyItemContainer:last-child div.historyItem {
|
||||
border-bottom: solid @border-default-color 1px;
|
||||
}
|
||||
|
||||
// Nested history items
|
||||
div.historyItem div.historyItem {
|
||||
margin-right: -11px;
|
||||
}
|
||||
|
||||
// Change background/border color depending on state
|
||||
div.historyItem-ok,
|
||||
div.historyItem-failed_metadata {
|
||||
background: @state-success-bg;
|
||||
.state-icon {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
|
||||
div.historyItem-error {
|
||||
background: @state-danger-bg;
|
||||
.state-icon {
|
||||
.sprite(@data_error);
|
||||
}
|
||||
}
|
||||
|
||||
div.historyItem-empty {
|
||||
background: @state-danger-bg;
|
||||
.state-icon {
|
||||
.sprite(@data_empty);
|
||||
}
|
||||
}
|
||||
|
||||
div.historyItem-running {
|
||||
background: @state-running-bg;
|
||||
.state-icon {
|
||||
background-image: url(data_running.gif);
|
||||
}
|
||||
}
|
||||
|
||||
div.historyItem-setting_metadata {
|
||||
background: @state-running-bg;
|
||||
.state-icon {
|
||||
background-image: url(data_running.gif);
|
||||
}
|
||||
}
|
||||
|
||||
div.historyItem-upload {
|
||||
background: @state-info-bg;
|
||||
.state-icon {
|
||||
background-image: url(data_upload.gif);
|
||||
}
|
||||
}
|
||||
|
||||
div.historyItem-queued {
|
||||
background: @state-default-bg;
|
||||
.state-icon {
|
||||
.sprite(@data_queued);
|
||||
}
|
||||
}
|
||||
|
||||
div.historyItem-noPermission {
|
||||
filter: alpha(opacity=60);
|
||||
-moz-opacity: .60;
|
||||
opacity: .60;
|
||||
.state-icon {
|
||||
line-height: 16px;
|
||||
.fa-icon;
|
||||
&:before {
|
||||
content: "\f05e";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
div.historyItem-paused {
|
||||
background: @state-paused-bg;
|
||||
.state-icon {
|
||||
line-height: 16px;
|
||||
.fa-icon;
|
||||
&:before {
|
||||
content: "\f04c";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
div.historyItem-new {
|
||||
.state-icon {
|
||||
line-height: 16px;
|
||||
.fa-icon;
|
||||
&:before {
|
||||
content: "\f071";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Special case for showing the spinner but not changing the background
|
||||
div.historyItemTitleBar.spinner .state-icon {
|
||||
background: url(data_running.gif) 0 1px no-repeat !important;
|
||||
}
|
||||
|
||||
// Buttons
|
||||
div.historyItemButtons {
|
||||
float: right;
|
||||
|
||||
.icon-button.display {
|
||||
.sprite(@eye_icon);
|
||||
}
|
||||
.icon-button.display:hover {
|
||||
.sprite(@eye_icon_dark);
|
||||
}
|
||||
.icon-button.display_disabled {
|
||||
.sprite(@eye_icon_grey);
|
||||
}
|
||||
.icon-button.delete {
|
||||
.sprite(@delete_icon);
|
||||
}
|
||||
.icon-button.delete:hover {
|
||||
.sprite(@delete_icon_dark);
|
||||
}
|
||||
.icon-button.delete_disabled {
|
||||
.sprite(@delete_icon_grey);
|
||||
}
|
||||
.icon-button.edit {
|
||||
.sprite(@pencil_icon);
|
||||
}
|
||||
.icon-button.edit:hover {
|
||||
.sprite(@pencil_icon_dark);
|
||||
}
|
||||
.icon-button.edit_disabled {
|
||||
.sprite(@pencil_icon_grey);
|
||||
}
|
||||
}
|
||||
|
||||
div.historyItemBody div {
|
||||
padding-top: 2px;
|
||||
}
|
||||
|
||||
pre.peek {
|
||||
background: white;
|
||||
color: black;
|
||||
|
||||
@@ -119,10 +119,13 @@ th.button_heading{
|
||||
}
|
||||
|
||||
#library_toolbar {
|
||||
margin-bottom: 1em;
|
||||
margin-bottom: 0.5em;
|
||||
span {
|
||||
margin-right: 0.2em;
|
||||
}
|
||||
.toolbar-item{
|
||||
margin-left: 1em;
|
||||
}
|
||||
}
|
||||
|
||||
img.expanderIcon {
|
||||
|
||||
@@ -55,6 +55,8 @@
|
||||
color: @btn-default-color;
|
||||
cursor: pointer;
|
||||
vertical-align: middle;
|
||||
// prevent text highlight when holding shift to select a range
|
||||
.user-select(none);
|
||||
}
|
||||
.selector:hover {
|
||||
color: maroon;
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user