mirror of
https://github.com/galaxyproject/galaxy.git
synced 2026-09-24 16:30:27 +08:00
Merge remote-tracking branch 'upstream/dev' into fri18n
This commit is contained in:
Executable
+34
@@ -0,0 +1,34 @@
|
||||
#!/bin/sh
|
||||
|
||||
if command -v ack-grep >/dev/null; then
|
||||
ACK=ack-grep
|
||||
else
|
||||
ACK=ack
|
||||
fi
|
||||
|
||||
export ACK_OPTIONS=" --type python \
|
||||
--ignore-dir=.git/ \
|
||||
--ignore-dir=.tox/ \
|
||||
--ignore-dir=.venv/ \
|
||||
--ignore-dir=client/node_modules/ \
|
||||
--ignore-dir=database/ \
|
||||
--ignore-dir=doc/build/ \
|
||||
--ignore-dir=eggs/ \
|
||||
--ignore-dir=static/maps/ \
|
||||
--ignore-dir=static/scripts/"
|
||||
|
||||
PYTHON2_ONLY_MODULES="__builtin__ _winreg BaseHTTPServer CGIHTTPServer \
|
||||
ConfigParser Cookie cookielib copy_reg cPickle cStringIO Dialog dummy_thread \
|
||||
FileDialog gdbm htmlentitydefs HTMLParser httplib Queue robotparser \
|
||||
ScrolledText SimpleDialog SimpleHTTPServer SimpleXMLRPCServer SocketServer \
|
||||
StringIO thread Tix tkColorChooser tkCommonDialog Tkconstants Tkdnd tkFont \
|
||||
Tkinter tkFileDialog tkMessageBox tkSimpleDialog ttk urllib urllib2 urlparse \
|
||||
xmlrpclib"
|
||||
|
||||
ret=0
|
||||
for mod in $PYTHON2_ONLY_MODULES; do
|
||||
$ACK "^import $mod(\n|\.)|^from $mod import "
|
||||
if [ $? -eq 0 ]; then ret=1; fi
|
||||
done
|
||||
|
||||
exit $ret
|
||||
@@ -10,7 +10,7 @@ bash run.sh --daemon && \
|
||||
sleep 1
|
||||
i=$((i + 1))
|
||||
done
|
||||
kill -9 "$(cat paster.pid)"
|
||||
kill -9 "$(cat galaxy.pid)"
|
||||
echo "exit code:$EXIT_CODE, showing startup log:"
|
||||
cat paster.log
|
||||
cat galaxy.log
|
||||
exit $EXIT_CODE
|
||||
|
||||
@@ -1,94 +0,0 @@
|
||||
client/galaxy/style/source_material/circle.py
|
||||
contrib/
|
||||
cron/
|
||||
doc/parse_gx_xsd.py
|
||||
doc/patch.py
|
||||
lib/galaxy/actions/
|
||||
lib/galaxy/auth/
|
||||
lib/galaxy/config.py
|
||||
lib/galaxy/containers/
|
||||
lib/galaxy/dataset_collections/
|
||||
lib/galaxy/datatypes/
|
||||
lib/galaxy/dependencies/
|
||||
lib/galaxy/eggs/
|
||||
lib/galaxy/exceptions/
|
||||
lib/galaxy/external_services/
|
||||
lib/galaxy/forms/
|
||||
lib/galaxy/jobs/
|
||||
lib/galaxy/managers/
|
||||
lib/galaxy/model/
|
||||
lib/galaxy/objectstore/
|
||||
lib/galaxy/openid/
|
||||
lib/galaxy/quota/
|
||||
lib/galaxy/sample_tracking/
|
||||
lib/galaxy/security/
|
||||
lib/galaxy/tags/
|
||||
lib/galaxy/tools/
|
||||
lib/galaxy/tours/
|
||||
lib/galaxy/util/
|
||||
lib/galaxy/visualization/
|
||||
lib/galaxy/web/base/
|
||||
lib/galaxy/web/buildapp.py
|
||||
lib/galaxy/web/framework/base.py
|
||||
lib/galaxy/web/framework/decorators.py
|
||||
lib/galaxy/web/framework/helpers/grids.py
|
||||
lib/galaxy/web/framework/__init__.py
|
||||
lib/galaxy/web/framework/middleware/error.py
|
||||
lib/galaxy/web/framework/middleware/static.py
|
||||
lib/galaxy/web/framework/middleware/statsd.py
|
||||
lib/galaxy/web/framework/webapp.py
|
||||
lib/galaxy/web/__init__.py
|
||||
lib/galaxy/webapps/galaxy/api/histories.py
|
||||
lib/galaxy/webapps/galaxy/api/library_datasets.py
|
||||
lib/galaxy/webapps/galaxy/api/tours.py
|
||||
lib/galaxy/webapps/galaxy/api/users.py
|
||||
lib/galaxy/webapps/galaxy/api/workflows.py
|
||||
lib/galaxy/webapps/galaxy/buildapp.py
|
||||
lib/galaxy/webapps/galaxy/controllers/admin_toolshed.py
|
||||
lib/galaxy/webapps/galaxy/controllers/external_services.py
|
||||
lib/galaxy/webapps/galaxy/controllers/forms.py
|
||||
lib/galaxy/webapps/galaxy/controllers/history.py
|
||||
lib/galaxy/webapps/galaxy/controllers/library_common.py
|
||||
lib/galaxy/webapps/galaxy/controllers/search.py
|
||||
lib/galaxy/webapps/galaxy/controllers/userskeys.py
|
||||
lib/galaxy/webapps/reports/config.py
|
||||
lib/galaxy/webapps/reports/controllers/tools.py
|
||||
lib/galaxy/webapps/reports/__init__.py
|
||||
lib/galaxy/webapps/tool_shed/config.py
|
||||
lib/galaxy/webapps/tool_shed/controllers/groups.py
|
||||
lib/galaxy/webapps/tool_shed/controllers/user.py
|
||||
lib/galaxy/webapps/tool_shed/framework/middleware/remoteuser.py
|
||||
lib/galaxy/webapps/tool_shed/__init__.py
|
||||
lib/galaxy/webapps/tool_shed/model/__init__.py
|
||||
lib/galaxy/webapps/tool_shed/search/repo_search.py
|
||||
lib/galaxy/webapps/tool_shed/util/ratings_util.py
|
||||
lib/galaxy/webhooks/
|
||||
lib/galaxy/work/
|
||||
lib/galaxy/workflow/
|
||||
lib/galaxy_ext/
|
||||
lib/log_tempfile.py
|
||||
lib/mimeparse.py
|
||||
lib/psyco_full.py
|
||||
lib/tool_shed/
|
||||
scripts/api/
|
||||
scripts/auth/
|
||||
scripts/bootstrap_history.py
|
||||
scripts/build_toolbox.py
|
||||
scripts/check_eggs.py
|
||||
scripts/check_galaxy.py
|
||||
scripts/check_python.py
|
||||
scripts/cleanup_datasets/
|
||||
scripts/communication/
|
||||
scripts/data_libraries/build_whoosh_index.py
|
||||
scripts/db_shell.py
|
||||
scripts/drmaa_external_runner.py
|
||||
scripts/metagenomics/
|
||||
scripts/microbes/BeautifulSoup.py
|
||||
scripts/microbes/harvest_bacteria.py
|
||||
scripts/secret_decoder_ring.py
|
||||
scripts/tool_shed/api/common.py
|
||||
scripts/tool_shed/build_ts_whoosh_index.py
|
||||
scripts/tool_shed/deprecate_repositories_without_metadata.py
|
||||
test/
|
||||
tool_list.py
|
||||
tools/
|
||||
+11
-5
@@ -9,18 +9,24 @@ env:
|
||||
- TOX_ENV=py27-unit
|
||||
- TOX_ENV=qunit
|
||||
- TOX_ENV=first_startup
|
||||
- TOX_ENV=validate-test-tools
|
||||
- TOX_ENV=py27-lint-docstring-include-list
|
||||
- TOX_ENV=check-python-dependencies
|
||||
|
||||
matrix:
|
||||
include:
|
||||
- env: TOX_ENV=validate-test-tools
|
||||
addons:
|
||||
apt:
|
||||
packages:
|
||||
- libxml2-utils
|
||||
- env: TOX_ENV=check_py3_compatibility
|
||||
addons:
|
||||
apt:
|
||||
packages:
|
||||
- ack-grep
|
||||
allow_failures:
|
||||
- env: TOX_ENV=check-python-dependencies
|
||||
|
||||
addons:
|
||||
apt:
|
||||
packages:
|
||||
- libxml2-utils
|
||||
|
||||
install:
|
||||
- pip install tox
|
||||
|
||||
+9
-22
@@ -1,26 +1,13 @@
|
||||
module.exports = function(grunt) {
|
||||
"use strict";
|
||||
|
||||
var GALAXY_PATHS = {
|
||||
dist : '../static/scripts',
|
||||
maps : '../static/maps',
|
||||
// this symlink allows us to serve uncompressed scripts in DEV_PATH for use with sourcemaps
|
||||
srcSymlink : '../static/src',
|
||||
},
|
||||
TOOLSHED_PATHS = {
|
||||
dist : '../static/toolshed/scripts',
|
||||
maps : '../static/toolshed/maps',
|
||||
srcSymlink : '../static/toolshed/src',
|
||||
};
|
||||
|
||||
grunt.config.set( 'app', 'galaxy' );
|
||||
grunt.config.set( 'paths', GALAXY_PATHS );
|
||||
if( grunt.option( 'app' ) === 'toolshed' ){
|
||||
grunt.config.set( 'app', grunt.option( 'app' ) );
|
||||
grunt.config.set( 'paths', TOOLSHED_PATHS );
|
||||
}
|
||||
|
||||
dist: "../static/scripts",
|
||||
maps: "../static/maps",
|
||||
// this symlink allows us to serve uncompressed scripts in DEV_PATH for use with sourcemaps
|
||||
srcSymlink: "../static/src"
|
||||
};
|
||||
grunt.config.set("app", "galaxy");
|
||||
grunt.config.set("paths", GALAXY_PATHS);
|
||||
// see the sub directory grunt-tasks/ for individual task definitions
|
||||
grunt.loadTasks( 'grunt-tasks' );
|
||||
grunt.registerTask( 'default', [ 'uglify' ] );
|
||||
grunt.loadTasks("grunt-tasks");
|
||||
grunt.registerTask("default", ["style"]);
|
||||
};
|
||||
|
||||
@@ -66,13 +66,3 @@ As mentioned above, 'make client' will also rebuild styles. If you *only* want
|
||||
to run the style task, use the following command from the `client` directory:
|
||||
|
||||
yarn run style
|
||||
|
||||
|
||||
The Toolshed Client Build
|
||||
=========================
|
||||
|
||||
The toolshed client is not tightly integrated with the rest of the build
|
||||
system. To build the toolshed client, execute the following command from the
|
||||
`client` directory.
|
||||
|
||||
yarn run build-toolshed
|
||||
|
||||
@@ -5,13 +5,13 @@ import GalaxyApp from "galaxy";
|
||||
import AdminPanel from "./panels/admin-panel";
|
||||
import FormWrapper from "mvc/form/form-wrapper";
|
||||
import GridView from "mvc/grid/grid-view";
|
||||
import Ui from "mvc/ui/ui-misc";
|
||||
import QueryStringParsing from "utils/query-string-parsing";
|
||||
import Router from "layout/router";
|
||||
import Utils from "utils/utils";
|
||||
import Page from "layout/page";
|
||||
import Vue from "libs/vue";
|
||||
import UserAPIKeys from "components/UserAPIKeys.vue";
|
||||
import UserAPIKeys from "components/admin/UserAPIKeys.vue";
|
||||
import DataTables from "components/admin/DataTables.vue";
|
||||
import Vue from "vue";
|
||||
|
||||
window.app = function app(options, bootstrapped) {
|
||||
window.Galaxy = new GalaxyApp.GalaxyApp(options, bootstrapped);
|
||||
@@ -28,10 +28,11 @@ window.app = function app(options, bootstrapped) {
|
||||
"(/)admin(/)repositories": "show_repositories",
|
||||
"(/)admin(/)forms": "show_forms",
|
||||
"(/)admin(/)form(/)(:form_id)": "show_form",
|
||||
"(/)admin/api_keys": "show_user_api_keys"
|
||||
"(/)admin/api_keys": "show_user_api_keys",
|
||||
"(/)admin/data_tables": "show_data_tables"
|
||||
},
|
||||
|
||||
authenticate: function(args, name) {
|
||||
authenticate: function() {
|
||||
return Galaxy.user && Galaxy.user.id && Galaxy.user.get("is_admin");
|
||||
},
|
||||
|
||||
@@ -39,8 +40,7 @@ window.app = function app(options, bootstrapped) {
|
||||
this.page.display(
|
||||
new GridView({
|
||||
url_base: `${Galaxy.root}admin/users_list`,
|
||||
url_data: Galaxy.params,
|
||||
dict_format: true
|
||||
url_data: Galaxy.params
|
||||
})
|
||||
);
|
||||
},
|
||||
@@ -49,8 +49,7 @@ window.app = function app(options, bootstrapped) {
|
||||
this.page.display(
|
||||
new GridView({
|
||||
url_base: `${Galaxy.root}admin/roles_list`,
|
||||
url_data: Galaxy.params,
|
||||
dict_format: true
|
||||
url_data: Galaxy.params
|
||||
})
|
||||
);
|
||||
},
|
||||
@@ -59,8 +58,7 @@ window.app = function app(options, bootstrapped) {
|
||||
this.page.display(
|
||||
new GridView({
|
||||
url_base: `${Galaxy.root}admin/groups_list`,
|
||||
url_data: Galaxy.params,
|
||||
dict_format: true
|
||||
url_data: Galaxy.params
|
||||
})
|
||||
);
|
||||
},
|
||||
@@ -69,8 +67,7 @@ window.app = function app(options, bootstrapped) {
|
||||
this.page.display(
|
||||
new GridView({
|
||||
url_base: `${Galaxy.root}admin_toolshed/browse_repositories`,
|
||||
url_data: Galaxy.params,
|
||||
dict_format: true
|
||||
url_data: Galaxy.params
|
||||
})
|
||||
);
|
||||
},
|
||||
@@ -79,8 +76,7 @@ window.app = function app(options, bootstrapped) {
|
||||
this.page.display(
|
||||
new GridView({
|
||||
url_base: `${Galaxy.root}admin/tool_versions_list`,
|
||||
url_data: Galaxy.params,
|
||||
dict_format: true
|
||||
url_data: Galaxy.params
|
||||
})
|
||||
);
|
||||
},
|
||||
@@ -89,24 +85,28 @@ window.app = function app(options, bootstrapped) {
|
||||
this.page.display(
|
||||
new GridView({
|
||||
url_base: `${Galaxy.root}admin/quotas_list`,
|
||||
url_data: Galaxy.params,
|
||||
dict_format: true
|
||||
url_data: Galaxy.params
|
||||
})
|
||||
);
|
||||
},
|
||||
|
||||
show_user_api_keys: function() {
|
||||
var vuemount = document.createElement("div");
|
||||
this.page.display(vuemount);
|
||||
new Vue(UserAPIKeys).$mount(vuemount);
|
||||
var vueMount = document.createElement("div");
|
||||
this.page.display(vueMount);
|
||||
new Vue(UserAPIKeys).$mount(vueMount);
|
||||
},
|
||||
|
||||
show_data_tables: function() {
|
||||
var vueMount = document.createElement("div");
|
||||
this.page.display(vueMount);
|
||||
new Vue(DataTables).$mount(vueMount);
|
||||
},
|
||||
|
||||
show_forms: function() {
|
||||
this.page.display(
|
||||
new GridView({
|
||||
url_base: `${Galaxy.root}forms/forms_list`,
|
||||
url_data: Galaxy.params,
|
||||
dict_format: true
|
||||
url_data: Galaxy.params
|
||||
})
|
||||
);
|
||||
},
|
||||
|
||||
@@ -22,7 +22,7 @@ import Ui from "mvc/ui/ui-misc";
|
||||
import DatasetError from "mvc/dataset/dataset-error";
|
||||
import DatasetEditAttributes from "mvc/dataset/dataset-edit-attributes";
|
||||
import Citations from "components/Citations.vue";
|
||||
import Vue from "libs/vue";
|
||||
import Vue from "vue";
|
||||
|
||||
/** define the 'Analyze Data'/analysis/main/home page for Galaxy
|
||||
* * has a masthead
|
||||
@@ -47,6 +47,7 @@ window.app = function app(options, bootstrapped) {
|
||||
"(/)tours(/)(:tour_id)": "show_tours",
|
||||
"(/)user(/)": "show_user",
|
||||
"(/)user(/)(:form_id)": "show_user_form",
|
||||
"(/)openids(/)list": "show_openids",
|
||||
"(/)pages(/)create(/)": "show_pages_create",
|
||||
"(/)pages(/)edit(/)": "show_pages_edit",
|
||||
"(/)pages(/)(:action_id)": "show_pages",
|
||||
@@ -76,7 +77,7 @@ window.app = function app(options, bootstrapped) {
|
||||
|
||||
show_tours: function(tour_id) {
|
||||
if (tour_id) {
|
||||
Tours.giveTour(tour_id);
|
||||
Tours.giveTourById(tour_id);
|
||||
} else {
|
||||
this.page.display(new Tours.ToursView());
|
||||
}
|
||||
@@ -115,8 +116,7 @@ window.app = function app(options, bootstrapped) {
|
||||
show_workflows_published: function() {
|
||||
this.page.display(
|
||||
new GridView({
|
||||
url_base: `${Galaxy.root}workflow/list_published`,
|
||||
dict_format: true
|
||||
url_base: `${Galaxy.root}workflow/list_published`
|
||||
})
|
||||
);
|
||||
},
|
||||
@@ -157,11 +157,18 @@ window.app = function app(options, bootstrapped) {
|
||||
);
|
||||
},
|
||||
|
||||
show_openids: function() {
|
||||
this.page.display(
|
||||
new GridView({
|
||||
url_base: `${Galaxy.root}user/openids_list`
|
||||
})
|
||||
);
|
||||
},
|
||||
|
||||
show_datasets: function() {
|
||||
this.page.display(
|
||||
new GridView({
|
||||
url_base: `${Galaxy.root}dataset/list`,
|
||||
dict_format: true
|
||||
url_base: `${Galaxy.root}dataset/list`
|
||||
})
|
||||
);
|
||||
},
|
||||
|
||||
@@ -1,11 +1,37 @@
|
||||
import _l from "utils/localization";
|
||||
import GalaxyApp from "galaxy";
|
||||
import WorkflowView from "mvc/workflow/workflow-view";
|
||||
import Trackster from "viz/trackster";
|
||||
import Circster from "viz/circster";
|
||||
import Phyloviz from "viz/phyloviz";
|
||||
import Sweepster from "viz/sweepster";
|
||||
import GalaxyLibrary from "galaxy.library";
|
||||
import AdminToolshed from "admin.toolshed";
|
||||
import Masthead from "layout/masthead";
|
||||
import user from "mvc/user/user-model";
|
||||
import Modal from "mvc/ui/ui-modal";
|
||||
import pagesEditorOnload from "galaxy.pages";
|
||||
import Data from "mvc/dataset/data";
|
||||
import History from "mvc/history/history-model";
|
||||
import StructureView from "mvc/history/history-structure-view";
|
||||
import HistoryContents from "mvc/history/history-contents";
|
||||
import MultiPanel from "mvc/history/multi-panel";
|
||||
import HistoryView from "mvc/history/history-view";
|
||||
import HistoryViewEdit from "mvc/history/history-view-edit";
|
||||
import HistoryViewAnnotated from "mvc/history/history-view-annotated";
|
||||
import HistoryCopyDialog from "mvc/history/copy-dialog";
|
||||
import HDAListItemEdit from "mvc/history/hda-li-edit";
|
||||
import HDAModel from "mvc/history/hda-model";
|
||||
import addLogging from "utils/add-logging";
|
||||
import LegacyGridView from "legacy/grid/grid-view";
|
||||
import * as run_stats from "reports/run_stats";
|
||||
import ToolshedGroups from "toolshed/toolshed.groups";
|
||||
|
||||
if (window.Galaxy && window.Galaxy.debug === undefined) {
|
||||
//TODO: (kind of a temporary hack?) Must have Galaxy.logging for some of the imports
|
||||
//here; remove when imports are all fixed.
|
||||
addLogging(window.Galaxy, "GalaxyApp");
|
||||
}
|
||||
|
||||
export function mastheadEntry(options) {
|
||||
if (!Galaxy.user) {
|
||||
@@ -34,17 +60,139 @@ export function workflowEntry(options) {
|
||||
new WorkflowView(options);
|
||||
}
|
||||
|
||||
function libraryEntry(options) {
|
||||
export function libraryEntry(options) {
|
||||
new GalaxyLibrary.GalaxyApp(options);
|
||||
}
|
||||
|
||||
export function historyEntry(options) {
|
||||
$("#toggle-deleted").modeButton({
|
||||
initialMode: options.initialModeDeleted,
|
||||
modes: [
|
||||
{ mode: "showing_deleted", html: _l("Exclude deleted") },
|
||||
{ mode: "not_showing_deleted", html: _l("Include deleted") }
|
||||
]
|
||||
});
|
||||
$("#toggle-hidden").modeButton({
|
||||
initialMode: options.initialModeHidden,
|
||||
modes: [
|
||||
{ mode: "showing_hidden", html: _l("Exclude hidden") },
|
||||
{ mode: "not_showing_hidden", html: _l("Include hidden") }
|
||||
]
|
||||
});
|
||||
$("#switch").click(function() {
|
||||
//##HACK:ity hack hack
|
||||
//##TODO: remove when out of iframe
|
||||
var hview =
|
||||
Galaxy.currHistoryPanel || (window.top.Galaxy && window.top.Galaxy.currHistoryPanel)
|
||||
? window.top.Galaxy.currHistoryPanel
|
||||
: null;
|
||||
if (hview) {
|
||||
hview.switchToHistory("${ history[ 'id' ] }");
|
||||
} else {
|
||||
window.location = "${ switch_to_url }";
|
||||
}
|
||||
});
|
||||
// use_panels effects where the the center_panel() is rendered:
|
||||
// w/o it renders to the body, w/ it renders to #center - we need to adjust a few things for scrolling to work
|
||||
if (options.hasMasthead) {
|
||||
$("#center").addClass("flex-vertical-container");
|
||||
}
|
||||
|
||||
let viewClass = options.userIsOwner ? HistoryViewEdit.HistoryViewEdit : HistoryView.HistoryView;
|
||||
let historyModel = new History.History(options.historyJSON);
|
||||
|
||||
// attach the copy dialog to the import button now that we have a history
|
||||
$("#import").click(function() {
|
||||
HistoryCopyDialog(historyModel, {
|
||||
useImport: true,
|
||||
// use default datasets option to match the toggle-deleted button
|
||||
allDatasets: $("#toggle-deleted").modeButton("getMode").mode === "showing_deleted"
|
||||
}).done(function() {
|
||||
if (window === window.parent) {
|
||||
window.location = Galaxy.root;
|
||||
} else if (Galaxy.currHistoryPanel) {
|
||||
Galaxy.currHistoryPanel.loadCurrentHistory();
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
let historyView = new viewClass({
|
||||
el: $("#history-" + options.historyJSON.id),
|
||||
className: viewClass.prototype.className + " wide",
|
||||
$scrollContainer: options.hasMasthead
|
||||
? function() {
|
||||
return this.$el.parent();
|
||||
}
|
||||
: undefined,
|
||||
model: historyModel,
|
||||
show_deleted: options.showDeletedJson,
|
||||
show_hidden: options.showHiddenJson,
|
||||
purgeAllowed: options.allow_user_dataset_purge
|
||||
});
|
||||
historyView.trigger("loading");
|
||||
historyModel
|
||||
.fetchContents({ silent: true })
|
||||
.fail(function() {
|
||||
alert("Galaxy history failed to load");
|
||||
})
|
||||
.done(function() {
|
||||
historyView.trigger("loading-done");
|
||||
historyView.render();
|
||||
});
|
||||
$("#toggle-deleted").on("click", function() {
|
||||
historyView.toggleShowDeleted();
|
||||
});
|
||||
$("#toggle-hidden").on("click", function() {
|
||||
historyView.toggleShowHidden();
|
||||
});
|
||||
}
|
||||
|
||||
export function multiHistoryEntry(options) {
|
||||
let histories = new History.HistoryCollection([], {
|
||||
includeDeleted: options.includingDeleted,
|
||||
order: options.order,
|
||||
limitOnFirstFetch: options.limit,
|
||||
limitPerFetch: options.limit,
|
||||
currentHistoryId: options.current_history_id
|
||||
});
|
||||
|
||||
let multipanel = new MultiPanel.MultiPanelColumns({
|
||||
el: $("#center").get(0),
|
||||
histories: histories
|
||||
});
|
||||
|
||||
histories.fetchFirst({ silent: true }).done(function() {
|
||||
multipanel.createColumns();
|
||||
multipanel.render(0);
|
||||
});
|
||||
}
|
||||
|
||||
export const bundleEntries = {
|
||||
library: libraryEntry,
|
||||
masthead: mastheadEntry,
|
||||
workflow: workflowEntry,
|
||||
trackster: tracksterEntry,
|
||||
circster: circsterEntry,
|
||||
adminToolshed: adminToolshedEntry
|
||||
adminToolshed: adminToolshedEntry,
|
||||
pages: pagesEditorOnload,
|
||||
phyloviz: Phyloviz.PhylovizView,
|
||||
createTabularDatasetChunkedView: Data.createTabularDatasetChunkedView,
|
||||
multiHistory: multiHistoryEntry,
|
||||
history: historyEntry,
|
||||
History: History.History,
|
||||
HistoryContents: HistoryContents.HistoryContents,
|
||||
SweepsterVisualization: Sweepster.SweepsterVisualization,
|
||||
SweepsterVisualizationView: Sweepster.SweepsterVisualizationView,
|
||||
HistoryCopyDialog,
|
||||
HistoryViewAnnotated,
|
||||
Trackster,
|
||||
StructureView,
|
||||
HDAListItemEdit,
|
||||
HDAModel,
|
||||
GalaxyApp,
|
||||
LegacyGridView,
|
||||
run_stats,
|
||||
ToolshedGroups
|
||||
};
|
||||
|
||||
window.bundleEntries = bundleEntries;
|
||||
|
||||
@@ -22,7 +22,8 @@ var AdminPanel = Backbone.View.extend({
|
||||
},
|
||||
{
|
||||
title: _l("Data tables"),
|
||||
url: "admin/view_tool_data_tables"
|
||||
url: "admin/data_tables",
|
||||
target: "__use_router__"
|
||||
},
|
||||
{
|
||||
title: _l("Display applications"),
|
||||
|
||||
@@ -59,7 +59,7 @@ var ToolPanel = Backbone.View.extend({
|
||||
self.$("#internal-workflows").append(
|
||||
self._templateAllWorkflow({
|
||||
title: _l("All workflows"),
|
||||
href: "workflow"
|
||||
href: "workflows/list"
|
||||
})
|
||||
);
|
||||
_.each(this.stored_workflow_menu_entries, menu_entry => {
|
||||
|
||||
@@ -0,0 +1,20 @@
|
||||
<template>
|
||||
<div v-if="message" :class="[status + 'message']">{{ message }}</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
name: 'Message',
|
||||
props: {
|
||||
message: {
|
||||
type: String,
|
||||
required: true
|
||||
},
|
||||
status: {
|
||||
type: String,
|
||||
default: 'done'
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
@@ -0,0 +1,43 @@
|
||||
<template>
|
||||
<div class="toolForm">
|
||||
<div class="toolFormTitle">
|
||||
<slot name="title">{{ title }}</slot>
|
||||
</div>
|
||||
<div class="toolFormBody">
|
||||
<table class="manage-table colored" border="0" cellspacing="0" cellpadding="0" width="100%">
|
||||
<slot name="columns">
|
||||
<th v-for="column in columns" bgcolor="#D8D8D8">{{ column.text }}</th>
|
||||
</slot>
|
||||
<slot name="rows">
|
||||
<template v-for="(row, index) in rows">
|
||||
<tr :class="[index % 2 === 0 ? 'tr' : 'odd_row']">
|
||||
<td v-for="column in columns">{{ row[column.dataIndex] }}</td>
|
||||
</tr>
|
||||
</template>
|
||||
</slot>
|
||||
</table>
|
||||
<div v-if="isLoaded !== undefined && !isLoaded" :style="{ textAlign: 'center', padding: '7px 0' }">
|
||||
Loading...
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
props: {
|
||||
isLoaded: {
|
||||
type: Boolean
|
||||
},
|
||||
title: {
|
||||
type: String
|
||||
},
|
||||
columns: {
|
||||
type: Array
|
||||
},
|
||||
rows: {
|
||||
type: Array
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
@@ -0,0 +1,47 @@
|
||||
<template>
|
||||
<table class="tabletip">
|
||||
<thead>
|
||||
<tr>
|
||||
<th :colspan="dataManagerColumns.length" style="font-size:120%">
|
||||
Data Manager: {{ dataManagerTableName }}
|
||||
<a class="icon-btn" href="javascript:void(0)"
|
||||
@click="handleReloadButtonClick"
|
||||
:title="`Reload ${dataManagerTableName} tool data table`"
|
||||
><span class="fa fa-refresh"></span></a>
|
||||
</th>
|
||||
</tr>
|
||||
<tr>
|
||||
<th v-for="column in dataManagerColumns">{{ column }}</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody v-for="item in dataManagerItems">
|
||||
<tr>
|
||||
<td v-for="column in dataManagerColumns">{{ item[column] }}</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
props: {
|
||||
dataManagerTableName: {
|
||||
type: String,
|
||||
required: true
|
||||
},
|
||||
dataManagerColumns: {
|
||||
type: Array,
|
||||
required: true
|
||||
},
|
||||
dataManagerItems: {
|
||||
type: Array,
|
||||
required: true
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
handleReloadButtonClick(event) {
|
||||
this.$emit('reloaddatamanager', this.dataManagerTableName);
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
@@ -0,0 +1,117 @@
|
||||
<template>
|
||||
<div>
|
||||
<message :message="message" :status="status"></message>
|
||||
<component
|
||||
:is="currentView"
|
||||
v-if="status !== 'error'"
|
||||
v-bind="currentProps"
|
||||
@changeview="showDataManager"
|
||||
@reloaddatamanager="reloadDataManager">
|
||||
</component>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import axios from 'axios';
|
||||
import Message from '../Message.vue';
|
||||
import DataTablesGrid from './DataTablesGrid.vue';
|
||||
import DataManagerGrid from './DataManagerGrid.vue';
|
||||
|
||||
export default {
|
||||
data() {
|
||||
return {
|
||||
currentView: 'data-tables-grid',
|
||||
isLoaded: false,
|
||||
dataTables: [],
|
||||
dataManagerTableName: '',
|
||||
dataManagerColumns: [],
|
||||
dataManagerItems: [],
|
||||
message: '',
|
||||
status: ''
|
||||
}
|
||||
},
|
||||
|
||||
components: {
|
||||
message: Message,
|
||||
'data-tables-grid': DataTablesGrid,
|
||||
'data-manager-grid': DataManagerGrid
|
||||
},
|
||||
|
||||
computed: {
|
||||
currentProps() {
|
||||
let props;
|
||||
|
||||
if (this.currentView === 'data-tables-grid') {
|
||||
props = {
|
||||
isLoaded: this.isLoaded,
|
||||
rows: this.dataTables
|
||||
};
|
||||
} else {
|
||||
props = {
|
||||
dataManagerTableName: this.dataManagerTableName,
|
||||
dataManagerColumns: this.dataManagerColumns,
|
||||
dataManagerItems: this.dataManagerItems
|
||||
};
|
||||
}
|
||||
|
||||
return props;
|
||||
}
|
||||
},
|
||||
|
||||
methods: {
|
||||
showDataManager(dataManagerTableName) {
|
||||
axios.get(`${Galaxy.root}data_manager/tool_data_table_items`, {
|
||||
params: {
|
||||
table_name: dataManagerTableName
|
||||
}
|
||||
})
|
||||
.then((response) => {
|
||||
this.message = response.data.message;
|
||||
this.status = response.data.status;
|
||||
|
||||
if (response.data.status !== 'error' && response.data.status !== 'warning') {
|
||||
this.dataManagerTableName = dataManagerTableName;
|
||||
this.dataManagerColumns = response.data.data.columns;
|
||||
this.dataManagerItems = response.data.data.items;
|
||||
this.currentView = 'data-manager-grid';
|
||||
}
|
||||
})
|
||||
.catch((error) => {
|
||||
console.error(error);
|
||||
});
|
||||
},
|
||||
|
||||
reloadDataManager(dataManagerTableName) {
|
||||
axios.get(`${Galaxy.root}data_manager/reload_tool_data_table`, {
|
||||
params: {
|
||||
table_name: dataManagerTableName
|
||||
}
|
||||
})
|
||||
.then((response) => {
|
||||
this.message = response.data.message;
|
||||
this.status = response.data.status;
|
||||
|
||||
if (response.data.status !== 'error' && response.data.status !== 'warning') {
|
||||
this.dataManagerItems = response.data.data.items;
|
||||
}
|
||||
})
|
||||
.catch((error) => {
|
||||
console.error(error);
|
||||
});
|
||||
}
|
||||
},
|
||||
|
||||
created() {
|
||||
axios.get(`${Galaxy.root}admin/data_tables_list`)
|
||||
.then((response) => {
|
||||
this.isLoaded = true;
|
||||
this.dataTables = response.data.data;
|
||||
this.message = response.data.message;
|
||||
this.status = response.data.status;
|
||||
})
|
||||
.catch((error) => {
|
||||
console.error(error);
|
||||
});
|
||||
}
|
||||
}
|
||||
</script>
|
||||
@@ -0,0 +1,53 @@
|
||||
<template>
|
||||
<base-grid :is-loaded="isLoaded" :columns="columns">
|
||||
<template slot="title">
|
||||
Current data table registry contains {{ rows.length }} data tables
|
||||
</template>
|
||||
<template slot="rows" v-for="(row, index) in rows">
|
||||
<tr :class="[index % 2 === 0 ? 'tr' : 'odd_row']">
|
||||
<td><a href="javascript:void(0)" @click="handleTableNameClick">{{ row.name }}</a></td>
|
||||
<td>{{ row.filename }}</td>
|
||||
<td>{{ row.tool_data_path }}</td>
|
||||
<td>{{ row.errors }}</td>
|
||||
</tr>
|
||||
</template>
|
||||
</base-grid>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import BaseGrid from './BaseGrid.vue';
|
||||
|
||||
export default {
|
||||
props: {
|
||||
isLoaded: {
|
||||
type: Boolean,
|
||||
required: true
|
||||
},
|
||||
rows: {
|
||||
type: Array,
|
||||
required: true
|
||||
}
|
||||
},
|
||||
|
||||
data() {
|
||||
return {
|
||||
columns: [
|
||||
{ text: 'Name', dataIndex: 'name' },
|
||||
{ text: 'Filename', dataIndex: 'filename' },
|
||||
{ text: 'Tool data path', dataIndex: 'tool_data_path' },
|
||||
{ text: 'Errors', dataIndex: 'errors' }
|
||||
]
|
||||
}
|
||||
},
|
||||
|
||||
components: {
|
||||
'base-grid': BaseGrid
|
||||
},
|
||||
|
||||
methods: {
|
||||
handleTableNameClick(event) {
|
||||
this.$emit('changeview', event.target.text);
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
@@ -6,9 +6,7 @@
|
||||
export function append_notebook(url) {
|
||||
clear_main_area();
|
||||
$("#main").append(
|
||||
`<iframe frameBorder="0" seamless="seamless" style="width: 100%; height: 100%; overflow:hidden;" scrolling="no" src="${
|
||||
url
|
||||
}"></iframe>`
|
||||
`<iframe frameBorder="0" seamless="seamless" style="width: 100%; height: 100%; overflow:hidden;" scrolling="no" src="${url}"></iframe>`
|
||||
);
|
||||
}
|
||||
|
||||
@@ -21,9 +19,7 @@ export function clear_main_area() {
|
||||
|
||||
export function display_spinner() {
|
||||
$("#main").append(
|
||||
`<img id="spinner" src="${
|
||||
galaxy_root
|
||||
}static/style/largespinner.gif" style="position:absolute;margin:auto;top:0;left:0;right:0;bottom:0;">`
|
||||
`<img id="spinner" src="${galaxy_root}static/style/largespinner.gif" style="position:absolute;margin:auto;top:0;left:0;right:0;bottom:0;">`
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
@@ -256,6 +256,9 @@ GalaxyApp.prototype.toString = function toString() {
|
||||
return `GalaxyApp(${userEmail})`;
|
||||
};
|
||||
|
||||
// This is not great. Rework to be a singleton-style Galaxy app accessible/created by import
|
||||
window.Galaxy = window.Galaxy || new GalaxyApp();
|
||||
|
||||
// ============================================================================
|
||||
export default {
|
||||
GalaxyApp: GalaxyApp
|
||||
|
||||
@@ -176,7 +176,7 @@ var GalaxyLibrary = Backbone.View.extend({
|
||||
mod_toastr.info("You must select at least one dataset to download");
|
||||
Galaxy.libraries.library_router.navigate(`folders/${folder_id}`, { trigger: true, replace: true });
|
||||
} else {
|
||||
Galaxy.libraries.folderToolbarView.download(folder_id, format);
|
||||
Galaxy.libraries.folderToolbarView.download(format);
|
||||
Galaxy.libraries.library_router.navigate(`folders/${folder_id}`, { trigger: false, replace: true });
|
||||
}
|
||||
});
|
||||
|
||||
@@ -5,6 +5,8 @@ import "libs/jquery/jquery.wymeditor";
|
||||
import "libs/jquery/jquery.autocomplete";
|
||||
import GridView from "mvc/grid/grid-view";
|
||||
|
||||
var WYMeditor = window.WYMeditor;
|
||||
|
||||
var CONTROLS = {
|
||||
// Item types.
|
||||
ITEM_HISTORY: "item_history",
|
||||
@@ -154,11 +156,7 @@ WYMeditor.editor.prototype.dialog = function(dialogType, dialogFeatures, bodyHtm
|
||||
}
|
||||
show_modal(
|
||||
"Create Link",
|
||||
`<div><div><label id='link_attribute_label'>URL <span style='float: right; font-size: 90%'><a href='#' id='set_link_id'>Create in-page anchor</a></span></label><br><input type='text' class='wym_href' value='${
|
||||
curURL
|
||||
}' size='40' /></div><div><label>Title</label><br><input type='text' class='wym_title' value='${
|
||||
curTitle
|
||||
}' size='40' /></div><div>`,
|
||||
`<div><div><label id='link_attribute_label'>URL <span style='float: right; font-size: 90%'><a href='#' id='set_link_id'>Create in-page anchor</a></span></label><br><input type='text' class='wym_href' value='${curURL}' size='40' /></div><div><label>Title</label><br><input type='text' class='wym_title' value='${curTitle}' size='40' /></div><div>`,
|
||||
{
|
||||
"Make link": function() {
|
||||
// Get URL, name/title.
|
||||
@@ -332,7 +330,6 @@ WYMeditor.editor.prototype.dialog = function(dialogType, dialogFeatures, bodyHtm
|
||||
|
||||
var grid = new GridView({
|
||||
url_base: item_info.list_ajax_url,
|
||||
dict_format: true,
|
||||
embedded: true
|
||||
});
|
||||
Galaxy.modal.show({
|
||||
@@ -426,7 +423,6 @@ WYMeditor.editor.prototype.dialog = function(dialogType, dialogFeatures, bodyHtm
|
||||
|
||||
var grid = new GridView({
|
||||
url_base: item_info.list_ajax_url,
|
||||
dict_format: true,
|
||||
embedded: true
|
||||
});
|
||||
Galaxy.modal.show({
|
||||
@@ -493,7 +489,7 @@ WYMeditor.editor.prototype.dialog = function(dialogType, dialogFeatures, bodyHtm
|
||||
}
|
||||
};
|
||||
|
||||
export default function editor_onload() {
|
||||
export default function pagesEditorOnload() {
|
||||
// Generic error handling
|
||||
$(document).ajaxError((e, x) => {
|
||||
// console.log( e, x );
|
||||
|
||||
@@ -154,9 +154,9 @@ function show_in_overlay(options) {
|
||||
closeButton: true,
|
||||
title: " ",
|
||||
body: $(
|
||||
`<div style='margin: -5px;'><iframe style='margin: 0; padding: 0;' src='${options.url}' width='${
|
||||
width
|
||||
}' height='${height}' scrolling='${scroll}' frameborder='0'></iframe></div>`
|
||||
`<div style='margin: -5px;'><iframe style='margin: 0; padding: 0;' src='${
|
||||
options.url
|
||||
}' width='${width}' height='${height}' scrolling='${scroll}' frameborder='0'></iframe></div>`
|
||||
)
|
||||
});
|
||||
modal.show({ backdrop: true });
|
||||
|
||||
@@ -17,10 +17,8 @@ var Router = Backbone.Router.extend({
|
||||
data.__identifer = Math.random()
|
||||
.toString(36)
|
||||
.substr(2);
|
||||
if (!$.isEmptyObject(data)) {
|
||||
url += url.indexOf("?") == -1 ? "?" : "&";
|
||||
url += $.param(data, true);
|
||||
}
|
||||
url += url.indexOf("?") == -1 ? "?" : "&";
|
||||
url += $.param(data, true);
|
||||
Galaxy.params = data;
|
||||
this.navigate(url, { trigger: true });
|
||||
},
|
||||
|
||||
@@ -0,0 +1,114 @@
|
||||
// legacy grid model, used by reports and toolshed
|
||||
export default Backbone.Model.extend({
|
||||
defaults: {
|
||||
url_base: "",
|
||||
async: false,
|
||||
async_ops: [],
|
||||
categorical_filters: [],
|
||||
filters: {},
|
||||
sort_key: null,
|
||||
show_item_checkboxes: false,
|
||||
advanced_search: false,
|
||||
cur_page: 1,
|
||||
num_pages: 1,
|
||||
operation: undefined,
|
||||
item_ids: undefined
|
||||
},
|
||||
|
||||
/**
|
||||
* Return true if operation can be done asynchronously.
|
||||
*/
|
||||
can_async_op: function(op) {
|
||||
return _.indexOf(this.attributes.async_ops, op) !== -1;
|
||||
},
|
||||
|
||||
/**
|
||||
* Add filtering criterion.
|
||||
*/
|
||||
add_filter: function(key, value, append) {
|
||||
// Update URL arg with new condition.
|
||||
if (append) {
|
||||
// Update or append value.
|
||||
var cur_val = this.attributes.filters[key];
|
||||
|
||||
var new_val;
|
||||
if (cur_val === null || cur_val === undefined) {
|
||||
new_val = value;
|
||||
} else if (typeof cur_val == "string") {
|
||||
if (cur_val == "All" || cur_val == value) {
|
||||
new_val = value;
|
||||
} else {
|
||||
// Replace string with array.
|
||||
var values = [];
|
||||
values[0] = cur_val;
|
||||
values[1] = value;
|
||||
new_val = values;
|
||||
}
|
||||
} else {
|
||||
// Current value is an array.
|
||||
new_val = cur_val;
|
||||
if (new_val.indexOf(value) === -1) {
|
||||
new_val.push(value);
|
||||
}
|
||||
}
|
||||
this.attributes.filters[key] = new_val;
|
||||
} else {
|
||||
// Replace value.
|
||||
this.attributes.filters[key] = value;
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Remove filtering criterion.
|
||||
*/
|
||||
remove_filter: function(key, condition) {
|
||||
var cur_val = this.attributes.filters[key];
|
||||
if (cur_val === null || cur_val === undefined) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (typeof cur_val === "string") {
|
||||
// overwrite/remove condition.
|
||||
this.attributes.filters[key] = "";
|
||||
} else {
|
||||
// filter contains an array of conditions.
|
||||
var condition_index = _.indexOf(cur_val, condition);
|
||||
if (condition_index !== -1) {
|
||||
cur_val[condition_index] = "";
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Returns URL data for obtaining a new grid.
|
||||
*/
|
||||
get_url_data: function() {
|
||||
var url_data = {
|
||||
async: this.attributes.async,
|
||||
sort: this.attributes.sort_key,
|
||||
page: this.attributes.cur_page,
|
||||
show_item_checkboxes: this.attributes.show_item_checkboxes,
|
||||
advanced_search: this.attributes.advanced_search
|
||||
};
|
||||
|
||||
// Add operation, item_ids only if they have values.
|
||||
if (this.attributes.operation) {
|
||||
url_data.operation = this.attributes.operation;
|
||||
}
|
||||
if (this.attributes.item_ids) {
|
||||
url_data.id = this.attributes.item_ids;
|
||||
}
|
||||
|
||||
// Add filter arguments to data, placing "f-" in front of all arguments.
|
||||
var self = this;
|
||||
_.each(_.pairs(self.attributes.filters), k => {
|
||||
url_data[`f-${k[0]}`] = k[1];
|
||||
});
|
||||
return url_data;
|
||||
},
|
||||
|
||||
// Return URL for obtaining a new grid
|
||||
get_url: function(args) {
|
||||
return `${this.get("url_base")}?${$.param(this.get_url_data())}&${$.param(args)}`;
|
||||
}
|
||||
});
|
||||
@@ -0,0 +1,547 @@
|
||||
// dependencies
|
||||
import Utils from "utils/utils";
|
||||
// grid view templates
|
||||
export default {
|
||||
// template
|
||||
grid: function(options) {
|
||||
var tmpl = "";
|
||||
if (options.embedded) {
|
||||
tmpl = this.grid_header(options) + this.grid_table(options);
|
||||
} else {
|
||||
tmpl = `<div class="loading-elt-overlay"></div><table><tr><td width="75%">${this.grid_header(
|
||||
options
|
||||
)}</td><td></td><td></td></tr><tr><td width="100%" id="grid-message" valign="top"></td><td></td><td></td></tr></table>${this.grid_table(
|
||||
options
|
||||
)}`;
|
||||
}
|
||||
|
||||
// add info text
|
||||
if (options.info_text) {
|
||||
tmpl += `<br><div class="toolParamHelp" style="clear: both;">${options.info_text}</div>`;
|
||||
}
|
||||
|
||||
// return
|
||||
return tmpl;
|
||||
},
|
||||
|
||||
// template
|
||||
grid_table: function() {
|
||||
return `
|
||||
<form method="post" onsubmit="return false;">
|
||||
<table id="grid-table" class="grid">
|
||||
<thead id="grid-table-header"></thead>
|
||||
<tbody id="grid-table-body"></tbody>
|
||||
<tfoot id="grid-table-footer"></tfoot>
|
||||
</table>
|
||||
</form>`;
|
||||
},
|
||||
|
||||
// template
|
||||
grid_header: function(options) {
|
||||
var tmpl = '<div class="grid-header">';
|
||||
if (!options.embedded) {
|
||||
tmpl += `<h2>${options.title}</h2>`;
|
||||
}
|
||||
if (options.global_actions) {
|
||||
tmpl += '<ul class="manage-table-actions">';
|
||||
var show_popup = options.global_actions.length >= 3;
|
||||
if (show_popup) {
|
||||
tmpl +=
|
||||
'<li><a class="action-button" id="popup-global-actions" class="menubutton">Actions</a></li>' +
|
||||
'<div popupmenu="popup-global-actions">';
|
||||
}
|
||||
for (let action of options.global_actions) {
|
||||
tmpl += `<li><a class="action-button use-target" target="${action.target}" href="${
|
||||
action.url_args
|
||||
}" onclick="return false;" >${action.label}</a></li>`;
|
||||
}
|
||||
if (show_popup) {
|
||||
tmpl += "</div>";
|
||||
}
|
||||
tmpl += "</ul>";
|
||||
}
|
||||
if (options.insert) {
|
||||
tmpl += options.insert;
|
||||
}
|
||||
|
||||
// add grid filters
|
||||
tmpl += this.grid_filters(options);
|
||||
tmpl += "</div>";
|
||||
|
||||
// return template
|
||||
return tmpl;
|
||||
},
|
||||
|
||||
// template
|
||||
header: function(options) {
|
||||
// start
|
||||
var tmpl = "<tr>";
|
||||
|
||||
// add checkbox
|
||||
if (options.show_item_checkboxes) {
|
||||
tmpl += "<th>";
|
||||
if (options.items.length > 0) {
|
||||
tmpl +=
|
||||
'<input type="checkbox" id="check_all" name=select_all_checkbox value="true">' +
|
||||
'<input type="hidden" name=select_all_checkbox value="true">';
|
||||
}
|
||||
tmpl += "</th>";
|
||||
}
|
||||
|
||||
// create header elements
|
||||
for (let column of options.columns) {
|
||||
if (column.visible) {
|
||||
tmpl += `<th id="${column.key}-header">`;
|
||||
if (column.href) {
|
||||
tmpl += `<a href="${column.href}" class="sort-link" sort_key="${column.key}">${column.label}</a>`;
|
||||
} else {
|
||||
tmpl += column.label;
|
||||
}
|
||||
tmpl += `<span class="sort-arrow">${column.extra}</span></th>`;
|
||||
}
|
||||
}
|
||||
|
||||
// finalize
|
||||
tmpl += "</tr>";
|
||||
|
||||
// return template
|
||||
return tmpl;
|
||||
},
|
||||
|
||||
// template
|
||||
body: function(options) {
|
||||
// initialize
|
||||
var tmpl = "";
|
||||
var items_length = options.items.length;
|
||||
|
||||
// empty grid?
|
||||
if (items_length === 0) {
|
||||
// No results.
|
||||
tmpl += '<tr><td colspan="100"><em>No Items</em></td></tr>';
|
||||
}
|
||||
|
||||
// create rows
|
||||
for (let item of options.items) {
|
||||
// Tag current
|
||||
tmpl += "<tr ";
|
||||
if (options.current_item_id == item.id) {
|
||||
tmpl += 'class="current"';
|
||||
}
|
||||
tmpl += ">";
|
||||
|
||||
// Item selection column
|
||||
if (options.show_item_checkboxes) {
|
||||
tmpl += `<td style="width: 1.5em;"><input type="checkbox" name="id" value="${item.encode_id}" id="${
|
||||
item.encode_id
|
||||
}" class="grid-row-select-checkbox" /></td>`;
|
||||
}
|
||||
|
||||
// Data columns
|
||||
for (let column of options.columns) {
|
||||
if (column.visible) {
|
||||
// Nowrap
|
||||
var nowrap = "";
|
||||
if (column.nowrap) {
|
||||
nowrap = 'style="white-space:nowrap;"';
|
||||
}
|
||||
|
||||
// get column settings
|
||||
var column_settings = item.column_config[column.label];
|
||||
|
||||
// load attributes
|
||||
var link = column_settings.link;
|
||||
var value = column_settings.value;
|
||||
var target = column_settings.target;
|
||||
|
||||
// unescape value
|
||||
if (jQuery.type(value) === "string") {
|
||||
value = value.replace(/\/\//g, "/");
|
||||
}
|
||||
|
||||
// Attach popup menu?
|
||||
var id = "";
|
||||
var cls = "";
|
||||
if (column.attach_popup) {
|
||||
id = `grid-${item.encode_id}-popup`;
|
||||
cls = "menubutton";
|
||||
if (link !== "") {
|
||||
cls += " split";
|
||||
}
|
||||
cls += " popup";
|
||||
}
|
||||
|
||||
// Check for row wrapping
|
||||
tmpl += `<td ${nowrap}>`;
|
||||
|
||||
// Link
|
||||
if (link) {
|
||||
if (options.operations.length !== 0) {
|
||||
tmpl += `<div id="${id}" class="${cls}" style="float: left;">`;
|
||||
}
|
||||
tmpl += `<a class="menubutton-label use-target" target="${target}" href="${link}" onclick="return false;">${value}</a>`;
|
||||
if (options.operations.length !== 0) {
|
||||
tmpl += "</div>";
|
||||
}
|
||||
} else {
|
||||
tmpl += `<div id="${id}" class="${cls}"><label id="${column.label_id_prefix}${
|
||||
item.encode_id
|
||||
}" for="${item.encode_id}">${value || ""}</label></div>`;
|
||||
}
|
||||
tmpl += "</td>";
|
||||
}
|
||||
}
|
||||
tmpl += "</tr>";
|
||||
}
|
||||
return tmpl;
|
||||
},
|
||||
|
||||
// template
|
||||
footer: function(options) {
|
||||
// create template string
|
||||
var tmpl = "";
|
||||
|
||||
// paging
|
||||
if (options.use_paging && options.num_pages > 1) {
|
||||
// get configuration
|
||||
var num_page_links = options.num_page_links;
|
||||
var cur_page_num = options.cur_page_num;
|
||||
var num_pages = options.num_pages;
|
||||
|
||||
// First pass on min page.
|
||||
var page_link_range = num_page_links / 2;
|
||||
var min_page = cur_page_num - page_link_range;
|
||||
var min_offset = 0;
|
||||
if (min_page <= 0) {
|
||||
// Min page is too low.
|
||||
min_page = 1;
|
||||
min_offset = page_link_range - (cur_page_num - min_page);
|
||||
}
|
||||
|
||||
// Set max page.
|
||||
var max_range = page_link_range + min_offset;
|
||||
var max_page = cur_page_num + max_range;
|
||||
var max_offset;
|
||||
if (max_page <= num_pages) {
|
||||
// Max page is fine.
|
||||
max_offset = 0;
|
||||
} else {
|
||||
// Max page is too high.
|
||||
max_page = num_pages;
|
||||
// +1 to account for the +1 in the loop below.
|
||||
max_offset = max_range - (max_page + 1 - cur_page_num);
|
||||
}
|
||||
|
||||
// Second and final pass on min page to add any unused
|
||||
// offset from max to min.
|
||||
if (max_offset !== 0) {
|
||||
min_page -= max_offset;
|
||||
if (min_page < 1) {
|
||||
min_page = 1;
|
||||
}
|
||||
}
|
||||
|
||||
// template header
|
||||
tmpl += '<tr id="page-links-row">';
|
||||
if (options.show_item_checkboxes) {
|
||||
tmpl += "<td></td>";
|
||||
}
|
||||
tmpl += '<td colspan="100">' + '<span id="page-link-container">' + "Page:";
|
||||
|
||||
if (min_page > 1) {
|
||||
tmpl +=
|
||||
'<span class="page-link" id="page-link-1"><a href="javascript:void(0);" page_num="1" onclick="return false;">1</a></span> ...';
|
||||
}
|
||||
|
||||
// create page urls
|
||||
for (var page_index = min_page; page_index < max_page + 1; page_index++) {
|
||||
if (page_index == options.cur_page_num) {
|
||||
tmpl += `<span class="page-link inactive-link" id="page-link-${page_index}">${page_index}</span>`;
|
||||
} else {
|
||||
tmpl += `<span class="page-link" id="page-link-${page_index}"><a href="javascript:void(0);" onclick="return false;" page_num="${page_index}">${page_index}</a></span>`;
|
||||
}
|
||||
}
|
||||
|
||||
// show last page
|
||||
if (max_page < num_pages) {
|
||||
tmpl += `...<span class="page-link" id="page-link-${num_pages}"><a href="javascript:void(0);" onclick="return false;" page_num="${num_pages}">${num_pages}</a></span>`;
|
||||
}
|
||||
tmpl += "</span>";
|
||||
|
||||
// Show all link
|
||||
tmpl += `
|
||||
<span class="page-link" id="show-all-link-span"> | <a href="javascript:void(0);" onclick="return false;" page_num="all">Show All</a></span>
|
||||
</td>
|
||||
</tr>`;
|
||||
}
|
||||
|
||||
// Grid operations for multiple items.
|
||||
if (options.show_item_checkboxes) {
|
||||
// start template
|
||||
tmpl += `
|
||||
<tr>
|
||||
<input type="hidden" id="operation" name="operation" value="">
|
||||
<td></td>
|
||||
<td colspan="100">
|
||||
For <span class="grid-selected-count"></span> selected items:
|
||||
`;
|
||||
|
||||
// configure buttons for operations
|
||||
for (let operation of options.operations) {
|
||||
if (operation.allow_multiple) {
|
||||
tmpl += `<input type="button" value="${
|
||||
operation.label
|
||||
}" class="operation-button action-button"> `;
|
||||
}
|
||||
}
|
||||
|
||||
// finalize template
|
||||
tmpl += "</td>" + "</tr>";
|
||||
}
|
||||
|
||||
// count global operations
|
||||
var found_global = false;
|
||||
for (let operation of options.operations) {
|
||||
if (operation.global_operation) {
|
||||
found_global = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// add global operations
|
||||
if (found_global) {
|
||||
tmpl += "<tr>" + '<td colspan="100">';
|
||||
for (let operation of options.operations) {
|
||||
if (operation.global_operation) {
|
||||
tmpl += `<a class="action-button" href="${operation.global_operation}">${operation.label}</a>`;
|
||||
}
|
||||
}
|
||||
tmpl += "</td>" + "</tr>";
|
||||
}
|
||||
|
||||
// add legend
|
||||
if (options.legend) {
|
||||
tmpl += `<tr><td colspan="100">${options.legend}</td></tr>`;
|
||||
}
|
||||
|
||||
// return
|
||||
return tmpl;
|
||||
},
|
||||
|
||||
// template
|
||||
message: function(options) {
|
||||
var status = options.status;
|
||||
if (["success", "ok"].indexOf(status) != -1) {
|
||||
status = "done";
|
||||
}
|
||||
return `<p><div class="${status}message transient-message">${_.escape(
|
||||
options.message
|
||||
)}</div><div style="clear: both"></div></p>`;
|
||||
},
|
||||
|
||||
// template
|
||||
grid_filters: function(options) {
|
||||
// get filters
|
||||
var default_filter_dict = options.default_filter_dict;
|
||||
var filters = options.filters;
|
||||
|
||||
// show advanced search if flag set or if there are filters for advanced search fields
|
||||
var advanced_search_display = "none";
|
||||
if (options.advanced_search) {
|
||||
advanced_search_display = "block";
|
||||
}
|
||||
|
||||
// identify columns with advanced filtering
|
||||
var show_advanced_search_link = false;
|
||||
for (let column of options.columns) {
|
||||
if (column.filterable == "advanced") {
|
||||
var column_key = column.key;
|
||||
var f_key = filters[column_key];
|
||||
var d_key = default_filter_dict[column_key];
|
||||
if (f_key && d_key && f_key != d_key) {
|
||||
advanced_search_display = "block";
|
||||
}
|
||||
show_advanced_search_link = true;
|
||||
}
|
||||
}
|
||||
|
||||
// hide standard search if advanced is shown
|
||||
var standard_search_display = "block";
|
||||
if (advanced_search_display == "block") {
|
||||
standard_search_display = "none";
|
||||
}
|
||||
|
||||
//
|
||||
// standard search
|
||||
//
|
||||
var tmpl = `<div id="standard-search" style="display: ${standard_search_display};"><table><tr><td style="padding: 0;"><table>`;
|
||||
|
||||
// add standard filters
|
||||
for (let column of options.columns) {
|
||||
if (column.filterable == "standard") {
|
||||
tmpl += this.grid_column_filter(options, column);
|
||||
}
|
||||
}
|
||||
|
||||
// finalize standard search
|
||||
tmpl += "</table>" + "</td>" + "</tr>" + "<tr>" + "<td>";
|
||||
|
||||
// show advanced search link in standard display
|
||||
if (show_advanced_search_link) {
|
||||
tmpl += '<a href="" class="advanced-search-toggle">Advanced Search</a>';
|
||||
}
|
||||
|
||||
// finalize standard search display
|
||||
tmpl += "</td>" + "</tr>" + "</table>" + "</div>";
|
||||
|
||||
//
|
||||
// advanced search
|
||||
//
|
||||
tmpl += `<div id="advanced-search" style="display: ${advanced_search_display}; margin-top: 5px; border: 1px solid #ccc;"><table><tr><td style="text-align: left" colspan="100"><a href="" class="advanced-search-toggle">Close Advanced Search</a></td></tr>`;
|
||||
|
||||
// add advanced filters
|
||||
for (let column of options.columns) {
|
||||
if (column.filterable == "advanced") {
|
||||
tmpl += this.grid_column_filter(options, column);
|
||||
}
|
||||
}
|
||||
|
||||
// finalize advanced search template
|
||||
tmpl += "</table>" + "</div>";
|
||||
|
||||
// return template
|
||||
return tmpl;
|
||||
},
|
||||
|
||||
// template
|
||||
grid_column_filter: function(options, column) {
|
||||
// collect parameters
|
||||
var filters = options.filters;
|
||||
var column_label = column.label;
|
||||
var column_key = column.key;
|
||||
if (column.filterable == "advanced") {
|
||||
column_label = column_label.toLowerCase();
|
||||
}
|
||||
|
||||
// start
|
||||
var tmpl = "<tr>";
|
||||
|
||||
if (column.filterable == "advanced") {
|
||||
tmpl += `<td align="left" style="padding-left: 10px">${column_label}:</td>`;
|
||||
}
|
||||
tmpl += '<td style="padding-bottom: 1px;">';
|
||||
if (column.is_text) {
|
||||
tmpl += `<form class="text-filter-form" column_key="${column_key}" action="${options.url}" method="get" >`;
|
||||
// Carry forward filtering criteria with hidden inputs.
|
||||
for (let column of options.columns) {
|
||||
var filter_value = filters[column.key];
|
||||
if (filter_value) {
|
||||
if (filter_value != "All") {
|
||||
if (column.is_text) {
|
||||
filter_value = JSON.stringify(filter_value);
|
||||
}
|
||||
tmpl += `<input type="hidden" id="${column.key}" name="f-${
|
||||
column.key
|
||||
}" value="${filter_value}"/>`;
|
||||
}
|
||||
}
|
||||
}
|
||||
// Print current filtering criteria and links to delete.
|
||||
tmpl += `<span id="${column_key}-filtering-criteria">`;
|
||||
|
||||
// add filters
|
||||
var column_filter = filters[column_key];
|
||||
if (column_filter) {
|
||||
// identify type
|
||||
var type = jQuery.type(column_filter);
|
||||
|
||||
// single filter value
|
||||
if (type == "string") {
|
||||
if (column_filter != "All") {
|
||||
// append template
|
||||
tmpl += this.filter_element(column_key, column_filter);
|
||||
}
|
||||
}
|
||||
|
||||
// multiple filter values
|
||||
if (type == "array") {
|
||||
for (let i in column_filter) {
|
||||
// copy filters and remove entry
|
||||
var params = column_filter;
|
||||
params = params.slice(i);
|
||||
|
||||
// append template
|
||||
tmpl += this.filter_element(column_key, column_filter[i]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// close span
|
||||
tmpl += "</span>";
|
||||
|
||||
// Set value, size of search input field. Minimum size is 20 characters.
|
||||
var value = "";
|
||||
var size = 20;
|
||||
if (column.filterable == "standard") {
|
||||
value = column.label.toLowerCase();
|
||||
if (value.length < 20) {
|
||||
size = value.length;
|
||||
}
|
||||
// +4 to account for space after placeholder
|
||||
size = size + 4;
|
||||
}
|
||||
|
||||
// print input field for column
|
||||
tmpl += `
|
||||
<span class="search-box">
|
||||
<input class="search-box-input" id="input-${column_key}-filter" name="f-${column_key}" type="text" placeholder="${value}" size="${size}"/>
|
||||
<button type="submit" style="background: transparent; border: none; padding: 4px; margin: 0px;">
|
||||
<i class="fa fa-search"></i>
|
||||
</button>
|
||||
</span>
|
||||
</form>`;
|
||||
} else {
|
||||
// filter criteria
|
||||
tmpl += `<span id="${column_key}-filtering-criteria">`;
|
||||
|
||||
// add category filters
|
||||
var seperator = false;
|
||||
for (var cf_label in options.categorical_filters[column_key]) {
|
||||
// get category filter
|
||||
var cf = options.categorical_filters[column_key][cf_label];
|
||||
|
||||
// each filter will have only a single argument, so get that single argument
|
||||
var cf_key = "";
|
||||
var cf_arg = "";
|
||||
for (var key in cf) {
|
||||
cf_key = key;
|
||||
cf_arg = cf[key];
|
||||
}
|
||||
|
||||
// add seperator
|
||||
if (seperator) {
|
||||
tmpl += " | ";
|
||||
}
|
||||
seperator = true;
|
||||
|
||||
// add category
|
||||
var filter = filters[column_key];
|
||||
if (filter && cf[column_key] && filter == cf_arg) {
|
||||
tmpl += `<span class="categorical-filter ${column_key}-filter current-filter">${cf_label}</span>`;
|
||||
} else {
|
||||
tmpl += `<span class="categorical-filter ${column_key}-filter"><a href="javascript:void(0);" filter_key="${cf_key}" filter_val="${cf_arg}">${cf_label}</a></span>`;
|
||||
}
|
||||
}
|
||||
tmpl += "</span>";
|
||||
}
|
||||
tmpl += "</td>" + "</tr>";
|
||||
|
||||
// return template
|
||||
return tmpl;
|
||||
},
|
||||
|
||||
// template for filter items
|
||||
filter_element: function(filter_key, filter_value) {
|
||||
filter_value = Utils.sanitize(filter_value);
|
||||
return `<span class="text-filter-val">${filter_value}<a href="javascript:void(0);" filter_key="${filter_key}" filter_val="${filter_value}"><i class="fa fa-times" style="padding-left: 5px; padding-bottom: 6px;"/></a></span>`;
|
||||
}
|
||||
};
|
||||
@@ -0,0 +1,677 @@
|
||||
// This is necessary so that, when nested arrays are used in ajax/post/get methods, square brackets ('[]') are
|
||||
// not appended to the identifier of a nested array.
|
||||
jQuery.ajaxSettings.traditional = true;
|
||||
|
||||
// dependencies
|
||||
import Utils from "utils/utils";
|
||||
import GridModel from "legacy/grid/grid-model";
|
||||
import Templates from "legacy/grid/grid-template";
|
||||
import PopupMenu from "mvc/ui/popup-menu";
|
||||
// grid view
|
||||
export default Backbone.View.extend({
|
||||
// model
|
||||
grid: null,
|
||||
|
||||
// Initialize
|
||||
initialize: function(grid_config) {
|
||||
this.grid = new GridModel();
|
||||
this.dict_format = grid_config.dict_format;
|
||||
this.title = grid_config.title;
|
||||
var self = this;
|
||||
window.add_tag_to_grid_filter = (tag_name, tag_value) => {
|
||||
// Put tag name and value together.
|
||||
var tag = tag_name + (tag_value !== undefined && tag_value !== "" ? `:${tag_value}` : "");
|
||||
var advanced_search = $("#advanced-search").is(":visible");
|
||||
if (!advanced_search) {
|
||||
$("#standard-search").slideToggle("fast");
|
||||
$("#advanced-search").slideToggle("fast");
|
||||
}
|
||||
self.add_filter_condition("tags", tag);
|
||||
};
|
||||
|
||||
// set element
|
||||
if (this.dict_format) {
|
||||
this.setElement("<div/>");
|
||||
if (grid_config.url_base && !grid_config.items) {
|
||||
var url_data = grid_config.url_data || {};
|
||||
_.each(grid_config.filters, (v, k) => {
|
||||
url_data[`f-${k}`] = v;
|
||||
});
|
||||
$.ajax({
|
||||
url: `${grid_config.url_base}?${$.param(url_data)}`,
|
||||
success: function(response) {
|
||||
response.embedded = grid_config.embedded;
|
||||
response.filters = grid_config.filters || {};
|
||||
self.init_grid(response);
|
||||
}
|
||||
});
|
||||
} else {
|
||||
this.init_grid(grid_config);
|
||||
}
|
||||
} else {
|
||||
this.setElement("#grid-container");
|
||||
this.init_grid(grid_config);
|
||||
}
|
||||
|
||||
// fix padding
|
||||
if (grid_config.use_panels) {
|
||||
$("#center").css({
|
||||
padding: "10px",
|
||||
overflow: "auto"
|
||||
});
|
||||
}
|
||||
},
|
||||
|
||||
// refresh frames
|
||||
handle_refresh: function(refresh_frames) {
|
||||
if (refresh_frames) {
|
||||
if ($.inArray("history", refresh_frames) > -1) {
|
||||
if (window.top.Galaxy && window.top.Galaxy.currHistoryPanel) {
|
||||
window.top.Galaxy.currHistoryPanel.loadCurrentHistory();
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
// Initialize
|
||||
init_grid: function(grid_config) {
|
||||
this.grid.set(grid_config);
|
||||
|
||||
// get options
|
||||
var options = this.grid.attributes;
|
||||
|
||||
if (this.allow_title_display && options.title) {
|
||||
Utils.setWindowTitle(options.title);
|
||||
}
|
||||
// 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));
|
||||
var self = this;
|
||||
if (options.use_hide_message) {
|
||||
setTimeout(() => {
|
||||
self.$el.find("#grid-message").html("");
|
||||
}, 5000);
|
||||
}
|
||||
}
|
||||
|
||||
// configure elements
|
||||
this.init_grid_elements();
|
||||
this.init_grid_controls();
|
||||
|
||||
// attach global event handler
|
||||
// TODO: redundant (the onload/standard page handlers do this) - but needed because these are constructed after page ready
|
||||
if (window.init_refresh_on_change) {
|
||||
window.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);
|
||||
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();
|
||||
$(this).click(function() {
|
||||
self.set_sort_condition($(this).attr("sort_key"));
|
||||
return false;
|
||||
});
|
||||
});
|
||||
|
||||
// Initialize text filters.
|
||||
this.$el.find(".text-filter-form").each(function() {
|
||||
$(this).off();
|
||||
$(this).submit(function() {
|
||||
var column_key = $(this).attr("column_key");
|
||||
var text_input_obj = $(`#input-${column_key}-filter`);
|
||||
var text_input = text_input_obj.val();
|
||||
text_input_obj.val("");
|
||||
self.add_filter_condition(column_key, text_input);
|
||||
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"));
|
||||
|
||||
// Return
|
||||
return false;
|
||||
});
|
||||
});
|
||||
|
||||
// Initialize categorical filters.
|
||||
this.$el.find(".categorical-filter > a").each(function() {
|
||||
$(this).off();
|
||||
$(this).click(function() {
|
||||
self.set_categorical_filter($(this).attr("filter_key"), $(this).attr("filter_val"));
|
||||
return false;
|
||||
});
|
||||
});
|
||||
|
||||
// Initialize standard, advanced search toggles.
|
||||
this.$el.find(".advanced-search-toggle").each(function() {
|
||||
$(this).off();
|
||||
$(this).click(() => {
|
||||
self.$el.find("#standard-search").slideToggle("fast");
|
||||
self.$el.find("#advanced-search").slideToggle("fast");
|
||||
return false;
|
||||
});
|
||||
});
|
||||
|
||||
// Add event to check all box
|
||||
this.$el.find("#check_all").off();
|
||||
this.$el.find("#check_all").on("click", () => {
|
||||
self.check_all_items();
|
||||
});
|
||||
},
|
||||
|
||||
// Initialize grid elements.
|
||||
init_grid_elements: function() {
|
||||
// Initialize grid selection checkboxes.
|
||||
this.$el.find(".grid").each(function() {
|
||||
var checkboxes = $(this).find("input.grid-row-select-checkbox");
|
||||
var check_count = $(this).find("span.grid-selected-count");
|
||||
var update_checked = () => {
|
||||
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({});
|
||||
|
||||
// get options
|
||||
var options = this.grid.attributes;
|
||||
var self = this;
|
||||
|
||||
//
|
||||
// add page click events
|
||||
//
|
||||
this.$el.find(".page-link > a").each(function() {
|
||||
$(this).click(function() {
|
||||
self.set_page($(this).attr("page_num"));
|
||||
return false;
|
||||
});
|
||||
});
|
||||
|
||||
//
|
||||
// add inbound/outbound events
|
||||
//
|
||||
this.$el.find(".use-target").each(function() {
|
||||
$(this).click(function() {
|
||||
self.execute({
|
||||
href: $(this).attr("href"),
|
||||
target: $(this).attr("target")
|
||||
});
|
||||
return false;
|
||||
});
|
||||
});
|
||||
|
||||
// empty grid?
|
||||
var items_length = options.items.length;
|
||||
if (items_length === 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
// add operation popup menus
|
||||
_.each(options.items, (item, index) => {
|
||||
var button = self.$(`#grid-${item.encode_id}-popup`).off();
|
||||
var popup = new PopupMenu(button);
|
||||
_.each(options.operations, operation => {
|
||||
self._add_operation(popup, operation, item);
|
||||
});
|
||||
});
|
||||
},
|
||||
|
||||
/** Add an operation to the items menu */
|
||||
_add_operation: function(popup, operation, item) {
|
||||
var self = this;
|
||||
var settings = item.operation_config[operation.label];
|
||||
if (settings.allowed && operation.allow_popup) {
|
||||
popup.addItem({
|
||||
html: operation.label,
|
||||
href: settings.url_args,
|
||||
target: settings.target,
|
||||
confirmation_text: operation.confirm,
|
||||
func: function(e) {
|
||||
e.preventDefault();
|
||||
var label = $(e.target).html();
|
||||
if (operation.onclick) {
|
||||
operation.onclick(item.encode_id);
|
||||
} else {
|
||||
self.execute(this.findItemByHtml(label));
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
},
|
||||
|
||||
// Add a condition to the grid filter; this adds the condition and refreshes the grid.
|
||||
add_filter_condition: function(name, value) {
|
||||
// Do nothing is value is empty.
|
||||
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;
|
||||
t.click(function() {
|
||||
// Remove visible element.
|
||||
$(this).remove();
|
||||
|
||||
// Remove filter condition.
|
||||
self.remove_filter_condition(name, value);
|
||||
});
|
||||
|
||||
// append to container
|
||||
var container = this.$el.find(`#${name}-filtering-criteria`);
|
||||
container.append(t);
|
||||
|
||||
// execute
|
||||
this.go_page_one();
|
||||
this.execute();
|
||||
},
|
||||
|
||||
// Remove a condition to the grid filter; this adds the condition and refreshes the grid.
|
||||
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
|
||||
// currently sorted column, sort is reversed.
|
||||
var cur_sort = this.grid.get("sort_key");
|
||||
var new_sort = col_key;
|
||||
if (cur_sort.indexOf(col_key) !== -1) {
|
||||
// Reverse sort.
|
||||
if (cur_sort.substring(0, 1) !== "-") {
|
||||
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();
|
||||
this.execute();
|
||||
},
|
||||
|
||||
// Set new value for categorical filter.
|
||||
set_categorical_filter: function(name, new_value) {
|
||||
// Update filter hyperlinks to reflect new filter value.
|
||||
var category_filter = this.grid.get("categorical_filters")[name];
|
||||
|
||||
var cur_value = this.grid.get("filters")[name];
|
||||
var self = this;
|
||||
this.$el.find(`.${name}-filter`).each(function() {
|
||||
var text = $.trim($(this).text());
|
||||
var filter = category_filter[text];
|
||||
var filter_value = filter[name];
|
||||
if (filter_value == new_value) {
|
||||
// Remove filter link since grid will be using this filter. It is assumed that
|
||||
// this element has a single child, a hyperlink/anchor with text.
|
||||
$(this).empty();
|
||||
$(this).addClass("current-filter");
|
||||
$(this).append(text);
|
||||
} else if (filter_value == cur_value) {
|
||||
// Add hyperlink for this filter since grid will no longer be using this filter. It is assumed that
|
||||
// this element has a single child, a hyperlink/anchor.
|
||||
$(this).empty();
|
||||
var t = $(`<a href="#">${text}</a>`);
|
||||
t.click(() => {
|
||||
self.set_categorical_filter(name, filter_value);
|
||||
});
|
||||
$(this).removeClass("current-filter");
|
||||
$(this).append(t);
|
||||
}
|
||||
});
|
||||
|
||||
// Update grid.
|
||||
this.grid.add_filter(name, new_value);
|
||||
this.go_page_one();
|
||||
this.execute();
|
||||
},
|
||||
|
||||
// Set page to view.
|
||||
set_page: function(new_page) {
|
||||
// Update page hyperlink to reflect new page.
|
||||
var self = this;
|
||||
this.$el.find(".page-link").each(function() {
|
||||
var id = $(this).attr("id");
|
||||
|
||||
var // Id has form 'page-link-<page_num>
|
||||
page_num = parseInt(id.split("-")[2], 10);
|
||||
|
||||
var cur_page = self.grid.get("cur_page");
|
||||
var text;
|
||||
if (page_num === new_page) {
|
||||
// Remove link to page since grid will be on this page. It is assumed that
|
||||
// this element has a single child, a hyperlink/anchor with text.
|
||||
text = $(this)
|
||||
.children()
|
||||
.text();
|
||||
$(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.
|
||||
text = $(this).text();
|
||||
$(this).empty();
|
||||
$(this).removeClass("inactive-link");
|
||||
var t = $(`<a href="#">${text}</a>`);
|
||||
t.click(() => {
|
||||
self.set_page(page_num);
|
||||
});
|
||||
$(this).append(t);
|
||||
}
|
||||
});
|
||||
|
||||
if (new_page === "all") {
|
||||
this.grid.set("cur_page", new_page);
|
||||
} else {
|
||||
this.grid.set("cur_page", parseInt(new_page, 10));
|
||||
}
|
||||
this.execute();
|
||||
},
|
||||
|
||||
// confirmation/submission of operation request
|
||||
submit_operation: function(operation_button, confirmation_text) {
|
||||
// identify operation
|
||||
var operation_name = $(operation_button).val();
|
||||
|
||||
// verify any item is selected
|
||||
var number_of_checked_ids = this.$el.find('input[name="id"]:checked').length;
|
||||
if (number_of_checked_ids < 1) {
|
||||
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
|
||||
var options = {
|
||||
operation: operation_name,
|
||||
id: item_ids,
|
||||
confirmation_text: confirmation_text
|
||||
};
|
||||
if (operation.target == "top" || operation.target == "center") {
|
||||
options = _.extend(options, {
|
||||
href: operation.href,
|
||||
target: operation.target
|
||||
});
|
||||
}
|
||||
this.execute(options);
|
||||
return true;
|
||||
},
|
||||
|
||||
check_all_items: function() {
|
||||
var check = this.$(".grid-row-select-checkbox");
|
||||
var state = this.$("#check_all").prop("checked");
|
||||
_.each(check, c => {
|
||||
$(c).prop("checked", state);
|
||||
});
|
||||
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);
|
||||
}
|
||||
},
|
||||
|
||||
//
|
||||
// execute operations and hyperlink requests
|
||||
//
|
||||
execute: function(options) {
|
||||
// get url
|
||||
var id = null;
|
||||
var href = null;
|
||||
var operation = null;
|
||||
var confirmation_text = null;
|
||||
var target = null;
|
||||
|
||||
// check for options
|
||||
if (options) {
|
||||
// get options
|
||||
href = options.href;
|
||||
operation = options.operation;
|
||||
id = options.id;
|
||||
confirmation_text = options.confirmation_text;
|
||||
target = options.target;
|
||||
|
||||
// check if input contains the operation tag
|
||||
if (href !== undefined && href.indexOf("operation=") != -1) {
|
||||
// Get operation, id in hyperlink's href.
|
||||
var href_parts = href.split("?");
|
||||
if (href_parts.length > 1) {
|
||||
var href_parms_str = href_parts[1];
|
||||
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.
|
||||
operation = href_parms[index].split("=")[1];
|
||||
operation = operation.replace(/\+/g, " ");
|
||||
} else if (href_parms[index].indexOf("id") != -1) {
|
||||
// Found id parm; get id value.
|
||||
id = href_parms[index].split("=")[1];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// check for operation details
|
||||
if (operation && id) {
|
||||
// show confirmation box
|
||||
if (
|
||||
confirmation_text &&
|
||||
confirmation_text !== "" &&
|
||||
confirmation_text != "None" &&
|
||||
confirmation_text != "null"
|
||||
)
|
||||
if (!confirm(confirmation_text)) return false;
|
||||
|
||||
// use small characters for operation?!
|
||||
operation = operation.toLowerCase();
|
||||
|
||||
// Update grid.
|
||||
this.grid.set({
|
||||
operation: operation,
|
||||
item_ids: id
|
||||
});
|
||||
|
||||
// Do operation. If operation cannot be performed asynchronously, redirect to location.
|
||||
if (target == "top") {
|
||||
window.top.location = `${href}?${$.param(this.grid.get_url_data())}`;
|
||||
} else if (target == "center") {
|
||||
$("#galaxy_main").attr("src", `${href}?${$.param(this.grid.get_url_data())}`);
|
||||
} else if (this.grid.can_async_op(operation) || this.dict_format) {
|
||||
this.update_grid();
|
||||
} else {
|
||||
this.go_to(target, href);
|
||||
}
|
||||
|
||||
// done
|
||||
return false;
|
||||
}
|
||||
|
||||
// refresh grid
|
||||
if (href) {
|
||||
this.go_to(target, href);
|
||||
return false;
|
||||
}
|
||||
|
||||
// refresh grid
|
||||
if (this.grid.get("async") || this.dict_format) {
|
||||
this.update_grid();
|
||||
} else {
|
||||
this.go_to(target, href);
|
||||
}
|
||||
|
||||
// done
|
||||
return false;
|
||||
},
|
||||
|
||||
// go to url
|
||||
go_to: function(target, href) {
|
||||
// get aysnc status
|
||||
var async = this.grid.get("async");
|
||||
this.grid.set("async", false);
|
||||
|
||||
// get slide status
|
||||
var 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
|
||||
});
|
||||
switch (target) {
|
||||
case "center":
|
||||
$("#galaxy_main").attr("src", href);
|
||||
break;
|
||||
case "top":
|
||||
window.top.location = href;
|
||||
break;
|
||||
default:
|
||||
window.location = href;
|
||||
}
|
||||
},
|
||||
|
||||
// Update grid.
|
||||
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;
|
||||
$.ajax({
|
||||
type: method,
|
||||
url: self.grid.get("url_base"),
|
||||
data: self.grid.get_url_data(),
|
||||
error: function() {
|
||||
alert("Grid refresh failed");
|
||||
},
|
||||
success: function(response_text) {
|
||||
// backup
|
||||
var embedded = self.grid.get("embedded");
|
||||
var insert = self.grid.get("insert");
|
||||
var advanced_search = self.$el.find("#advanced-search").is(":visible");
|
||||
|
||||
// request new configuration
|
||||
var json = self.dict_format ? response_text : $.parseJSON(response_text);
|
||||
|
||||
// update
|
||||
json.embedded = embedded;
|
||||
json.insert = insert;
|
||||
json.advanced_search = advanced_search;
|
||||
|
||||
// Initialize new grid config
|
||||
self.init_grid(json);
|
||||
|
||||
// Hide loading overlay.
|
||||
self.$el.find(".loading-elt-overlay").hide();
|
||||
},
|
||||
complete: function() {
|
||||
// Clear grid of transient request attributes.
|
||||
self.grid.set({
|
||||
operation: undefined,
|
||||
item_ids: undefined
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
@@ -4754,3 +4754,5 @@ WYMeditor.WymClassSafari.prototype.getTagForStyle = function(style) {
|
||||
if(/super/.test(style)) return 'sup';
|
||||
return false;
|
||||
};
|
||||
|
||||
window.WYMeditor = WYMeditor;
|
||||
|
||||
@@ -278,8 +278,8 @@ var ListCollectionCreator = Backbone.View.extend(BASE_MVC.LoggableMixin)
|
||||
|
||||
/** describe what is wrong with a particular element if anything */
|
||||
_isElementInvalid: function(element) {
|
||||
if (element.history_content_type !== "dataset") {
|
||||
return _l("is not a dataset");
|
||||
if (element.history_content_type === "dataset_collection") {
|
||||
return _l("is a collection, this is not allowed");
|
||||
}
|
||||
var validState = element.state === STATES.OK || _.contains(STATES.NOT_READY_STATES, element.state);
|
||||
if (!validState) {
|
||||
|
||||
@@ -1796,7 +1796,8 @@ var pairedCollectionCreatorModal = function _pairedCollectionCreatorModal(datase
|
||||
oncreate: function(creator, response) {
|
||||
Galaxy.modal.hide();
|
||||
deferred.resolve(response);
|
||||
}
|
||||
},
|
||||
title: _l("Create a collection of paired datasets")
|
||||
});
|
||||
|
||||
if (!window.Galaxy || !Galaxy.modal) {
|
||||
@@ -1805,7 +1806,7 @@ var pairedCollectionCreatorModal = function _pairedCollectionCreatorModal(datase
|
||||
|
||||
creator = new PairedCollectionCreator(options);
|
||||
Galaxy.modal.show({
|
||||
title: _l("Create a collection of paired datasets"),
|
||||
title: options.title,
|
||||
body: creator.$el,
|
||||
width: "80%",
|
||||
height: "800px",
|
||||
|
||||
@@ -1,8 +1,6 @@
|
||||
import _l from "utils/localization";
|
||||
// Additional dependencies: jQuery, underscore.
|
||||
import Modal from "mvc/ui/ui-modal";
|
||||
import Frames from "mvc/ui/ui-frames";
|
||||
import mod_icon_btn from "mvc/ui/icon-button";
|
||||
// Additional dependencies: jQuery, underscore.
|
||||
/**
|
||||
* Dataset metedata.
|
||||
*/
|
||||
|
||||
@@ -286,47 +286,39 @@ var DatasetListItemView = _super.extend(
|
||||
return this._renderMetaFileDownloadButton();
|
||||
}
|
||||
|
||||
return $(
|
||||
[
|
||||
'<a class="download-btn icon-btn" ',
|
||||
'href="',
|
||||
this.model.urls.download,
|
||||
`" title="${_l("Download")}" download>`,
|
||||
'<span class="fa fa-floppy-o"></span>',
|
||||
"</a>"
|
||||
].join("")
|
||||
);
|
||||
return $(`
|
||||
<a class="download-btn icon-btn" href="${this.model.urls.download}" title="${_l("Download")}">
|
||||
<span class="fa fa-floppy-o"></span>
|
||||
</a>`);
|
||||
},
|
||||
|
||||
/** Render the download button which opens a dropdown with links to download assoc. meta files (indeces, etc.) */
|
||||
/** Render the download button which opens a dropdown with links to download assoc. meta files (indices, etc.) */
|
||||
_renderMetaFileDownloadButton: function() {
|
||||
var urls = this.model.urls;
|
||||
return $(
|
||||
[
|
||||
'<div class="metafile-dropdown dropdown">',
|
||||
'<a class="download-btn icon-btn" href="javascript:void(0)" data-toggle="dropdown"',
|
||||
` title="${_l("Download")}">`,
|
||||
'<span class="fa fa-floppy-o"></span>',
|
||||
"</a>",
|
||||
'<ul class="dropdown-menu" role="menu" aria-labelledby="dLabel">',
|
||||
`<li><a href="${urls.download}" download>`,
|
||||
_l("Download dataset"),
|
||||
"</a></li>",
|
||||
_.map(this.model.get("meta_files"), meta_file =>
|
||||
[
|
||||
'<li><a href="',
|
||||
urls.meta_download + meta_file.file_type,
|
||||
'">',
|
||||
_l("Download"),
|
||||
" ",
|
||||
meta_file.file_type,
|
||||
"</a></li>"
|
||||
].join("")
|
||||
).join("\n"),
|
||||
"</ul>",
|
||||
"</div>"
|
||||
].join("\n")
|
||||
);
|
||||
return $(`
|
||||
<div class="metafile-dropdown dropdown">
|
||||
<a class="download-btn icon-btn" href="${urls.download}" data-toggle="dropdown" title="${_l(
|
||||
"Download"
|
||||
)}">
|
||||
<span class="fa fa-floppy-o"></span>
|
||||
</a>
|
||||
<ul class="dropdown-menu" role="menu" aria-labelledby="dLabel">
|
||||
<li>
|
||||
<a href="${urls.download}">
|
||||
${_l("Download dataset")}
|
||||
</a>
|
||||
</li>
|
||||
${_.map(
|
||||
this.model.get("meta_files"),
|
||||
meta_file =>
|
||||
`<li>
|
||||
<a href="${urls.meta_download + meta_file.file_type}">
|
||||
${_l("Download")} ${meta_file.file_type}
|
||||
</a>
|
||||
</li>`
|
||||
)}
|
||||
</ul>
|
||||
</div>`);
|
||||
},
|
||||
|
||||
_renderNametags: function() {
|
||||
|
||||
@@ -17,13 +17,6 @@ export default Backbone.Model.extend({
|
||||
item_ids: undefined
|
||||
},
|
||||
|
||||
/**
|
||||
* Return true if operation can be done asynchronously.
|
||||
*/
|
||||
can_async_op: function(op) {
|
||||
return _.indexOf(this.attributes.async_ops, op) !== -1;
|
||||
},
|
||||
|
||||
/**
|
||||
* Add filtering criterion.
|
||||
*/
|
||||
|
||||
@@ -10,7 +10,6 @@ var View = Backbone.View.extend({
|
||||
$.ajax({
|
||||
url: `${Galaxy.root + this.item}/${this.model.get("action_id")}?${$.param(Galaxy.params)}`,
|
||||
success: function(response) {
|
||||
response["dict_format"] = true;
|
||||
self.model.set(response);
|
||||
self.render();
|
||||
}
|
||||
|
||||
@@ -25,16 +25,15 @@ export default {
|
||||
},
|
||||
|
||||
// template
|
||||
grid_table: function(options) {
|
||||
return (
|
||||
'<form method="post" onsubmit="return false;">' +
|
||||
'<table id="grid-table" class="grid">' +
|
||||
'<thead id="grid-table-header"></thead>' +
|
||||
'<tbody id="grid-table-body"></tbody>' +
|
||||
'<tfoot id="grid-table-footer"></tfoot>' +
|
||||
"</table>" +
|
||||
"</form>"
|
||||
);
|
||||
grid_table: function() {
|
||||
return `
|
||||
<form method="post" onsubmit="return false;">
|
||||
<table id="grid-table" class="grid">
|
||||
<thead id="grid-table-header"></thead>
|
||||
<tbody id="grid-table-body"></tbody>
|
||||
<tfoot id="grid-table-footer"></tfoot>
|
||||
</table>
|
||||
</form>`;
|
||||
},
|
||||
|
||||
// template
|
||||
@@ -51,8 +50,7 @@ export default {
|
||||
'<li><a class="action-button" id="popup-global-actions" class="menubutton">Actions</a></li>' +
|
||||
'<div popupmenu="popup-global-actions">';
|
||||
}
|
||||
for (var i in options.global_actions) {
|
||||
var action = options.global_actions[i];
|
||||
for (let action of options.global_actions) {
|
||||
tmpl += `<li><a class="action-button use-target" target="${action.target}" href="${
|
||||
action.url_args
|
||||
}" onclick="return false;" >${action.label}</a></li>`;
|
||||
@@ -91,8 +89,7 @@ export default {
|
||||
}
|
||||
|
||||
// create header elements
|
||||
for (var i in options.columns) {
|
||||
var column = options.columns[i];
|
||||
for (let column of options.columns) {
|
||||
if (column.visible) {
|
||||
tmpl += `<th id="${column.key}-header">`;
|
||||
if (column.href) {
|
||||
@@ -115,23 +112,16 @@ export default {
|
||||
body: function(options) {
|
||||
// initialize
|
||||
var tmpl = "";
|
||||
var num_rows_rendered = 0;
|
||||
var items_length = options.items.length;
|
||||
|
||||
// empty grid?
|
||||
if (items_length == 0) {
|
||||
if (items_length === 0) {
|
||||
// No results.
|
||||
tmpl += '<tr><td colspan="100"><em>No Items</em></td></tr>';
|
||||
num_rows_rendered = 1;
|
||||
}
|
||||
|
||||
// create rows
|
||||
for (var i in options.items) {
|
||||
// encode ids
|
||||
var item = options.items[i];
|
||||
var encoded_id = item.encode_id;
|
||||
var popupmenu_id = `grid-${i}-popup`;
|
||||
|
||||
for (let item of options.items) {
|
||||
// Tag current
|
||||
tmpl += "<tr ";
|
||||
if (options.current_item_id == item.id) {
|
||||
@@ -141,14 +131,13 @@ export default {
|
||||
|
||||
// Item selection column
|
||||
if (options.show_item_checkboxes) {
|
||||
tmpl += `<td style="width: 1.5em;"><input type="checkbox" name="id" value="${encoded_id}" id="${
|
||||
encoded_id
|
||||
tmpl += `<td style="width: 1.5em;"><input type="checkbox" name="id" value="${item.encode_id}" id="${
|
||||
item.encode_id
|
||||
}" class="grid-row-select-checkbox" /></td>`;
|
||||
}
|
||||
|
||||
// Data columns
|
||||
for (var j in options.columns) {
|
||||
var column = options.columns[j];
|
||||
for (let column of options.columns) {
|
||||
if (column.visible) {
|
||||
// Nowrap
|
||||
var nowrap = "";
|
||||
@@ -173,9 +162,9 @@ export default {
|
||||
var id = "";
|
||||
var cls = "";
|
||||
if (column.attach_popup) {
|
||||
id = `grid-${i}-popup`;
|
||||
id = `grid-${item.encode_id}-popup`;
|
||||
cls = "menubutton";
|
||||
if (link != "") {
|
||||
if (link !== "") {
|
||||
cls += " split";
|
||||
}
|
||||
cls += " popup";
|
||||
@@ -186,25 +175,22 @@ export default {
|
||||
|
||||
// Link
|
||||
if (link) {
|
||||
if (options.operations.length != 0) {
|
||||
if (options.operations.length !== 0) {
|
||||
tmpl += `<div id="${id}" class="${cls}" style="float: left;">`;
|
||||
}
|
||||
tmpl += `<a class="menubutton-label use-target" target="${target}" href="${
|
||||
link
|
||||
}" onclick="return false;">${value}</a>`;
|
||||
if (options.operations.length != 0) {
|
||||
tmpl += `<a class="menubutton-label use-target" target="${target}" href="${link}" onclick="return false;">${value}</a>`;
|
||||
if (options.operations.length !== 0) {
|
||||
tmpl += "</div>";
|
||||
}
|
||||
} else {
|
||||
tmpl += `<div id="${id}" class="${cls}"><label id="${column.label_id_prefix}${
|
||||
encoded_id
|
||||
}" for="${encoded_id}">${value || ""}</label></div>`;
|
||||
item.encode_id
|
||||
}" for="${item.encode_id}">${value || ""}</label></div>`;
|
||||
}
|
||||
tmpl += "</td>";
|
||||
}
|
||||
}
|
||||
tmpl += "</tr>";
|
||||
num_rows_rendered++;
|
||||
}
|
||||
return tmpl;
|
||||
},
|
||||
@@ -247,7 +233,7 @@ export default {
|
||||
|
||||
// Second and final pass on min page to add any unused
|
||||
// offset from max to min.
|
||||
if (max_offset != 0) {
|
||||
if (max_offset !== 0) {
|
||||
min_page -= max_offset;
|
||||
if (min_page < 1) {
|
||||
min_page = 1;
|
||||
@@ -271,44 +257,36 @@ export default {
|
||||
if (page_index == options.cur_page_num) {
|
||||
tmpl += `<span class="page-link inactive-link" id="page-link-${page_index}">${page_index}</span>`;
|
||||
} else {
|
||||
tmpl += `<span class="page-link" id="page-link-${
|
||||
page_index
|
||||
}"><a href="javascript:void(0);" onclick="return false;" page_num="${page_index}">${
|
||||
page_index
|
||||
}</a></span>`;
|
||||
tmpl += `<span class="page-link" id="page-link-${page_index}"><a href="javascript:void(0);" onclick="return false;" page_num="${page_index}">${page_index}</a></span>`;
|
||||
}
|
||||
}
|
||||
|
||||
// show last page
|
||||
if (max_page < num_pages) {
|
||||
tmpl += `...<span class="page-link" id="page-link-${
|
||||
num_pages
|
||||
}"><a href="javascript:void(0);" onclick="return false;" page_num="${num_pages}">${
|
||||
num_pages
|
||||
}</a></span>`;
|
||||
tmpl += `...<span class="page-link" id="page-link-${num_pages}"><a href="javascript:void(0);" onclick="return false;" page_num="${num_pages}">${num_pages}</a></span>`;
|
||||
}
|
||||
tmpl += "</span>";
|
||||
|
||||
// Show all link
|
||||
tmpl +=
|
||||
'<span class="page-link" id="show-all-link-span"> | <a href="javascript:void(0);" onclick="return false;" page_num="all">Show All</a></span>' +
|
||||
"</td>" +
|
||||
"</tr>";
|
||||
tmpl += `
|
||||
<span class="page-link" id="show-all-link-span"> | <a href="javascript:void(0);" onclick="return false;" page_num="all">Show All</a></span>
|
||||
</td>
|
||||
</tr>`;
|
||||
}
|
||||
|
||||
// Grid operations for multiple items.
|
||||
if (options.show_item_checkboxes) {
|
||||
// start template
|
||||
tmpl +=
|
||||
"<tr>" +
|
||||
'<input type="hidden" id="operation" name="operation" value="">' +
|
||||
"<td></td>" +
|
||||
'<td colspan="100">' +
|
||||
'For <span class="grid-selected-count"></span> selected items: ';
|
||||
tmpl += `
|
||||
<tr>
|
||||
<input type="hidden" id="operation" name="operation" value="">
|
||||
<td></td>
|
||||
<td colspan="100">
|
||||
For <span class="grid-selected-count"></span> selected items:
|
||||
`;
|
||||
|
||||
// configure buttons for operations
|
||||
for (var i in options.operations) {
|
||||
var operation = options.operations[i];
|
||||
for (let operation of options.operations) {
|
||||
if (operation.allow_multiple) {
|
||||
tmpl += `<input type="button" value="${
|
||||
operation.label
|
||||
@@ -322,8 +300,8 @@ export default {
|
||||
|
||||
// count global operations
|
||||
var found_global = false;
|
||||
for (i in options.operations) {
|
||||
if (options.operations[i].global_operation) {
|
||||
for (let operation of options.operations) {
|
||||
if (operation.global_operation) {
|
||||
found_global = true;
|
||||
break;
|
||||
}
|
||||
@@ -332,8 +310,7 @@ export default {
|
||||
// add global operations
|
||||
if (found_global) {
|
||||
tmpl += "<tr>" + '<td colspan="100">';
|
||||
for (i in options.operations) {
|
||||
var operation = options.operations[i];
|
||||
for (let operation of options.operations) {
|
||||
if (operation.global_operation) {
|
||||
tmpl += `<a class="action-button" href="${operation.global_operation}">${operation.label}</a>`;
|
||||
}
|
||||
@@ -375,8 +352,7 @@ export default {
|
||||
|
||||
// identify columns with advanced filtering
|
||||
var show_advanced_search_link = false;
|
||||
for (var i in options.columns) {
|
||||
var column = options.columns[i];
|
||||
for (let column of options.columns) {
|
||||
if (column.filterable == "advanced") {
|
||||
var column_key = column.key;
|
||||
var f_key = filters[column_key];
|
||||
@@ -397,13 +373,10 @@ export default {
|
||||
//
|
||||
// standard search
|
||||
//
|
||||
var tmpl = `<div id="standard-search" style="display: ${
|
||||
standard_search_display
|
||||
};"><table><tr><td style="padding: 0;"><table>`;
|
||||
var tmpl = `<div id="standard-search" style="display: ${standard_search_display};"><table><tr><td style="padding: 0;"><table>`;
|
||||
|
||||
// add standard filters
|
||||
for (var i in options.columns) {
|
||||
var column = options.columns[i];
|
||||
for (let column of options.columns) {
|
||||
if (column.filterable == "standard") {
|
||||
tmpl += this.grid_column_filter(options, column);
|
||||
}
|
||||
@@ -423,13 +396,10 @@ export default {
|
||||
//
|
||||
// advanced search
|
||||
//
|
||||
tmpl += `<div id="advanced-search" style="display: ${
|
||||
advanced_search_display
|
||||
}; margin-top: 5px; border: 1px solid #ccc;"><table><tr><td style="text-align: left" colspan="100"><a href="" class="advanced-search-toggle">Close Advanced Search</a></td></tr>`;
|
||||
tmpl += `<div id="advanced-search" style="display: ${advanced_search_display}; margin-top: 5px; border: 1px solid #ccc;"><table><tr><td style="text-align: left" colspan="100"><a href="" class="advanced-search-toggle">Close Advanced Search</a></td></tr>`;
|
||||
|
||||
// add advanced filters
|
||||
for (var i in options.columns) {
|
||||
var column = options.columns[i];
|
||||
for (let column of options.columns) {
|
||||
if (column.filterable == "advanced") {
|
||||
tmpl += this.grid_column_filter(options, column);
|
||||
}
|
||||
@@ -445,7 +415,6 @@ export default {
|
||||
// template
|
||||
grid_column_filter: function(options, column) {
|
||||
// collect parameters
|
||||
var default_filter_dict = options.default_filter_dict;
|
||||
var filters = options.filters;
|
||||
var column_label = column.label;
|
||||
var column_key = column.key;
|
||||
@@ -463,17 +432,16 @@ export default {
|
||||
if (column.is_text) {
|
||||
tmpl += `<form class="text-filter-form" column_key="${column_key}" action="${options.url}" method="get" >`;
|
||||
// Carry forward filtering criteria with hidden inputs.
|
||||
for (i in options.columns) {
|
||||
var temp_column = options.columns[i];
|
||||
var filter_value = filters[temp_column.key];
|
||||
for (let column of options.columns) {
|
||||
var filter_value = filters[column.key];
|
||||
if (filter_value) {
|
||||
if (filter_value != "All") {
|
||||
if (temp_column.is_text) {
|
||||
if (column.is_text) {
|
||||
filter_value = JSON.stringify(filter_value);
|
||||
}
|
||||
tmpl += `<input type="hidden" id="${temp_column.key}" name="f-${temp_column.key}" value="${
|
||||
filter_value
|
||||
}"/>`;
|
||||
tmpl += `<input type="hidden" id="${column.key}" name="f-${
|
||||
column.key
|
||||
}" value="${filter_value}"/>`;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -496,16 +464,13 @@ export default {
|
||||
|
||||
// multiple filter values
|
||||
if (type == "array") {
|
||||
for (var i in column_filter) {
|
||||
// get filter
|
||||
var filter = column_filter[i];
|
||||
|
||||
for (let i in column_filter) {
|
||||
// copy filters and remove entry
|
||||
var params = column_filter;
|
||||
params = params.slice(i);
|
||||
|
||||
// append template
|
||||
tmpl += this.filter_element(column_key, filter);
|
||||
tmpl += this.filter_element(column_key, column_filter[i]);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -515,22 +480,25 @@ export default {
|
||||
|
||||
// Set value, size of search input field. Minimum size is 20 characters.
|
||||
var value = "";
|
||||
var size = 20;
|
||||
if (column.filterable == "standard") {
|
||||
value = column.label.toLowerCase();
|
||||
var size = value.length;
|
||||
if (size < 20) {
|
||||
size = 20;
|
||||
if (value.length < 20) {
|
||||
size = value.length;
|
||||
}
|
||||
// +4 to account for space after placeholder
|
||||
size = size + 4;
|
||||
}
|
||||
|
||||
// print input field for column
|
||||
tmpl += `<span class="search-box"><input class="search-box-input" id="input-${column_key}-filter" name="f-${
|
||||
column_key
|
||||
}" type="text" placeholder="${value}" size="${
|
||||
size
|
||||
}"/><button type="submit" style="background: transparent; border: none; padding: 4px; margin: 0px;"><i class="fa fa-search"></i></button></span></form>`;
|
||||
tmpl += `
|
||||
<span class="search-box">
|
||||
<input class="search-box-input" id="input-${column_key}-filter" name="f-${column_key}" type="text" placeholder="${value}" size="${size}"/>
|
||||
<button type="submit" style="background: transparent; border: none; padding: 4px; margin: 0px;">
|
||||
<i class="fa fa-search"></i>
|
||||
</button>
|
||||
</span>
|
||||
</form>`;
|
||||
} else {
|
||||
// filter criteria
|
||||
tmpl += `<span id="${column_key}-filtering-criteria">`;
|
||||
@@ -560,11 +528,7 @@ export default {
|
||||
if (filter && cf[column_key] && filter == cf_arg) {
|
||||
tmpl += `<span class="categorical-filter ${column_key}-filter current-filter">${cf_label}</span>`;
|
||||
} else {
|
||||
tmpl += `<span class="categorical-filter ${
|
||||
column_key
|
||||
}-filter"><a href="javascript:void(0);" filter_key="${cf_key}" filter_val="${cf_arg}">${
|
||||
cf_label
|
||||
}</a></span>`;
|
||||
tmpl += `<span class="categorical-filter ${column_key}-filter"><a href="javascript:void(0);" filter_key="${cf_key}" filter_val="${cf_arg}">${cf_label}</a></span>`;
|
||||
}
|
||||
}
|
||||
tmpl += "</span>";
|
||||
@@ -578,10 +542,6 @@ export default {
|
||||
// template for filter items
|
||||
filter_element: function(filter_key, filter_value) {
|
||||
filter_value = Utils.sanitize(filter_value);
|
||||
return `<span class="text-filter-val">${filter_value}<a href="javascript:void(0);" filter_key="${
|
||||
filter_key
|
||||
}" filter_val="${
|
||||
filter_value
|
||||
}"><i class="fa fa-times" style="padding-left: 5px; padding-bottom: 6px;"/></a></span>`;
|
||||
return `<span class="text-filter-val">${filter_value}<a href="javascript:void(0);" filter_key="${filter_key}" filter_val="${filter_value}"><i class="fa fa-times" style="padding-left: 5px; padding-bottom: 6px;"/></a></span>`;
|
||||
}
|
||||
};
|
||||
|
||||
@@ -15,7 +15,6 @@ export default Backbone.View.extend({
|
||||
// Initialize
|
||||
initialize: function(grid_config) {
|
||||
this.grid = new GridModel();
|
||||
this.dict_format = grid_config.dict_format;
|
||||
this.title = grid_config.title;
|
||||
var self = this;
|
||||
window.add_tag_to_grid_filter = (tag_name, tag_value) => {
|
||||
@@ -30,26 +29,21 @@ export default Backbone.View.extend({
|
||||
};
|
||||
|
||||
// set element
|
||||
if (this.dict_format) {
|
||||
this.setElement("<div/>");
|
||||
if (grid_config.url_base && !grid_config.items) {
|
||||
var url_data = grid_config.url_data || {};
|
||||
_.each(grid_config.filters, (v, k) => {
|
||||
url_data[`f-${k}`] = v;
|
||||
});
|
||||
$.ajax({
|
||||
url: `${grid_config.url_base}?${$.param(url_data)}`,
|
||||
success: function(response) {
|
||||
response.embedded = grid_config.embedded;
|
||||
response.filters = grid_config.filters || {};
|
||||
self.init_grid(response);
|
||||
}
|
||||
});
|
||||
} else {
|
||||
this.init_grid(grid_config);
|
||||
}
|
||||
this.setElement("<div/>");
|
||||
if (grid_config.url_base && !grid_config.items) {
|
||||
var url_data = grid_config.url_data || {};
|
||||
_.each(grid_config.filters, (v, k) => {
|
||||
url_data[`f-${k}`] = v;
|
||||
});
|
||||
$.ajax({
|
||||
url: `${grid_config.url_base}?${$.param(url_data)}`,
|
||||
success: function(response) {
|
||||
response.embedded = grid_config.embedded;
|
||||
response.filters = grid_config.filters || {};
|
||||
self.init_grid(response);
|
||||
}
|
||||
});
|
||||
} else {
|
||||
this.setElement("#grid-container");
|
||||
this.init_grid(grid_config);
|
||||
}
|
||||
|
||||
@@ -116,7 +110,9 @@ export default Backbone.View.extend({
|
||||
|
||||
// attach global event handler
|
||||
// TODO: redundant (the onload/standard page handlers do this) - but needed because these are constructed after page ready
|
||||
init_refresh_on_change();
|
||||
if (window.init_refresh_on_change) {
|
||||
window.init_refresh_on_change();
|
||||
}
|
||||
},
|
||||
|
||||
// Initialize grid controls
|
||||
@@ -247,7 +243,7 @@ export default Backbone.View.extend({
|
||||
// add inbound/outbound events
|
||||
//
|
||||
this.$el.find(".use-target").each(function() {
|
||||
$(this).click(function(e) {
|
||||
$(this).click(function() {
|
||||
self.execute({
|
||||
href: $(this).attr("href"),
|
||||
target: $(this).attr("target")
|
||||
@@ -258,15 +254,15 @@ export default Backbone.View.extend({
|
||||
|
||||
// empty grid?
|
||||
var items_length = options.items.length;
|
||||
if (items_length == 0) {
|
||||
if (items_length === 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
// add operation popup menus
|
||||
_.each(options.items, (item, index) => {
|
||||
var button = self.$(`#grid-${index}-popup`).off();
|
||||
var button = self.$(`#grid-${item.encode_id}-popup`).off();
|
||||
var popup = new PopupMenu(button);
|
||||
_.each(options["operations"], operation => {
|
||||
_.each(options.operations, operation => {
|
||||
self._add_operation(popup, operation, item);
|
||||
});
|
||||
});
|
||||
@@ -448,9 +444,9 @@ export default Backbone.View.extend({
|
||||
// identify operation
|
||||
var operation_name = $(operation_button).val();
|
||||
|
||||
// verify in any item is selected
|
||||
// verify any item is selected
|
||||
var number_of_checked_ids = this.$el.find('input[name="id"]:checked').length;
|
||||
if (!number_of_checked_ids > 0) {
|
||||
if (number_of_checked_ids < 1) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -548,7 +544,7 @@ export default Backbone.View.extend({
|
||||
// show confirmation box
|
||||
if (
|
||||
confirmation_text &&
|
||||
confirmation_text != "" &&
|
||||
confirmation_text !== "" &&
|
||||
confirmation_text != "None" &&
|
||||
confirmation_text != "null"
|
||||
)
|
||||
@@ -568,10 +564,8 @@ export default Backbone.View.extend({
|
||||
window.top.location = `${href}?${$.param(this.grid.get_url_data())}`;
|
||||
} else if (target == "center") {
|
||||
$("#galaxy_main").attr("src", `${href}?${$.param(this.grid.get_url_data())}`);
|
||||
} else if (this.grid.can_async_op(operation) || this.dict_format) {
|
||||
this.update_grid();
|
||||
} else {
|
||||
this.go_to(target, href);
|
||||
this.update_grid();
|
||||
}
|
||||
|
||||
// done
|
||||
@@ -585,11 +579,7 @@ export default Backbone.View.extend({
|
||||
}
|
||||
|
||||
// refresh grid
|
||||
if (this.grid.get("async") || this.dict_format) {
|
||||
this.update_grid();
|
||||
} else {
|
||||
this.go_to(target, href);
|
||||
}
|
||||
this.update_grid();
|
||||
|
||||
// done
|
||||
return false;
|
||||
@@ -597,10 +587,6 @@ export default Backbone.View.extend({
|
||||
|
||||
// go to url
|
||||
go_to: function(target, href) {
|
||||
// get aysnc status
|
||||
var async = this.grid.get("async");
|
||||
this.grid.set("async", false);
|
||||
|
||||
// get slide status
|
||||
var advanced_search = this.$el.find("#advanced-search").is(":visible");
|
||||
this.grid.set("advanced_search", advanced_search);
|
||||
@@ -613,8 +599,7 @@ export default Backbone.View.extend({
|
||||
// clear grid of transient request attributes.
|
||||
this.grid.set({
|
||||
operation: undefined,
|
||||
item_ids: undefined,
|
||||
async: async
|
||||
item_ids: undefined
|
||||
});
|
||||
switch (target) {
|
||||
case "center":
|
||||
@@ -640,7 +625,7 @@ export default Backbone.View.extend({
|
||||
type: method,
|
||||
url: self.grid.get("url_base"),
|
||||
data: self.grid.get_url_data(),
|
||||
error: function(response) {
|
||||
error: function() {
|
||||
alert("Grid refresh failed");
|
||||
},
|
||||
success: function(response_text) {
|
||||
@@ -650,7 +635,7 @@ export default Backbone.View.extend({
|
||||
var advanced_search = self.$el.find("#advanced-search").is(":visible");
|
||||
|
||||
// request new configuration
|
||||
var json = self.dict_format ? response_text : $.parseJSON(response_text);
|
||||
var json = response_text;
|
||||
|
||||
// update
|
||||
json.embedded = embedded;
|
||||
|
||||
@@ -75,9 +75,7 @@ var HDCAListItemView = _super.extend(
|
||||
} else if (!jobStatesSummary || !jobStatesSummary.hasDetails()) {
|
||||
return `
|
||||
<div class="progress state-progress">
|
||||
<span class="note">Loading job data for ${
|
||||
collectionTypeDescription
|
||||
}.<span class="blinking">..</span></span>
|
||||
<span class="note">Loading job data for ${collectionTypeDescription}.<span class="blinking">..</span></span>
|
||||
<div class="progress-bar info" style="width:100%">
|
||||
</div>`;
|
||||
} else {
|
||||
|
||||
@@ -1,15 +1,14 @@
|
||||
function _labelIfName(tag) {
|
||||
if (tag.indexOf("name:") == 0) {
|
||||
return `<span class="label label-info">${_.escape(tag.slice(5))}</span>`;
|
||||
} else {
|
||||
return "";
|
||||
}
|
||||
function _templateNametag(tag) {
|
||||
return `<span class="label label-info">${_.escape(tag.slice(5))}</span>`;
|
||||
}
|
||||
|
||||
function nametagTemplate(historyItem) {
|
||||
return `<span class="nametags">${_.sortBy(_.uniq(historyItem.tags))
|
||||
.map(_labelIfName)
|
||||
.join("")}</span>`;
|
||||
let uniqueNametags = _.filter(_.uniq(historyItem.tags), t => t.indexOf("name:") === 0);
|
||||
let nametagsDisplay = _.sortBy(uniqueNametags).map(_templateNametag);
|
||||
return `
|
||||
<div class="nametags" title="${uniqueNametags.length} nametags">
|
||||
${nametagsDisplay.join("")}
|
||||
</div>`;
|
||||
}
|
||||
|
||||
export default {
|
||||
|
||||
@@ -4,34 +4,8 @@ import Utils from "utils/utils";
|
||||
import GridView from "mvc/grid/grid-view";
|
||||
import HistoryModel from "mvc/history/history-model";
|
||||
import historyCopyDialog from "mvc/history/copy-dialog";
|
||||
var View = Backbone.View.extend({
|
||||
title: _l("Histories"),
|
||||
initialize: function(options) {
|
||||
var self = this;
|
||||
this.setElement($("<div/>"));
|
||||
this.model = new Backbone.Model();
|
||||
Utils.get({
|
||||
url: `${Galaxy.root}history/${options.action_id}?${$.param(Galaxy.params)}`,
|
||||
success: function(response) {
|
||||
response["dict_format"] = true;
|
||||
_.each(response["operations"], operation => {
|
||||
if (operation.label == "Copy") {
|
||||
operation.onclick = id => {
|
||||
self._showCopyDialog(id);
|
||||
};
|
||||
}
|
||||
});
|
||||
self.model.set(response);
|
||||
self.render();
|
||||
}
|
||||
});
|
||||
},
|
||||
|
||||
render: function() {
|
||||
var grid = new GridView(this.model.attributes);
|
||||
this.$el.empty().append(grid.$el);
|
||||
},
|
||||
|
||||
var HistoryGridView = GridView.extend({
|
||||
_showCopyDialog: function(id) {
|
||||
var history = new HistoryModel.History({ id: id });
|
||||
history
|
||||
@@ -47,6 +21,54 @@ var View = Backbone.View.extend({
|
||||
window.location.reload(true);
|
||||
});
|
||||
});
|
||||
},
|
||||
/** Add an operation to the items menu */
|
||||
_add_operation: function(popup, operation, item) {
|
||||
var self = this;
|
||||
var settings = item.operation_config[operation.label];
|
||||
if (operation.label == "Copy") {
|
||||
operation.onclick = id => {
|
||||
self._showCopyDialog(id);
|
||||
};
|
||||
}
|
||||
if (settings.allowed && operation.allow_popup) {
|
||||
popup.addItem({
|
||||
html: operation.label,
|
||||
href: settings.url_args,
|
||||
target: settings.target,
|
||||
confirmation_text: operation.confirm,
|
||||
func: function(e) {
|
||||
e.preventDefault();
|
||||
var label = $(e.target).html();
|
||||
if (operation.onclick) {
|
||||
operation.onclick(item.encode_id);
|
||||
} else {
|
||||
self.execute(this.findItemByHtml(label));
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
var View = Backbone.View.extend({
|
||||
title: _l("Histories"),
|
||||
initialize: function(options) {
|
||||
var self = this;
|
||||
this.setElement($("<div/>"));
|
||||
this.model = new Backbone.Model();
|
||||
Utils.get({
|
||||
url: `${Galaxy.root}history/${options.action_id}?${$.param(Galaxy.params)}`,
|
||||
success: function(response) {
|
||||
self.model.set(response);
|
||||
self.render();
|
||||
}
|
||||
});
|
||||
},
|
||||
|
||||
render: function() {
|
||||
var grid = new HistoryGridView(this.model.attributes);
|
||||
this.$el.empty().append(grid.$el);
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -124,9 +124,9 @@ var LibraryView = Backbone.View.extend({
|
||||
placeholder: "Click to select a role",
|
||||
container: self.$el.find(`#${id}`),
|
||||
ajax: {
|
||||
url: `${Galaxy.root}api/libraries/${self.id}/permissions?scope=available&is_library_access=${
|
||||
is_library_access
|
||||
}`,
|
||||
url: `${Galaxy.root}api/libraries/${
|
||||
self.id
|
||||
}/permissions?scope=available&is_library_access=${is_library_access}`,
|
||||
dataType: "json",
|
||||
quietMillis: 100,
|
||||
data: function(term, page) {
|
||||
|
||||
@@ -7,7 +7,7 @@ import Deferred from "utils/deferred";
|
||||
import Ui from "mvc/ui/ui-misc";
|
||||
import FormBase from "mvc/form/form-view";
|
||||
import Citations from "components/Citations.vue";
|
||||
import Vue from "libs/vue";
|
||||
import Vue from "vue";
|
||||
export default FormBase.extend({
|
||||
initialize: function(options) {
|
||||
var self = this;
|
||||
|
||||
@@ -269,9 +269,7 @@ var View = Backbone.View.extend({
|
||||
} else if (batch_n !== n) {
|
||||
this.form.highlight(
|
||||
input_id,
|
||||
`Please make sure that you select the same number of inputs for all batch mode fields. This field contains <b>${
|
||||
n
|
||||
}</b> selection(s) while a previous field contains <b>${batch_n}</b>.`
|
||||
`Please make sure that you select the same number of inputs for all batch mode fields. This field contains <b>${n}</b> selection(s) while a previous field contains <b>${batch_n}</b>.`
|
||||
);
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -48,7 +48,7 @@ var ToolShedRepositoryView = Backbone.View.extend({
|
||||
this.options.tps_select_options = this.templatePanelSelectOptions;
|
||||
this.options.tool_dependencies = models.get("tool_dependencies");
|
||||
this.options.shed_tool_conf = this.templateShedToolConf({
|
||||
stc_html: models.get("shed_conf")
|
||||
shed_tool_confs: models.get("shed_conf")
|
||||
});
|
||||
this.options.panel_section_dict = models.get("panel_section_dict");
|
||||
this.options.api_url = `${Galaxy.root}api/tool_shed_repositories/install?async=True`;
|
||||
@@ -474,7 +474,11 @@ var ToolShedRepositoryView = Backbone.View.extend({
|
||||
'<div class="toolFormTitle">Shed tool configuration file:</div>',
|
||||
'<div class="toolFormBody">',
|
||||
'<div class="form-row">',
|
||||
"<%= stc_html %>",
|
||||
'<select name="shed_tool_conf">',
|
||||
"<% _.each(shed_tool_confs.options, function(conf) { %>",
|
||||
'<option value="<%= conf.value %>"><%= conf.label %></option>',
|
||||
"<% }); %>",
|
||||
"</select>",
|
||||
'<div class="toolParamHelp" style="clear: both;">Select the file whose <b>tool_path</b> setting you want used for installing repositories.</div>',
|
||||
"</div>",
|
||||
"</div>"
|
||||
|
||||
@@ -4,7 +4,10 @@ import _l from "utils/localization";
|
||||
* rendering a tour menu.
|
||||
*/
|
||||
|
||||
import * as BootstrapTour from "libs/bootstrap-tour";
|
||||
// bootstrap-tour configures a window.Tour object; keep a local ref.
|
||||
import "libs/bootstrap-tour";
|
||||
let Tour = window.Tour;
|
||||
|
||||
var gxy_root = typeof Galaxy === "undefined" ? "/" : Galaxy.root;
|
||||
|
||||
var tourpage_template = `<h2>Galaxy Tours</h2>
|
||||
@@ -93,31 +96,8 @@ var Tours = Backbone.Collection.extend({
|
||||
model: TourItem
|
||||
});
|
||||
|
||||
var giveTour = tour_id => {
|
||||
var url = `${gxy_root}api/tours/${tour_id}`;
|
||||
$.getJSON(url, data => {
|
||||
// Set hooks for additional click and data entry actions.
|
||||
var tourdata = hooked_tour_from_data(data);
|
||||
sessionStorage.setItem("activeGalaxyTour", JSON.stringify(data));
|
||||
// Store tour steps in sessionStorage to easily persist w/o hackery.
|
||||
var tour = new Tour(
|
||||
_.extend(
|
||||
{
|
||||
steps: tourdata.steps
|
||||
},
|
||||
tour_opts
|
||||
)
|
||||
);
|
||||
// Always clean restart, since this is a new, explicit giveTour execution.
|
||||
tour.init();
|
||||
tour.goTo(0);
|
||||
tour.restart();
|
||||
});
|
||||
};
|
||||
|
||||
var ToursView = Backbone.View.extend({
|
||||
export var ToursView = Backbone.View.extend({
|
||||
title: _l("Tours"),
|
||||
// initialize
|
||||
initialize: function() {
|
||||
var self = this;
|
||||
this.setElement("<div/>");
|
||||
@@ -165,7 +145,7 @@ var ToursView = Backbone.View.extend({
|
||||
)
|
||||
.on("click", ".tourItem", function(e) {
|
||||
e.preventDefault();
|
||||
giveTour($(this).data("tour.id"));
|
||||
giveTourById($(this).data("tour.id"));
|
||||
})
|
||||
.on("click", ".tag-selector-button", e => {
|
||||
var elem = $(e.target);
|
||||
@@ -183,9 +163,53 @@ var ToursView = Backbone.View.extend({
|
||||
}
|
||||
});
|
||||
|
||||
export function giveTourWithData(data) {
|
||||
let hookedTourData = hooked_tour_from_data(data);
|
||||
sessionStorage.setItem("activeGalaxyTour", JSON.stringify(data));
|
||||
// Store tour steps in sessionStorage to easily persist w/o hackery.
|
||||
let tour = new Tour(_.extend({ steps: hookedTourData.steps }, tour_opts));
|
||||
// Always clean restart, since this is a new, explicit execution.
|
||||
tour.init();
|
||||
tour.goTo(0);
|
||||
tour.restart();
|
||||
return tour;
|
||||
}
|
||||
|
||||
export function giveTourById(tour_id) {
|
||||
var url = `${gxy_root}api/tours/${tour_id}`;
|
||||
$.getJSON(url, data => {
|
||||
giveTourWithData(data);
|
||||
});
|
||||
}
|
||||
|
||||
export function activeGalaxyTourRunner() {
|
||||
var et = JSON.parse(sessionStorage.getItem("activeGalaxyTour"));
|
||||
if (et) {
|
||||
et = hooked_tour_from_data(et);
|
||||
if (et && et.steps) {
|
||||
if (window && window.self === window.top) {
|
||||
// Only kick off a new tour if this is the toplevel window (non-iframe). This
|
||||
// functionality actually *could* be useful, but we'd need to handle it better and
|
||||
// come up with some design guidelines for tours jumping between windows.
|
||||
// Disabling for now.
|
||||
var tour = new Tour(
|
||||
_.extend(
|
||||
{
|
||||
steps: et.steps
|
||||
},
|
||||
tour_opts
|
||||
)
|
||||
);
|
||||
tour.init();
|
||||
tour.restart();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export default {
|
||||
ToursView: ToursView,
|
||||
hooked_tour_from_data: hooked_tour_from_data,
|
||||
tour_opts: tour_opts,
|
||||
giveTour: giveTour
|
||||
giveTourWithData: giveTourWithData,
|
||||
giveTourById: giveTourById,
|
||||
activeGalaxyTourRunner: activeGalaxyTourRunner
|
||||
};
|
||||
|
||||
@@ -39,17 +39,16 @@ export var View = Backbone.View.extend({
|
||||
if (!this.visible) {
|
||||
this.visible = true;
|
||||
this.$el.fadeIn("fast");
|
||||
if (this.options.closing_events) {
|
||||
var self = this;
|
||||
$(document).on("keyup.ui-modal", e => {
|
||||
if (e.keyCode == 27) {
|
||||
self.hide(true);
|
||||
}
|
||||
});
|
||||
this.$backdrop.on("click", () => {
|
||||
self.hide(true);
|
||||
});
|
||||
}
|
||||
}
|
||||
if (this.options.closing_events) {
|
||||
$(document).on("keyup.ui-modal", e => {
|
||||
if (e.keyCode == 27) {
|
||||
this.hide(true);
|
||||
}
|
||||
});
|
||||
this.$backdrop.on("click", () => {
|
||||
this.hide(true);
|
||||
});
|
||||
}
|
||||
},
|
||||
|
||||
|
||||
@@ -5,7 +5,6 @@ import Utils from "utils/utils";
|
||||
import Buttons from "mvc/ui/ui-buttons";
|
||||
var View = Backbone.View.extend({
|
||||
initialize: function(options) {
|
||||
var self = this;
|
||||
this.data = [];
|
||||
this.data2 = [];
|
||||
this.model =
|
||||
@@ -28,7 +27,9 @@ var View = Backbone.View.extend({
|
||||
pagesize: 20
|
||||
}).set(options);
|
||||
this.on("change", () => {
|
||||
self.model.get("onchange") && self.model.get("onchange")(self.value());
|
||||
if (this.model.get("onchange")) {
|
||||
this.model.get("onchange")(this.value());
|
||||
}
|
||||
});
|
||||
this.listenTo(this.model, "change:data", this._changeData, this);
|
||||
this.listenTo(this.model, "change:disabled", this._changeDisabled, this);
|
||||
@@ -40,8 +41,11 @@ var View = Backbone.View.extend({
|
||||
},
|
||||
|
||||
render: function() {
|
||||
var self = this;
|
||||
this.model.get("searchable") ? this._renderSearchable() : this._renderClassic();
|
||||
if (this.model.get("searchable")) {
|
||||
this._renderSearchable();
|
||||
} else {
|
||||
this._renderClassic();
|
||||
}
|
||||
this.$el.addClass(this.model.get("cls")).attr("id", this.model.get("id"));
|
||||
this.$select
|
||||
.empty()
|
||||
@@ -49,8 +53,8 @@ var View = Backbone.View.extend({
|
||||
.attr("id", `${this.model.get("id")}_select`)
|
||||
.prop("multiple", this.model.get("multiple"))
|
||||
.on("change", () => {
|
||||
self.value(self._getValue());
|
||||
self.trigger("change");
|
||||
this.value(this._getValue());
|
||||
this.trigger("change");
|
||||
});
|
||||
this._changeData();
|
||||
this._changeWait();
|
||||
@@ -60,7 +64,6 @@ var View = Backbone.View.extend({
|
||||
|
||||
/** Renders the classic selection field */
|
||||
_renderClassic: function() {
|
||||
var self = this;
|
||||
this.$el
|
||||
.addClass(this.model.get("multiple") ? "ui-select-multiple" : "ui-select")
|
||||
.append((this.$select = $("<select/>")))
|
||||
@@ -76,12 +79,12 @@ var View = Backbone.View.extend({
|
||||
.off("mousedown")
|
||||
.on("mousedown", event => {
|
||||
var currentY = event.pageY;
|
||||
var currentHeight = self.$select.height();
|
||||
self.minHeight = self.minHeight || currentHeight;
|
||||
var currentHeight = this.$select.height();
|
||||
this.minHeight = this.minHeight || currentHeight;
|
||||
$("#dd-helper")
|
||||
.show()
|
||||
.on("mousemove", event => {
|
||||
self.$select.height(Math.max(currentHeight + (event.pageY - currentY), self.minHeight));
|
||||
this.$select.height(Math.max(currentHeight + (event.pageY - currentY), this.minHeight));
|
||||
})
|
||||
.on("mouseup mouseleave", () => {
|
||||
$("#dd-helper")
|
||||
@@ -98,25 +101,27 @@ var View = Backbone.View.extend({
|
||||
|
||||
/** Renders the default select2 field */
|
||||
_renderSearchable: function() {
|
||||
var self = this;
|
||||
this.$el.append((this.$select = $("<div/>"))).append((this.$dropdown = $("<div/>")));
|
||||
this.$dropdown.hide();
|
||||
if (!this.model.get("multiple")) {
|
||||
this.$dropdown.show().on("click", () => {
|
||||
self.$select.select2 && self.$select.select2("open");
|
||||
if (this.$select.select2) {
|
||||
this.$select.select2("open");
|
||||
}
|
||||
});
|
||||
}
|
||||
this.all_button = null;
|
||||
if (this.model.get("multiple") && !this.model.get("individual") && !this.model.get("readonly")) {
|
||||
this.all_button = new Buttons.ButtonCheck({
|
||||
onclick: function() {
|
||||
onclick: () => {
|
||||
var new_value = [];
|
||||
self.all_button.value() !== 0 &&
|
||||
_.each(self.model.get("data"), option => {
|
||||
if (this.all_button.value() !== 0) {
|
||||
_.each(this.model.get("data"), option => {
|
||||
new_value.push(option.value);
|
||||
});
|
||||
self.value(new_value);
|
||||
self.trigger("change");
|
||||
}
|
||||
this.value(new_value);
|
||||
this.trigger("change");
|
||||
}
|
||||
});
|
||||
this.$el.prepend(this.all_button.$el);
|
||||
@@ -127,7 +132,7 @@ var View = Backbone.View.extend({
|
||||
_match: function(term, text) {
|
||||
return (
|
||||
!term ||
|
||||
term == "" ||
|
||||
term === "" ||
|
||||
String(text)
|
||||
.toUpperCase()
|
||||
.indexOf(term.toUpperCase()) >= 0
|
||||
@@ -136,18 +141,17 @@ var View = Backbone.View.extend({
|
||||
|
||||
/** Updates the selection options */
|
||||
_changeData: function() {
|
||||
var self = this;
|
||||
this.data = [];
|
||||
if (!this.model.get("multiple") && this.model.get("optional")) {
|
||||
this.data.push({
|
||||
value: "__null__",
|
||||
label: self.model.get("empty_text")
|
||||
label: this.model.get("empty_text")
|
||||
});
|
||||
}
|
||||
_.each(this.model.get("data"), option => {
|
||||
self.data.push(option);
|
||||
this.data.push(option);
|
||||
});
|
||||
if (this.length() == 0) {
|
||||
if (this.length() === 0) {
|
||||
this.data.push({
|
||||
value: "__null__",
|
||||
label: this.model.get("error_text")
|
||||
@@ -156,61 +160,71 @@ var View = Backbone.View.extend({
|
||||
if (this.model.get("searchable")) {
|
||||
this.data2 = [];
|
||||
_.each(this.data, (option, index) => {
|
||||
self.data2.push({
|
||||
this.data2.push({
|
||||
order: index,
|
||||
id: option.value,
|
||||
text: option.label,
|
||||
tags: option.tags
|
||||
});
|
||||
});
|
||||
this.$select.data("select2") && this.$select.select2("destroy");
|
||||
if (this.$select.data("select2")) {
|
||||
this.$select.select2("destroy");
|
||||
}
|
||||
this.matched_tags = {};
|
||||
this.$select.select2({
|
||||
data: self.data2,
|
||||
data: this.data2,
|
||||
closeOnSelect: !this.model.get("multiple"),
|
||||
multiple: this.model.get("multiple"),
|
||||
query: function(q) {
|
||||
self.matched_tags = {};
|
||||
var pagesize = self.model.get("pagesize");
|
||||
var results = _.filter(self.data2, e => {
|
||||
query: q => {
|
||||
this.matched_tags = {};
|
||||
var pagesize = this.model.get("pagesize");
|
||||
var results = _.filter(this.data2, e => {
|
||||
var found = false;
|
||||
_.each(e.tags, tag => {
|
||||
if (self._match(q.term, tag)) {
|
||||
found = self.matched_tags[tag] = true;
|
||||
if (this._match(q.term, tag)) {
|
||||
found = this.matched_tags[tag] = true;
|
||||
}
|
||||
});
|
||||
return found || self._match(q.term, e.text);
|
||||
return found || this._match(q.term, e.text);
|
||||
});
|
||||
q.callback({
|
||||
results: results.slice((q.page - 1) * pagesize, q.page * pagesize),
|
||||
more: results.length >= q.page * pagesize
|
||||
});
|
||||
},
|
||||
formatResult: function(result) {
|
||||
return `${_.escape(result.text)}<div class="ui-tags">${_.reduce(
|
||||
result.tags,
|
||||
(memo, tag) => {
|
||||
if (self.matched_tags[tag]) {
|
||||
formatResult: result => {
|
||||
let extraTagWarning = "";
|
||||
let filteredTags = _.filter(result.tags, t => this.matched_tags.hasOwnProperty(t));
|
||||
if (filteredTags.length > 5) {
|
||||
extraTagWarning = ` <div class="label label-warning">${filteredTags.length -
|
||||
5} more tags</div>`;
|
||||
}
|
||||
return `
|
||||
${_.escape(result.text)}
|
||||
<div class="ui-tags">
|
||||
${_.reduce(
|
||||
filteredTags.slice(0, 5),
|
||||
(memo, tag) => {
|
||||
return `${memo} <div class="label label-info">${_.escape(tag)}</div>`;
|
||||
}
|
||||
return memo;
|
||||
},
|
||||
""
|
||||
)}</div>`;
|
||||
},
|
||||
""
|
||||
)}
|
||||
${extraTagWarning}
|
||||
</div>`;
|
||||
}
|
||||
});
|
||||
this.$(".select2-container .select2-search input").off("blur");
|
||||
} else {
|
||||
this.$select.find("option").remove();
|
||||
_.each(this.data, option => {
|
||||
self.$select.append(
|
||||
this.$select.append(
|
||||
$("<option/>")
|
||||
.attr("value", option.value)
|
||||
.html(_.escape(option.label))
|
||||
);
|
||||
});
|
||||
}
|
||||
this.model.set("disabled", this.model.get("readonly") || this.length() == 0);
|
||||
this.model.set("disabled", this.model.get("readonly") || this.length() === 0);
|
||||
this._changeValue();
|
||||
},
|
||||
|
||||
@@ -252,7 +266,9 @@ var View = Backbone.View.extend({
|
||||
|
||||
/** Return/Set current selection */
|
||||
value: function(new_value) {
|
||||
new_value !== undefined && this.model.set("value", new_value);
|
||||
if (new_value !== undefined) {
|
||||
this.model.set("value", new_value);
|
||||
}
|
||||
return this._getValue();
|
||||
},
|
||||
|
||||
@@ -311,9 +327,13 @@ var View = Backbone.View.extend({
|
||||
/** Update all available options at once */
|
||||
add: function(options, sorter) {
|
||||
_.each(this.model.get("data"), v => {
|
||||
v.keep && !_.findWhere(options, { value: v.value }) && options.push(v);
|
||||
if (v.keep && !_.findWhere(options, { value: v.value })) {
|
||||
options.push(v);
|
||||
}
|
||||
});
|
||||
sorter && options && options.sort(sorter);
|
||||
if (sorter && options) {
|
||||
options.sort(sorter);
|
||||
}
|
||||
this.model.set("data", options);
|
||||
},
|
||||
|
||||
@@ -334,7 +354,6 @@ var View = Backbone.View.extend({
|
||||
|
||||
/** Set value to dom */
|
||||
_setValue: function(new_value) {
|
||||
var self = this;
|
||||
if (new_value === null || new_value === undefined) {
|
||||
new_value = "__null__";
|
||||
}
|
||||
@@ -351,8 +370,10 @@ var View = Backbone.View.extend({
|
||||
if ($.isArray(new_value)) {
|
||||
var val = [];
|
||||
_.each(new_value, v => {
|
||||
var d = _.findWhere(self.data2, { id: v });
|
||||
d && val.push(d);
|
||||
var d = _.findWhere(this.data2, { id: v });
|
||||
if (d) {
|
||||
val.push(d);
|
||||
}
|
||||
});
|
||||
new_value = val;
|
||||
} else {
|
||||
|
||||
@@ -1,8 +1,7 @@
|
||||
// dependencies
|
||||
import Utils from "utils/utils";
|
||||
import Ui from "mvc/ui/ui-misc";
|
||||
import Table from "mvc/ui/ui-table";
|
||||
import List from "mvc/ui/ui-list";
|
||||
|
||||
// collection of libraries
|
||||
var Libraries = Backbone.Collection.extend({
|
||||
url: `${Galaxy.root}api/libraries?deleted=false`
|
||||
@@ -85,7 +84,9 @@ var View = Backbone.View.extend({
|
||||
|
||||
// add change event. fires on trigger
|
||||
this.on("change", () => {
|
||||
options.onchange && options.onchange(self.value());
|
||||
if (options.onchange) {
|
||||
options.onchange(self.value());
|
||||
}
|
||||
});
|
||||
|
||||
// create elements
|
||||
|
||||
@@ -62,7 +62,7 @@ var Model = Backbone.Model.extend({
|
||||
description: _l("Associate OpenIDs with your account."),
|
||||
icon: "fa-openid",
|
||||
onclick: function() {
|
||||
window.location.href = `${Galaxy.root}user/openid_manage?cntrller=user&use_panels=True`;
|
||||
window.location.href = `${Galaxy.root}openids/list`;
|
||||
}
|
||||
},
|
||||
custom_builds: {
|
||||
|
||||
@@ -106,7 +106,7 @@ $.extend(CanvasManager.prototype, {
|
||||
// If it appears that the user is trying to copy/paste text, we
|
||||
// pass that through.
|
||||
if (window.getSelection().toString() === "") {
|
||||
if (this.app.workflow.active_node) {
|
||||
if (this.app.workflow.active_node && this.app.workflow.active_node.type !== "subworkflow") {
|
||||
e.clipboardData.setData(
|
||||
"application/json",
|
||||
JSON.stringify({
|
||||
|
||||
@@ -35,9 +35,7 @@ $.extend(Workflow.prototype, {
|
||||
}
|
||||
if (!this.canLabelOutputWith(toLabel)) {
|
||||
Toastr.warning(
|
||||
`Workflow contains duplicate workflow output labels ${
|
||||
toLabel
|
||||
}. This must be fixed before it can be saved.`
|
||||
`Workflow contains duplicate workflow output labels ${toLabel}. This must be fixed before it can be saved.`
|
||||
);
|
||||
}
|
||||
if (toLabel) {
|
||||
|
||||
@@ -8,10 +8,10 @@ function CollectionTypeDescription(collectionType) {
|
||||
|
||||
var NULL_COLLECTION_TYPE_DESCRIPTION = {
|
||||
isCollection: false,
|
||||
canMatch: function(other) {
|
||||
canMatch: function() {
|
||||
return false;
|
||||
},
|
||||
canMapOver: function(other) {
|
||||
canMapOver: function() {
|
||||
return false;
|
||||
},
|
||||
toString: function() {
|
||||
@@ -30,13 +30,13 @@ var ANY_COLLECTION_TYPE_DESCRIPTION = {
|
||||
canMatch: function(other) {
|
||||
return NULL_COLLECTION_TYPE_DESCRIPTION !== other;
|
||||
},
|
||||
canMapOver: function(other) {
|
||||
canMapOver: function() {
|
||||
return false;
|
||||
},
|
||||
toString: function() {
|
||||
return "AnyCollectionType[]";
|
||||
},
|
||||
append: function(otherCollectionType) {
|
||||
append: function() {
|
||||
throw "Cannot append to ANY_COLLECTION_TYPE_DESCRIPTION";
|
||||
},
|
||||
equal: function(other) {
|
||||
@@ -50,7 +50,7 @@ $.extend(CollectionTypeDescription.prototype, {
|
||||
return this;
|
||||
}
|
||||
if (otherCollectionTypeDescription === ANY_COLLECTION_TYPE_DESCRIPTION) {
|
||||
return otherCollectionType;
|
||||
return otherCollectionTypeDescription;
|
||||
}
|
||||
return new CollectionTypeDescription(`${this.collectionType}:${otherCollectionTypeDescription.collectionType}`);
|
||||
},
|
||||
@@ -143,7 +143,9 @@ var Terminal = Backbone.Model.extend({
|
||||
},
|
||||
destroyInvalidConnections: function() {
|
||||
_.each(this.connectors, connector => {
|
||||
connector && connector.destroyIfInvalid();
|
||||
if (connector) {
|
||||
connector.destroyIfInvalid();
|
||||
}
|
||||
});
|
||||
},
|
||||
setMapOver: function(val) {
|
||||
@@ -258,7 +260,7 @@ var BaseInputTerminal = Terminal.extend({
|
||||
if (this._collectionAttached()) {
|
||||
// Can only attach one collection to multiple input
|
||||
// data parameter.
|
||||
inputsFilled = true;
|
||||
inputFilled = true;
|
||||
} else {
|
||||
inputFilled = false;
|
||||
}
|
||||
@@ -317,17 +319,17 @@ var BaseInputTerminal = Terminal.extend({
|
||||
if (thisDatatype == "input") {
|
||||
return true;
|
||||
}
|
||||
var cat_outputs = new Array();
|
||||
var cat_outputs = [];
|
||||
cat_outputs = cat_outputs.concat(other.datatypes);
|
||||
if (other.node.post_job_actions) {
|
||||
for (var pja_i in other.node.post_job_actions) {
|
||||
var pja = other.node.post_job_actions[pja_i];
|
||||
if (
|
||||
pja.action_type == "ChangeDatatypeAction" &&
|
||||
(pja.output_name == "" || pja.output_name == other.name) &&
|
||||
(pja.output_name === "" || pja.output_name == other.name) &&
|
||||
pja.action_arguments
|
||||
) {
|
||||
cat_outputs.push(pja.action_arguments["newtype"]);
|
||||
cat_outputs.push(pja.action_arguments.newtype);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -277,9 +277,7 @@ export default Backbone.View.extend({
|
||||
if (upgrade_message) {
|
||||
window.show_modal(
|
||||
"Issues loading this workflow",
|
||||
`Please review the following issues, possibly resulting from tool upgrades or changes.<p><ul>${
|
||||
upgrade_message
|
||||
}</ul></p>`,
|
||||
`Please review the following issues, possibly resulting from tool upgrades or changes.<p><ul>${upgrade_message}</ul></p>`,
|
||||
{ Continue: hide_modal }
|
||||
);
|
||||
} else {
|
||||
@@ -560,9 +558,7 @@ export default Backbone.View.extend({
|
||||
if (upgrade_message) {
|
||||
window.show_modal(
|
||||
"Subworkflow embedded with changes",
|
||||
`Problems were encountered loading this workflow (possibly a result of tool upgrades). Please review the following parameters and then save.<ul>${
|
||||
upgrade_message
|
||||
}</ul>`,
|
||||
`Problems were encountered loading this workflow (possibly a result of tool upgrades). Please review the following parameters and then save.<ul>${upgrade_message}</ul>`,
|
||||
{ Continue: hide_modal }
|
||||
);
|
||||
} else {
|
||||
@@ -764,13 +760,15 @@ export default Backbone.View.extend({
|
||||
// Fix width to computed width
|
||||
// Now add floats
|
||||
var buttons = $("<div class='buttons' style='float: right;'></div>");
|
||||
buttons.append(
|
||||
$("<div/>")
|
||||
.addClass("fa-icon-button fa fa-files-o")
|
||||
.click(e => {
|
||||
node.clone();
|
||||
})
|
||||
);
|
||||
if (type !== "subworkflow") {
|
||||
buttons.append(
|
||||
$("<div/>")
|
||||
.addClass("fa-icon-button fa fa-files-o")
|
||||
.click(e => {
|
||||
node.clone();
|
||||
})
|
||||
);
|
||||
}
|
||||
buttons.append(
|
||||
$("<div/>")
|
||||
.addClass("fa-icon-button fa fa-times")
|
||||
|
||||
@@ -5,7 +5,7 @@ import WORKFLOWS from "mvc/workflow/workflow-model";
|
||||
import QueryStringParsing from "utils/query-string-parsing";
|
||||
import _l from "utils/localization";
|
||||
/** View of the individual workflows */
|
||||
var WorkflowItemView = Backbone.View.extend({
|
||||
const WorkflowItemView = Backbone.View.extend({
|
||||
tagName: "tr", // name of (orphan) root tag in this.el
|
||||
initialize: function() {
|
||||
_.bindAll(
|
||||
@@ -33,15 +33,20 @@ var WorkflowItemView = Backbone.View.extend({
|
||||
},
|
||||
|
||||
showInToolPanel: function() {
|
||||
this.model.set("show_in_tool_panel", !this.model.get("show_in_tool_panel"));
|
||||
this.model.save();
|
||||
// This reloads the whole page, so that the workflow appears in the tool panel.
|
||||
// Ideally we would notify only the tool panel of a change
|
||||
window.location = `${Galaxy.root}workflows/list`;
|
||||
this.model.save(
|
||||
{ show_in_tool_panel: !this.model.get("show_in_tool_panel") },
|
||||
{
|
||||
success: function() {
|
||||
window.location = `${Galaxy.root}workflows/list`;
|
||||
}
|
||||
}
|
||||
);
|
||||
},
|
||||
|
||||
removeWorkflow: function() {
|
||||
var wfName = this.model.get("name");
|
||||
const wfName = this.model.get("name");
|
||||
if (window.confirm(`Are you sure you want to delete workflow '${wfName}'?`)) {
|
||||
this.model.destroy({
|
||||
success: function() {
|
||||
@@ -53,8 +58,8 @@ var WorkflowItemView = Backbone.View.extend({
|
||||
},
|
||||
|
||||
renameWorkflow: function() {
|
||||
var oldName = this.model.get("name");
|
||||
var newName = window.prompt(`Enter a new Name for workflow '${oldName}'`, oldName);
|
||||
const oldName = this.model.get("name");
|
||||
const newName = window.prompt(`Enter a new Name for workflow '${oldName}'`, oldName);
|
||||
if (newName) {
|
||||
this.model.save(
|
||||
{ name: newName },
|
||||
@@ -69,16 +74,15 @@ var WorkflowItemView = Backbone.View.extend({
|
||||
},
|
||||
|
||||
copyWorkflow: function() {
|
||||
var self = this;
|
||||
var oldName = this.model.get("name");
|
||||
const oldName = this.model.get("name");
|
||||
$.getJSON(`${this.model.urlRoot}/${this.model.id}/download`, wfJson => {
|
||||
var newName = `Copy of ${oldName}`;
|
||||
var currentOwner = self.model.get("owner");
|
||||
let newName = `Copy of ${oldName}`;
|
||||
const currentOwner = this.model.get("owner");
|
||||
if (currentOwner != Galaxy.user.attributes.username) {
|
||||
newName += ` shared by user ${currentOwner}`;
|
||||
}
|
||||
wfJson.name = newName;
|
||||
self.collection.create(wfJson, {
|
||||
this.collection.create(wfJson, {
|
||||
at: 0,
|
||||
wait: true,
|
||||
success: function() {
|
||||
@@ -96,16 +100,14 @@ var WorkflowItemView = Backbone.View.extend({
|
||||
},
|
||||
|
||||
_rowTemplate: function() {
|
||||
var show = this.model.get("show_in_tool_panel");
|
||||
var wfId = this.model.id;
|
||||
var checkboxHtml = `<input id="show-in-tool-panel" type="checkbox" class="show-in-tool-panel" ${
|
||||
let show = this.model.get("show_in_tool_panel");
|
||||
let wfId = this.model.id;
|
||||
const checkboxHtml = `<input id="show-in-tool-panel" type="checkbox" class="show-in-tool-panel" ${
|
||||
show ? `checked="${show}"` : ""
|
||||
} value="${wfId}">`;
|
||||
var trHtml = `<td><div class="dropdown"><button class="menubutton" type="button" data-toggle="dropdown">${_.escape(
|
||||
const trHtml = `<td><div class="dropdown"><button class="menubutton" type="button" data-toggle="dropdown">${_.escape(
|
||||
this.model.get("name")
|
||||
)}<span class="caret"></span></button>${this._templateActions()}</div></td><td><span><div class="${
|
||||
wfId
|
||||
} tags-display"></div></td><td>${
|
||||
)}<span class="caret"></span></button>${this._templateActions()}</div></td><td><span><div class="${wfId} tags-display"></div></td><td>${
|
||||
this.model.get("owner") === Galaxy.user.attributes.username ? "You" : this.model.get("owner")
|
||||
}</span></td><td>${this.model.get("number_of_steps")}</td><td>${
|
||||
this.model.get("published") ? "Yes" : "No"
|
||||
@@ -114,7 +116,7 @@ var WorkflowItemView = Backbone.View.extend({
|
||||
},
|
||||
|
||||
renderTagEditor: function() {
|
||||
var TagEditor = new TAGS.TagsEditor({
|
||||
const TagEditor = new TAGS.TagsEditor({
|
||||
model: this.model,
|
||||
el: $.find(`.${this.model.id}.tags-display`),
|
||||
workflow_mode: true
|
||||
@@ -152,7 +154,7 @@ var WorkflowItemView = Backbone.View.extend({
|
||||
});
|
||||
|
||||
/** View of the main workflow list page */
|
||||
var WorkflowListView = Backbone.View.extend({
|
||||
const WorkflowListView = Backbone.View.extend({
|
||||
title: _l("Workflows"),
|
||||
initialize: function() {
|
||||
this.setElement("<div/>");
|
||||
@@ -182,18 +184,16 @@ var WorkflowListView = Backbone.View.extend({
|
||||
// TODO: check that file is valid galaxy workflow
|
||||
this.unhighlightDropZone();
|
||||
e.preventDefault();
|
||||
var files = e.dataTransfer.files;
|
||||
var self = this;
|
||||
for (var i = 0, f; (f = files[i]); i++) {
|
||||
self.readWorkflowFiles(f);
|
||||
const files = e.dataTransfer.files;
|
||||
for (let i = 0, f; (f = files[i]); i++) {
|
||||
this.readWorkflowFiles(f);
|
||||
}
|
||||
},
|
||||
|
||||
readWorkflowFiles: function(f) {
|
||||
var self = this;
|
||||
var reader = new FileReader();
|
||||
const reader = new FileReader();
|
||||
reader.onload = theFile => {
|
||||
var wf_json;
|
||||
let wf_json;
|
||||
try {
|
||||
wf_json = JSON.parse(reader.result);
|
||||
} catch (e) {
|
||||
@@ -201,7 +201,7 @@ var WorkflowListView = Backbone.View.extend({
|
||||
wf_json = null;
|
||||
}
|
||||
if (wf_json) {
|
||||
self.collection.create(wf_json, {
|
||||
this.collection.create(wf_json, {
|
||||
at: 0,
|
||||
wait: true,
|
||||
success: function() {
|
||||
@@ -218,8 +218,8 @@ var WorkflowListView = Backbone.View.extend({
|
||||
|
||||
_showArgErrors: _.once(() => {
|
||||
// Parse args out of params, display if there's a message.
|
||||
var msg_text = QueryStringParsing.get("message");
|
||||
var msg_status = QueryStringParsing.get("status");
|
||||
const msg_text = QueryStringParsing.get("message");
|
||||
const msg_status = QueryStringParsing.get("status");
|
||||
if (msg_status === "error") {
|
||||
mod_toastr.error(_.escape(msg_text || "Unknown Error, please report this to an administrator."));
|
||||
} else if (msg_text) {
|
||||
@@ -229,18 +229,17 @@ var WorkflowListView = Backbone.View.extend({
|
||||
|
||||
render: function() {
|
||||
// Add workflow header
|
||||
var header = this._templateHeader();
|
||||
const header = this._templateHeader();
|
||||
// Add the actions buttons
|
||||
var templateActions = this._templateActionButtons();
|
||||
var tableTemplate = this._templateWorkflowTable();
|
||||
const templateActions = this._templateActionButtons();
|
||||
const tableTemplate = this._templateWorkflowTable();
|
||||
this.$el.html(header + templateActions + tableTemplate);
|
||||
var self = this;
|
||||
_(this.collection.models).each(item => {
|
||||
// in case collection is not empty
|
||||
self.appendItem(item);
|
||||
self.confirmDelete(item);
|
||||
this.appendItem(item);
|
||||
this.confirmDelete(item);
|
||||
}, this);
|
||||
var minQueryLength = 3;
|
||||
const minQueryLength = 3;
|
||||
this.searchWorkflow(this.$(".search-wf"), this.$(".workflow-search tr"), minQueryLength);
|
||||
this.adjustActiondropdown();
|
||||
this._showArgErrors();
|
||||
@@ -248,7 +247,7 @@ var WorkflowListView = Backbone.View.extend({
|
||||
},
|
||||
|
||||
appendItem: function(item) {
|
||||
var workflowItemView = new WorkflowItemView({
|
||||
const workflowItemView = new WorkflowItemView({
|
||||
model: item,
|
||||
collection: this.collection
|
||||
});
|
||||
@@ -258,7 +257,7 @@ var WorkflowListView = Backbone.View.extend({
|
||||
|
||||
/** Add confirm box before removing/unsharing workflow */
|
||||
confirmDelete: function(workflow) {
|
||||
var $el_shared_wf_link = this.$(`.link-confirm-shared-${workflow.id}`);
|
||||
const $el_shared_wf_link = this.$(`.link-confirm-shared-${workflow.id}`);
|
||||
$el_shared_wf_link.click(() =>
|
||||
window.confirm(`Are you sure you want to remove the shared workflow '${workflow.attributes.name}'?`)
|
||||
);
|
||||
@@ -267,12 +266,12 @@ var WorkflowListView = Backbone.View.extend({
|
||||
/** Implement client side workflow search/filtering */
|
||||
searchWorkflow: function($el_searchinput, $el_tabletr, min_querylen) {
|
||||
$el_searchinput.on("keyup", function() {
|
||||
var query = $(this).val();
|
||||
const query = $(this).val();
|
||||
// Filter when query is at least 3 characters
|
||||
// otherwise show all rows
|
||||
if (query.length >= min_querylen) {
|
||||
// Ignore the query's case using 'i'
|
||||
var regular_expression = new RegExp(query, "i");
|
||||
const regular_expression = new RegExp(query, "i");
|
||||
$el_tabletr.hide();
|
||||
$el_tabletr
|
||||
.filter(function() {
|
||||
@@ -313,7 +312,7 @@ var WorkflowListView = Backbone.View.extend({
|
||||
|
||||
/** Template for workflow table */
|
||||
_templateWorkflowTable: function() {
|
||||
var tableHtml =
|
||||
const tableHtml =
|
||||
'<table class="table colored"><thead>' +
|
||||
'<tr class="header">' +
|
||||
"<th>Name</th>" +
|
||||
@@ -323,9 +322,7 @@ var WorkflowListView = Backbone.View.extend({
|
||||
"<th>Published</th>" +
|
||||
"<th>Show in tools panel</th>" +
|
||||
"</tr></thead>";
|
||||
return `${
|
||||
tableHtml
|
||||
}<tbody class="workflow-search "><div class="hidden_description_layer"><p>Drop workflow files here to import</p></tbody></table></div>`;
|
||||
return `${tableHtml}<tbody class="workflow-search "><div class="hidden_description_layer"><p>Drop workflow files here to import</p></tbody></table></div>`;
|
||||
},
|
||||
|
||||
/** Main template */
|
||||
@@ -343,7 +340,7 @@ var WorkflowListView = Backbone.View.extend({
|
||||
}
|
||||
});
|
||||
|
||||
var ImportWorkflowView = Backbone.View.extend({
|
||||
const ImportWorkflowView = Backbone.View.extend({
|
||||
initialize: function() {
|
||||
this.setElement("<div/>");
|
||||
this.render();
|
||||
@@ -351,9 +348,8 @@ var ImportWorkflowView = Backbone.View.extend({
|
||||
|
||||
/** Open page to import workflow */
|
||||
render: function() {
|
||||
var self = this;
|
||||
$.getJSON(`${Galaxy.root}workflow/upload_import_workflow`, options => {
|
||||
self.$el.empty().append(self._mainTemplate(options));
|
||||
this.$el.empty().append(this._mainTemplate(options));
|
||||
});
|
||||
},
|
||||
|
||||
|
||||
@@ -1,169 +1,244 @@
|
||||
/** zh localization */
|
||||
/* by steven shen */
|
||||
define({
|
||||
// ----------------------------------------------------------------------------- masthead
|
||||
"Analyze Data": "数据分析",
|
||||
Workflow: "流程",
|
||||
"Shared Data": "共享的数据",
|
||||
"Data Libraries": "数据库",
|
||||
Histories: "已发布的历史",
|
||||
Workflows: "已发布的流程",
|
||||
Visualizations: "已发布的可视化",
|
||||
Pages: "已发布的页面",
|
||||
Visualization: "可视化",
|
||||
"New Track Browser": "创建 Track Browser",
|
||||
"Saved Visualizations": "已保存的保存",
|
||||
"Interactive Environments": "交互式环境",
|
||||
Admin: "管理员",
|
||||
Help: "帮助",
|
||||
Support: "支持",
|
||||
Search: "搜索",
|
||||
"Mailing Lists": "邮件列表",
|
||||
Videos: "视频",
|
||||
Wiki: false,
|
||||
"How to Cite Galaxy": "引用",
|
||||
"Interactive Tours": "使用引导",
|
||||
User: "用户",
|
||||
Login: "登陆",
|
||||
Register: "注册",
|
||||
"Login or Register": "登陆或注册",
|
||||
"Logged in as": "您已登陆为",
|
||||
Preferences: "用户偏好性",
|
||||
"Custom Builds": "自定义构建集",
|
||||
Logout: "退出",
|
||||
"Saved Histories": "保存历史",
|
||||
"Saved Datasets": "保存数据集",
|
||||
"Saved Pages": "保存页面",
|
||||
|
||||
//Tooltip
|
||||
"Account and saved data": "账号及数据保存",
|
||||
"Account registration or login": "注册与登录",
|
||||
"Support, contact, and community": "支持,联系,社区",
|
||||
"Administer this Galaxy": "管理您的Galaxy",
|
||||
"Visualize datasets": "数据集可视化",
|
||||
"Access published resources": "访问发布的资源",
|
||||
"Chain tools into workflows": "将工具链接到工作流程中",
|
||||
"Analysis home view": "数据分析主页",
|
||||
|
||||
// ---------------------------------------------------------------------------- histories
|
||||
// ---- history/options-menu
|
||||
"History Lists": "历史列表",
|
||||
// Saved histories is defined above.
|
||||
"Saved Histories": "保存历史",
|
||||
"Histories Shared with Me": "分享给您历史",
|
||||
"Current History": "当前历史",
|
||||
"Create New": "创建新的历史",
|
||||
"Copy History": "复制历史",
|
||||
"Share or Publish": "分享或发布历史",
|
||||
"Show Structure": "展示结构",
|
||||
"Extract Workflow": "提取为工作流",
|
||||
// Delete is defined elsewhere, but is also in this menu.
|
||||
Delete: "删除",
|
||||
"Delete Permanently": "永久删除",
|
||||
"Dataset Actions": "数据操作",
|
||||
"Copy Datasets": "复制数据集",
|
||||
"Dataset Security": "数据安全",
|
||||
"Resume Paused Jobs": "恢复已暂停的任务",
|
||||
"Collapse Expanded Datasets": false,
|
||||
"Unhide Hidden Datasets": "取消隐藏的数据集",
|
||||
"Delete Hidden Datasets": "删除隐藏的数据集",
|
||||
"Purge Deleted Datasets": "清空已删除的数据集",
|
||||
Downloads: "下载",
|
||||
"Export Tool Citations": "导出工具文献引用",
|
||||
"Export History to File": "导出历史到文件",
|
||||
"Other Actions": "其他操作",
|
||||
"Import from File": "从文件导入",
|
||||
Webhooks: false,
|
||||
|
||||
// ---- history-model
|
||||
// ---- history-view
|
||||
"This history is empty": "历史已空",
|
||||
"No matching datasets found": "未找到匹配的数据集",
|
||||
//"An error occurred while getting updates from the server" :
|
||||
//false,
|
||||
//"Please contact a Galaxy administrator if the problem persists" :
|
||||
//false,
|
||||
"An error occurred while getting updates from the server": "从服务器更新出现错误",
|
||||
"Please contact a Galaxy administrator if the problem persists": "如果问题仍然存在,请联系Galaxy管理员",
|
||||
//TODO:
|
||||
//"An error was encountered while <% where %>" :
|
||||
//false,
|
||||
"Search datasets": "搜索数据集",
|
||||
"You are currently viewing a deleted history!": "正在查看已删除的历史",
|
||||
"You are over your disk quota": "您已超过磁盘配额",
|
||||
//"Tool execution is on hold until your disk usage drops below your allocated quota" :
|
||||
//false,
|
||||
All: "皆",
|
||||
"An error was encountered while <% where %>": "出现错误,当执行以下操作时: <% where %>",
|
||||
"search datasets": "搜索数据集",
|
||||
"You are currently viewing a deleted history!": "您正在查看已删除的历史记录",
|
||||
"You are over your disk quota": "您超出了您的磁盘配额",
|
||||
"Tool execution is on hold until your disk usage drops below your allocated quota":
|
||||
"工具执行处于暂停状态,直到您的磁盘使用量低于您分配的配额",
|
||||
All: "所有的",
|
||||
None: "一个也没有",
|
||||
"For all selected": "为每个选定",
|
||||
|
||||
// ---- history-view-edit
|
||||
//"Edit history tags" :
|
||||
//false,
|
||||
//"Edit history Annotation" :
|
||||
//false,
|
||||
"Edit history tags": "编辑历史标签",
|
||||
"Edit history Annotation": "编辑历史备注",
|
||||
"Click to rename history": "单击要重命名的历史",
|
||||
// multi operations
|
||||
"Operations on multiple datasets": "编辑多个数据集",
|
||||
//"Hide datasets" :
|
||||
//false,
|
||||
//"Unhide datasets" :
|
||||
//false,
|
||||
//"Delete datasets" :
|
||||
//false,
|
||||
//"Undelete datasets" :
|
||||
//false,
|
||||
"Hide datasets": "隐藏数据集",
|
||||
"Unhide datasets": "显示数据集",
|
||||
"Delete datasets": "删除数据集",
|
||||
"Undelete datasets": "取消删除数据集",
|
||||
"Permanently delete datasets": "永久删除数据集",
|
||||
"This will permanently remove the data in your datasets. Are you sure?": "这将永久在你的数据集删除数据。你确定?",
|
||||
"This will permanently remove the data in your datasets. Are you sure?": "这将永久在您的数据集删除数据。你确定?",
|
||||
|
||||
// ---- history-view-annotated
|
||||
Dataset: "数据集",
|
||||
//"Annotation" :
|
||||
//false,
|
||||
Dataset: "数据",
|
||||
Annotation: "备注",
|
||||
|
||||
// ---- history-view-edit-current
|
||||
"This history is empty. Click 'Get Data' on the left tool menu to start": "历史已空,请单击左边窗格中‘获取数据’",
|
||||
"You must be logged in to create histories": "你必须登录后才能创建历史",
|
||||
//TODO:
|
||||
//"You can <% loadYourOwn %> or <% externalSource %>" :
|
||||
"You can ": "您可以 ",
|
||||
" or ": " 或者 ",
|
||||
"load your own data": "上传您的个人数据",
|
||||
"get data from an external source": "从外部来源获取数据",
|
||||
|
||||
// these aren't in zh/ginga.po and the template doesn't localize
|
||||
//"Include Deleted Datasets" :
|
||||
//false,
|
||||
//"load your own data" :
|
||||
//false,
|
||||
//"get data from an external source" :
|
||||
//"Include Hidden Datasets" :
|
||||
//false,
|
||||
|
||||
// ---------------------------------------------------------------------------- datasets
|
||||
// ---- hda-model
|
||||
//"Unable to purge dataset" :
|
||||
//false,
|
||||
"Unable to purge dataset": "无法清除数据集",
|
||||
|
||||
// ---- hda-base
|
||||
// display button
|
||||
//"Cannot display datasets removed from disk" :
|
||||
//false,
|
||||
//"This dataset must finish uploading before it can be viewed" :
|
||||
//false,
|
||||
//"This dataset is not yet viewable" :
|
||||
//false,
|
||||
"View data": "数据",
|
||||
"Cannot display datasets removed from disk": "无法显示已从磁盘中删除的数据集",
|
||||
"This dataset must finish uploading before it can be viewed": "此数据集必须先完成上传, 然后才能查看",
|
||||
"This dataset is not yet viewable": "此数据集是不可见的",
|
||||
"View data": "查看数据",
|
||||
// download button
|
||||
Download: "下载",
|
||||
"Download dataset": "下载数据集",
|
||||
//"Additional files" :
|
||||
//false,
|
||||
"Additional files": "其他文件",
|
||||
// info/show_params
|
||||
"View details": "查看详情",
|
||||
|
||||
// dataset states
|
||||
// state: new
|
||||
//"This is a new dataset and not all of its data are available yet" :
|
||||
//false,
|
||||
"This is a new dataset and not all of its data are available yet": "这是一个新的数据集,并不是所有的数据都可用",
|
||||
// state: noPermission
|
||||
//"You do not have permission to view this dataset" :
|
||||
//false,
|
||||
"You do not have permission to view this dataset": "您无权查看此数据集",
|
||||
// state: discarded
|
||||
//"The job creating this dataset was cancelled before completion" :
|
||||
//false,
|
||||
"The job creating this dataset was cancelled before completion": "创建此数据集的任务在完成之前已被取消",
|
||||
// state: queued
|
||||
"This job is waiting to run": "等待运行的进程",
|
||||
"This job is waiting to run": "任务正在等待运行",
|
||||
// state: upload
|
||||
//"This dataset is currently uploading" :
|
||||
//false,
|
||||
"This dataset is currently uploading": "此数据集正在上传中",
|
||||
// state: setting_metadata
|
||||
//"Metadata is being auto-detected" :
|
||||
//false,
|
||||
"Metadata is being auto-detected": "元数据正在被自动检测中",
|
||||
// state: running
|
||||
"This job is currently running": "正在运行的进程",
|
||||
"This job is currently running": "任务正在运行中",
|
||||
// state: paused
|
||||
//"This job is paused. Use the \"Resume Paused Jobs\" in the history menu to resume" :
|
||||
//false,
|
||||
'This job is paused. Use the "Resume Paused Jobs" in the history menu to resume':
|
||||
'此任务已暂停。使用历史菜单中的 "恢复已暂停的工作" 来恢复',
|
||||
// state: error
|
||||
"An error occurred with this dataset": "进程运行时出错",
|
||||
"An error occurred with this dataset": "此数据集发生错误",
|
||||
// state: empty
|
||||
"No data": "没有数据",
|
||||
// state: failed_metadata
|
||||
//"An error occurred setting the metadata for this dataset" :
|
||||
//false,
|
||||
"An error occurred setting the metadata for this dataset": "设置此数据集的元数据时发生错误",
|
||||
|
||||
// ajax error prefix
|
||||
//"There was an error getting the data for this dataset" :
|
||||
//false,
|
||||
"There was an error getting the data for this dataset": "获取此数据集的数据时出错",
|
||||
|
||||
// purged'd/del'd msg
|
||||
//"This dataset has been deleted and removed from disk" :
|
||||
//false,
|
||||
"This dataset has been deleted and removed from disk": "此数据集已被删除并从磁盘中删除",
|
||||
"This dataset has been deleted": "此数据集已被删除",
|
||||
"This dataset has been hidden": "此数据集已隐藏",
|
||||
"This dataset has been hidden": "此数据集已被隐藏",
|
||||
|
||||
format: "格式",
|
||||
database: "数据库",
|
||||
|
||||
// ---- hda-edit
|
||||
"Edit attributes": "编辑属性",
|
||||
//"Cannot edit attributes of datasets removed from disk" :
|
||||
//false,
|
||||
//"Undelete dataset to edit attributes" :
|
||||
//false,
|
||||
//"This dataset must finish uploading before it can be edited" :
|
||||
//false,
|
||||
//"This dataset is not yet editable" :
|
||||
//false,
|
||||
"Cannot edit attributes of datasets removed from disk": "无法编辑已从磁盘中删除的数据集的属性",
|
||||
"Undelete dataset to edit attributes": "取消删除数据集以编辑属性",
|
||||
"This dataset must finish uploading before it can be edited": "该数据集必须先上传完成, 然后才能编辑",
|
||||
"This dataset is not yet editable": "该数据集不可编辑",
|
||||
|
||||
Delete: "删除",
|
||||
//"Dataset is already deleted" :
|
||||
//false,
|
||||
"Dataset is already deleted": "数据集已被删除",
|
||||
|
||||
"View or report this error": "报告错误",
|
||||
"View or report this error": "查看或报告此错误",
|
||||
|
||||
"Run this job again": "重新运行",
|
||||
"Run this job again": "重新运行此任务",
|
||||
|
||||
Visualize: "图形",
|
||||
//"Visualize in" :
|
||||
//false,
|
||||
Visualize: "可视化",
|
||||
"Visualize in": "的可视化",
|
||||
|
||||
"Undelete it": "反删除",
|
||||
"Undelete it": "取消删除",
|
||||
"Permanently remove it from disk": "从磁盘中永久删除",
|
||||
"Unhide it": "取消隐藏"
|
||||
"Unhide it": "取消隐藏",
|
||||
|
||||
//"You may be able to" :
|
||||
//false,
|
||||
//"set it manually or retry auto-detection" :
|
||||
//false,
|
||||
"You may be able to": "您可能可以",
|
||||
"set it manually or retry auto-detection": "手动设置或重试自动检测",
|
||||
|
||||
//"Edit dataset tags" :
|
||||
//false,
|
||||
//"Edit dataset annotation" :
|
||||
//false,
|
||||
"Edit dataset tags": "编辑数据集标签",
|
||||
"Edit dataset annotation": "编辑数据集备注",
|
||||
|
||||
"Tool Help": "工具帮助",
|
||||
|
||||
// ---------------------------------------------------------------------------- admin
|
||||
"Search Tool Shed": "搜索 Tool Shed",
|
||||
"Monitor installing repositories": "",
|
||||
"Manage installed tools": "管理已安装的工具",
|
||||
"Reset metadata": "重置元数据",
|
||||
"Download local tool": "下载本地工具",
|
||||
"Tool lineage": false,
|
||||
"Reload a tool's configuration": "重新加载工具的配置",
|
||||
"Review tool migration stages": "查看工具迁移状态",
|
||||
"View Tool Error Logs": "查看工具错误日志",
|
||||
"Manage Display Whitelist": "管理显示白名单",
|
||||
"Manage Tool Dependencies": "管理工具依赖",
|
||||
Users: "用户",
|
||||
Groups: "组别",
|
||||
"API keys": false,
|
||||
"Impersonate a user": "模拟用户",
|
||||
Data: "数据",
|
||||
Quotas: "配额",
|
||||
Roles: "角色",
|
||||
"Local data": "本地数据",
|
||||
"Form Definitions": "定义表单",
|
||||
|
||||
// ---------------------------------------------------------------------------- Scratchbook
|
||||
"Enable/Disable Scratchbook": "启用/禁用 Scratchbook",
|
||||
"Show/Hide Scratchbook": "显示/隐藏 Scratchbook",
|
||||
|
||||
// ---------------------------------------------------------------------------- misc. MVC
|
||||
//"Tags" :
|
||||
//false,
|
||||
//"Annotation" :
|
||||
//false,
|
||||
//"Edit annotation" :
|
||||
//false,
|
||||
Tags: "标签",
|
||||
"Edit annotation": "编辑备注",
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
|
||||
ja: true,
|
||||
fr: true,
|
||||
zh: true
|
||||
});
|
||||
|
||||
@@ -19,13 +19,12 @@ import layout_modal from "layout/modal";
|
||||
_.extend(window, layout_modal);
|
||||
import async_save_text from "utils/async-save-text";
|
||||
window.async_save_text = async_save_text;
|
||||
import POPUPMENU from "ui/popupmenu";
|
||||
window.make_popupmenu = POPUPMENU.make_popupmenu;
|
||||
window.make_popup_menus = POPUPMENU.make_popup_menus;
|
||||
import Popupmenu from "ui/popupmenu";
|
||||
window.make_popupmenu = Popupmenu.make_popupmenu;
|
||||
window.make_popup_menus = Popupmenu.make_popup_menus;
|
||||
import init_tag_click_function from "ui/autocom_tagging";
|
||||
window.init_tag_click_function = init_tag_click_function;
|
||||
import TOURS from "mvc/tours";
|
||||
import QUERY_STRING from "utils/query-string-parsing";
|
||||
import Tours from "mvc/tours";
|
||||
// console.debug( 'galaxy globals loaded' );
|
||||
|
||||
// ============================================================================
|
||||
@@ -89,10 +88,7 @@ function init_refresh_on_change() {
|
||||
.change(function() {
|
||||
var select_field = $(this);
|
||||
var select_val = select_field.val();
|
||||
var refresh = false;
|
||||
|
||||
var ref_on_change_vals = select_field.attr("refresh_on_change_values");
|
||||
|
||||
if (ref_on_change_vals) {
|
||||
ref_on_change_vals = ref_on_change_vals.split(",");
|
||||
var last_selected_value = select_field.attr("last_selected_value");
|
||||
@@ -114,10 +110,7 @@ function init_refresh_on_change() {
|
||||
.click(function() {
|
||||
var select_field = $(this);
|
||||
var select_val = select_field.val();
|
||||
var refresh = false;
|
||||
|
||||
var ref_on_change_vals = select_field.attr("refresh_on_change_values");
|
||||
|
||||
if (ref_on_change_vals) {
|
||||
ref_on_change_vals = ref_on_change_vals.split(",");
|
||||
var last_selected_value = select_field.attr("last_selected_value");
|
||||
@@ -156,7 +149,7 @@ $(document).ready(() => {
|
||||
$("[title]").tooltip();
|
||||
}
|
||||
// Make popup menus.
|
||||
make_popup_menus();
|
||||
Popupmenu.make_popup_menus();
|
||||
|
||||
// Replace big selects.
|
||||
replace_big_select_inputs(20, 1500);
|
||||
@@ -165,7 +158,7 @@ $(document).ready(() => {
|
||||
// add use_panels=True and set target to self.
|
||||
$("a").click(function() {
|
||||
var anchor = $(this);
|
||||
var galaxy_main_exists = parent.frames && parent.frames.galaxy_main;
|
||||
var galaxy_main_exists = window.parent.frames && window.parent.frames.galaxy_main;
|
||||
if (anchor.attr("target") == "galaxy_main" && !galaxy_main_exists) {
|
||||
var href = anchor.attr("href");
|
||||
if (href.indexOf("?") == -1) {
|
||||
@@ -180,44 +173,26 @@ $(document).ready(() => {
|
||||
return anchor;
|
||||
});
|
||||
|
||||
var et = JSON.parse(sessionStorage.getItem("activeGalaxyTour"));
|
||||
if (et) {
|
||||
et = TOURS.hooked_tour_from_data(et);
|
||||
if (et && et.steps) {
|
||||
if (window && window.self === window.top) {
|
||||
// Only kick off a new tour if this is the toplevel window (non-iframe). This
|
||||
// functionality actually *could* be useful, but we'd need to handle it better and
|
||||
// come up with some design guidelines for tours jumping between windows.
|
||||
// Disabling for now.
|
||||
var tour = new Tour(
|
||||
_.extend(
|
||||
{
|
||||
steps: et.steps
|
||||
},
|
||||
TOURS.tour_opts
|
||||
)
|
||||
);
|
||||
tour.init();
|
||||
tour.restart();
|
||||
}
|
||||
}
|
||||
}
|
||||
Tours.activeGalaxyTourRunner();
|
||||
|
||||
function onloadWebhooks() {
|
||||
if (Galaxy.root !== undefined) {
|
||||
// Load all webhooks with the type 'onload'
|
||||
$.getJSON(`${Galaxy.root}api/webhooks/onload/all`, webhooks => {
|
||||
_.each(webhooks, webhook => {
|
||||
if (webhook.activate && webhook.script) {
|
||||
$("<script/>", { type: "text/javascript" })
|
||||
.text(webhook.script)
|
||||
.appendTo("head");
|
||||
$("<style/>", { type: "text/css" })
|
||||
.text(webhook.styles)
|
||||
.appendTo("head");
|
||||
}
|
||||
// Wait until Galaxy.config is loaded.
|
||||
if (Galaxy.config) {
|
||||
if (Galaxy.config.enable_webhooks) {
|
||||
// Load all webhooks with the type 'onload'
|
||||
$.getJSON(`${Galaxy.root}api/webhooks/onload/all`, webhooks => {
|
||||
_.each(webhooks, webhook => {
|
||||
if (webhook.activate && webhook.script) {
|
||||
$("<script/>", { type: "text/javascript" })
|
||||
.text(webhook.script)
|
||||
.appendTo("head");
|
||||
$("<style/>", { type: "text/css" })
|
||||
.text(webhook.styles)
|
||||
.appendTo("head");
|
||||
}
|
||||
});
|
||||
});
|
||||
});
|
||||
}
|
||||
} else {
|
||||
setTimeout(onloadWebhooks, 100);
|
||||
}
|
||||
|
||||
@@ -0,0 +1,138 @@
|
||||
import * as mod_toastr from "libs/toastr";
|
||||
import mod_group_model from "toolshed/groups/group-model";
|
||||
|
||||
// toolshed group detail view
|
||||
const GroupDetailView = Backbone.View.extend({
|
||||
el: "#groups_element",
|
||||
options: {},
|
||||
app: null,
|
||||
|
||||
initialize: function(options) {
|
||||
this.options = _.extend(this.options, options);
|
||||
this.app = window.globalTS.groups;
|
||||
|
||||
if (this.app.collection !== null) {
|
||||
this.model = this.app.collection.get(this.options.group_id);
|
||||
this.render();
|
||||
} else {
|
||||
this.fetchGroup();
|
||||
}
|
||||
},
|
||||
|
||||
fetchGroup: function(options) {
|
||||
const that = this;
|
||||
this.options = _.extend(this.options, options);
|
||||
this.model = new mod_group_model.Group({ id: this.options.group_id });
|
||||
this.model.fetch({
|
||||
success: function(model) {
|
||||
console.log("received data: ");
|
||||
console.log(model);
|
||||
that.render();
|
||||
},
|
||||
error: function(model, response) {
|
||||
if (typeof response.responseJSON !== "undefined") {
|
||||
mod_toastr.error(response.responseJSON.err_msg);
|
||||
} else {
|
||||
mod_toastr.error("An error occurred.");
|
||||
}
|
||||
}
|
||||
});
|
||||
},
|
||||
|
||||
render: function() {
|
||||
const template = this.templateRow();
|
||||
this.$el.html(template({ group: this.model }));
|
||||
$("#center [data-toggle]").tooltip();
|
||||
$("#center").css("overflow", "auto");
|
||||
},
|
||||
|
||||
templateRow: function() {
|
||||
return _.template(
|
||||
[
|
||||
"<div>",
|
||||
'<h3><%= _.escape(group.get("name")) %></h3>',
|
||||
'<p class="" style="color:gray;">',
|
||||
'A group of <%= group.get("members").length %> members with <%= group.get("repositories").length %> repositories and a total of <%= group.get("total_downloads") %> combined repository clones.</p>',
|
||||
|
||||
"<h3>Members</h3>",
|
||||
'<table class="grid table table-condensed">',
|
||||
"<thead>",
|
||||
"<th>Name</th>",
|
||||
"<th>Repositories</th>",
|
||||
"<th>Registered</th>",
|
||||
"</thead>",
|
||||
"<tbody>",
|
||||
'<% _.each(group.get("members"), function(member) { %>',
|
||||
"<tr>",
|
||||
"<td>",
|
||||
"<%= _.escape(member.username) %>",
|
||||
"</td>",
|
||||
"<td>",
|
||||
'<a data-toggle="tooltip" data-placement="top" title="Repositories of <%= _.escape(member.username) %>" href="/repository/browse_repositories_by_user?user_id=<%= member.id %>&use_panels=true" id="<%= member.id %>"><%= member.user_repos_count %></a>',
|
||||
"</td>",
|
||||
"<td>",
|
||||
"<%= member.time_created %>",
|
||||
"</td>",
|
||||
"</tr>",
|
||||
"<% }); %>",
|
||||
"</tbody>",
|
||||
"</table>",
|
||||
|
||||
"<h3>Repositories</h3>",
|
||||
'<table class="grid table table-condensed">',
|
||||
"<thead>",
|
||||
"<th>Name</th>",
|
||||
"<th>Description</th>",
|
||||
"<th>Clones</th>",
|
||||
"<th>Owner</th>",
|
||||
"<th>Categories</th>",
|
||||
"<th>Created</th>",
|
||||
"<th>Updated</th>",
|
||||
"<th>Rating</th>",
|
||||
"<th>Verified</th>",
|
||||
"</thead>",
|
||||
"<tbody>",
|
||||
'<% _.each(group.get("repositories"), function(repo) { %>',
|
||||
"<tr>",
|
||||
"<td>",
|
||||
'<a data-toggle="tooltip" data-placement="top" title="Details of <%= _.escape(repo.name) %>" href="/view/<%= _.escape(repo.owner) %>/<%= _escape(repo.name) %>" id="<%= repo.id %>"><%= _.escape(repo.name) %></a>',
|
||||
"</td>",
|
||||
"<td>",
|
||||
"<%= _.escape(repo.description) %>",
|
||||
"</td>",
|
||||
"<td>",
|
||||
"<%= repo.times_downloaded %>",
|
||||
"</td>",
|
||||
"<td>",
|
||||
"<%= _.escape(repo.owner) %>",
|
||||
"</td>",
|
||||
"<td>",
|
||||
"<% _.each((repo.categories), function(cat) { %>",
|
||||
'<a data-toggle="tooltip" data-placement="top" title="Repositories in <%= cat.name %>" href="/repository/browse_repositories_in_category?id=<%= cat.id %>&use_panels=true"><%= cat.name %></a><br/>',
|
||||
// '<%= repo.categories %>',
|
||||
"<% }); %>",
|
||||
"</td>",
|
||||
'<td data-toggle="tooltip" data-placement="top" title="<%= repo.time_created_full %>">',
|
||||
"<%= repo.time_created %>",
|
||||
"</td>",
|
||||
'<td data-toggle="tooltip" data-placement="top" title="<%= repo.time_updated_full %>">',
|
||||
"<%= repo.time_updated %>",
|
||||
"</td>",
|
||||
"<td>",
|
||||
"<%= repo.ratings_mean %>",
|
||||
"</td>",
|
||||
"<td>",
|
||||
"<%= repo.approved %>",
|
||||
"</td>",
|
||||
"</tr>",
|
||||
"<% }); %>",
|
||||
"</tbody>",
|
||||
"</table>",
|
||||
"</div>"
|
||||
].join("")
|
||||
);
|
||||
}
|
||||
});
|
||||
export default {
|
||||
GroupDetailView: GroupDetailView
|
||||
};
|
||||
@@ -0,0 +1,128 @@
|
||||
import * as mod_toastr from "libs/toastr";
|
||||
import mod_group_model from "toolshed/groups/group-model";
|
||||
import mod_group_row from "toolshed/groups/group-listrow-view";
|
||||
const GroupListView = Backbone.View.extend({
|
||||
el: "#groups_element",
|
||||
defaults: {},
|
||||
|
||||
/**
|
||||
* Initialize and fetch the groups from server.
|
||||
* Async render afterwards.
|
||||
* @param {object} options an object with options
|
||||
*/
|
||||
initialize: function(options) {
|
||||
this.options = _.defaults(this.options || {}, this.defaults, options);
|
||||
const that = this;
|
||||
window.globalTS.groups.collection = new mod_group_model.Groups();
|
||||
window.globalTS.groups.collection.fetch({
|
||||
success: function(model) {
|
||||
console.log("received data: ");
|
||||
console.log(model);
|
||||
that.render();
|
||||
},
|
||||
error: function(model, response) {
|
||||
if (typeof response.responseJSON !== "undefined") {
|
||||
mod_toastr.error(response.responseJSON.err_msg);
|
||||
} else {
|
||||
mod_toastr.error("An error occurred.");
|
||||
}
|
||||
}
|
||||
});
|
||||
},
|
||||
|
||||
fetch: function() {},
|
||||
|
||||
/**
|
||||
* Render the groups table from the object's own collection.
|
||||
*/
|
||||
render: function(options) {
|
||||
this.options = _.extend(this.options, options);
|
||||
$(".tooltip").hide();
|
||||
const template = this.templateGroupsList();
|
||||
this.$el.html(template({ length: window.globalTS.groups.collection.models.length }));
|
||||
this.renderRows(window.globalTS.groups.collection.models);
|
||||
$("#center [data-toggle]").tooltip();
|
||||
$("#center").css("overflow", "auto");
|
||||
},
|
||||
|
||||
/**
|
||||
* Render all given models as rows in the groups list
|
||||
* @param {array} groups_to_render array of group models to render
|
||||
*/
|
||||
renderRows: function(groups_to_render) {
|
||||
for (let i = 0; i < groups_to_render.length; i++) {
|
||||
const group = groups_to_render[i];
|
||||
this.renderOne({ group: group });
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Create a view for the given model and add it to the groups view.
|
||||
* @param {Group} model of the view that will be rendered
|
||||
*/
|
||||
renderOne: function(options) {
|
||||
const rowView = new mod_group_row.GroupListRowView(options);
|
||||
this.$el.find("#group_list_body").append(rowView.el);
|
||||
},
|
||||
|
||||
/**
|
||||
* Table heading was clicked, update sorting preferences and re-render.
|
||||
* @return {[type]} [description]
|
||||
*/
|
||||
// sort_clicked : function(){
|
||||
// if (Galaxy.libraries.preferences.get('sort_order') === 'asc'){
|
||||
// Galaxy.libraries.preferences.set({'sort_order': 'desc'});
|
||||
// } else {
|
||||
// Galaxy.libraries.preferences.set({'sort_order': 'asc'});
|
||||
// }
|
||||
// this.render();
|
||||
// },
|
||||
|
||||
/**
|
||||
* Sort the underlying collection according to the parameters received.
|
||||
* Currently supports only sorting by name.
|
||||
*/
|
||||
// sortLibraries: function(){
|
||||
// if (Galaxy.libraries.preferences.get('sort_by') === 'name'){
|
||||
// if (Galaxy.libraries.preferences.get('sort_order') === 'asc'){
|
||||
// this.collection.sortByNameAsc();
|
||||
// } else if (Galaxy.libraries.preferences.get('sort_order') === 'desc'){
|
||||
// this.collection.sortByNameDesc();
|
||||
// }
|
||||
// }
|
||||
// },
|
||||
|
||||
// MMMMMMMMMMMMMMMMMM
|
||||
// === TEMPLATES ====
|
||||
// MMMMMMMMMMMMMMMMMM
|
||||
|
||||
templateGroupsList: function() {
|
||||
const tmpl_array = [];
|
||||
|
||||
tmpl_array.push('<div id="groups">');
|
||||
tmpl_array.push("</div>");
|
||||
tmpl_array.push('<div class="groups_container table-responsive">');
|
||||
tmpl_array.push("<% if(length === 0) { %>");
|
||||
tmpl_array.push("<div>There are no groups yet.</div>");
|
||||
tmpl_array.push("<% } else{ %>");
|
||||
tmpl_array.push('<table class="grid table table-condensed">');
|
||||
tmpl_array.push(" <thead>");
|
||||
tmpl_array.push(" <th>Name</th>");
|
||||
// tmpl_array.push(' <th style="width:22%;">description</th>');
|
||||
tmpl_array.push(" <th>Members</th> ");
|
||||
tmpl_array.push(" <th>Repositories</th>");
|
||||
tmpl_array.push(" </thead>");
|
||||
tmpl_array.push(' <tbody id="group_list_body">');
|
||||
// group item views will attach here
|
||||
tmpl_array.push(" </tbody>");
|
||||
tmpl_array.push("</table>");
|
||||
tmpl_array.push("<% }%>");
|
||||
tmpl_array.push("</div>");
|
||||
|
||||
return _.template(tmpl_array.join(""));
|
||||
}
|
||||
});
|
||||
|
||||
export default {
|
||||
GroupListView: GroupListView
|
||||
};
|
||||
+13
-17
@@ -1,36 +1,32 @@
|
||||
define([],
|
||||
function() {
|
||||
|
||||
// toolshed group row view
|
||||
var GroupListRowView = Backbone.View.extend({
|
||||
const GroupListRowView = Backbone.View.extend({
|
||||
events: {},
|
||||
|
||||
initialize : function( options ){
|
||||
this.render( options.group );
|
||||
initialize: function(options) {
|
||||
this.render(options.group);
|
||||
},
|
||||
|
||||
render: function( group ){
|
||||
var tmpl = this.templateRow();
|
||||
this.setElement(tmpl( { group:group } ));
|
||||
render: function(group) {
|
||||
const tmpl = this.templateRow();
|
||||
this.setElement(tmpl({ group: group }));
|
||||
this.$el.show();
|
||||
return this;
|
||||
},
|
||||
|
||||
templateRow: function() {
|
||||
return _.template([
|
||||
return _.template(
|
||||
[
|
||||
'<tr class="" data-id="<%- group.get("id") %>">',
|
||||
'<td><a href="groups#/<%= group.get("id") %>"><%= _.escape(group.get("name")) %></a></td>',
|
||||
// '<td>description</td>',
|
||||
'<td><%= group.get("total_members") %></td>',
|
||||
'<td><%= group.get("total_repos") %></td>',
|
||||
'</tr>'
|
||||
].join(''));
|
||||
}
|
||||
|
||||
"</tr>"
|
||||
].join("")
|
||||
);
|
||||
}
|
||||
});
|
||||
|
||||
return {
|
||||
export default {
|
||||
GroupListRowView: GroupListRowView
|
||||
};
|
||||
|
||||
});
|
||||
@@ -0,0 +1,17 @@
|
||||
// ============================================================================
|
||||
// TS GROUP RELATED MODELS
|
||||
|
||||
const Group = Backbone.Model.extend({
|
||||
urlRoot: `${Galaxy.root}api/groups`
|
||||
});
|
||||
|
||||
const Groups = Backbone.Collection.extend({
|
||||
url: `${Galaxy.root}api/groups`,
|
||||
|
||||
model: Group
|
||||
});
|
||||
|
||||
export default {
|
||||
Group: Group,
|
||||
Groups: Groups
|
||||
};
|
||||
@@ -0,0 +1,42 @@
|
||||
// MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM
|
||||
// === MAIN TOOLSHED GROUP MODULE ====
|
||||
// MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM
|
||||
|
||||
import mod_group_list from "toolshed/groups/group-list-view";
|
||||
import mod_group_detail from "toolshed/groups/group-detail-view";
|
||||
|
||||
// ============================================================================
|
||||
// ROUTER
|
||||
const ToolshedRouter = Backbone.Router.extend({
|
||||
routes: {
|
||||
"": "groups",
|
||||
":group_id": "group_page"
|
||||
}
|
||||
});
|
||||
|
||||
const ToolshedGroups = Backbone.View.extend({
|
||||
groupListView: null,
|
||||
groupDetailView: null,
|
||||
collection: null,
|
||||
|
||||
initialize: function() {
|
||||
window.globalTS.groups = this;
|
||||
|
||||
this.ts_router = new ToolshedRouter();
|
||||
this.ts_router.on("route:groups", () => {
|
||||
window.globalTS.groups.groupListView = new mod_group_list.GroupListView();
|
||||
});
|
||||
|
||||
this.ts_router.on("route:group_page", group_id => {
|
||||
window.globalTS.groups.groupDetailView = new mod_group_detail.GroupDetailView({
|
||||
group_id: group_id
|
||||
});
|
||||
});
|
||||
|
||||
Backbone.history.start({ pushState: false });
|
||||
}
|
||||
});
|
||||
|
||||
export default {
|
||||
ToolshedGroups: ToolshedGroups
|
||||
};
|
||||
@@ -1,6 +1,7 @@
|
||||
import _l from "utils/localization";
|
||||
import * as _ from "libs/underscore";
|
||||
import * as d3 from "libs/d3";
|
||||
import * as d3 from "d3";
|
||||
import { event as currentEvent } from "d3";
|
||||
import visualization from "viz/visualization";
|
||||
import mod_utils from "utils/utils";
|
||||
import config from "utils/config";
|
||||
@@ -193,7 +194,6 @@ var CircsterView = Backbone.View.extend({
|
||||
tracks_start_radii = d3.range(radius_start, min_dimension / 2, dataset_arc_height + track_gap);
|
||||
|
||||
// Map from track start to bounds.
|
||||
var self = this;
|
||||
return _.map(tracks_start_radii, radius => [radius, radius + dataset_arc_height]);
|
||||
},
|
||||
|
||||
@@ -221,8 +221,8 @@ var CircsterView = Backbone.View.extend({
|
||||
.call(
|
||||
d3.behavior.zoom().on("zoom", () => {
|
||||
// Do zoom, drag.
|
||||
var scale = d3.event.scale;
|
||||
svg.attr("transform", `translate(${d3.event.translate}) scale(${scale})`);
|
||||
var scale = currentEvent.scale;
|
||||
svg.attr("transform", `translate(${currentEvent.translate}) scale(${scale})`);
|
||||
|
||||
// Propagate scale changes to views.
|
||||
if (self.scale !== scale) {
|
||||
@@ -628,7 +628,6 @@ var CircsterTrackView = Backbone.View.extend({
|
||||
* default data bounds.
|
||||
*/
|
||||
_update_data_bounds: function(new_bounds) {
|
||||
var old_bounds = this.data_bounds;
|
||||
this.data_bounds =
|
||||
new_bounds || this.get_data_bounds(this.track.get("data_manager").get_genome_wide_data(this.genome));
|
||||
this._transition_chrom_data();
|
||||
@@ -962,7 +961,6 @@ var CircsterQuantitativeTrackView = CircsterTrackView.extend({
|
||||
_data_bounds_ticks_fn: function() {
|
||||
// Closure vars.
|
||||
var self = this;
|
||||
visibleChroms = 0;
|
||||
|
||||
// Return function for locating ticks based on chrom arc data.
|
||||
return (
|
||||
@@ -1101,9 +1099,9 @@ var Circster = Backbone.View.extend({
|
||||
// load css
|
||||
mod_utils.cssLoadFile("static/style/circster.css");
|
||||
// -- Configure visualization --
|
||||
var genome = new visualization.Genome(galaxy_config.app.genome);
|
||||
var genome = new visualization.Genome(window.galaxy_config.app.genome);
|
||||
|
||||
var vis = new visualization.GenomeVisualization(galaxy_config.app.viz_config);
|
||||
var vis = new visualization.GenomeVisualization(window.galaxy_config.app.viz_config);
|
||||
|
||||
// Add Circster-specific config options.
|
||||
vis.get("config").add([
|
||||
@@ -1143,7 +1141,7 @@ var Circster = Backbone.View.extend({
|
||||
|
||||
// setup title
|
||||
$("#center .unified-panel-header-inner").append(
|
||||
`${galaxy_config.app.viz_config.title} ${galaxy_config.app.viz_config.dbkey}`
|
||||
`${window.galaxy_config.app.viz_config.title} ${window.galaxy_config.app.viz_config.dbkey}`
|
||||
);
|
||||
|
||||
// setup menu
|
||||
|
||||
@@ -2,13 +2,7 @@ import _l from "utils/localization";
|
||||
/**
|
||||
* Top-level trackster code, used for creating/loading visualizations and user interface elements.
|
||||
*/
|
||||
|
||||
// global variables
|
||||
var ui = null;
|
||||
var view = null;
|
||||
var browser_router = null;
|
||||
|
||||
// trackster viewer
|
||||
//import * as $ from 'jquery';
|
||||
import * as _ from "libs/underscore";
|
||||
import tracks from "viz/trackster/tracks";
|
||||
import visualization from "viz/visualization";
|
||||
@@ -25,6 +19,12 @@ import "libs/farbtastic";
|
||||
import "libs/jquery/jquery.form";
|
||||
import "libs/jquery/jquery.rating";
|
||||
import "ui/editable-text";
|
||||
|
||||
// trackster global variables
|
||||
var ui = null;
|
||||
var view = null;
|
||||
var browser_router = null;
|
||||
|
||||
/**
|
||||
* Base Object/Model for inhertiance.
|
||||
*/
|
||||
@@ -152,7 +152,7 @@ var TracksterUI = Base.extend({
|
||||
title: _l("Bookmarks"),
|
||||
on_click: function() {
|
||||
// HACK -- use style to determine if panel is hidden and hide/show accordingly.
|
||||
force_right_panel($("div#right").css("right") == "0px" ? "hide" : "show");
|
||||
window.force_right_panel($("div#right").css("right") == "0px" ? "hide" : "show");
|
||||
}
|
||||
},
|
||||
{
|
||||
@@ -250,7 +250,7 @@ var TracksterUI = Base.extend({
|
||||
// Create view.
|
||||
var self = this;
|
||||
|
||||
var view = new tracks.TracksterView(_.extend(view_config, { header: false }));
|
||||
view = new tracks.TracksterView(_.extend(view_config, { header: false }));
|
||||
|
||||
view.editor = true;
|
||||
$.when(view.load_chroms_deferred).then(chrom_info => {
|
||||
@@ -408,10 +408,10 @@ var TracksterView = Backbone.View.extend({
|
||||
});
|
||||
|
||||
// hide right panel
|
||||
force_right_panel("hide");
|
||||
window.force_right_panel("hide");
|
||||
|
||||
// check if id is available
|
||||
if (galaxy_config.app.id) {
|
||||
if (window.galaxy_config.app.id) {
|
||||
this.view_existing();
|
||||
} else if (query_string.get("dataset_id")) {
|
||||
this.choose_existing_or_new();
|
||||
@@ -459,7 +459,6 @@ var TracksterView = Backbone.View.extend({
|
||||
view_in_saved: function(dataset_params) {
|
||||
var tracks_grid = new GridView({
|
||||
url_base: `${Galaxy.root}visualization/list_tracks`,
|
||||
dict_format: true,
|
||||
embedded: true
|
||||
});
|
||||
Galaxy.modal.show({
|
||||
@@ -484,7 +483,7 @@ var TracksterView = Backbone.View.extend({
|
||||
// view
|
||||
view_existing: function() {
|
||||
// get config
|
||||
var viz_config = galaxy_config.app.viz_config;
|
||||
var viz_config = window.galaxy_config.app.viz_config;
|
||||
|
||||
// view
|
||||
view = ui.create_visualization(
|
||||
@@ -534,8 +533,11 @@ var TracksterView = Backbone.View.extend({
|
||||
|
||||
// select default
|
||||
var dbkeys_in_genomes = response.map(r => 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);
|
||||
if (
|
||||
window.galaxy_config.app.default_dbkey &&
|
||||
_.contains(dbkeys_in_genomes, window.galaxy_config.app.default_dbkey)
|
||||
) {
|
||||
$("#new-dbkey").val(window.galaxy_config.app.default_dbkey);
|
||||
}
|
||||
|
||||
// change focus
|
||||
@@ -589,7 +591,7 @@ var TracksterView = Backbone.View.extend({
|
||||
name: name,
|
||||
dbkey: dbkey
|
||||
},
|
||||
galaxy_config.app.gene_region
|
||||
window.galaxy_config.app.gene_region
|
||||
);
|
||||
|
||||
// initialize editor
|
||||
@@ -605,9 +607,9 @@ var TracksterView = Backbone.View.extend({
|
||||
$("#center .unified-panel-title").text(`${view.config.get_value("name")} (${view.dbkey})`);
|
||||
|
||||
// add dataset
|
||||
if (galaxy_config.app.add_dataset)
|
||||
if (window.galaxy_config.app.add_dataset)
|
||||
$.ajax({
|
||||
url: `${Galaxy.root}api/datasets/${galaxy_config.app.add_dataset}`,
|
||||
url: `${Galaxy.root}api/datasets/${window.galaxy_config.app.add_dataset}`,
|
||||
data: { hda_ldda: "hda", data_type: "track_config" },
|
||||
dataType: "json",
|
||||
success: function(track_data) {
|
||||
|
||||
@@ -46,14 +46,12 @@ var select_datasets = (filters, success_fn) => {
|
||||
var history_grid = new GridView({
|
||||
url_base: `${Galaxy.root}visualization/list_history_datasets`,
|
||||
filters: filters,
|
||||
dict_format: true,
|
||||
embedded: true
|
||||
});
|
||||
|
||||
// library dataset selection tab
|
||||
var library_grid = new GridView({
|
||||
url_base: `${Galaxy.root}visualization/list_library_datasets`,
|
||||
dict_format: true,
|
||||
embedded: true
|
||||
});
|
||||
|
||||
|
||||
@@ -221,7 +221,9 @@
|
||||
}
|
||||
}
|
||||
}
|
||||
span.nametags {
|
||||
div.nametags {
|
||||
max-height:26px;
|
||||
overflow-y: auto;
|
||||
span.label {
|
||||
display: inline-block;
|
||||
margin-right: 2px;
|
||||
@@ -698,4 +700,4 @@
|
||||
margin-right: 8px;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -104,7 +104,7 @@ th.button_heading{
|
||||
margin-right: 0.2em;
|
||||
}
|
||||
.toolbar-item{
|
||||
margin-left: 1em;
|
||||
margin-left: 0.5em;
|
||||
}
|
||||
}
|
||||
#libraries_element, #folder_items_element{
|
||||
@@ -112,7 +112,7 @@ th.button_heading{
|
||||
margin-left: 0.5em;
|
||||
}
|
||||
}
|
||||
.help-button{
|
||||
.right-float{
|
||||
float: right;
|
||||
}
|
||||
.pagination-sm{
|
||||
@@ -128,11 +128,6 @@ th.button_heading{
|
||||
.libimport-select-all{
|
||||
margin-left: 0.5em;
|
||||
}
|
||||
.library-modal-item{
|
||||
width:90%;
|
||||
margin-left: 1em;
|
||||
margin-right: 1em;
|
||||
}
|
||||
.paginator-bottom{
|
||||
width: 27em;
|
||||
margin-left: auto;
|
||||
@@ -143,60 +138,26 @@ th.button_heading{
|
||||
}
|
||||
|
||||
// Extra style for Galaxy modal
|
||||
.modal-content #selected_history_content{
|
||||
ul{
|
||||
list-style-type: none;
|
||||
.modal-content{
|
||||
.library_selected_history_content{
|
||||
ul{
|
||||
list-style-type: none;
|
||||
}
|
||||
}
|
||||
.library-collection-type-select, .library-collection-history-select{
|
||||
margin-left:auto;
|
||||
margin-right:auto;
|
||||
width:200px;
|
||||
min-width:200px;
|
||||
}
|
||||
.library-modal-item{
|
||||
width:90%;
|
||||
margin-left: auto;
|
||||
margin-right: auto;
|
||||
margin-top: 1.5em;
|
||||
}
|
||||
}
|
||||
|
||||
// Follows the style for the deprecated admin libraries interface
|
||||
#library-grid{
|
||||
ul{
|
||||
list-style: none;
|
||||
}
|
||||
span.expandLink{
|
||||
padding-left: 12px;
|
||||
display: inline-block;
|
||||
vertical-align: middle;
|
||||
background: url(../images/silk/resultset_next.png) no-repeat;
|
||||
}
|
||||
.folderRow.expanded span.expandLink{
|
||||
background: url(../images/silk/resultset_bottom.png) no-repeat;
|
||||
}
|
||||
.folderRow span.rowIcon{
|
||||
float: left;
|
||||
margin-right: 5px;
|
||||
width: 16px;
|
||||
height: 16px;
|
||||
display: inline-block;
|
||||
vertical-align: middle;
|
||||
background: url(../images/silk/folder.png);
|
||||
}
|
||||
.libraryItem-error{
|
||||
margin-right: 2px;
|
||||
padding: 0 2px 0 2px;
|
||||
border: 1px solid @state-danger-border;
|
||||
background: @state-danger-bg;
|
||||
}
|
||||
.libraryItem-queued{
|
||||
margin-right: 2px;
|
||||
padding: 0 2px 0 2px;
|
||||
border: 1px solid @state-default-border;
|
||||
background: @state-default-bg;
|
||||
}
|
||||
.libraryItem-running{
|
||||
margin-right: 2px;
|
||||
padding: 0 2px 0 2px;
|
||||
border: 1px solid @state-running-border;
|
||||
background: @state-running-bg;
|
||||
}
|
||||
.libraryItem-upload{
|
||||
margin-right: 2px;
|
||||
padding: 0 2px 0 2px;
|
||||
border: 1px solid @state-upload-border;
|
||||
background: @state-upload-bg;
|
||||
}
|
||||
}
|
||||
.libraryTitle{
|
||||
th{
|
||||
text-align: left;
|
||||
|
||||
@@ -1,80 +0,0 @@
|
||||
/**
|
||||
* Grunt task to fetch third-party libraries using bower and copy them to the proper location in client/.
|
||||
* @param {Object} grunt main grunt file
|
||||
* @return {Function} callback to build this task
|
||||
*/
|
||||
module.exports = function( grunt ){
|
||||
"use strict";
|
||||
|
||||
var dev_path = './galaxy/scripts',
|
||||
|
||||
// where to move fetched bower components into the build structure (libName: [ bower-location, libs-location ])
|
||||
libraryLocations = {
|
||||
'farbtastic': [ 'src/farbtastic.js', 'farbtastic.js' ],
|
||||
'bootstrap-tour': [ 'build/js/bootstrap-tour.js', 'bootstrap-tour.js' ],
|
||||
|
||||
// these need to be updated and tested
|
||||
//'jquery-form': [ 'jquery.form.js', 'jquery/jquery.form.js' ],
|
||||
//'jquery-autocomplete': [ 'src/jquery.autocomplete.js', 'jquery/jquery.autocomplete.js' ],
|
||||
//'select2': [ 'select2.js', 'jquery/select2.js' ],
|
||||
//'jStorage': [ 'jstorage.js', 'jquery/jstorage.js' ],
|
||||
//'dynatree': [ 'dist/jquery.dynatree.js', 'jquery/jquery.dynatree.js' ],
|
||||
//'jquery.event.drag-drop': [
|
||||
// [ 'event.drag/jquery.event.drag.js', 'jquery/jquery.event.drag.js' ],
|
||||
// [ 'event.drag/jquery.event.drop.js', 'jquery/jquery.event.drop.js' ]
|
||||
//],
|
||||
|
||||
// these are complicated by additional css/less
|
||||
//'toastr': [ 'toastr.js', 'toastr.js' ],
|
||||
//'wymeditor': [ 'dist/wymeditor/jquery.wymeditor.js', 'jquery/jquery.wymeditor.js' ],
|
||||
//'jstree': [ 'jstree.js', 'jquery/jstree.js' ],
|
||||
|
||||
// these have been customized by Galaxy
|
||||
//'bootstrap': [ 'dist/js/bootstrap.js', 'bootstrap.js' ],
|
||||
//'jquery-ui': [
|
||||
// // multiple components now
|
||||
// [ '', 'jquery/jquery-ui.js' ]
|
||||
//],
|
||||
|
||||
};
|
||||
|
||||
// call bower to install libraries and other external resources
|
||||
grunt.config( 'bower-install-simple', {
|
||||
options: {
|
||||
color: true
|
||||
},
|
||||
"prod": {
|
||||
options: {
|
||||
production: true
|
||||
}
|
||||
},
|
||||
"dev": {
|
||||
options: {
|
||||
production: false
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
/** copy external libraries from bower components to scripts/libs */
|
||||
function copyLibs(){
|
||||
var lib_path = dev_path + '/libs/';
|
||||
for( var libName in libraryLocations ){
|
||||
if( libraryLocations.hasOwnProperty( libName ) ){
|
||||
|
||||
var bower_dir = 'bower_components',
|
||||
location = libraryLocations[ libName ],
|
||||
source = [ bower_dir, libName, location[0] ].join( '/' ),
|
||||
destination = lib_path + location[1];
|
||||
|
||||
grunt.log.writeln( source + ' -> ' + destination );
|
||||
grunt.file.copy( source, destination );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
grunt.loadNpmTasks( 'grunt-bower-install-simple' );
|
||||
|
||||
grunt.registerTask( 'copy-libs', 'copy external libraries to src', copyLibs );
|
||||
grunt.registerTask( 'install-libs', 'fetch external libraries and copy to src',
|
||||
[ 'bower-install-simple:prod', 'copy-libs' ] );
|
||||
};
|
||||
@@ -1,123 +0,0 @@
|
||||
/**
|
||||
* Grunt task to compress/bundle static/scripts.
|
||||
* @param {Object} grunt main grunt file
|
||||
* @return {Function} callback to build this task
|
||||
*/
|
||||
module.exports = function( grunt ){
|
||||
"use strict";
|
||||
|
||||
var app = grunt.config.get( 'app' ),
|
||||
paths = grunt.config.get( 'paths' ),
|
||||
// uglify settings used when scripts are decompressed/not-minified
|
||||
decompressedSettings = {
|
||||
mangle : false,
|
||||
beautify : true,
|
||||
compress : {
|
||||
drop_debugger : false
|
||||
}
|
||||
},
|
||||
// uglify settings used when scripts are compressed/minified
|
||||
compressedSettings = {
|
||||
mangle : {
|
||||
screw_ie8 : true
|
||||
},
|
||||
compress : {
|
||||
// high compression options
|
||||
screw_ie8 : true,
|
||||
sequences: true,
|
||||
dead_code : true,
|
||||
drop_debugger : true,
|
||||
comparisons : true,
|
||||
conditionals : true,
|
||||
evaluate : true,
|
||||
booleans : true,
|
||||
loops : true,
|
||||
unused : true,
|
||||
hoist_funs : true,
|
||||
if_return : true,
|
||||
join_vars : true,
|
||||
cascade : true,
|
||||
}
|
||||
};
|
||||
|
||||
grunt.config( 'uglify', {
|
||||
target : {
|
||||
files: [{
|
||||
expand : true,
|
||||
cwd : paths.srcSymlink,
|
||||
// NOTE: do not use uglify in the apps dir (webpack will do that section)
|
||||
src : [ '**/*.js', '!apps/**/*.js' ],
|
||||
// src : '**/*.js',
|
||||
dest : paths.dist
|
||||
}],
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
if (grunt.option( 'develop' )){
|
||||
grunt.config( 'uglify.options', decompressedSettings );
|
||||
} else {
|
||||
grunt.config( 'uglify.target.options', {
|
||||
sourceMap : true,
|
||||
sourceMapName : function( path ){
|
||||
// rewrite name to have all source maps in 'static/maps'
|
||||
return path.replace( paths.dist, paths.maps ) + '.map';
|
||||
}
|
||||
});
|
||||
grunt.config( 'uglify.options', compressedSettings );
|
||||
}
|
||||
|
||||
// -------------------------------------------------------------------------- decompress for easier debugging
|
||||
grunt.registerTask( 'decompress', function(){
|
||||
grunt.log.writeln( "decompressing... (don't forget to call 'grunt' again before committing)" );
|
||||
grunt.config( 'uglify.options', decompressedSettings );
|
||||
grunt.config( 'uglify.target.options', {});
|
||||
grunt.task.run( 'uglify' );
|
||||
});
|
||||
// alias for symmetry
|
||||
grunt.registerTask( 'compress', [ 'uglify' ] );
|
||||
|
||||
// -------------------------------------------------------------------------- watch & copy,pack only those changed
|
||||
// use 'grunt watch' (from a new tab in your terminal) to have grunt re-copy changed files automatically
|
||||
grunt.config( 'watch', {
|
||||
watch: {
|
||||
// watch for changes in the src dir
|
||||
// NOTE: but not in the apps dir (which is only used by webpack)
|
||||
files: [ paths.srcSymlink + '/**', '!' + paths.srcSymlink + '/apps/**' ],
|
||||
tasks: [ 'uglify' ],
|
||||
options: {
|
||||
spawn: false
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
// outer scope variable for the event handler and onChange fn - begin with empty hash
|
||||
var changedFiles = Object.create(null);
|
||||
|
||||
// when files are changed, set the copy src and packScripts target to the filenames of the updated files
|
||||
var onChange = grunt.util._.debounce(function() {
|
||||
grunt.log.writeln( 'onChange, changedFiles:', Object.keys( changedFiles ) );
|
||||
grunt.config( 'uglify.target.files', [{
|
||||
expand : true,
|
||||
cwd : paths.srcSymlink,
|
||||
src : Object.keys( changedFiles ),
|
||||
dest : paths.dist
|
||||
}]);
|
||||
changedFiles = Object.create(null);
|
||||
}, 200);
|
||||
|
||||
var addChangedFile = function( action, filepath ) {
|
||||
// store each filepath in a Files obj, the debounced fn above will use it as an aggregate list for copying
|
||||
// we need to take galaxy/scripts out of the filepath or it will be copied to the wrong loc
|
||||
filepath = filepath.replace( paths.srcSymlink + '/', '' );
|
||||
// grunt.log.writeln( 'on.watch, filepath:', filepath );
|
||||
changedFiles[ filepath ] = action;
|
||||
onChange();
|
||||
};
|
||||
grunt.event.on( 'watch', addChangedFile );
|
||||
|
||||
// --------------------------------------------------------------------------
|
||||
grunt.loadNpmTasks( 'grunt-contrib-watch' );
|
||||
grunt.loadNpmTasks( 'grunt-contrib-uglify' );
|
||||
};
|
||||
+1
-2
@@ -34,8 +34,7 @@ var paths = {
|
||||
'jquery-mousewheel': [ 'jquery.mousewheel.js', 'jquery/jquery.mousewheel.js' ],
|
||||
'raven-js': ['dist/raven.js', 'raven.js'],
|
||||
'requirejs': [ 'require.js', 'require.js' ],
|
||||
'underscore': [ 'underscore.js', 'underscore.js' ],
|
||||
'vue': [ 'dist/vue.js', 'vue.js' ]
|
||||
'underscore': [ 'underscore.js', 'underscore.js' ]
|
||||
},
|
||||
libs: ['galaxy/scripts/libs/**/*.js']
|
||||
};
|
||||
|
||||
+7
-7
@@ -28,7 +28,7 @@
|
||||
"raven-js": "^3.17.0",
|
||||
"requirejs": "2",
|
||||
"underscore": "^1.8.3",
|
||||
"vue": "^2.5.3"
|
||||
"vue": "^2.5.9"
|
||||
},
|
||||
"scripts": {
|
||||
"watch": "gulp stage-libs && concurrently \"yarn run webpack-watch\" \"yarn run gulp watch\" \"yarn run style-watch\"",
|
||||
@@ -55,11 +55,11 @@
|
||||
"devDependencies": {
|
||||
"babel-core": "^6.24.0",
|
||||
"babel-loader": "^7.1.1",
|
||||
"babel-preset-env": "^1.3.2",
|
||||
"concurrently": "^3.4.0",
|
||||
"babel-preset-env": "^1.6.1",
|
||||
"concurrently": "^3.5.1",
|
||||
"css-loader": "^0.28.7",
|
||||
"del": "^3.0.0",
|
||||
"expose-loader": "^0.7.3",
|
||||
"expose-loader": "^0.7.4",
|
||||
"grunt": "^0.4.5",
|
||||
"grunt-bower-install-simple": "^1.1.0",
|
||||
"grunt-cli": "^0.1.13",
|
||||
@@ -87,11 +87,11 @@
|
||||
"karma-webpack": "^2.0.6",
|
||||
"phantomjs-polyfill": "0.0.2",
|
||||
"phantomjs-prebuilt": "^2.1.7",
|
||||
"prettier": "^1.7.4",
|
||||
"prettier": "^1.9.1",
|
||||
"qunitjs": "^2.4.1",
|
||||
"sinon": "^4.1.2",
|
||||
"vue-loader": "^13.5.0",
|
||||
"vue-template-compiler": "^2.5.3",
|
||||
"webpack": "^3.6.0"
|
||||
"vue-template-compiler": "^2.5.9",
|
||||
"webpack": "^3.10.0"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,146 +0,0 @@
|
||||
define([
|
||||
"libs/toastr",
|
||||
"toolshed/scripts/mvc/groups/group-model"
|
||||
],
|
||||
function(
|
||||
mod_toastr,
|
||||
mod_group_model
|
||||
) {
|
||||
|
||||
// toolshed group detail view
|
||||
var GroupDetailView = Backbone.View.extend({
|
||||
el: "#groups_element",
|
||||
options: {},
|
||||
app: null,
|
||||
|
||||
initialize : function( options ){
|
||||
this.options = _.extend( this.options, options );
|
||||
this.app = window.globalTS.groups;
|
||||
|
||||
if ( this.app.collection !== null ){
|
||||
this.model = this.app.collection.get( this.options.group_id );
|
||||
this.render();
|
||||
} else {
|
||||
this.fetchGroup();
|
||||
}
|
||||
|
||||
},
|
||||
|
||||
fetchGroup: function( options ){
|
||||
var that = this;
|
||||
this.options = _.extend( this.options, options );
|
||||
this.model = new mod_group_model.Group( { id: this.options.group_id } );
|
||||
this.model.fetch({
|
||||
success: function( model ){
|
||||
console.log( 'received data: ' );
|
||||
console.log( model );
|
||||
that.render();
|
||||
},
|
||||
error: function( model, response ){
|
||||
if ( typeof response.responseJSON !== "undefined" ){
|
||||
mod_toastr.error( response.responseJSON.err_msg );
|
||||
} else {
|
||||
mod_toastr.error( 'An error occurred.' );
|
||||
}
|
||||
}
|
||||
});
|
||||
},
|
||||
|
||||
render: function(){
|
||||
var template = this.templateRow();
|
||||
this.$el.html( template( { group: this.model } ) );
|
||||
$( "#center [data-toggle]" ).tooltip();
|
||||
$( "#center" ).css( 'overflow','auto' );
|
||||
},
|
||||
|
||||
templateRow: function() {
|
||||
return _.template([
|
||||
'<div>',
|
||||
'<h3><%= _.escape(group.get("name")) %></h3>',
|
||||
'<p class="" style="color:gray;">',
|
||||
'A group of <%= group.get("members").length %> members with <%= group.get("repositories").length %> repositories and a total of <%= group.get("total_downloads") %> combined repository clones.</p>',
|
||||
|
||||
'<h3>Members</h3>',
|
||||
'<table class="grid table table-condensed">',
|
||||
'<thead>',
|
||||
'<th>Name</th>',
|
||||
'<th>Repositories</th>',
|
||||
'<th>Registered</th>',
|
||||
'</thead>',
|
||||
'<tbody>',
|
||||
'<% _.each(group.get("members"), function(member) { %>',
|
||||
'<tr>',
|
||||
'<td>',
|
||||
'<%= _.escape(member.username) %>',
|
||||
'</td>',
|
||||
'<td>',
|
||||
'<a data-toggle="tooltip" data-placement="top" title="Repositories of <%= _.escape(member.username) %>" href="/repository/browse_repositories_by_user?user_id=<%= member.id %>&use_panels=true" id="<%= member.id %>"><%= member.user_repos_count %></a>',
|
||||
'</td>',
|
||||
'<td>',
|
||||
'<%= member.time_created %>',
|
||||
'</td>',
|
||||
'</tr>',
|
||||
'<% }); %>',
|
||||
'</tbody>',
|
||||
'</table>',
|
||||
|
||||
'<h3>Repositories</h3>',
|
||||
'<table class="grid table table-condensed">',
|
||||
'<thead>',
|
||||
'<th>Name</th>',
|
||||
'<th>Description</th>',
|
||||
'<th>Clones</th>',
|
||||
'<th>Owner</th>',
|
||||
'<th>Categories</th>',
|
||||
'<th>Created</th>',
|
||||
'<th>Updated</th>',
|
||||
'<th>Rating</th>',
|
||||
'<th>Verified</th>',
|
||||
'</thead>',
|
||||
'<tbody>',
|
||||
'<% _.each(group.get("repositories"), function(repo) { %>',
|
||||
'<tr>',
|
||||
'<td>',
|
||||
'<a data-toggle="tooltip" data-placement="top" title="Details of <%= _.escape(repo.name) %>" href="/view/<%= _.escape(repo.owner) %>/<%= _escape(repo.name) %>" id="<%= repo.id %>"><%= _.escape(repo.name) %></a>',
|
||||
'</td>',
|
||||
'<td>',
|
||||
'<%= _.escape(repo.description) %>',
|
||||
'</td>',
|
||||
'<td>',
|
||||
'<%= repo.times_downloaded %>',
|
||||
'</td>',
|
||||
'<td>',
|
||||
'<%= _.escape(repo.owner) %>',
|
||||
'</td>',
|
||||
'<td>',
|
||||
'<% _.each((repo.categories), function(cat) { %>',
|
||||
'<a data-toggle="tooltip" data-placement="top" title="Repositories in <%= cat.name %>" href="/repository/browse_repositories_in_category?id=<%= cat.id %>&use_panels=true"><%= cat.name %></a><br/>',
|
||||
// '<%= repo.categories %>',
|
||||
'<% }); %>',
|
||||
'</td>',
|
||||
'<td data-toggle="tooltip" data-placement="top" title="<%= repo.time_created_full %>">',
|
||||
'<%= repo.time_created %>',
|
||||
'</td>',
|
||||
'<td data-toggle="tooltip" data-placement="top" title="<%= repo.time_updated_full %>">',
|
||||
'<%= repo.time_updated %>',
|
||||
'</td>',
|
||||
'<td>',
|
||||
'<%= repo.ratings_mean %>',
|
||||
'</td>',
|
||||
'<td>',
|
||||
'<%= repo.approved %>',
|
||||
'</td>',
|
||||
'</tr>',
|
||||
'<% }); %>',
|
||||
'</tbody>',
|
||||
'</table>',
|
||||
'</div>'
|
||||
].join(''));
|
||||
}
|
||||
});
|
||||
return {
|
||||
GroupDetailView: GroupDetailView
|
||||
};
|
||||
|
||||
});
|
||||
|
||||
@@ -1,141 +0,0 @@
|
||||
define([
|
||||
"libs/toastr",
|
||||
"toolshed/scripts/mvc/groups/group-model",
|
||||
"toolshed/scripts/mvc/groups/group-listrow-view"
|
||||
|
||||
], function(
|
||||
mod_toastr,
|
||||
mod_group_model,
|
||||
mod_group_row
|
||||
){
|
||||
|
||||
var GroupListView = Backbone.View.extend({
|
||||
el: '#groups_element',
|
||||
defaults: {},
|
||||
|
||||
/**
|
||||
* Initialize and fetch the groups from server.
|
||||
* Async render afterwards.
|
||||
* @param {object} options an object with options
|
||||
*/
|
||||
initialize : function( options ){
|
||||
this.options = _.defaults( this.options || {}, this.defaults, options );
|
||||
var that = this;
|
||||
window.globalTS.groups.collection = new mod_group_model.Groups();
|
||||
window.globalTS.groups.collection.fetch({
|
||||
success: function( model ){
|
||||
console.log( 'received data: ' );
|
||||
console.log( model );
|
||||
that.render();
|
||||
},
|
||||
error: function( model, response ){
|
||||
if ( typeof response.responseJSON !== "undefined" ){
|
||||
mod_toastr.error( response.responseJSON.err_msg );
|
||||
} else {
|
||||
mod_toastr.error( 'An error occurred.' );
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
},
|
||||
|
||||
fetch: function(){
|
||||
},
|
||||
|
||||
/**
|
||||
* Render the groups table from the object's own collection.
|
||||
*/
|
||||
render: function ( options ) {
|
||||
this.options = _.extend( this.options, options );
|
||||
$( ".tooltip" ).hide();
|
||||
var template = this.templateGroupsList();
|
||||
this.$el.html( template( { length: window.globalTS.groups.collection.models.length } ) );
|
||||
this.renderRows( window.globalTS.groups.collection.models );
|
||||
$( "#center [data-toggle]" ).tooltip();
|
||||
$( "#center" ).css( 'overflow','auto' );
|
||||
},
|
||||
|
||||
/**
|
||||
* Render all given models as rows in the groups list
|
||||
* @param {array} groups_to_render array of group models to render
|
||||
*/
|
||||
renderRows: function( groups_to_render ){
|
||||
for ( var i = 0; i < groups_to_render.length; i++ ) {
|
||||
var group = groups_to_render[i];
|
||||
this.renderOne( { group: group } )
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Create a view for the given model and add it to the groups view.
|
||||
* @param {Group} model of the view that will be rendered
|
||||
*/
|
||||
renderOne: function( options ){
|
||||
var rowView = new mod_group_row.GroupListRowView( options );
|
||||
this.$el.find( '#group_list_body' ).append( rowView.el );
|
||||
},
|
||||
|
||||
/**
|
||||
* Table heading was clicked, update sorting preferences and re-render.
|
||||
* @return {[type]} [description]
|
||||
*/
|
||||
// sort_clicked : function(){
|
||||
// if (Galaxy.libraries.preferences.get('sort_order') === 'asc'){
|
||||
// Galaxy.libraries.preferences.set({'sort_order': 'desc'});
|
||||
// } else {
|
||||
// Galaxy.libraries.preferences.set({'sort_order': 'asc'});
|
||||
// }
|
||||
// this.render();
|
||||
// },
|
||||
|
||||
/**
|
||||
* Sort the underlying collection according to the parameters received.
|
||||
* Currently supports only sorting by name.
|
||||
*/
|
||||
// sortLibraries: function(){
|
||||
// if (Galaxy.libraries.preferences.get('sort_by') === 'name'){
|
||||
// if (Galaxy.libraries.preferences.get('sort_order') === 'asc'){
|
||||
// this.collection.sortByNameAsc();
|
||||
// } else if (Galaxy.libraries.preferences.get('sort_order') === 'desc'){
|
||||
// this.collection.sortByNameDesc();
|
||||
// }
|
||||
// }
|
||||
// },
|
||||
|
||||
// MMMMMMMMMMMMMMMMMM
|
||||
// === TEMPLATES ====
|
||||
// MMMMMMMMMMMMMMMMMM
|
||||
|
||||
templateGroupsList: function(){
|
||||
var tmpl_array = [];
|
||||
|
||||
tmpl_array.push('<div id="groups">');
|
||||
tmpl_array.push('</div>');
|
||||
tmpl_array.push('<div class="groups_container table-responsive">');
|
||||
tmpl_array.push('<% if(length === 0) { %>');
|
||||
tmpl_array.push('<div>There are no groups yet.</div>');
|
||||
tmpl_array.push('<% } else{ %>');
|
||||
tmpl_array.push('<table class="grid table table-condensed">');
|
||||
tmpl_array.push(' <thead>');
|
||||
tmpl_array.push(' <th>Name</th>');
|
||||
// tmpl_array.push(' <th style="width:22%;">description</th>');
|
||||
tmpl_array.push(' <th>Members</th> ');
|
||||
tmpl_array.push(' <th>Repositories</th>');
|
||||
tmpl_array.push(' </thead>');
|
||||
tmpl_array.push(' <tbody id="group_list_body">');
|
||||
// group item views will attach here
|
||||
tmpl_array.push(' </tbody>');
|
||||
tmpl_array.push('</table>');
|
||||
tmpl_array.push('<% }%>');
|
||||
tmpl_array.push('</div>');
|
||||
|
||||
return _.template(tmpl_array.join(''));
|
||||
},
|
||||
|
||||
});
|
||||
|
||||
return {
|
||||
GroupListView: GroupListView
|
||||
};
|
||||
|
||||
});
|
||||
@@ -1,22 +0,0 @@
|
||||
define([], function() {
|
||||
|
||||
// ============================================================================
|
||||
// TS GROUP RELATED MODELS
|
||||
|
||||
var Group = Backbone.Model.extend({
|
||||
urlRoot: Galaxy.root + 'api/groups',
|
||||
});
|
||||
|
||||
var Groups = Backbone.Collection.extend({
|
||||
url: Galaxy.root + 'api/groups',
|
||||
|
||||
model: Group,
|
||||
|
||||
});
|
||||
|
||||
return {
|
||||
Group: Group,
|
||||
Groups: Groups
|
||||
};
|
||||
|
||||
});
|
||||
@@ -1,51 +0,0 @@
|
||||
// MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM
|
||||
// === MAIN TOOLSHED GROUP MODULE ====
|
||||
// MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM
|
||||
|
||||
define([
|
||||
'toolshed/scripts/mvc/groups/group-list-view',
|
||||
'toolshed/scripts/mvc/groups/group-detail-view',
|
||||
'toolshed/scripts/mvc/groups/group-model'
|
||||
],
|
||||
function(
|
||||
mod_group_list,
|
||||
mod_group_detail,
|
||||
mod_group_model
|
||||
) {
|
||||
|
||||
// ============================================================================
|
||||
// ROUTER
|
||||
var ToolshedRouter = Backbone.Router.extend({
|
||||
routes: {
|
||||
"" : "groups",
|
||||
":group_id" : "group_page"
|
||||
}
|
||||
|
||||
});
|
||||
|
||||
var ToolshedGroups = Backbone.View.extend({
|
||||
groupListView: null,
|
||||
groupDetailView: null,
|
||||
collection: null,
|
||||
|
||||
initialize : function(){
|
||||
window.globalTS.groups = this;
|
||||
|
||||
this.ts_router = new ToolshedRouter();
|
||||
this.ts_router.on( 'route:groups', function() {
|
||||
window.globalTS.groups.groupListView = new mod_group_list.GroupListView();
|
||||
});
|
||||
|
||||
this.ts_router.on( 'route:group_page', function( group_id ) {
|
||||
window.globalTS.groups.groupDetailView = new mod_group_detail.GroupDetailView( { group_id: group_id } );
|
||||
});
|
||||
|
||||
Backbone.history.start({pushState: false});
|
||||
}
|
||||
});
|
||||
|
||||
return {
|
||||
ToolshedGroups: ToolshedGroups
|
||||
};
|
||||
|
||||
});
|
||||
@@ -25,7 +25,7 @@ var commonLibs = [
|
||||
"libs/farbtastic",
|
||||
"libs/bootstrap",
|
||||
"libs/bootstrap-tour",
|
||||
"libs/vue",
|
||||
"vue",
|
||||
// mvc
|
||||
"libs/underscore",
|
||||
"libs/backbone",
|
||||
|
||||
+23
-19
@@ -887,9 +887,9 @@ babel-plugin-transform-strict-mode@^6.24.1:
|
||||
babel-runtime "^6.22.0"
|
||||
babel-types "^6.24.1"
|
||||
|
||||
babel-preset-env@^1.3.2:
|
||||
version "1.6.0"
|
||||
resolved "https://registry.yarnpkg.com/babel-preset-env/-/babel-preset-env-1.6.0.tgz#2de1c782a780a0a5d605d199c957596da43c44e4"
|
||||
babel-preset-env@^1.6.1:
|
||||
version "1.6.1"
|
||||
resolved "https://registry.yarnpkg.com/babel-preset-env/-/babel-preset-env-1.6.1.tgz#a18b564cc9b9afdf4aae57ae3c1b0d99188e6f48"
|
||||
dependencies:
|
||||
babel-plugin-check-es2015-constants "^6.22.0"
|
||||
babel-plugin-syntax-trailing-function-commas "^6.22.0"
|
||||
@@ -1659,9 +1659,9 @@ concat-stream@~1.4.6:
|
||||
readable-stream "~1.1.9"
|
||||
typedarray "~0.0.5"
|
||||
|
||||
concurrently@^3.4.0:
|
||||
version "3.5.0"
|
||||
resolved "https://registry.yarnpkg.com/concurrently/-/concurrently-3.5.0.tgz#8cf1b7707a6916a78a4ff5b77bb04dec54b379b2"
|
||||
concurrently@^3.5.1:
|
||||
version "3.5.1"
|
||||
resolved "https://registry.yarnpkg.com/concurrently/-/concurrently-3.5.1.tgz#ee8b60018bbe86b02df13e5249453c6ececd2521"
|
||||
dependencies:
|
||||
chalk "0.5.1"
|
||||
commander "2.6.0"
|
||||
@@ -2616,9 +2616,9 @@ expand-tilde@^2.0.2:
|
||||
dependencies:
|
||||
homedir-polyfill "^1.0.1"
|
||||
|
||||
expose-loader@^0.7.3:
|
||||
version "0.7.3"
|
||||
resolved "https://registry.yarnpkg.com/expose-loader/-/expose-loader-0.7.3.tgz#35fbd3659789e4faa81f59de8b7e9fc39e466d51"
|
||||
expose-loader@^0.7.4:
|
||||
version "0.7.4"
|
||||
resolved "https://registry.yarnpkg.com/expose-loader/-/expose-loader-0.7.4.tgz#9bcdd3878b5da9107930b55a03f65afe90b3314a"
|
||||
|
||||
extend@^3.0.0, extend@~3.0.0, extend@~3.0.1:
|
||||
version "3.0.1"
|
||||
@@ -5894,10 +5894,14 @@ preserve@^0.2.0:
|
||||
version "0.2.0"
|
||||
resolved "https://registry.yarnpkg.com/preserve/-/preserve-0.2.0.tgz#815ed1f6ebc65926f865b310c0713bcb3315ce4b"
|
||||
|
||||
prettier@^1.7.0, prettier@^1.7.4:
|
||||
prettier@^1.7.0:
|
||||
version "1.8.2"
|
||||
resolved "https://registry.yarnpkg.com/prettier/-/prettier-1.8.2.tgz#bff83e7fd573933c607875e5ba3abbdffb96aeb8"
|
||||
|
||||
prettier@^1.9.1:
|
||||
version "1.9.1"
|
||||
resolved "https://registry.yarnpkg.com/prettier/-/prettier-1.9.1.tgz#41638a0d47c1efbd1b7d5a742aaa5548eab86d70"
|
||||
|
||||
pretty-bytes@^1.0.0:
|
||||
version "1.0.4"
|
||||
resolved "https://registry.yarnpkg.com/pretty-bytes/-/pretty-bytes-1.0.4.tgz#0a22e8210609ad35542f8c8d5d2159aff0751c84"
|
||||
@@ -7495,9 +7499,9 @@ vue-style-loader@^3.0.0:
|
||||
hash-sum "^1.0.2"
|
||||
loader-utils "^1.0.2"
|
||||
|
||||
vue-template-compiler@^2.5.3:
|
||||
version "2.5.3"
|
||||
resolved "https://registry.yarnpkg.com/vue-template-compiler/-/vue-template-compiler-2.5.3.tgz#ab631b0694e211a6aaf0d800102b37836aae36a4"
|
||||
vue-template-compiler@^2.5.9:
|
||||
version "2.5.9"
|
||||
resolved "https://registry.yarnpkg.com/vue-template-compiler/-/vue-template-compiler-2.5.9.tgz#7fabc73c8d3d12d32340cd86c5fc33e00e86d686"
|
||||
dependencies:
|
||||
de-indent "^1.0.2"
|
||||
he "^1.1.0"
|
||||
@@ -7506,9 +7510,9 @@ vue-template-es2015-compiler@^1.6.0:
|
||||
version "1.6.0"
|
||||
resolved "https://registry.yarnpkg.com/vue-template-es2015-compiler/-/vue-template-es2015-compiler-1.6.0.tgz#dc42697133302ce3017524356a6c61b7b69b4a18"
|
||||
|
||||
vue@^2.5.3:
|
||||
version "2.5.3"
|
||||
resolved "https://registry.yarnpkg.com/vue/-/vue-2.5.3.tgz#e1a3b1f49b6e93e574ce040b95cbc873912fecc1"
|
||||
vue@^2.5.9:
|
||||
version "2.5.9"
|
||||
resolved "https://registry.yarnpkg.com/vue/-/vue-2.5.9.tgz#b2380cd040915dca69881dafd121d760952e65f7"
|
||||
|
||||
walk-sync@0.3.1:
|
||||
version "0.3.1"
|
||||
@@ -7577,9 +7581,9 @@ webpack@^1.14.0:
|
||||
watchpack "^0.2.1"
|
||||
webpack-core "~0.6.9"
|
||||
|
||||
webpack@^3.6.0:
|
||||
version "3.7.0"
|
||||
resolved "https://registry.yarnpkg.com/webpack/-/webpack-3.7.0.tgz#9081d0b1ba4950e34ddc54195721eba4bf205e83"
|
||||
webpack@^3.10.0:
|
||||
version "3.10.0"
|
||||
resolved "https://registry.yarnpkg.com/webpack/-/webpack-3.10.0.tgz#5291b875078cf2abf42bdd23afe3f8f96c17d725"
|
||||
dependencies:
|
||||
acorn "^5.0.0"
|
||||
acorn-dynamic-import "^2.0.0"
|
||||
|
||||
@@ -920,6 +920,11 @@ use_interactive = True
|
||||
# create symlinks to them).
|
||||
#user_library_import_symlink_whitelist = None
|
||||
|
||||
# In conjunction or alternatively, Galaxy can restrict user library imports to
|
||||
# those files that the user can read (by checking basic unix permissions).
|
||||
# For this to work, the username has to match the username on the filesystem.
|
||||
#user_library_import_check_permissions = False
|
||||
|
||||
# Allow admins to paste filesystem paths during upload. For libraries this
|
||||
# adds an option to the admin library upload tool allowing admins to paste
|
||||
# filesystem paths to files and directories in a box, and these paths will be
|
||||
|
||||
@@ -248,6 +248,31 @@
|
||||
a requirement of the other.
|
||||
|
||||
-->
|
||||
<!-- <param id="requests_cpu">500m</param> -->
|
||||
<!-- <param id="requests_memory">500Mi</param> -->
|
||||
<!-- <param id="limits_cpu">2</param> -->
|
||||
<!-- <param id="limits_memory">2Gi</param> -->
|
||||
<!-- Kubernetes resource Requests and Limits
|
||||
Parameters above (requests_* and limits_*) set default minimal (requests) and
|
||||
maximal (limits) CPU and memory resources to be allocated to all containers in
|
||||
Kubernetes jobs by default.
|
||||
|
||||
Limits and requests for CPU resources are measured in cpu units. Fractional requests are allowed. A
|
||||
Container with requests_cpu of 0.5 is guaranteed half as much CPU as one that asks for 1 CPU. The
|
||||
expression 0.1 is equivalent to the expression 100m, which can be read as “one hundred millicpu”. Some
|
||||
people say “one hundred millicores”, and this is understood to mean the same thing. A request with a
|
||||
decimal point, like 0.1, is converted to 100m by the API, and precision finer than 1m is not allowed.
|
||||
For this reason, the form 100m might be preferred.
|
||||
|
||||
You can express memory as a plain integer or as a fixed-point integer using one of these suffixes: E,
|
||||
P, T, G, M, K. You can also use the power-of-two equivalents: Ei, Pi, Ti, Gi, Mi, Ki.
|
||||
For instance, "1Gi" stands for 1 Gigabyte, and "500Mi" stands for 500 megabytes. Using other formats
|
||||
will make the jobs fail as Kubernetes won't recognize the assignment.
|
||||
|
||||
For more details on the Kubernetes resources management, see:
|
||||
https://kubernetes.io/docs/concepts/configuration/manage-compute-resources-container/
|
||||
-->
|
||||
|
||||
</plugin>
|
||||
<plugin id="godocker" type="runner" load="galaxy.jobs.runners.godocker:GodockerJobRunner">
|
||||
<!-- Go-Docker is a batch computing/cluster management tool using Docker
|
||||
|
||||
@@ -52,6 +52,16 @@
|
||||
</object_store>
|
||||
-->
|
||||
|
||||
<!-- Sample Cloud Object Store
|
||||
<object_store type="cloud">
|
||||
<auth access_key="..." secret_key="..." />
|
||||
<bucket name="..." use_reduced_redundancy="False" />
|
||||
<cache path="database/object_store_cache" size="1000" />
|
||||
<extra_dir type="job_work" path="database/job_working_directory_s3"/>
|
||||
<extra_dir type="temp" path="database/tmp_s3"/>
|
||||
</object_store>
|
||||
-->
|
||||
|
||||
|
||||
</backends>
|
||||
</object_store>
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
<?xml version="1.0"?>
|
||||
<openid>
|
||||
<provider file="genomespace.xml" />
|
||||
<provider file="yahoo.xml" />
|
||||
<provider file="aol.xml" />
|
||||
<provider file="launchpad.xml" />
|
||||
</openid>
|
||||
|
||||
@@ -6,6 +6,6 @@
|
||||
# appropriate files.
|
||||
---
|
||||
-
|
||||
image: bgruening/docker-hicbrowser
|
||||
image: quay.io/bgruening/hicbrowser
|
||||
description: |
|
||||
A simple web browser to visualize Hi-C and other genomic tracks
|
||||
|
||||
@@ -38,6 +38,8 @@
|
||||
error : function(){},
|
||||
assert : function(){}
|
||||
};
|
||||
window.jQuery = window.jquery = window.$;
|
||||
define( 'jquery', [], function(){ return window.$; })
|
||||
require.config({
|
||||
baseUrl: Galaxy.root + "static/scripts/",
|
||||
paths: {
|
||||
@@ -64,4 +66,4 @@
|
||||
});
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
</html>
|
||||
|
||||
@@ -174,7 +174,7 @@ class TourGenerator(object):
|
||||
elif input.type == 'select':
|
||||
params = []
|
||||
if name in test_inputs:
|
||||
for option in self._tool.inputs[name].static_options:
|
||||
for option in input.static_options:
|
||||
for test_option in self._test.inputs[name]:
|
||||
if test_option == option[1]:
|
||||
params.append(option[0])
|
||||
|
||||
@@ -58,17 +58,7 @@ $(document).ready(function() {
|
||||
},
|
||||
|
||||
_generateTour: function(data) {
|
||||
var tourData = Tours.default.hooked_tour_from_data(data);
|
||||
sessionStorage.setItem('activeGalaxyTour', JSON.stringify(data));
|
||||
|
||||
// Generate and run the tour
|
||||
var tour = new Tour(_.extend({
|
||||
steps: tourData.steps
|
||||
}));
|
||||
tour.init();
|
||||
tour.goTo(0);
|
||||
tour.restart();
|
||||
|
||||
var tour = Tours.giveTourWithData(data);
|
||||
// Force ending the tour when pressing the Execute button
|
||||
$('#execute').on('mousedown', function() {
|
||||
if (tour) {
|
||||
|
||||
@@ -59,7 +59,7 @@ galaxy_status() {
|
||||
if [[ $(grep '\[server:' $RUN_IN/config/galaxy.ini|awk -F'(:)|(])' '{ print $2 }') == 'main' ]]
|
||||
then
|
||||
echo -n "$SERVICE_NAME status: "
|
||||
status -p $RUN_IN/paster.pid galaxy
|
||||
status -p $RUN_IN/galaxy.pid galaxy
|
||||
else
|
||||
for proc in $(grep '\[server:' $RUN_IN/config/galaxy.ini|awk -F'(:)|(])' '{ print $2 }')
|
||||
do
|
||||
|
||||
@@ -13,10 +13,15 @@ import os
|
||||
import socket
|
||||
import sys
|
||||
import time
|
||||
import urllib2
|
||||
import warnings
|
||||
from user import home
|
||||
|
||||
from six.moves.urllib.request import (
|
||||
build_opener,
|
||||
HTTPCookieProcessor,
|
||||
Request
|
||||
)
|
||||
|
||||
with warnings.catch_warnings():
|
||||
warnings.simplefilter('ignore')
|
||||
import twill.commands as tc
|
||||
@@ -85,7 +90,7 @@ class Browser:
|
||||
dprint("no cookie jar at above path, creating")
|
||||
tc.save_cookies(self.cookie_jar)
|
||||
tc.load_cookies(self.cookie_jar)
|
||||
self.opener = urllib2.build_opener(urllib2.HTTPCookieProcessor(tc.get_browser().cj))
|
||||
self.opener = build_opener(HTTPCookieProcessor(tc.get_browser().cj))
|
||||
|
||||
def get(self, path):
|
||||
tc.go("%s%s" % (self.server, path))
|
||||
@@ -94,9 +99,9 @@ class Browser:
|
||||
def req(self, path, data=None, method=None):
|
||||
url = self.server + path
|
||||
if data:
|
||||
req = urllib2.Request(url, headers={'Content-Type': 'application/json'}, data=json.dumps(data))
|
||||
req = Request(url, headers={'Content-Type': 'application/json'}, data=json.dumps(data))
|
||||
else:
|
||||
req = urllib2.Request(url, headers={'Content-Type': 'application/json'})
|
||||
req = Request(url, headers={'Content-Type': 'application/json'})
|
||||
if method:
|
||||
req.get_method = lambda: method
|
||||
res = self.opener.open(req)
|
||||
|
||||
@@ -21,7 +21,7 @@ Many of the following instructions are best practices for any production applica
|
||||
|
||||
* Create a **NON-ROOT** user called galaxy. Running as an existing user will cause problems down the line when you want to grant or restrict access to data.
|
||||
* Start with a fresh checkout of Galaxy, don't try to convert one previously used for development. Download and install it in the galaxy user home directory.
|
||||
* Galaxy should be a managed system service (like Apache, mail servers, database servers, *etc.*) run by the galaxy user. Init scripts, OS X launchd definitions and Solaris SMF manifests are provided in the `contrib/` directory of the distribution. You can also use the `--daemon` and `--stop-daemon` arguments to `run.sh` to start and stop by hand, but still run detached. When running as a daemon the server's output log will be written to `paster.log` instead of the terminal, unless instructed otherwise with the `--log-file` argument.
|
||||
* Galaxy should be a managed system service (like Apache, mail servers, database servers, *etc.*) run by the galaxy user. Init scripts, OS X launchd definitions and Solaris SMF manifests are provided in the `contrib/` directory of the distribution. You can also use the `--daemon` and `--stop-daemon` arguments to `run.sh` to start and stop by hand, but still run detached. When running as a daemon the server's output log will be written to `galaxy.log` instead of the terminal, unless instructed otherwise with the `--log-file` argument.
|
||||
* Give Galaxy its own database user and database to prevent Galaxy's schema from conflicting with other tables in your database. Also, restrict Galaxy's database user so it only has access to its own database.
|
||||
* Make sure Galaxy is using a clean Python interpreter. Conflicts in $PYTHONPATH or the interpreter's `site-packages/` directory could cause problems. Galaxy manages its own dependencies for the framework, so you do not need to worry about these. The easiest way to do this is with a [virtualenv](http://pypi.python.org/pypi/virtualenv):
|
||||
|
||||
|
||||
@@ -180,7 +180,7 @@ For this to work, the user under which your nginx server runs will need read acc
|
||||
### External user authentication
|
||||
|
||||
- [Apache for External Authentication](https://galaxyproject.org/admin/config/apache-external-user-auth/)
|
||||
- [Built-in Galaxy External Authentication](../authentication.md)
|
||||
- [Built-in Galaxy External Authentication](../authentication.html)
|
||||
|
||||
### Display Sites
|
||||
|
||||
|
||||
@@ -1,4 +1,6 @@
|
||||
AGalaxy contains a plugin infrastructure for collecting and displaying metrics for job execution (see the original [pull request](https://bitbucket.org/galaxy/galaxy-central/pull-request/352) for implementation details).
|
||||
# Job Metrics
|
||||
|
||||
Galaxy contains a plugin infrastructure for collecting and displaying metrics for job execution (see the original [pull request](https://bitbucket.org/galaxy/galaxy-central/pull-request/352) for implementation details).
|
||||
|
||||
## Configuration
|
||||
|
||||
|
||||
@@ -304,4 +304,4 @@ server { # This you should already have.
|
||||
### External User Authentication
|
||||
|
||||
- [Nginx for External Authentication](https://galaxyproject.org/admin/config/nginx-external-user-auth/)
|
||||
- [Built-in Galaxy External Authentication](../authentication.md)
|
||||
- [Built-in Galaxy External Authentication](../authentication.html)
|
||||
|
||||
@@ -8,6 +8,11 @@ import os.path
|
||||
from markupsafe import escape
|
||||
|
||||
from galaxy import util
|
||||
from galaxy.exceptions import (
|
||||
AdminRequiredException,
|
||||
ConfigDoesNotAllowException,
|
||||
RequestParameterInvalidException,
|
||||
)
|
||||
from galaxy.tools.actions import upload_common
|
||||
from galaxy.tools.parameters import populate_state
|
||||
from galaxy.util.path import (
|
||||
@@ -19,6 +24,47 @@ from galaxy.util.path import (
|
||||
log = logging.getLogger(__name__)
|
||||
|
||||
|
||||
def validate_server_directory_upload(trans, server_dir):
|
||||
if server_dir in [None, 'None', '']:
|
||||
raise RequestParameterInvalidException("Invalid or unspecified server_dir parameter")
|
||||
|
||||
if trans.user_is_admin():
|
||||
import_dir = trans.app.config.library_import_dir
|
||||
import_dir_desc = 'library_import_dir'
|
||||
if not import_dir:
|
||||
raise ConfigDoesNotAllowException('"library_import_dir" is not set in the Galaxy configuration')
|
||||
else:
|
||||
import_dir = trans.app.config.user_library_import_dir
|
||||
if not import_dir:
|
||||
raise ConfigDoesNotAllowException('"user_library_import_dir" is not set in the Galaxy configuration')
|
||||
if server_dir != trans.user.email:
|
||||
import_dir = os.path.join(import_dir, trans.user.email)
|
||||
import_dir_desc = 'user_library_import_dir'
|
||||
|
||||
full_dir = os.path.join(import_dir, server_dir)
|
||||
unsafe = None
|
||||
if safe_relpath(server_dir):
|
||||
username = trans.user.username if trans.app.config.user_library_import_check_permissions else None
|
||||
if import_dir_desc == 'user_library_import_dir' and safe_contains(import_dir, full_dir, whitelist=trans.app.config.user_library_import_symlink_whitelist, username=username):
|
||||
for unsafe in unsafe_walk(full_dir, whitelist=[import_dir] + trans.app.config.user_library_import_symlink_whitelist):
|
||||
log.error('User attempted to import a path that resolves to a path outside of their import dir: %s -> %s', unsafe, os.path.realpath(unsafe))
|
||||
else:
|
||||
log.error('User attempted to import a directory path that resolves to a path outside of their import dir: %s -> %s', server_dir, os.path.realpath(full_dir))
|
||||
unsafe = True
|
||||
if unsafe:
|
||||
raise RequestParameterInvalidException("Invalid server_dir specified")
|
||||
|
||||
return full_dir, import_dir_desc
|
||||
|
||||
|
||||
def validate_path_upload(trans):
|
||||
if not trans.app.config.allow_library_path_paste:
|
||||
raise ConfigDoesNotAllowException('"allow_path_paste" is not set to True in the Galaxy configuration file')
|
||||
|
||||
if not trans.user_is_admin():
|
||||
raise AdminRequiredException('Uploading files via filesystem paths can only be performed by administrators')
|
||||
|
||||
|
||||
class LibraryActions(object):
|
||||
"""
|
||||
Mixin for controllers that provide library functionality.
|
||||
@@ -42,37 +88,11 @@ class LibraryActions(object):
|
||||
upload_option = kwd.get('upload_option', 'upload_file')
|
||||
response_code = 200
|
||||
if upload_option == 'upload_directory':
|
||||
if server_dir in [None, 'None', '']:
|
||||
response_code = 400
|
||||
if trans.user_is_admin():
|
||||
import_dir = trans.app.config.library_import_dir
|
||||
import_dir_desc = 'library_import_dir'
|
||||
else:
|
||||
import_dir = trans.app.config.user_library_import_dir
|
||||
if server_dir != trans.user.email:
|
||||
import_dir = os.path.join(import_dir, trans.user.email)
|
||||
import_dir_desc = 'user_library_import_dir'
|
||||
full_dir = os.path.join(import_dir, server_dir)
|
||||
unsafe = None
|
||||
if safe_relpath(server_dir):
|
||||
if import_dir_desc == 'user_library_import_dir' and safe_contains(import_dir, full_dir, whitelist=trans.app.config.user_library_import_symlink_whitelist):
|
||||
for unsafe in unsafe_walk(full_dir, whitelist=[import_dir] + trans.app.config.user_library_import_symlink_whitelist):
|
||||
log.error('User attempted to import a path that resolves to a path outside of their import dir: %s -> %s', unsafe, os.path.realpath(unsafe))
|
||||
else:
|
||||
log.error('User attempted to import a directory path that resolves to a path outside of their import dir: %s -> %s', server_dir, os.path.realpath(full_dir))
|
||||
unsafe = True
|
||||
if unsafe:
|
||||
response_code = 403
|
||||
message = 'Invalid server_dir'
|
||||
if import_dir:
|
||||
message = 'Select a directory'
|
||||
else:
|
||||
response_code = 403
|
||||
message = '"%s" is not defined in the Galaxy configuration file' % import_dir_desc
|
||||
full_dir, import_dir_desc = validate_server_directory_upload(trans, server_dir)
|
||||
message = 'Select a directory'
|
||||
elif upload_option == 'upload_paths':
|
||||
if not trans.app.config.allow_library_path_paste:
|
||||
response_code = 403
|
||||
message = '"allow_library_path_paste" is not defined in the Galaxy configuration file'
|
||||
# Library API already checked this - following check isn't actually needed.
|
||||
validate_path_upload(trans)
|
||||
# Some error handling should be added to this method.
|
||||
try:
|
||||
# FIXME: instead of passing params here ( which have been processed by util.Params(), the original kwd
|
||||
@@ -118,6 +138,43 @@ class LibraryActions(object):
|
||||
uploaded_datasets.append(self._make_library_uploaded_dataset(trans, params, name, file, 'server_dir', library_bunch))
|
||||
return uploaded_datasets, 200, None
|
||||
|
||||
def _get_server_dir_files(self, params, full_dir, import_dir_desc):
|
||||
files = []
|
||||
try:
|
||||
for entry in os.listdir(full_dir):
|
||||
# Only import regular files
|
||||
path = os.path.join(full_dir, entry)
|
||||
link_data_only = params.get('link_data_only', 'copy_files')
|
||||
if os.path.islink(full_dir) and link_data_only == 'link_to_files':
|
||||
# If we're linking instead of copying and the
|
||||
# sub-"directory" in the import dir is actually a symlink,
|
||||
# dereference the symlink, but not any of its contents.
|
||||
link_path = os.readlink(full_dir)
|
||||
if os.path.isabs(link_path):
|
||||
path = os.path.join(link_path, entry)
|
||||
else:
|
||||
path = os.path.abspath(os.path.join(link_path, entry))
|
||||
elif os.path.islink(path) and os.path.isfile(path) and link_data_only == 'link_to_files':
|
||||
# If we're linking instead of copying and the "file" in the
|
||||
# sub-directory of the import dir is actually a symlink,
|
||||
# dereference the symlink (one dereference only, Vasili).
|
||||
link_path = os.readlink(path)
|
||||
if os.path.isabs(link_path):
|
||||
path = link_path
|
||||
else:
|
||||
path = os.path.abspath(os.path.join(os.path.dirname(path), link_path))
|
||||
if os.path.isfile(path):
|
||||
files.append(path)
|
||||
except Exception as e:
|
||||
message = "Unable to get file list for configured %s, error: %s" % (import_dir_desc, str(e))
|
||||
response_code = 500
|
||||
return None, response_code, message
|
||||
if not files:
|
||||
message = "The directory '%s' contains no valid files" % full_dir
|
||||
response_code = 400
|
||||
return None, response_code, message
|
||||
return files, None, None
|
||||
|
||||
def _get_path_paste_uploaded_datasets(self, trans, params, library_bunch, response_code, message):
|
||||
preserve_dirs = util.string_as_bool(params.get('preserve_dirs', False))
|
||||
uploaded_datasets = []
|
||||
|
||||
@@ -371,9 +371,6 @@ class Configuration(object):
|
||||
# Beta containers interface used by GIEs
|
||||
self.enable_beta_containers_interface = string_as_bool(kwargs.get('enable_beta_containers_interface', 'False'))
|
||||
|
||||
# Deprecated API for sample tracking
|
||||
self.enable_legacy_sample_tracking_api = string_as_bool(kwargs.get('enable_legacy_sample_tracking_api', 'False'))
|
||||
|
||||
# Certain modules such as the pause module will automatically cause
|
||||
# workflows to be scheduled in job handlers the way all workflows will
|
||||
# be someday - the following two properties can also be used to force this
|
||||
@@ -429,6 +426,7 @@ class Configuration(object):
|
||||
self.library_import_dir = kwargs.get('library_import_dir', None)
|
||||
self.user_library_import_dir = kwargs.get('user_library_import_dir', None)
|
||||
self.user_library_import_symlink_whitelist = listify(kwargs.get('user_library_import_symlink_whitelist', []), do_strip=True)
|
||||
self.user_library_import_check_permissions = string_as_bool(kwargs.get('user_library_import_check_permissions', False))
|
||||
# Searching data libraries
|
||||
self.ftp_upload_dir = kwargs.get('ftp_upload_dir', None)
|
||||
self.ftp_upload_dir_identifier = kwargs.get('ftp_upload_dir_identifier', 'email') # attribute on user - email, username, id, etc...
|
||||
|
||||
@@ -46,7 +46,7 @@ SWARM_MANAGER_CONF_DEFAULTS = {
|
||||
'service_wait_count_limit': 0,
|
||||
'service_wait_time_limit': 5,
|
||||
'slots_min_limit': 0,
|
||||
'slots_max_limit': sys.maxint,
|
||||
'slots_max_limit': sys.maxsize,
|
||||
'slots_min_spare': 0,
|
||||
'node_idle_limit': 120,
|
||||
'limits': [],
|
||||
@@ -137,7 +137,7 @@ class SwarmManager(object):
|
||||
state = node_state['state']
|
||||
if not nodes:
|
||||
nodes = self._docker_interface.nodes()
|
||||
node = (filter(lambda x: x.name == name, nodes) + [None])[0]
|
||||
node = ([x for x in nodes if x.name == name] + [None])[0]
|
||||
if not node:
|
||||
if elapsed > self._conf.spawn_wait_time:
|
||||
log.warning("spawning node '%s' not found in `docker node ls` and spawn_wait_time exceeded! %d seconds have elapsed", name, elapsed)
|
||||
|
||||
+49
-181
@@ -16,12 +16,13 @@ from json import dumps
|
||||
|
||||
import h5py
|
||||
import pysam
|
||||
import pysam.bcftools
|
||||
from bx.seq.twobit import TWOBIT_MAGIC_NUMBER, TWOBIT_MAGIC_NUMBER_SWAP, TWOBIT_MAGIC_SIZE
|
||||
|
||||
from galaxy import util
|
||||
from galaxy.datatypes import metadata
|
||||
from galaxy.datatypes.metadata import DictParameter, ListParameter, MetadataElement, MetadataParameter
|
||||
from galaxy.util import FILENAME_VALID_CHARS, nice_size, sqlite, which
|
||||
from galaxy.util import FILENAME_VALID_CHARS, nice_size, sqlite
|
||||
from galaxy.util.checkers import is_bz2, is_gzip
|
||||
from . import data, dataproviders
|
||||
|
||||
@@ -206,186 +207,85 @@ class Bam(Binary):
|
||||
MetadataElement(name="column_types", default=['str', 'int', 'str', 'int', 'int', 'str', 'str', 'int', 'int', 'str', 'str', 'str'], desc="Column types", param=metadata.ColumnTypesParameter, readonly=True, visible=False, no_value=[])
|
||||
MetadataElement(name="column_names", default=['QNAME', 'FLAG', 'RNAME', 'POS', 'MAPQ', 'CIGAR', 'MRNM', 'MPOS', 'ISIZE', 'SEQ', 'QUAL', 'OPT'], desc="Column names", readonly=True, visible=False, optional=True, no_value=[])
|
||||
|
||||
def _get_samtools_version(self):
|
||||
version = '0.0.0'
|
||||
samtools_exec = which('samtools')
|
||||
if not samtools_exec:
|
||||
message = 'Attempting to use functionality requiring samtools, but it cannot be located on Galaxy\'s PATH.'
|
||||
raise Exception(message)
|
||||
|
||||
p = subprocess.Popen(['samtools', '--version-only'], stdout=subprocess.PIPE, stderr=subprocess.PIPE)
|
||||
output, error = p.communicate()
|
||||
# --version-only is available
|
||||
# Format is <version x.y.z>+htslib-<a.b.c>
|
||||
if p.returncode == 0:
|
||||
version = output.split('+')[0]
|
||||
return version
|
||||
|
||||
output = subprocess.Popen(['samtools'], stderr=subprocess.PIPE, stdout=subprocess.PIPE).communicate()[1]
|
||||
lines = output.split('\n')
|
||||
for line in lines:
|
||||
if line.lower().startswith('version'):
|
||||
# Assuming line looks something like: version: 0.1.12a (r862)
|
||||
version = line.split()[1]
|
||||
break
|
||||
return version
|
||||
|
||||
@staticmethod
|
||||
def merge(split_files, output_file):
|
||||
"""
|
||||
Merges BAM files
|
||||
|
||||
tmp_dir = tempfile.mkdtemp()
|
||||
stderr_name = tempfile.NamedTemporaryFile(dir=tmp_dir, prefix="bam_merge_stderr").name
|
||||
command = ["samtools", "merge", "-f", output_file] + split_files
|
||||
proc = subprocess.Popen(args=command, stderr=open(stderr_name, 'wb'))
|
||||
exit_code = proc.wait()
|
||||
# Did merge succeed?
|
||||
stderr = open(stderr_name).read().strip()
|
||||
if stderr:
|
||||
if exit_code != 0:
|
||||
shutil.rmtree(tmp_dir) # clean up
|
||||
raise Exception("Error merging BAM files: %s" % stderr)
|
||||
else:
|
||||
print(stderr)
|
||||
os.unlink(stderr_name)
|
||||
os.rmdir(tmp_dir)
|
||||
|
||||
def _is_coordinate_sorted(self, file_name):
|
||||
"""See if the input BAM file is sorted from the header information."""
|
||||
output = subprocess.check_output(["samtools", "view", "-H", file_name])
|
||||
return 'SO:coordinate' in output or 'SO:sorted' in output
|
||||
:param split_files: List of bam file paths to merge
|
||||
:param output_file: Write merged bam file to this location
|
||||
"""
|
||||
pysam.merge('-O', 'BAM', output_file, *split_files)
|
||||
|
||||
def dataset_content_needs_grooming(self, file_name):
|
||||
"""See if file_name is a sorted BAM file"""
|
||||
version = self._get_samtools_version()
|
||||
if version < '0.1.13':
|
||||
return not self._is_coordinate_sorted(file_name)
|
||||
else:
|
||||
# Samtools version 0.1.13 or newer produces an error condition when attempting to index an
|
||||
# unsorted bam file - see http://biostar.stackexchange.com/questions/5273/is-my-bam-file-sorted.
|
||||
# So when using a newer version of samtools, we'll first check if the input BAM file is sorted
|
||||
# from the header information. If the header is present and sorted, we do nothing by returning False.
|
||||
# If it's present and unsorted or if it's missing, we'll index the bam file to see if it produces the
|
||||
# error. If it does, sorting is needed so we return True (otherwise False).
|
||||
#
|
||||
# TODO: we're creating an index file here and throwing it away. We then create it again when
|
||||
# the set_meta() method below is called later in the job process. We need to enhance this overall
|
||||
# process so we don't create an index twice. In order to make it worth the time to implement the
|
||||
# upload tool / framework to allow setting metadata from directly within the tool itself, it should be
|
||||
# done generically so that all tools will have the ability. In testing, a 6.6 gb BAM file took 128
|
||||
# seconds to index with samtools, and 45 minutes to sort, so indexing is relatively inexpensive.
|
||||
if self._is_coordinate_sorted(file_name):
|
||||
return False
|
||||
index_name = tempfile.NamedTemporaryFile(prefix="bam_index").name
|
||||
stderr_name = tempfile.NamedTemporaryFile(prefix="bam_index_stderr").name
|
||||
proc = subprocess.Popen(['samtools', 'index', file_name, index_name], stderr=open(stderr_name, 'wb'))
|
||||
proc.wait()
|
||||
stderr = open(stderr_name).read().strip()
|
||||
if stderr:
|
||||
try:
|
||||
os.unlink(index_name)
|
||||
except OSError:
|
||||
pass
|
||||
try:
|
||||
os.unlink(stderr_name)
|
||||
except OSError:
|
||||
pass
|
||||
# Return True if unsorted error condition is found (find returns -1 if string is not found).
|
||||
return stderr.find("[bam_index_core] the alignment is not sorted") != -1
|
||||
try:
|
||||
os.unlink(index_name)
|
||||
except OSError:
|
||||
pass
|
||||
try:
|
||||
os.unlink(stderr_name)
|
||||
except OSError:
|
||||
pass
|
||||
return False
|
||||
"""
|
||||
Check if file_name is a coordinate-sorted BAM file
|
||||
"""
|
||||
# The best way to ensure that BAM files are coordinate-sorted and indexable
|
||||
# is to actually index them.
|
||||
index_name = tempfile.NamedTemporaryFile(prefix="bam_index").name
|
||||
try:
|
||||
# If pysam fails to index a file it will write to stderr,
|
||||
# and this causes the set_meta script to fail. So instead
|
||||
# we start another process and discard stderr.
|
||||
cmd = ['python', '-c', "import pysam; pysam.index('%s', '%s')" % (file_name, index_name)]
|
||||
with open(os.devnull, 'w') as devnull:
|
||||
subprocess.check_call(cmd, stderr=devnull, shell=False)
|
||||
needs_sorting = False
|
||||
except subprocess.CalledProcessError:
|
||||
needs_sorting = True
|
||||
try:
|
||||
os.unlink(index_name)
|
||||
except Exception:
|
||||
pass
|
||||
return needs_sorting
|
||||
|
||||
def groom_dataset_content(self, file_name):
|
||||
"""
|
||||
Ensures that the Bam file contents are sorted. This function is called
|
||||
Ensures that the BAM file contents are sorted. This function is called
|
||||
on an output dataset after the content is initially generated.
|
||||
"""
|
||||
# Use samtools to sort the Bam file
|
||||
# $ samtools sort
|
||||
# Usage: samtools sort [-on] [-m <maxMem>] <in.bam> <out.prefix>
|
||||
# Sort alignments by leftmost coordinates. File <out.prefix>.bam will be created.
|
||||
# This command may also create temporary files <out.prefix>.%d.bam when the
|
||||
# whole alignment cannot be fitted into memory ( controlled by option -m ).
|
||||
# Use pysam to sort the BAM file
|
||||
# This command may also creates temporary files <out.prefix>.%d.bam when the
|
||||
# whole alignment cannot fit into memory.
|
||||
# do this in a unique temp directory, because of possible <out.prefix>.%d.bam temp files
|
||||
if not self.dataset_content_needs_grooming(file_name):
|
||||
# Don't re-sort if already sorted
|
||||
return
|
||||
tmp_dir = tempfile.mkdtemp()
|
||||
tmp_sorted_dataset_file_name_prefix = os.path.join(tmp_dir, 'sorted')
|
||||
stderr_name = tempfile.NamedTemporaryFile(dir=tmp_dir, prefix="bam_sort_stderr").name
|
||||
samtools_created_sorted_file_name = "%s.bam" % tmp_sorted_dataset_file_name_prefix # samtools accepts a prefix, not a filename, it always adds .bam to the prefix
|
||||
proc = subprocess.Popen(['samtools', 'sort', file_name, tmp_sorted_dataset_file_name_prefix],
|
||||
cwd=tmp_dir, stderr=open(stderr_name, 'wb'))
|
||||
exit_code = proc.wait()
|
||||
# Did sort succeed?
|
||||
stderr = open(stderr_name).read().strip()
|
||||
if stderr:
|
||||
if exit_code != 0:
|
||||
shutil.rmtree(tmp_dir) # clean up
|
||||
raise Exception("Error Grooming BAM file contents: %s" % stderr)
|
||||
else:
|
||||
print(stderr)
|
||||
sorted_file_name = "%s.bam" % tmp_sorted_dataset_file_name_prefix
|
||||
slots = os.environ.get('GALAXY_SLOTS', 1)
|
||||
try:
|
||||
pysam.sort("-@%s" % slots, file_name, '-T', tmp_sorted_dataset_file_name_prefix, '-O', 'BAM', '-o', sorted_file_name)
|
||||
except Exception:
|
||||
shutil.rmtree(tmp_dir, ignore_errors=True)
|
||||
raise
|
||||
# Move samtools_created_sorted_file_name to our output dataset location
|
||||
shutil.move(samtools_created_sorted_file_name, file_name)
|
||||
shutil.move(sorted_file_name, file_name)
|
||||
# Remove temp file and empty temporary directory
|
||||
os.unlink(stderr_name)
|
||||
os.rmdir(tmp_dir)
|
||||
|
||||
def init_meta(self, dataset, copy_from=None):
|
||||
Binary.init_meta(self, dataset, copy_from=copy_from)
|
||||
|
||||
def set_meta(self, dataset, overwrite=True, **kwd):
|
||||
""" Creates the index for the BAM file. """
|
||||
# These metadata values are not accessible by users, always overwrite
|
||||
index_file = dataset.metadata.bam_index
|
||||
if not index_file:
|
||||
index_file = dataset.metadata.spec['bam_index'].param.new_file(dataset=dataset)
|
||||
# Create the Bam index
|
||||
# $ samtools index
|
||||
# Usage: samtools index <in.bam> [<out.index>]
|
||||
stderr_name = tempfile.NamedTemporaryFile(prefix="bam_index_stderr").name
|
||||
command = ['samtools', 'index', dataset.file_name, index_file.file_name]
|
||||
exit_code = subprocess.call(args=command, stderr=open(stderr_name, 'wb'))
|
||||
# Did index succeed?
|
||||
if exit_code == -6:
|
||||
# SIGABRT, most likely samtools 1.0+ which does not accept the index name parameter.
|
||||
dataset_symlink = os.path.join(os.path.dirname(index_file.file_name),
|
||||
'__dataset_%d_%s' % (dataset.id, os.path.basename(index_file.file_name)))
|
||||
os.symlink(dataset.file_name, dataset_symlink)
|
||||
try:
|
||||
command = ['samtools', 'index', dataset_symlink]
|
||||
exit_code = subprocess.call(args=command, stderr=open(stderr_name, 'wb'))
|
||||
shutil.move(dataset_symlink + '.bai', index_file.file_name)
|
||||
except Exception as e:
|
||||
open(stderr_name, 'ab+').write('Galaxy attempted to build the BAM index with samtools 1.0+ but failed: %s\n' % e)
|
||||
exit_code = 1 # Make sure an exception raised by shutil.move() is re-raised below
|
||||
finally:
|
||||
os.unlink(dataset_symlink)
|
||||
stderr = open(stderr_name).read().strip()
|
||||
if stderr:
|
||||
if exit_code != 0:
|
||||
os.unlink(stderr_name) # clean up
|
||||
raise Exception("Error Setting BAM Metadata: %s" % stderr)
|
||||
else:
|
||||
print(stderr)
|
||||
pysam.index(dataset.file_name, index_file.file_name)
|
||||
dataset.metadata.bam_index = index_file
|
||||
# Remove temp file
|
||||
os.unlink(stderr_name)
|
||||
# Now use pysam with BAI index to determine additional metadata
|
||||
try:
|
||||
bam_file = pysam.AlignmentFile(dataset.file_name, mode='rb', index_filename=index_file.file_name)
|
||||
# TODO: Reference names, lengths, read_groups and headers can become very large, truncate when necessary
|
||||
dataset.metadata.reference_names = list(bam_file.references)
|
||||
dataset.metadata.reference_lengths = list(bam_file.lengths)
|
||||
dataset.metadata.bam_header = bam_file.header
|
||||
dataset.metadata.read_groups = [read_group['ID'] for read_group in dataset.metadata.bam_header.get('RG', []) if 'ID' in read_group]
|
||||
dataset.metadata.sort_order = dataset.metadata.bam_header.get('HD', {}).get('SO', None)
|
||||
dataset.metadata.bam_version = dataset.metadata.bam_header.get('HD', {}).get('VN', None)
|
||||
dataset.metadata.sort_order = bam_file.header.get('HD', {}).get('SO', None)
|
||||
dataset.metadata.bam_version = bam_file.header.get('HD', {}).get('VN', None)
|
||||
except Exception:
|
||||
# Per Dan, don't log here because doing so will cause datasets that
|
||||
# fail metadata to end in the error state
|
||||
@@ -586,24 +486,7 @@ class CRAM(Binary):
|
||||
|
||||
def set_index_file(self, dataset, index_file):
|
||||
try:
|
||||
# @todo when pysam 1.2.1 or pysam 1.3.0 gets released and becomes
|
||||
# a dependency of galaxy, use pysam.index(alignment, target_idx)
|
||||
# This currently gives coredump in the current release but is
|
||||
# fixed in the dev branch:
|
||||
# xref: https://github.com/samtools/samtools/issues/199
|
||||
|
||||
dataset_symlink = os.path.join(os.path.dirname(index_file.file_name), '__dataset_%d_%s' % (dataset.id, os.path.basename(index_file.file_name)))
|
||||
os.symlink(dataset.file_name, dataset_symlink)
|
||||
pysam.index(dataset_symlink)
|
||||
|
||||
tmp_index = dataset_symlink + ".crai"
|
||||
if os.path.isfile(tmp_index):
|
||||
shutil.move(tmp_index, index_file.file_name)
|
||||
return index_file.file_name
|
||||
else:
|
||||
os.unlink(dataset_symlink)
|
||||
log.warning('%s, expected crai index not created for: %s', self, dataset.file_name)
|
||||
return False
|
||||
pysam.index(dataset.file_name, index_file.file_name)
|
||||
except Exception as exc:
|
||||
log.warning('%s, set_index_file Exception: %s', self, exc)
|
||||
return False
|
||||
@@ -635,13 +518,6 @@ class Bcf(BaseBcf):
|
||||
"""
|
||||
Class describing a (BGZF-compressed) BCF file
|
||||
|
||||
>>> from galaxy.datatypes.sniff import get_test_fname
|
||||
>>> fname = get_test_fname('1.bcf')
|
||||
>>> Bcf().sniff(fname)
|
||||
True
|
||||
>>> fname = get_test_fname('1.bcf_uncompressed')
|
||||
>>> Bcf().sniff(fname)
|
||||
False
|
||||
"""
|
||||
file_ext = "bcf"
|
||||
|
||||
@@ -665,24 +541,16 @@ class Bcf(BaseBcf):
|
||||
if not index_file:
|
||||
index_file = dataset.metadata.spec['bcf_index'].param.new_file(dataset=dataset)
|
||||
# Create the bcf index
|
||||
# $ bcftools index
|
||||
# Usage: bcftools index <in.bcf>
|
||||
|
||||
dataset_symlink = os.path.join(os.path.dirname(index_file.file_name),
|
||||
'__dataset_%d_%s' % (dataset.id, os.path.basename(index_file.file_name)))
|
||||
os.symlink(dataset.file_name, dataset_symlink)
|
||||
|
||||
stderr_name = tempfile.NamedTemporaryFile(prefix="bcf_index_stderr").name
|
||||
command = ['bcftools', 'index', dataset_symlink]
|
||||
try:
|
||||
subprocess.check_call(args=command, stderr=open(stderr_name, 'wb'))
|
||||
shutil.move(dataset_symlink + '.csi', index_file.file_name) # this will fail if bcftools < 1.0 is used, because it creates a .bci index file instead of .csi
|
||||
pysam.bcftools.index(dataset_symlink)
|
||||
shutil.move(dataset_symlink + '.csi', index_file.file_name)
|
||||
except Exception as e:
|
||||
stderr = open(stderr_name).read().strip()
|
||||
raise Exception('Error setting BCF metadata: %s' % (stderr or str(e)))
|
||||
raise Exception('Error setting BCF metadata: %s' % (str(e)))
|
||||
finally:
|
||||
# Remove temp file and symlink
|
||||
os.remove(stderr_name)
|
||||
os.remove(dataset_symlink)
|
||||
dataset.metadata.bcf_index = index_file
|
||||
|
||||
|
||||
@@ -5,6 +5,7 @@ Uses pysam to convert a CRAM file to a sorted bam file.
|
||||
usage: %prog in_file out_file
|
||||
"""
|
||||
import optparse
|
||||
import os
|
||||
|
||||
import pysam
|
||||
|
||||
@@ -14,7 +15,8 @@ def main():
|
||||
parser = optparse.OptionParser()
|
||||
(options, args) = parser.parse_args()
|
||||
input_fname, output_fname = args
|
||||
pysam.sort('-o', output_fname, '-O', 'bam', '-T', '.', input_fname)
|
||||
slots = os.getenv('GALAXY_SLOTS', 1)
|
||||
pysam.sort("-@%s" % slots, '-o', output_fname, '-O', 'bam', '-T', '.', input_fname)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
|
||||
@@ -21,20 +21,29 @@ def main():
|
||||
parser.add_option('-e', '--end-col', type='int', dest='end_col')
|
||||
parser.add_option('-P', '--preset', dest='preset')
|
||||
(options, args) = parser.parse_args()
|
||||
input_fname, index_fname, out_fname = args
|
||||
_, bgzip_fname, out_fname = args
|
||||
to_tabix(bgzip_fname=bgzip_fname,
|
||||
out_fname=out_fname,
|
||||
preset=options.preset,
|
||||
chrom_col=options.chrom_col,
|
||||
start_col=options.start_col,
|
||||
end_col=options.end_col)
|
||||
|
||||
|
||||
def to_tabix(bgzip_fname, out_fname, preset=None, chrom_col=None, start_col=None, end_col=None):
|
||||
# Create index.
|
||||
if options.preset:
|
||||
if preset:
|
||||
# Preset type.
|
||||
pysam.tabix_index(filename=index_fname, preset=options.preset, keep_original=True,
|
||||
index_filename=out_fname)
|
||||
bgzip_fname = pysam.tabix_index(filename=bgzip_fname, preset=preset, keep_original=True,
|
||||
index=out_fname, force=True)
|
||||
else:
|
||||
# For interval files; column indices are 0-based.
|
||||
pysam.tabix_index(filename=index_fname, seq_col=(options.chrom_col - 1),
|
||||
start_col=(options.start_col - 1), end_col=(options.end_col - 1),
|
||||
keep_original=True, index_filename=out_fname)
|
||||
if os.path.getsize(index_fname) == 0:
|
||||
bgzip_fname = pysam.tabix_index(filename=bgzip_fname, seq_col=(chrom_col - 1),
|
||||
start_col=(start_col - 1), end_col=(end_col - 1),
|
||||
keep_original=True, index=out_fname, force=True)
|
||||
if os.path.getsize(out_fname) == 0:
|
||||
sys.stderr.write("The converted tabix index file is empty, meaning the input data is invalid.")
|
||||
return bgzip_fname
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
|
||||
@@ -449,7 +449,7 @@ class ImzML(Binary):
|
||||
def generate_primary_file(self, dataset=None):
|
||||
rval = ['<html><head><title>imzML Composite Dataset </title></head><p/>']
|
||||
rval.append('<div>This composite dataset is composed of the following files:<p/><ul>')
|
||||
for composite_name, composite_file in self.get_composite_files(dataset=dataset).iteritems():
|
||||
for composite_name, composite_file in self.get_composite_files(dataset=dataset).items():
|
||||
fn = composite_name
|
||||
opt_text = ''
|
||||
if composite_file.get('description'):
|
||||
@@ -493,7 +493,7 @@ class Analyze75(Binary):
|
||||
def generate_primary_file(self, dataset=None):
|
||||
rval = ['<html><head><title>Analyze75 Composite Dataset.</title></head><p/>']
|
||||
rval.append('<div>This composite dataset is composed of the following files:<p/><ul>')
|
||||
for composite_name, composite_file in self.get_composite_files(dataset=dataset).iteritems():
|
||||
for composite_name, composite_file in self.get_composite_files(dataset=dataset).items():
|
||||
fn = composite_name
|
||||
opt_text = ''
|
||||
if composite_file.optional:
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
<tool id="__SET_METADATA__" name="Set External Metadata" version="1.0.1" tool_type="set_metadata">
|
||||
<type class="SetMetadataTool" module="galaxy.tools"/>
|
||||
<requirements>
|
||||
<requirement type="package">samtools</requirement>
|
||||
<requirement type="package" version="1.5">bcftools</requirement>
|
||||
</requirements>
|
||||
<action module="galaxy.tools.actions.metadata" class="SetMetadataToolAction"/>
|
||||
|
||||
@@ -104,7 +104,7 @@ def check_newlines(fname, bytes_to_read=52428800):
|
||||
return False
|
||||
|
||||
|
||||
def convert_newlines(fname, in_place=True, tmp_dir=None, tmp_prefix=None):
|
||||
def convert_newlines(fname, in_place=True, tmp_dir=None, tmp_prefix="gxupload"):
|
||||
"""
|
||||
Converts in place a file from universal line endings
|
||||
to Posix line endings.
|
||||
@@ -166,7 +166,7 @@ def sep2tabs(fname, in_place=True, patt="\\s+"):
|
||||
return (i, temp_name)
|
||||
|
||||
|
||||
def convert_newlines_sep2tabs(fname, in_place=True, patt="\\s+", tmp_dir=None, tmp_prefix=None):
|
||||
def convert_newlines_sep2tabs(fname, in_place=True, patt="\\s+", tmp_dir=None, tmp_prefix="gxupload"):
|
||||
"""
|
||||
Combines above methods: convert_newlines() and sep2tabs()
|
||||
so that files do not need to be read twice
|
||||
|
||||
@@ -15,6 +15,8 @@ import tempfile
|
||||
from cgi import escape
|
||||
from json import dumps
|
||||
|
||||
import pysam
|
||||
|
||||
from galaxy import util
|
||||
from galaxy.datatypes import binary, data, metadata
|
||||
from galaxy.datatypes.metadata import MetadataElement
|
||||
@@ -23,6 +25,7 @@ from galaxy.datatypes.sniff import (
|
||||
iter_headers
|
||||
)
|
||||
from galaxy.util import compression_utils
|
||||
from galaxy.util.checkers import is_gzip
|
||||
from . import dataproviders
|
||||
|
||||
if sys.version_info > (3,):
|
||||
@@ -733,6 +736,11 @@ class BaseVcf(Tabular):
|
||||
class Vcf(BaseVcf):
|
||||
file_ext = 'vcf'
|
||||
|
||||
def sniff(self, filename):
|
||||
if is_gzip(filename):
|
||||
return False
|
||||
return super(Vcf, self).sniff(filename)
|
||||
|
||||
|
||||
class VcfGz(BaseVcf, binary.Binary):
|
||||
file_ext = 'vcf_bgzip'
|
||||
@@ -740,33 +748,23 @@ class VcfGz(BaseVcf, binary.Binary):
|
||||
|
||||
MetadataElement(name="tabix_index", desc="Vcf Index File", param=metadata.FileParameter, file_ext="tbi", readonly=True, no_value=None, visible=False, optional=True)
|
||||
|
||||
def sniff(self, filename):
|
||||
if not is_gzip(filename):
|
||||
return False
|
||||
return super(VcfGz, self).sniff(filename)
|
||||
|
||||
def set_meta(self, dataset, **kwd):
|
||||
super(BaseVcf, self).set_meta(dataset, **kwd)
|
||||
""" Creates the index for the VCF file. """
|
||||
# These metadata values are not accessible by users, always overwrite
|
||||
index_file = dataset.metadata.bcf_index
|
||||
index_file = dataset.metadata.tabix_index
|
||||
if not index_file:
|
||||
index_file = dataset.metadata.spec['tabix_index'].param.new_file(dataset=dataset)
|
||||
# Create the bcf index
|
||||
# $ bcftools index
|
||||
# Usage: bcftools index <in.bcf>
|
||||
|
||||
dataset_symlink = os.path.join(os.path.dirname(index_file.file_name),
|
||||
'__dataset_%d_%s' % (dataset.id, os.path.basename(index_file.file_name))) + ".vcf.gz"
|
||||
os.symlink(dataset.file_name, dataset_symlink)
|
||||
|
||||
stderr_name = tempfile.NamedTemporaryFile(prefix="bcf_index_stderr").name
|
||||
command = ['bcftools', 'index', '-t', dataset_symlink]
|
||||
try:
|
||||
subprocess.check_call(args=command, stderr=open(stderr_name, 'wb'))
|
||||
shutil.move(dataset_symlink + '.tbi', index_file.file_name) # this will fail if bcftools < 1.0 is used, because it creates a .bci index file instead of .csi
|
||||
pysam.tabix_index(dataset.file_name, index=index_file.file_name, preset='vcf', force=True)
|
||||
except Exception as e:
|
||||
stderr = open(stderr_name).read().strip()
|
||||
raise Exception('Error setting BCF metadata: %s' % (stderr or str(e)))
|
||||
finally:
|
||||
# Remove temp file and symlink
|
||||
os.remove(stderr_name)
|
||||
os.remove(dataset_symlink)
|
||||
raise Exception('Error setting VCF.gz metadata: %s' % (str(e)))
|
||||
dataset.metadata.tabix_index = index_file
|
||||
|
||||
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user