Make it prettier, rebuild

This commit is contained in:
Dannon Baker
2017-10-17 12:58:15 -04:00
parent 2c26532c3a
commit 87873c5e2f
117 changed files with 13635 additions and 10859 deletions
+151 -110
View File
@@ -1,128 +1,169 @@
define(["mvc/toolshed/shed-list-view",
define(
[
"mvc/toolshed/shed-list-view",
"mvc/toolshed/categories-view",
"mvc/toolshed/repositories-view",
"mvc/toolshed/repository-view",
"mvc/toolshed/repository-queue-view",
"mvc/toolshed/repo-status-view",
"mvc/toolshed/workflows-view"],
function(mod_shed_list_view,
mod_categories_view,
mod_repositories_view,
mod_repository_view,
mod_repoqueue_view,
mod_repo_status_view,
mod_workflows_view) {
"mvc/toolshed/workflows-view"
],
function(
mod_shed_list_view,
mod_categories_view,
mod_repositories_view,
mod_repository_view,
mod_repoqueue_view,
mod_repo_status_view,
mod_workflows_view
) {
var AdminToolshedRouter = Backbone.Router.extend({
initialize: function() {
this.routesHit = 0;
// keep count of number of routes handled by the application
Backbone.history.on(
"route",
function() {
this.routesHit++;
},
this
);
this.bind("route", this.trackPageview);
},
var AdminToolshedRouter = Backbone.Router.extend({
routes: {
"": "toolsheds",
sheds: "toolsheds",
queue: "queue",
workflows: "workflows",
"status/r/:repositories": "status",
status: "status",
"categories/s/:tool_shed": "categories",
"category/s/:tool_shed/c/:cateory_id": "repositories",
"repository/s/:tool_shed/r/:repository_id": "repository"
},
initialize: function() {
this.routesHit = 0;
// keep count of number of routes handled by the application
Backbone.history.on( 'route', function() { this.routesHit++; }, this );
this.bind( 'route', this.trackPageview );
},
routes: {
"" : "toolsheds",
"sheds" : "toolsheds",
"queue" : "queue",
"workflows" : "workflows",
"status/r/:repositories" : "status",
"status" : "status",
"categories/s/:tool_shed" : "categories",
"category/s/:tool_shed/c/:cateory_id" : "repositories",
"repository/s/:tool_shed/r/:repository_id" : "repository",
},
/**
/**
* If more than one route has been hit the user did not land on current
* page directly so we can go back safely. Otherwise go to the home page.
* Use replaceState if available so the navigation doesn't create an
* extra history entry
*/
back: function() {
if( this.routesHit > 1 ) {
window.history.back();
} else {
this.navigate( '#', { trigger:true, replace:true } );
back: function() {
if (this.routesHit > 1) {
window.history.back();
} else {
this.navigate("#", { trigger: true, replace: true });
}
}
}
});
});
var GalaxyAdminToolshedApp = Backbone.View.extend({
app_config: {
known_views: [
"toolsheds",
"queue",
"status",
"categories",
"repositories",
"repoository"
]
},
var GalaxyAdminToolshedApp = Backbone.View.extend({
initialize: function() {
Galaxy.admintoolshedapp = this;
this.admin_toolshed_router = new AdminToolshedRouter();
app_config: {
known_views: ['toolsheds', 'queue', 'status', 'categories', 'repositories', 'repoository']
},
this.admin_toolshed_router.on("route:queue", function() {
if (Galaxy.admintoolshedapp.adminRepoQueueView) {
Galaxy.admintoolshedapp.adminRepoQueueView.reDraw();
} else {
Galaxy.admintoolshedapp.adminRepoQueueView = new mod_repoqueue_view.RepoQueueView();
}
});
this.admin_toolshed_router.on("route:toolsheds", function() {
if (Galaxy.admintoolshedapp.adminShedListView) {
Galaxy.admintoolshedapp.adminShedListView.reDraw();
} else {
Galaxy.admintoolshedapp.adminShedListView = new mod_shed_list_view.ShedListView();
}
});
this.admin_toolshed_router.on("route:categories", function(
tool_shed
) {
if (Galaxy.admintoolshedapp.adminShedCategoriesView) {
Galaxy.admintoolshedapp.adminShedCategoriesView.reDraw({
tool_shed: tool_shed.replace(/\//g, "%2f")
});
} else {
Galaxy.admintoolshedapp.adminShedCategoriesView = new mod_categories_view.CategoryView(
{ tool_shed: tool_shed.replace(/\//g, "%2f") }
);
}
});
this.admin_toolshed_router.on("route:repositories", function(
tool_shed,
category_id
) {
if (Galaxy.admintoolshedapp.adminShedCategoryView) {
Galaxy.admintoolshedapp.adminShedCategoryView.reDraw({
tool_shed: tool_shed.replace(/\//g, "%2f"),
category_id: category_id
});
} else {
Galaxy.admintoolshedapp.adminShedCategoryView = new mod_repositories_view.Category(
{
tool_shed: tool_shed.replace(/\//g, "%2f"),
category_id: category_id
}
);
}
});
this.admin_toolshed_router.on("route:repository", function(
tool_shed,
repository_id
) {
if (Galaxy.admintoolshedapp.adminRepositoryView) {
Galaxy.admintoolshedapp.adminRepositoryView.reDraw({
tool_shed: tool_shed.replace(/\//g, "%2f"),
repository_id: repository_id
});
} else {
Galaxy.admintoolshedapp.adminRepositoryView = new mod_repository_view.RepoDetails(
{
tool_shed: tool_shed.replace(/\//g, "%2f"),
repository_id: repository_id
}
);
}
});
this.admin_toolshed_router.on("route:status", function(
repositories
) {
if (Galaxy.admintoolshedapp.adminRepoStatusView) {
Galaxy.admintoolshedapp.adminRepoStatusView.reDraw({
repositories: repositories.split("|")
});
} else {
Galaxy.admintoolshedapp.adminRepoStatusView = new mod_repo_status_view.RepoStatus(
{ repositories: repositories.split("|") }
);
}
});
this.admin_toolshed_router.on("route:workflows", function() {
if (Galaxy.admintoolshedapp.adminWorkflowsView) {
Galaxy.admintoolshedapp.adminWorkflowsView.reDraw();
} else {
Galaxy.admintoolshedapp.adminWorkflowsView = new mod_workflows_view.Workflows();
}
});
initialize: function() {
Galaxy.admintoolshedapp = this;
this.admin_toolshed_router = new AdminToolshedRouter();
Backbone.history.start({ pushState: false });
}
});
this.admin_toolshed_router.on('route:queue', function() {
if (Galaxy.admintoolshedapp.adminRepoQueueView) {
Galaxy.admintoolshedapp.adminRepoQueueView.reDraw();
}
else {
Galaxy.admintoolshedapp.adminRepoQueueView = new mod_repoqueue_view.RepoQueueView();
}
});
this.admin_toolshed_router.on('route:toolsheds', function() {
if (Galaxy.admintoolshedapp.adminShedListView) {
Galaxy.admintoolshedapp.adminShedListView.reDraw();
}
else {
Galaxy.admintoolshedapp.adminShedListView = new mod_shed_list_view.ShedListView();
}
});
this.admin_toolshed_router.on('route:categories', function(tool_shed) {
if (Galaxy.admintoolshedapp.adminShedCategoriesView) {
Galaxy.admintoolshedapp.adminShedCategoriesView.reDraw({tool_shed: tool_shed.replace(/\//g, '%2f')});
}
else {
Galaxy.admintoolshedapp.adminShedCategoriesView = new mod_categories_view.CategoryView({tool_shed: tool_shed.replace(/\//g, '%2f')});
}
});
this.admin_toolshed_router.on('route:repositories', function(tool_shed, category_id) {
if (Galaxy.admintoolshedapp.adminShedCategoryView) {
Galaxy.admintoolshedapp.adminShedCategoryView.reDraw({tool_shed: tool_shed.replace(/\//g, '%2f'), category_id: category_id});
}
else {
Galaxy.admintoolshedapp.adminShedCategoryView = new mod_repositories_view.Category({tool_shed: tool_shed.replace(/\//g, '%2f'), category_id: category_id});
}
});
this.admin_toolshed_router.on('route:repository', function(tool_shed, repository_id) {
if (Galaxy.admintoolshedapp.adminRepositoryView) {
Galaxy.admintoolshedapp.adminRepositoryView.reDraw({tool_shed: tool_shed.replace(/\//g, '%2f'), repository_id: repository_id});
}
else {
Galaxy.admintoolshedapp.adminRepositoryView = new mod_repository_view.RepoDetails({tool_shed: tool_shed.replace(/\//g, '%2f'), repository_id: repository_id});
}
});
this.admin_toolshed_router.on('route:status', function(repositories) {
if (Galaxy.admintoolshedapp.adminRepoStatusView) {
Galaxy.admintoolshedapp.adminRepoStatusView.reDraw({repositories: repositories.split('|')});
}
else {
Galaxy.admintoolshedapp.adminRepoStatusView = new mod_repo_status_view.RepoStatus({repositories: repositories.split('|')});
}
});
this.admin_toolshed_router.on('route:workflows', function() {
if (Galaxy.admintoolshedapp.adminWorkflowsView) {
Galaxy.admintoolshedapp.adminWorkflowsView.reDraw();
}
else {
Galaxy.admintoolshedapp.adminWorkflowsView = new mod_workflows_view.Workflows();
}
});
Backbone.history.start({pushState: false});
},
});
return {
GalaxyApp: GalaxyAdminToolshedApp
};
});
return {
GalaxyApp: GalaxyAdminToolshedApp
};
}
);
+109 -71
View File
@@ -1,137 +1,175 @@
var jQuery = require( 'jquery' ),
var jQuery = require("jquery"),
$ = jQuery,
GalaxyApp = require( 'galaxy' ).GalaxyApp,
AdminPanel = require( './panels/admin-panel' ),
FormWrapper = require( 'mvc/form/form-wrapper' ),
GridView = require( 'mvc/grid/grid-view' ),
Ui = require( 'mvc/ui/ui-misc' ),
QueryStringParsing = require( 'utils/query-string-parsing' ),
Router = require( 'layout/router' ),
Utils = require( 'utils/utils' ),
Page = require( 'layout/page' );
GalaxyApp = require("galaxy").GalaxyApp,
AdminPanel = require("./panels/admin-panel"),
FormWrapper = require("mvc/form/form-wrapper"),
GridView = require("mvc/grid/grid-view"),
Ui = require("mvc/ui/ui-misc"),
QueryStringParsing = require("utils/query-string-parsing"),
Router = require("layout/router"),
Utils = require("utils/utils"),
Page = require("layout/page");
window.app = function app( options, bootstrapped ){
window.Galaxy = new GalaxyApp( options, bootstrapped );
Galaxy.debug( 'admin app' );
window.app = function app(options, bootstrapped) {
window.Galaxy = new GalaxyApp(options, bootstrapped);
Galaxy.debug("admin app");
/** Routes */
var AdminRouter = Router.extend({
routes: {
'(/)admin(/)users' : 'show_users',
'(/)admin(/)roles' : 'show_roles',
'(/)admin(/)groups' : 'show_groups',
'(/)admin(/)tool_versions' : 'show_tool_versions',
'(/)admin(/)quotas' : 'show_quotas',
'(/)admin(/)forms' : 'show_forms',
'(/)admin(/)form(/)(:form_id)' : 'show_form'
"(/)admin(/)users": "show_users",
"(/)admin(/)roles": "show_roles",
"(/)admin(/)groups": "show_groups",
"(/)admin(/)tool_versions": "show_tool_versions",
"(/)admin(/)quotas": "show_quotas",
"(/)admin(/)forms": "show_forms",
"(/)admin(/)form(/)(:form_id)": "show_form"
},
authenticate: function( args, name ) {
return Galaxy.user && Galaxy.user.id && Galaxy.user.get( 'is_admin' );
authenticate: function(args, name) {
return Galaxy.user && Galaxy.user.id && Galaxy.user.get("is_admin");
},
show_users: function() {
this.page.display( new GridView( { url_base: Galaxy.root + 'admin/users_list', url_data: Galaxy.params, dict_format: true } ) );
this.page.display(
new GridView({
url_base: Galaxy.root + "admin/users_list",
url_data: Galaxy.params,
dict_format: true
})
);
},
show_roles: function() {
this.page.display( new GridView( { url_base: Galaxy.root + 'admin/roles_list', url_data: Galaxy.params, dict_format: true } ) );
this.page.display(
new GridView({
url_base: Galaxy.root + "admin/roles_list",
url_data: Galaxy.params,
dict_format: true
})
);
},
show_groups: function() {
this.page.display( new GridView( { url_base: Galaxy.root + 'admin/groups_list', url_data: Galaxy.params, dict_format: true } ) );
this.page.display(
new GridView({
url_base: Galaxy.root + "admin/groups_list",
url_data: Galaxy.params,
dict_format: true
})
);
},
show_tool_versions: function() {
this.page.display( new GridView( { url_base: Galaxy.root + 'admin/tool_versions_list', url_data: Galaxy.params, dict_format: true } ) );
this.page.display(
new GridView({
url_base: Galaxy.root + "admin/tool_versions_list",
url_data: Galaxy.params,
dict_format: true
})
);
},
show_quotas: function() {
this.page.display( new GridView( { url_base: Galaxy.root + 'admin/quotas_list', url_data: Galaxy.params, dict_format: true } ) );
this.page.display(
new GridView({
url_base: Galaxy.root + "admin/quotas_list",
url_data: Galaxy.params,
dict_format: true
})
);
},
show_forms: function() {
this.page.display( new GridView( { url_base: Galaxy.root + 'forms/forms_list', url_data: Galaxy.params, dict_format: true } ) );
this.page.display(
new GridView({
url_base: Galaxy.root + "forms/forms_list",
url_data: Galaxy.params,
dict_format: true
})
);
},
show_form : function( form_id ) {
var id = '?id=' + QueryStringParsing.get( 'id' );
show_form: function(form_id) {
var id = "?id=" + QueryStringParsing.get("id");
var form_defs = {
reset_user_password: {
title : 'Reset passwords',
url : 'admin/reset_user_password' + id,
icon : 'fa-user',
submit_title : 'Save new password',
redirect : 'admin/users'
title: "Reset passwords",
url: "admin/reset_user_password" + id,
icon: "fa-user",
submit_title: "Save new password",
redirect: "admin/users"
},
manage_roles_and_groups_for_user: {
url : 'admin/manage_roles_and_groups_for_user' + id,
icon : 'fa-users',
redirect : 'admin/users'
url: "admin/manage_roles_and_groups_for_user" + id,
icon: "fa-users",
redirect: "admin/users"
},
manage_users_and_groups_for_role: {
url : 'admin/manage_users_and_groups_for_role' + id,
redirect : 'admin/roles'
url: "admin/manage_users_and_groups_for_role" + id,
redirect: "admin/roles"
},
manage_users_and_roles_for_group: {
url : 'admin/manage_users_and_roles_for_group' + id,
redirect : 'admin/groups'
url: "admin/manage_users_and_roles_for_group" + id,
redirect: "admin/groups"
},
manage_users_and_groups_for_quota: {
url : 'admin/manage_users_and_groups_for_quota' + id,
redirect : 'admin/quotas'
url: "admin/manage_users_and_groups_for_quota" + id,
redirect: "admin/quotas"
},
create_role: {
url : 'admin/create_role',
redirect : 'admin/roles'
url: "admin/create_role",
redirect: "admin/roles"
},
create_group: {
url : 'admin/create_group',
redirect : 'admin/groups'
url: "admin/create_group",
redirect: "admin/groups"
},
create_quota: {
url : 'admin/create_quota',
redirect : 'admin/quotas'
url: "admin/create_quota",
redirect: "admin/quotas"
},
rename_role: {
url : 'admin/rename_role' + id,
redirect : 'admin/roles'
url: "admin/rename_role" + id,
redirect: "admin/roles"
},
rename_group: {
url : 'admin/rename_group' + id,
redirect : 'admin/groups'
url: "admin/rename_group" + id,
redirect: "admin/groups"
},
rename_quota: {
url : 'admin/rename_quota' + id,
redirect : 'admin/quotas'
url: "admin/rename_quota" + id,
redirect: "admin/quotas"
},
edit_quota: {
url : 'admin/edit_quota' + id,
redirect : 'admin/quotas'
url: "admin/edit_quota" + id,
redirect: "admin/quotas"
},
set_quota_default: {
url : 'admin/set_quota_default' + id,
redirect : 'admin/quotas'
url: "admin/set_quota_default" + id,
redirect: "admin/quotas"
},
create_form: {
url : 'forms/create_form',
redirect : 'admin/forms'
url: "forms/create_form",
redirect: "admin/forms"
},
edit_form: {
url : 'forms/edit_form' + id,
redirect : 'admin/forms'
},
url: "forms/edit_form" + id,
redirect: "admin/forms"
}
};
this.page.display( new FormWrapper.View ( form_defs[ form_id ] ) );
this.page.display(new FormWrapper.View(form_defs[form_id]));
}
});
$(function() {
_.extend( options.config, { active_view : 'admin' } );
_.extend(options.config, { active_view: "admin" });
Utils.setWindowTitle("Administration");
Galaxy.page = new Page.View( _.extend( options, {
Left : AdminPanel,
Router : AdminRouter
} ) );
Galaxy.page = new Page.View(
_.extend(options, {
Left: AdminPanel,
Router: AdminRouter
})
);
});
};
+193 -124
View File
@@ -1,25 +1,25 @@
var jQuery = require( 'jquery' ),
var jQuery = require("jquery"),
$ = jQuery,
GalaxyApp = require( 'galaxy' ).GalaxyApp,
Router = require( 'layout/router' ),
ToolPanel = require( './panels/tool-panel' ),
HistoryPanel = require( './panels/history-panel' ),
Page = require( 'layout/page' ),
ToolForm = require( 'mvc/tool/tool-form' ),
FormWrapper = require( 'mvc/form/form-wrapper' ),
UserPreferences = require( 'mvc/user/user-preferences' ),
CustomBuilds = require( 'mvc/user/user-custom-builds' ),
Tours = require( 'mvc/tours' ),
GridView = require( 'mvc/grid/grid-view' ),
GridShared = require( 'mvc/grid/grid-shared' ),
Workflows = require( 'mvc/workflow/workflow' ),
HistoryList = require( 'mvc/history/history-list' ),
ToolFormComposite = require( 'mvc/tool/tool-form-composite' ),
QueryStringParsing = require( 'utils/query-string-parsing' ),
Utils = require( 'utils/utils' ),
Ui = require( 'mvc/ui/ui-misc' ),
DatasetError = require( 'mvc/dataset/dataset-error' ),
DatasetEditAttributes = require('mvc/dataset/dataset-edit-attributes');
GalaxyApp = require("galaxy").GalaxyApp,
Router = require("layout/router"),
ToolPanel = require("./panels/tool-panel"),
HistoryPanel = require("./panels/history-panel"),
Page = require("layout/page"),
ToolForm = require("mvc/tool/tool-form"),
FormWrapper = require("mvc/form/form-wrapper"),
UserPreferences = require("mvc/user/user-preferences"),
CustomBuilds = require("mvc/user/user-custom-builds"),
Tours = require("mvc/tours"),
GridView = require("mvc/grid/grid-view"),
GridShared = require("mvc/grid/grid-shared"),
Workflows = require("mvc/workflow/workflow"),
HistoryList = require("mvc/history/history-list"),
ToolFormComposite = require("mvc/tool/tool-form-composite"),
QueryStringParsing = require("utils/query-string-parsing"),
Utils = require("utils/utils"),
Ui = require("mvc/ui/ui-misc"),
DatasetError = require("mvc/dataset/dataset-error"),
DatasetEditAttributes = require("mvc/dataset/dataset-edit-attributes");
/** define the 'Analyze Data'/analysis/main/home page for Galaxy
* * has a masthead
@@ -32,195 +32,264 @@ var jQuery = require( 'jquery' ),
* * tables showing the contents of datasets
* * etc.
*/
window.app = function app( options, bootstrapped ){
window.Galaxy = new GalaxyApp( options, bootstrapped );
Galaxy.debug( 'analysis app' );
window.app = function app(options, bootstrapped) {
window.Galaxy = new GalaxyApp(options, bootstrapped);
Galaxy.debug("analysis app");
/** Routes */
var AnalysisRouter = Router.extend({
routes : {
'(/)' : 'home',
'(/)root*' : 'home',
'(/)tours(/)(:tour_id)' : 'show_tours',
'(/)user(/)' : 'show_user',
'(/)user(/)(:form_id)' : 'show_user_form',
'(/)workflow(/)' : 'show_workflows',
'(/)workflow/run(/)' : 'show_run',
'(/)pages(/)create(/)' : 'show_pages_create',
'(/)pages(/)edit(/)' : 'show_pages_edit',
'(/)pages(/)(:action_id)' : 'show_pages',
'(/)visualizations(/)edit(/)' : 'show_visualizations_edit',
'(/)visualizations/(:action_id)' : 'show_visualizations',
'(/)workflows/list_published(/)' : 'show_workflows_published',
'(/)histories(/)rename(/)' : 'show_histories_rename',
'(/)histories(/)permissions(/)' : 'show_histories_permissions',
'(/)histories(/)(:action_id)' : 'show_histories',
'(/)datasets(/)list(/)' : 'show_datasets',
'(/)workflow/import_workflow' : 'show_import_workflow',
'(/)custom_builds' : 'show_custom_builds',
'(/)datasets/edit': 'show_dataset_edit_attributes',
'(/)datasets/error': 'show_dataset_error'
routes: {
"(/)": "home",
"(/)root*": "home",
"(/)tours(/)(:tour_id)": "show_tours",
"(/)user(/)": "show_user",
"(/)user(/)(:form_id)": "show_user_form",
"(/)workflow(/)": "show_workflows",
"(/)workflow/run(/)": "show_run",
"(/)pages(/)create(/)": "show_pages_create",
"(/)pages(/)edit(/)": "show_pages_edit",
"(/)pages(/)(:action_id)": "show_pages",
"(/)visualizations(/)edit(/)": "show_visualizations_edit",
"(/)visualizations/(:action_id)": "show_visualizations",
"(/)workflows/list_published(/)": "show_workflows_published",
"(/)histories(/)rename(/)": "show_histories_rename",
"(/)histories(/)permissions(/)": "show_histories_permissions",
"(/)histories(/)(:action_id)": "show_histories",
"(/)datasets(/)list(/)": "show_datasets",
"(/)workflow/import_workflow": "show_import_workflow",
"(/)custom_builds": "show_custom_builds",
"(/)datasets/edit": "show_dataset_edit_attributes",
"(/)datasets/error": "show_dataset_error"
},
require_login: [
'show_user',
'show_user_form',
'show_workflows'
],
require_login: ["show_user", "show_user_form", "show_workflows"],
authenticate: function( args, name ) {
return ( Galaxy.user && Galaxy.user.id ) || this.require_login.indexOf( name ) == -1;
authenticate: function(args, name) {
return (
(Galaxy.user && Galaxy.user.id) ||
this.require_login.indexOf(name) == -1
);
},
show_tours : function( tour_id ){
if ( tour_id ){
Tours.giveTour( tour_id );
show_tours: function(tour_id) {
if (tour_id) {
Tours.giveTour(tour_id);
} else {
this.page.display( new Tours.ToursView() );
this.page.display(new Tours.ToursView());
}
},
show_user : function(){
this.page.display( new UserPreferences.View() );
show_user: function() {
this.page.display(new UserPreferences.View());
},
show_user_form : function( form_id ) {
var model = new UserPreferences.Model( { user_id: Galaxy.params.id } );
this.page.display( new FormWrapper.View ( model.get( form_id ) ) );
show_user_form: function(form_id) {
var model = new UserPreferences.Model({
user_id: Galaxy.params.id
});
this.page.display(new FormWrapper.View(model.get(form_id)));
},
show_visualizations : function( action_id ) {
this.page.display( new GridShared.View( { action_id: action_id, plural: 'Visualizations', item: 'visualization' } ) );
show_visualizations: function(action_id) {
this.page.display(
new GridShared.View({
action_id: action_id,
plural: "Visualizations",
item: "visualization"
})
);
},
show_visualizations_edit : function() {
this.page.display( new FormWrapper.View ( { url : 'visualization/edit?id=' + QueryStringParsing.get( 'id' ), redirect: 'visualizations/list' } ) );
show_visualizations_edit: function() {
this.page.display(
new FormWrapper.View({
url:
"visualization/edit?id=" + QueryStringParsing.get("id"),
redirect: "visualizations/list"
})
);
},
show_workflows_published : function() {
this.page.display( new GridView( { url_base: Galaxy.root + 'workflow/list_published', dict_format: true } ) );
show_workflows_published: function() {
this.page.display(
new GridView({
url_base: Galaxy.root + "workflow/list_published",
dict_format: true
})
);
},
show_histories : function( action_id ) {
this.page.display( new HistoryList.View( { action_id: action_id } ) );
show_histories: function(action_id) {
this.page.display(new HistoryList.View({ action_id: action_id }));
},
show_histories_rename : function() {
this.page.display( new FormWrapper.View ( { url : 'history/rename?id=' + QueryStringParsing.get( 'id' ), redirect: 'histories/list' } ) );
show_histories_rename: function() {
this.page.display(
new FormWrapper.View({
url: "history/rename?id=" + QueryStringParsing.get("id"),
redirect: "histories/list"
})
);
},
show_histories_permissions : function() {
this.page.display( new FormWrapper.View ( { url : 'history/permissions?id=' + QueryStringParsing.get( 'id' ), redirect: 'histories/list' } ) );
show_histories_permissions: function() {
this.page.display(
new FormWrapper.View({
url:
"history/permissions?id=" +
QueryStringParsing.get("id"),
redirect: "histories/list"
})
);
},
show_datasets : function() {
this.page.display( new GridView( { url_base: Galaxy.root + 'dataset/list', dict_format: true } ) );
show_datasets: function() {
this.page.display(
new GridView({
url_base: Galaxy.root + "dataset/list",
dict_format: true
})
);
},
show_pages : function( action_id ) {
this.page.display( new GridShared.View( { action_id: action_id, plural: 'Pages', item: 'page' } ) );
show_pages: function(action_id) {
this.page.display(
new GridShared.View({
action_id: action_id,
plural: "Pages",
item: "page"
})
);
},
show_pages_create : function() {
this.page.display( new FormWrapper.View ( { url : 'page/create', redirect: 'pages/list' } ) );
show_pages_create: function() {
this.page.display(
new FormWrapper.View({
url: "page/create",
redirect: "pages/list"
})
);
},
show_pages_edit : function() {
this.page.display( new FormWrapper.View ( { url : 'page/edit?id=' + QueryStringParsing.get( 'id' ), redirect: 'pages/list' } ) );
show_pages_edit: function() {
this.page.display(
new FormWrapper.View({
url: "page/edit?id=" + QueryStringParsing.get("id"),
redirect: "pages/list"
})
);
},
show_workflows : function(){
this.page.display( new Workflows.View() );
show_workflows: function() {
this.page.display(new Workflows.View());
},
show_run : function() {
show_run: function() {
this._loadWorkflow();
},
show_import_workflow : function() {
this.page.display( new Workflows.ImportWorkflowView() );
show_import_workflow: function() {
this.page.display(new Workflows.ImportWorkflowView());
},
show_custom_builds : function() {
show_custom_builds: function() {
var self = this;
var historyPanel = this.page.historyPanel.historyView;
if ( !historyPanel || !historyPanel.model || !historyPanel.model.id ) {
window.setTimeout(function() { self.show_custom_builds() }, 500)
if (
!historyPanel ||
!historyPanel.model ||
!historyPanel.model.id
) {
window.setTimeout(function() {
self.show_custom_builds();
}, 500);
return;
}
this.page.display( new CustomBuilds.View() );
this.page.display(new CustomBuilds.View());
},
show_dataset_edit_attributes : function() {
this.page.display( new DatasetEditAttributes.View() );
show_dataset_edit_attributes: function() {
this.page.display(new DatasetEditAttributes.View());
},
show_dataset_error : function() {
this.page.display( new DatasetError.View() );
show_dataset_error: function() {
this.page.display(new DatasetError.View());
},
/** */
home : function( params ){
home: function(params) {
// TODO: to router, remove Globals
// load a tool by id (tool_id) or rerun a previous tool execution (job_id)
if( params.tool_id || params.job_id ) {
if ( params.tool_id === 'upload1' ) {
if (params.tool_id || params.job_id) {
if (params.tool_id === "upload1") {
this.page.toolPanel.upload.show();
this._loadCenterIframe( 'welcome' );
this._loadCenterIframe("welcome");
} else {
this._loadToolForm( params );
this._loadToolForm(params);
}
} else {
// show the workflow run form
if( params.workflow_id ){
if (params.workflow_id) {
this._loadWorkflow();
// load the center iframe with controller.action: galaxy.org/?m_c=history&m_a=list -> history/list
} else if( params.m_c ){
this._loadCenterIframe( params.m_c + '/' + params.m_a );
// show the workflow run form
// load the center iframe with controller.action: galaxy.org/?m_c=history&m_a=list -> history/list
} else if (params.m_c) {
this._loadCenterIframe(params.m_c + "/" + params.m_a);
// show the workflow run form
} else {
this._loadCenterIframe( 'welcome' );
this._loadCenterIframe("welcome");
}
}
},
/** load the center panel with a tool form described by the given params obj */
_loadToolForm : function( params ){
_loadToolForm: function(params) {
//TODO: load tool form code async
params.id = decodeURIComponent( params.tool_id );
this.page.display( new ToolForm.View( params ) );
params.id = decodeURIComponent(params.tool_id);
this.page.display(new ToolForm.View(params));
},
/** load the center panel iframe using the given url */
_loadCenterIframe : function( url, root ){
_loadCenterIframe: function(url, root) {
root = root || Galaxy.root;
url = root + url;
this.page.$( '#galaxy_main' ).prop( 'src', url );
this.page.$("#galaxy_main").prop("src", url);
},
/** load workflow by its url in run mode */
_loadWorkflow: function() {
var self = this;
Utils.get({
url: Galaxy.root + 'api/workflows/' + Utils.getQueryString( 'id' ) + '/download?style=run',
success: function( response ) {
self.page.display( new ToolFormComposite.View( response ) );
url:
Galaxy.root +
"api/workflows/" +
Utils.getQueryString("id") +
"/download?style=run",
success: function(response) {
self.page.display(new ToolFormComposite.View(response));
},
error: function( response ) {
var error_msg = response.err_msg || 'Error occurred while loading the resource.';
var options = { 'message': error_msg, 'status': 'danger', 'persistent': true };
self.page.display( new Ui.Message( options ) );
error: function(response) {
var error_msg =
response.err_msg ||
"Error occurred while loading the resource.";
var options = {
message: error_msg,
status: "danger",
persistent: true
};
self.page.display(new Ui.Message(options));
}
});
}
});
// render and start the router
$(function(){
Galaxy.page = new Page.View( _.extend( options, {
Left : ToolPanel,
Right : HistoryPanel,
Router : AnalysisRouter
} ) );
$(function() {
Galaxy.page = new Page.View(
_.extend(options, {
Left: ToolPanel,
Right: HistoryPanel,
Router: AnalysisRouter
})
);
});
};
+29 -22
View File
@@ -1,40 +1,47 @@
var jQuery = require( 'jquery' ),
var jQuery = require("jquery"),
$ = jQuery,
GalaxyApp = require( 'galaxy' ).GalaxyApp,
_l = require( 'utils/localization' ),
Page = require( 'layout/page' );
GalaxyApp = require("galaxy").GalaxyApp,
_l = require("utils/localization"),
Page = require("layout/page");
window.app = function app( options, bootstrapped ){
window.Galaxy = new GalaxyApp( options, bootstrapped );
Galaxy.debug( 'login app' );
var redirect = encodeURI( options.redirect );
window.app = function app(options, bootstrapped) {
window.Galaxy = new GalaxyApp(options, bootstrapped);
Galaxy.debug("login app");
var redirect = encodeURI(options.redirect);
// TODO: remove iframe for user login (at least) and render login page from here
// then remove this redirect
if( !options.show_welcome_with_login ){
var params = jQuery.param({ use_panels : 'True', redirect : redirect });
window.location.href = Galaxy.root + 'user/login?' + params;
if (!options.show_welcome_with_login) {
var params = jQuery.param({ use_panels: "True", redirect: redirect });
window.location.href = Galaxy.root + "user/login?" + params;
return;
}
var LoginPage = Backbone.View.extend({
initialize: function( page ) {
initialize: function(page) {
this.page = page;
this.model = new Backbone.Model({ title : _l( 'Login required' ) } );
this.setElement( this._template() );
this.model = new Backbone.Model({ title: _l("Login required") });
this.setElement(this._template());
},
render: function() {
this.page.$( '#galaxy_main' ).prop( 'src', options.welcome_url );
this.page.$("#galaxy_main").prop("src", options.welcome_url);
},
_template : function() {
var login_url = options.root + 'user/login?' + $.param( { redirect : redirect } );
return '<iframe src="' + login_url + '" frameborder="0" style="width: 100%; height: 100%;"/>';
_template: function() {
var login_url =
options.root + "user/login?" + $.param({ redirect: redirect });
return (
'<iframe src="' +
login_url +
'" frameborder="0" style="width: 100%; height: 100%;"/>'
);
}
});
$(function(){
Galaxy.page = new Page.View( _.extend( options, {
Right : LoginPage
} ) );
$(function() {
Galaxy.page = new Page.View(
_.extend(options, {
Right: LoginPage
})
);
});
};
@@ -2,73 +2,107 @@
* Internal function to remove content from the main area and add the notebook.
* Not idempotent
*/
function append_notebook(url){
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>'
$("#main").append(
'<iframe frameBorder="0" seamless="seamless" style="width: 100%; height: 100%; overflow:hidden;" scrolling="no" src="' +
url +
'"></iframe>'
);
}
function clear_main_area(){
$('#spinner').remove();
$('#main').children().remove();
function clear_main_area() {
$("#spinner").remove();
$("#main")
.children()
.remove();
}
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;">');
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;">'
);
}
/* Create a spin_state object used by spin() and spin_again() */
function make_spin_state(type, ajax_timeout_init, ajax_timeout_max, ajax_timeout_step, sleep_init, sleep_max, sleep_step, log_attempts){
function make_spin_state(
type,
ajax_timeout_init,
ajax_timeout_max,
ajax_timeout_step,
sleep_init,
sleep_max,
sleep_step,
log_attempts
) {
var s = {
type: (typeof type !== 'undefined') ? type : "GIE spin",
ajax_timeout: (typeof ajax_timeout_init !== 'undefined') ? ajax_timeout_init : 2000,
ajax_timeout_max: (typeof ajax_timeout_max !== 'undefined') ? ajax_timeout_max : 16000,
ajax_timeout_step: (typeof ajax_timeout_step !== 'undefined') ? ajax_timeout_step : 500,
sleep: (typeof sleep_init !== 'undefined') ? sleep_init : 500,
sleep_max: (typeof sleep_max !== 'undefined') ? sleep_max : 8000,
sleep_step: (typeof sleep_step !== 'undefined') ? sleep_step : 100,
log_attempts: (typeof log_attempts !== 'undefined') ? log_attempts : true,
count: 0,
}
type: typeof type !== "undefined" ? type : "GIE spin",
ajax_timeout:
typeof ajax_timeout_init !== "undefined" ? ajax_timeout_init : 2000,
ajax_timeout_max:
typeof ajax_timeout_max !== "undefined" ? ajax_timeout_max : 16000,
ajax_timeout_step:
typeof ajax_timeout_step !== "undefined" ? ajax_timeout_step : 500,
sleep: typeof sleep_init !== "undefined" ? sleep_init : 500,
sleep_max: typeof sleep_max !== "undefined" ? sleep_max : 8000,
sleep_step: typeof sleep_step !== "undefined" ? sleep_step : 100,
log_attempts: typeof log_attempts !== "undefined" ? log_attempts : true,
count: 0
};
return s;
}
/* Log/display an error when spinning fails. */
function spin_error(console_msg, user_msg, clear){
function spin_error(console_msg, user_msg, clear) {
console.log(console_msg);
if(clear) clear_main_area();
if(typeof user_msg == "string"){
if (clear) clear_main_area();
if (typeof user_msg == "string") {
toastr.clear();
toastr.error(
user_msg,
"Error",
{'closeButton': true, 'timeOut': 0, 'extendedTimeOut': 0, 'tapToDismiss': false}
);
toastr.error(user_msg, "Error", {
closeButton: true,
timeOut: 0,
extendedTimeOut: 0,
tapToDismiss: false
});
}
}
/* Increase sleep time and spin again. */
function spin_again(spin_state){
if(spin_state.sleep < spin_state.sleep_max){
function spin_again(spin_state) {
if (spin_state.sleep < spin_state.sleep_max) {
spin_state.sleep += spin_state.sleep_step;
}
if(spin_state.log_attempts){
console.log(spin_state.type + " request " + spin_state.count + " request timeout " + spin_state.ajax_timeout + "ms sleeping " + spin_state.sleep / 1000 + "s");
if (spin_state.log_attempts) {
console.log(
spin_state.type +
" request " +
spin_state.count +
" request timeout " +
spin_state.ajax_timeout +
"ms sleeping " +
spin_state.sleep / 1000 +
"s"
);
}
window.setTimeout(spin_state.spinner, spin_state.sleep);
}
/*
* Spin on a URL as long as it times out, otherwise, call the provided success or error callback. If the callback
* returns `true`, the condition is considered "resolved" and spinning stops. Otherwise, continue spinning, increasing
* AJAX timeouts and/or sleep values as configured in the spin_state.
*/
function spin(url, bool_response, success_callback, timeout_callback, error_callback, spin_state){
var spinner = function(){
function spin(
url,
bool_response,
success_callback,
timeout_callback,
error_callback,
spin_state
) {
var spinner = function() {
var ajax_params = {
url: url,
xhrFields: {
@@ -76,74 +110,88 @@ function spin(url, bool_response, success_callback, timeout_callback, error_call
},
type: "GET",
timeout: spin_state.ajax_timeout,
success: function(data, status, jqxhr){
if(!success_callback(data, status, jqxhr)) spin_again(spin_state);
success: function(data, status, jqxhr) {
if (!success_callback(data, status, jqxhr))
spin_again(spin_state);
},
error: function(jqxhr, status, error){
if(status == "timeout"){
if(spin_state.ajax_timeout < spin_state.ajax_timeout_max){
error: function(jqxhr, status, error) {
if (status == "timeout") {
if (spin_state.ajax_timeout < spin_state.ajax_timeout_max) {
spin_state.ajax_timeout += spin_state.ajax_timeout_step;
}
spin_state.count++;
if(!timeout_callback(jqxhr, status, error)) spin_again(spin_state);
}else{
if (!timeout_callback(jqxhr, status, error))
spin_again(spin_state);
} else {
spin_state.count++;
if(!error_callback(jqxhr, status, error)) spin_again(spin_state);
if (!error_callback(jqxhr, status, error))
spin_again(spin_state);
}
},
}
if(bool_response) ajax_params["dataType"] = "json";
}
};
if (bool_response) ajax_params["dataType"] = "json";
$.ajax(ajax_params);
}
};
console.log("Setting up new spinner for " + spin_state.type + " on " + url);
spin_state.spinner = spinner;
window.setTimeout(spinner, spin_state.sleep);
}
/*
* Spin on a URL forever until there is an acceptable response.
* @param {String} url: URL to test response of. Must return a 200 (302->200 is OK).
* @param {Boolean} bool_response: If set to `true`, do not stop spinning until the response is `true`. Otherwise, stop
* as soon as a successful response is received.
*/
function spin_until(url, bool_response, messages, success_callback, spin_state){
var warn_at = 40; // ~2 mins
var message_once = function(message, spin_state){
if(spin_state.count == 1){
function spin_until(
url,
bool_response,
messages,
success_callback,
spin_state
) {
var warn_at = 40; // ~2 mins
var message_once = function(message, spin_state) {
if (spin_state.count == 1) {
display_spinner();
toastr.info(
message,
null,
{'closeButton': true, 'timeOut': 0, 'extendedTimeOut': 0, 'tapToDismiss': false}
);
toastr.info(message, null, {
closeButton: true,
timeOut: 0,
extendedTimeOut: 0,
tapToDismiss: false
});
}
}
var wrapped_success = function(data){
if(!bool_response || (bool_response && data == true)){
};
var wrapped_success = function(data) {
if (!bool_response || (bool_response && data == true)) {
console.log(messages["success"]);
clear_main_area();
toastr.clear();
success_callback();
}else if(bool_response && data == false){
} else if (bool_response && data == false) {
message_once(messages["not_ready"], spin_state);
return false; // keep spinning
}else{
spin_error("Invalid response to " + spin_state.type + " request", messages["invalid_response"], true);
}
return true; // stop spinning
}
var timeout_error = function(jqxhr, status, error){
message_once(messages["waiting"], spin_state)
if(spin_state.count == warn_at){
toastr.warning(
messages["wait_warn"],
"Warning",
{'closeButton': true, 'timeOut': 0, 'extendedTimeOut': 0, 'tapToDismiss': false}
return false; // keep spinning
} else {
spin_error(
"Invalid response to " + spin_state.type + " request",
messages["invalid_response"],
true
);
}
return true; // stop spinning
};
var timeout_error = function(jqxhr, status, error) {
message_once(messages["waiting"], spin_state);
if (spin_state.count == warn_at) {
toastr.warning(messages["wait_warn"], "Warning", {
closeButton: true,
timeOut: 0,
extendedTimeOut: 0,
tapToDismiss: false
});
}
return false; // keep spinning
}
};
spin(
url,
bool_response,
@@ -154,7 +202,6 @@ function spin_until(url, bool_response, messages, success_callback, spin_state){
);
}
/**
* Test a boolean (json) response from a URL, and call a callback when done.
* http://stackoverflow.com/q/25390206/347368
@@ -162,20 +209,24 @@ function spin_until(url, bool_response, messages, success_callback, spin_state){
* @param {String} callback: function to call once successfully connected.
*
*/
function load_when_ready(url, success_callback){
function load_when_ready(url, success_callback) {
var messages = {
success: "Galaxy reports IE container ready, returning",
not_ready: "Galaxy is launching a container in which to run this interactive environment. Please wait...",
unknown_response: "Galaxy failed to launch a container in which to run this interactive environment, contact a Galaxy administrator.",
waiting: "Galaxy is launching a container in which to run this interactive environment. Please wait...",
wait_warn: "It is taking an usually long time to start a container. Attempts will continue but you may want to report this condition to a Galaxy administrator if it does not succeed soon.",
error: "Galaxy encountered an error while attempting to determine the readiness of this interactive environment's container, contact a Galaxy administrator."
}
not_ready:
"Galaxy is launching a container in which to run this interactive environment. Please wait...",
unknown_response:
"Galaxy failed to launch a container in which to run this interactive environment, contact a Galaxy administrator.",
waiting:
"Galaxy is launching a container in which to run this interactive environment. Please wait...",
wait_warn:
"It is taking an usually long time to start a container. Attempts will continue but you may want to report this condition to a Galaxy administrator if it does not succeed soon.",
error:
"Galaxy encountered an error while attempting to determine the readiness of this interactive environment's container, contact a Galaxy administrator."
};
var spin_state = make_spin_state("IE container readiness");
spin_until(url, true, messages, success_callback, spin_state);
}
/**
* Test availability of a URL, and call a callback when done.
* http://stackoverflow.com/q/25390206/347368
@@ -183,13 +234,16 @@ function load_when_ready(url, success_callback){
* @param {String} callback: function to call once successfully connected.
*
*/
function test_ie_availability(url, success_callback){
function test_ie_availability(url, success_callback) {
var messages = {
success: "IE connection succeeded, returning",
waiting: "Interactive environment container is running, attempting to connect to the IE. Please wait...",
wait_warn: "It is taking an usually long time to connect to the interactive environment. Attempts will continue but you may want to report this condition to a Galaxy administrator if it does not succeed soon.",
error: "An error was encountered while attempting to connect to the interactive environment, contact your administrator."
}
waiting:
"Interactive environment container is running, attempting to connect to the IE. Please wait...",
wait_warn:
"It is taking an usually long time to connect to the interactive environment. Attempts will continue but you may want to report this condition to a Galaxy administrator if it does not succeed soon.",
error:
"An error was encountered while attempting to connect to the interactive environment, contact your administrator."
};
var spin_state = make_spin_state("IE availability");
spin_until(url, false, messages, success_callback, spin_state);
}
+276 -232
View File
@@ -1,16 +1,25 @@
define([
'libs/underscore',
'libs/backbone',
'mvc/base-mvc',
'mvc/user/user-model',
'utils/metrics-logger',
'utils/add-logging',
'utils/localization'
], function( _, Backbone, BASE_MVC, userModel, metricsLogger, addLogging, localize ){
// TODO: move into a singleton pattern and have dependents import Galaxy
// ============================================================================
/** Base galaxy client-side application.
define(
[
"libs/underscore",
"libs/backbone",
"mvc/base-mvc",
"mvc/user/user-model",
"utils/metrics-logger",
"utils/add-logging",
"utils/localization"
],
function(
_,
Backbone,
BASE_MVC,
userModel,
metricsLogger,
addLogging,
localize
) {
// TODO: move into a singleton pattern and have dependents import Galaxy
// ============================================================================
/** Base galaxy client-side application.
* Iniitializes:
* logger : the logger/metrics-logger
* localize : the string localizer
@@ -18,243 +27,278 @@ define([
* galaxy.ini available from the configuration API)
* user : the current user (as a mvc/user/user-model)
*/
function GalaxyApp( options, bootstrapped ){
var self = this;
return self._init( options || {}, bootstrapped || {} );
}
// add logging shortcuts for this object
addLogging( GalaxyApp, 'GalaxyApp' );
// a debug flag can be set via local storage and made available during script/page loading
var DEBUGGING_KEY = 'galaxy:debug',
NAMESPACE_KEY = DEBUGGING_KEY + ':namespaces',
localDebugging = false;
try {
localDebugging = localStorage.getItem( DEBUGGING_KEY ) == 'true';
} catch( storageErr ){
console.log( localize( 'localStorage not available for debug flag retrieval' ) );
}
/** initalize options and sub-components */
GalaxyApp.prototype._init = function __init( options, bootstrapped ){
var self = this;
_.extend( self, Backbone.Events );
if( localDebugging ){
self.logger = console;
console.debug( 'debugging galaxy:', 'options:', options, 'bootstrapped:', bootstrapped );
}
self._processOptions( options );
// add root and url parameters
self.root = options.root || '/';
self.params = options.params || {};
self.session_csrf_token = options.session_csrf_token || null;
self._initConfig( options.config || {} );
self._patchGalaxy( window.Galaxy );
self._initLogger( self.options.loggerOptions || {} );
// at this point, either logging or not and namespaces are enabled - chat it up
self.debug( 'GalaxyApp.options: ', self.options );
self.debug( 'GalaxyApp.config: ', self.config );
self.debug( 'GalaxyApp.logger: ', self.logger );
self._initLocale();
self.debug( 'GalaxyApp.localize: ', self.localize );
self.config = options.config || {};
self.debug( 'GalaxyApp.config: ', self.config );
self._initUser( options.user || {} );
self.debug( 'GalaxyApp.user: ', self.user );
self._setUpListeners();
self.trigger( 'ready', self );
return self;
};
/** default options */
GalaxyApp.prototype.defaultOptions = {
/** monkey patch attributes from existing window.Galaxy object? */
patchExisting : true,
/** root url of this app */
root : '/',
session_csrf_token : null,
};
/** filter to options present in defaultOptions (and default to them) */
GalaxyApp.prototype._processOptions = function _processOptions( options ){
var self = this,
defaults = self.defaultOptions;
self.options = {};
for( var k in defaults ){
if( defaults.hasOwnProperty( k ) ){
self.options[ k ] = ( options.hasOwnProperty( k ) )?( options[ k ] ):( defaults[ k ] );
function GalaxyApp(options, bootstrapped) {
var self = this;
return self._init(options || {}, bootstrapped || {});
}
}
return self;
};
/** parse the config and any extra info derived from it */
GalaxyApp.prototype._initConfig = function _initConfig( config ){
var self = this;
self.config = config;
// add logging shortcuts for this object
addLogging(GalaxyApp, "GalaxyApp");
// give precendence to localdebugging for this setting
self.config.debug = localDebugging || self.config.debug;
// a debug flag can be set via local storage and made available during script/page loading
var DEBUGGING_KEY = "galaxy:debug",
NAMESPACE_KEY = DEBUGGING_KEY + ":namespaces",
localDebugging = false;
try {
localDebugging = localStorage.getItem(DEBUGGING_KEY) == "true";
} catch (storageErr) {
console.log(
localize("localStorage not available for debug flag retrieval")
);
}
return self;
};
/** add an option from options if the key matches an option in defaultOptions */
GalaxyApp.prototype._patchGalaxy = function _patchGalaxy( patchWith ){
var self = this;
// in case req or plain script tag order has created a prev. version of the Galaxy obj...
if( self.options.patchExisting && patchWith ){
// self.debug( 'found existing Galaxy object:', patchWith );
// ...(for now) monkey patch any added attributes that the previous Galaxy may have had
//TODO: move those attributes to more formal assignment in GalaxyApp
for( var k in patchWith ){
if( patchWith.hasOwnProperty( k ) ){
// self.debug( '\t patching in ' + k + ' to Galaxy:', self[ k ] );
self[ k ] = patchWith[ k ];
/** initalize options and sub-components */
GalaxyApp.prototype._init = function __init(options, bootstrapped) {
var self = this;
_.extend(self, Backbone.Events);
if (localDebugging) {
self.logger = console;
console.debug(
"debugging galaxy:",
"options:",
options,
"bootstrapped:",
bootstrapped
);
}
}
}
};
/** set up the metrics logger (utils/metrics-logger) and pass loggerOptions */
GalaxyApp.prototype._initLogger = function _initLogger( loggerOptions ){
var self = this;
self._processOptions(options);
// default to console logging at the debug level if the debug flag is set
if( self.config.debug ){
loggerOptions.consoleLogger = loggerOptions.consoleLogger || console;
loggerOptions.consoleLevel = loggerOptions.consoleLevel || metricsLogger.MetricsLogger.ALL;
// load any logging namespaces from localStorage if we can
try {
loggerOptions.consoleNamespaceWhitelist = localStorage.getItem( NAMESPACE_KEY ).split( ',' );
} catch( storageErr ){}
}
// add root and url parameters
self.root = options.root || "/";
self.params = options.params || {};
self.session_csrf_token = options.session_csrf_token || null;
self.logger = new metricsLogger.MetricsLogger( loggerOptions );
self.emit = {};
[ 'log', 'debug', 'info', 'warn', 'error', 'metric' ].map(function( i ) {
self.emit[ i ] = function( data ){
self.logger.emit( i, arguments[ 0 ], Array.prototype.slice.call( arguments, 1 ) );
self._initConfig(options.config || {});
self._patchGalaxy(window.Galaxy);
self._initLogger(self.options.loggerOptions || {});
// at this point, either logging or not and namespaces are enabled - chat it up
self.debug("GalaxyApp.options: ", self.options);
self.debug("GalaxyApp.config: ", self.config);
self.debug("GalaxyApp.logger: ", self.logger);
self._initLocale();
self.debug("GalaxyApp.localize: ", self.localize);
self.config = options.config || {};
self.debug("GalaxyApp.config: ", self.config);
self._initUser(options.user || {});
self.debug("GalaxyApp.user: ", self.user);
self._setUpListeners();
self.trigger("ready", self);
return self;
};
});
if( self.config.debug ){
// add this logger to mvc's loggable mixin so that all models can use the logger
BASE_MVC.LoggableMixin.logger = self.logger;
}
return self;
};
/** add the localize fn to this object and the window namespace (as '_l') */
GalaxyApp.prototype._initLocale = function _initLocale( options ){
var self = this;
self.debug( '_initLocale:', options );
self.localize = localize;
// add to window as global shortened alias
// TODO: temporary - remove when can require for plugins
window._l = self.localize;
return self;
};
/** set up the current user as a Backbone model (mvc/user/user-model) */
GalaxyApp.prototype._initUser = function _initUser( userJSON ){
var self = this;
self.debug( '_initUser:', userJSON );
self.user = new userModel.User( userJSON );
self.user.logger = self.logger;
return self;
};
/** Set up DOM/jQuery/Backbone event listeners enabled for all pages */
GalaxyApp.prototype._setUpListeners = function _setUpListeners(){
var self = this;
// hook to jq beforeSend to record the most recent ajax call and cache some data about it
/** cached info about the last ajax call made through jQuery */
self.lastAjax = {};
$( document ).bind( 'ajaxSend', function( ev, xhr, options ){
var data = options.data;
try {
data = JSON.parse( data );
} catch( err ){}
self.lastAjax = {
url : location.href.slice( 0, -1 ) + options.url,
data : data
/** default options */
GalaxyApp.prototype.defaultOptions = {
/** monkey patch attributes from existing window.Galaxy object? */
patchExisting: true,
/** root url of this app */
root: "/",
session_csrf_token: null
};
//TODO:?? we might somehow manage to *retry* ajax using either this hook or Backbone.sync
});
return self;
};
/** Turn debugging/console-output on/off by passing boolean. Pass nothing to get current setting. */
GalaxyApp.prototype.debugging = function _debugging( setting ){
var self = this;
try {
if( setting === undefined ){
return localStorage.getItem( DEBUGGING_KEY ) === 'true';
}
if( setting ){
localStorage.setItem( DEBUGGING_KEY, true );
return true;
}
/** filter to options present in defaultOptions (and default to them) */
GalaxyApp.prototype._processOptions = function _processOptions(
options
) {
var self = this,
defaults = self.defaultOptions;
localStorage.removeItem( DEBUGGING_KEY );
// also remove all namespaces
self.debuggingNamespaces( null );
self.options = {};
for (var k in defaults) {
if (defaults.hasOwnProperty(k)) {
self.options[k] = options.hasOwnProperty(k)
? options[k]
: defaults[k];
}
}
return self;
};
} catch( storageErr ){
console.log( localize( 'localStorage not available for debug flag retrieval' ) );
}
return false;
};
/** parse the config and any extra info derived from it */
GalaxyApp.prototype._initConfig = function _initConfig(config) {
var self = this;
self.config = config;
/** Add, remove, or clear namespaces from the debugging filters
// give precendence to localdebugging for this setting
self.config.debug = localDebugging || self.config.debug;
return self;
};
/** add an option from options if the key matches an option in defaultOptions */
GalaxyApp.prototype._patchGalaxy = function _patchGalaxy(patchWith) {
var self = this;
// in case req or plain script tag order has created a prev. version of the Galaxy obj...
if (self.options.patchExisting && patchWith) {
// self.debug( 'found existing Galaxy object:', patchWith );
// ...(for now) monkey patch any added attributes that the previous Galaxy may have had
//TODO: move those attributes to more formal assignment in GalaxyApp
for (var k in patchWith) {
if (patchWith.hasOwnProperty(k)) {
// self.debug( '\t patching in ' + k + ' to Galaxy:', self[ k ] );
self[k] = patchWith[k];
}
}
}
};
/** set up the metrics logger (utils/metrics-logger) and pass loggerOptions */
GalaxyApp.prototype._initLogger = function _initLogger(loggerOptions) {
var self = this;
// default to console logging at the debug level if the debug flag is set
if (self.config.debug) {
loggerOptions.consoleLogger =
loggerOptions.consoleLogger || console;
loggerOptions.consoleLevel =
loggerOptions.consoleLevel ||
metricsLogger.MetricsLogger.ALL;
// load any logging namespaces from localStorage if we can
try {
loggerOptions.consoleNamespaceWhitelist = localStorage
.getItem(NAMESPACE_KEY)
.split(",");
} catch (storageErr) {}
}
self.logger = new metricsLogger.MetricsLogger(loggerOptions);
self.emit = {};
["log", "debug", "info", "warn", "error", "metric"].map(function(
i
) {
self.emit[i] = function(data) {
self.logger.emit(
i,
arguments[0],
Array.prototype.slice.call(arguments, 1)
);
};
});
if (self.config.debug) {
// add this logger to mvc's loggable mixin so that all models can use the logger
BASE_MVC.LoggableMixin.logger = self.logger;
}
return self;
};
/** add the localize fn to this object and the window namespace (as '_l') */
GalaxyApp.prototype._initLocale = function _initLocale(options) {
var self = this;
self.debug("_initLocale:", options);
self.localize = localize;
// add to window as global shortened alias
// TODO: temporary - remove when can require for plugins
window._l = self.localize;
return self;
};
/** set up the current user as a Backbone model (mvc/user/user-model) */
GalaxyApp.prototype._initUser = function _initUser(userJSON) {
var self = this;
self.debug("_initUser:", userJSON);
self.user = new userModel.User(userJSON);
self.user.logger = self.logger;
return self;
};
/** Set up DOM/jQuery/Backbone event listeners enabled for all pages */
GalaxyApp.prototype._setUpListeners = function _setUpListeners() {
var self = this;
// hook to jq beforeSend to record the most recent ajax call and cache some data about it
/** cached info about the last ajax call made through jQuery */
self.lastAjax = {};
$(document).bind("ajaxSend", function(ev, xhr, options) {
var data = options.data;
try {
data = JSON.parse(data);
} catch (err) {}
self.lastAjax = {
url: location.href.slice(0, -1) + options.url,
data: data
};
//TODO:?? we might somehow manage to *retry* ajax using either this hook or Backbone.sync
});
return self;
};
/** Turn debugging/console-output on/off by passing boolean. Pass nothing to get current setting. */
GalaxyApp.prototype.debugging = function _debugging(setting) {
var self = this;
try {
if (setting === undefined) {
return localStorage.getItem(DEBUGGING_KEY) === "true";
}
if (setting) {
localStorage.setItem(DEBUGGING_KEY, true);
return true;
}
localStorage.removeItem(DEBUGGING_KEY);
// also remove all namespaces
self.debuggingNamespaces(null);
} catch (storageErr) {
console.log(
localize(
"localStorage not available for debug flag retrieval"
)
);
}
return false;
};
/** Add, remove, or clear namespaces from the debugging filters
* Pass no arguments to retrieve the existing namespaces as an array.
* Pass in null to clear all namespaces (all logging messages will show now).
* Pass in an array of strings or single string of the namespaces to filter to.
* Returns the new/current namespaces as an array;
*/
GalaxyApp.prototype.debuggingNamespaces = function _debuggingNamespaces( namespaces ){
var self = this;
try {
if( namespaces === undefined ){
var csv = localStorage.getItem( NAMESPACE_KEY );
return typeof( csv ) === 'string'? csv.split( ',' ) : [];
} else if( namespaces === null ) {
localStorage.removeItem( NAMESPACE_KEY );
} else {
localStorage.setItem( NAMESPACE_KEY, namespaces );
}
var newSettings = self.debuggingNamespaces();
if( self.logger ){
self.logger.options.consoleNamespaceWhitelist = newSettings;
}
return newSettings;
} catch( storageErr ){
console.log( localize( 'localStorage not available for debug namespace retrieval' ) );
GalaxyApp.prototype.debuggingNamespaces = function _debuggingNamespaces(
namespaces
) {
var self = this;
try {
if (namespaces === undefined) {
var csv = localStorage.getItem(NAMESPACE_KEY);
return typeof csv === "string" ? csv.split(",") : [];
} else if (namespaces === null) {
localStorage.removeItem(NAMESPACE_KEY);
} else {
localStorage.setItem(NAMESPACE_KEY, namespaces);
}
var newSettings = self.debuggingNamespaces();
if (self.logger) {
self.logger.options.consoleNamespaceWhitelist = newSettings;
}
return newSettings;
} catch (storageErr) {
console.log(
localize(
"localStorage not available for debug namespace retrieval"
)
);
}
};
/** string rep */
GalaxyApp.prototype.toString = function toString() {
var userEmail = this.user
? this.user.get("email") || "(anonymous)"
: "uninitialized";
return "GalaxyApp(" + userEmail + ")";
};
// ============================================================================
return {
GalaxyApp: GalaxyApp
};
}
};
/** string rep */
GalaxyApp.prototype.toString = function toString(){
var userEmail = this.user? ( this.user.get( 'email' ) || '(anonymous)' ) : 'uninitialized';
return 'GalaxyApp(' + userEmail + ')';
};
// ============================================================================
return {
GalaxyApp : GalaxyApp
};
});
);
+297 -199
View File
@@ -2,233 +2,331 @@
// === MAIN GALAXY LIBRARY MODULE ====
// MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM
define([
"layout/masthead",
"utils/utils",
"libs/toastr",
"mvc/base-mvc",
"mvc/library/library-model",
"mvc/library/library-folderlist-view",
"mvc/library/library-librarylist-view",
"mvc/library/library-librarytoolbar-view",
"mvc/library/library-foldertoolbar-view",
"mvc/library/library-dataset-view",
"mvc/library/library-library-view",
"mvc/library/library-folder-view"
],
function(mod_masthead,
mod_utils,
mod_toastr,
mod_baseMVC,
mod_library_model,
mod_folderlist_view,
mod_librarylist_view,
mod_librarytoolbar_view,
mod_foldertoolbar_view,
mod_library_dataset_view,
mod_library_library_view,
mod_library_folder_view
) {
// ============================================================================
/**
define(
[
"layout/masthead",
"utils/utils",
"libs/toastr",
"mvc/base-mvc",
"mvc/library/library-model",
"mvc/library/library-folderlist-view",
"mvc/library/library-librarylist-view",
"mvc/library/library-librarytoolbar-view",
"mvc/library/library-foldertoolbar-view",
"mvc/library/library-dataset-view",
"mvc/library/library-library-view",
"mvc/library/library-folder-view"
],
function(
mod_masthead,
mod_utils,
mod_toastr,
mod_baseMVC,
mod_library_model,
mod_folderlist_view,
mod_librarylist_view,
mod_librarytoolbar_view,
mod_foldertoolbar_view,
mod_library_dataset_view,
mod_library_library_view,
mod_library_folder_view
) {
// ============================================================================
/**
* The Data Libraries router. Takes care about triggering routes
* and sends users to proper pieces of the application.
*/
var LibraryRouter = Backbone.Router.extend({
var LibraryRouter = Backbone.Router.extend({
initialize: function() {
this.routesHit = 0;
// keep count of number of routes handled by the application
Backbone.history.on(
"route",
function() {
this.routesHit++;
},
this
);
initialize: function() {
this.routesHit = 0;
// keep count of number of routes handled by the application
Backbone.history.on( 'route', function() { this.routesHit++; }, this );
this.bind("route", this.trackPageview);
},
this.bind( 'route', this.trackPageview );
},
routes: {
"": "libraries",
"page/:show_page": "libraries_page",
"library/:library_id/permissions": "library_permissions",
"folders/:folder_id/permissions": "folder_permissions",
"folders/:id": "folder_content",
"folders/:id/page/:show_page": "folder_page",
"folders/:folder_id/datasets/:dataset_id": "dataset_detail",
"folders/:folder_id/datasets/:dataset_id/permissions":
"dataset_permissions",
"folders/:folder_id/datasets/:dataset_id/versions/:ldda_id":
"dataset_version",
"folders/:folder_id/download/:format": "download",
"folders/:folder_id/import/:source": "import_datasets"
},
routes: {
"" : "libraries",
"page/:show_page" : "libraries_page",
"library/:library_id/permissions" : "library_permissions",
"folders/:folder_id/permissions" : "folder_permissions",
"folders/:id" : "folder_content",
"folders/:id/page/:show_page" : "folder_page",
"folders/:folder_id/datasets/:dataset_id" : "dataset_detail",
"folders/:folder_id/datasets/:dataset_id/permissions" : "dataset_permissions",
"folders/:folder_id/datasets/:dataset_id/versions/:ldda_id" : "dataset_version",
"folders/:folder_id/download/:format" : "download",
"folders/:folder_id/import/:source" : "import_datasets"
},
/**
/**
* If more than one route has been hit the user did not land on current
* page directly so we can go back safely. Otherwise go to the home page.
* Use replaceState if available so the navigation doesn't create an
* extra history entry
*/
back: function() {
if( this.routesHit > 1 ) {
window.history.back();
} else {
this.navigate( '#', { trigger:true, replace:true } );
}
},
back: function() {
if (this.routesHit > 1) {
window.history.back();
} else {
this.navigate("#", { trigger: true, replace: true });
}
},
/**
/**
* Track every route change as a page view in Google Analytics.
*/
trackPageview: function () {
var url = Backbone.history.getFragment();
//prepend slash
if (!/^\//.test(url) && url != "") {
url = "/" + url;
}
if ( typeof ga !== 'undefined' ) {
ga( 'send', 'pageview', Galaxy.root + 'library/list' + url );
}
}
});
trackPageview: function() {
var url = Backbone.history.getFragment();
//prepend slash
if (!/^\//.test(url) && url != "") {
url = "/" + url;
}
if (typeof ga !== "undefined") {
ga("send", "pageview", Galaxy.root + "library/list" + url);
}
}
});
// ============================================================================
/** session storage for library preferences */
var LibraryPrefs = mod_baseMVC.SessionStorageModel.extend({
defaults : {
with_deleted : false,
without_restricted: false,
sort_order : 'asc',
sort_by : 'name',
library_page_size : 20,
folder_page_size : 15
}
});
// ============================================================================
/** session storage for library preferences */
var LibraryPrefs = mod_baseMVC.SessionStorageModel.extend({
defaults: {
with_deleted: false,
without_restricted: false,
sort_order: "asc",
sort_by: "name",
library_page_size: 20,
folder_page_size: 15
}
});
// ============================================================================
/**
// ============================================================================
/**
* Main view of the Galaxy Data Libraries. Stores pointers to other subviews
* and defines what router should do on the route triggers.
*/
var GalaxyLibrary = Backbone.View.extend({
var GalaxyLibrary = Backbone.View.extend({
libraryToolbarView: null,
libraryListView: null,
library_router: null,
libraryView: null,
folderToolbarView: null,
folderListView: null,
datasetView: null,
libraryToolbarView: null,
libraryListView: null,
library_router: null,
libraryView: null,
folderToolbarView: null,
folderListView: null,
datasetView: null,
initialize: function() {
// This should go upstream in the js app once available
if (window.Galaxy.config.ga_code) {
(function(i, s, o, g, r, a, m) {
i["GoogleAnalyticsObject"] = r;
(i[r] =
i[r] ||
function() {
(i[r].q = i[r].q || []).push(arguments);
}),
(i[r].l = 1 * new Date());
(a = s.createElement(o)),
(m = s.getElementsByTagName(o)[0]);
a.async = 1;
a.src = g;
m.parentNode.insertBefore(a, m);
})(
window,
document,
"script",
"//www.google-analytics.com/analytics.js",
"ga"
);
ga("create", window.Galaxy.config.ga_code, "auto");
ga("send", "pageview");
}
initialize : function(){
Galaxy.libraries = this;
// This should go upstream in the js app once available
if ( window.Galaxy.config.ga_code ){
(function(i,s,o,g,r,a,m){i['GoogleAnalyticsObject']=r;i[r]=i[r]||function(){
(i[r].q=i[r].q||[]).push(arguments)},i[r].l=1*new Date();a=s.createElement(o),
m=s.getElementsByTagName(o)[0];a.async=1;a.src=g;m.parentNode.insertBefore(a,m)
})(window,document,'script','//www.google-analytics.com/analytics.js','ga');
ga('create', window.Galaxy.config.ga_code, 'auto');
ga('send', 'pageview');
}
this.preferences = new LibraryPrefs({ id: "global-lib-prefs" });
Galaxy.libraries = this;
this.library_router = new LibraryRouter();
this.preferences = new LibraryPrefs( { id: 'global-lib-prefs' } );
this.library_router.on("route:libraries", function() {
if (Galaxy.libraries.libraryToolbarView) {
Galaxy.libraries.libraryToolbarView.$el.unbind("click");
}
Galaxy.libraries.libraryToolbarView = new mod_librarytoolbar_view.LibraryToolbarView();
Galaxy.libraries.libraryListView = new mod_librarylist_view.LibraryListView();
});
this.library_router = new LibraryRouter();
this.library_router.on("route:libraries_page", function(
show_page
) {
if (Galaxy.libraries.libraryToolbarView === null) {
Galaxy.libraries.libraryToolbarView = new mod_librarytoolbar_view.LibraryToolbarView();
Galaxy.libraries.libraryListView = new mod_librarylist_view.LibraryListView(
{ show_page: show_page }
);
} else {
Galaxy.libraries.libraryListView.render({
show_page: show_page
});
}
});
this.library_router.on( 'route:libraries', function() {
if ( Galaxy.libraries.libraryToolbarView){
Galaxy.libraries.libraryToolbarView.$el.unbind('click');
}
Galaxy.libraries.libraryToolbarView = new mod_librarytoolbar_view.LibraryToolbarView();
Galaxy.libraries.libraryListView = new mod_librarylist_view.LibraryListView();
});
this.library_router.on("route:folder_content", function(id) {
if (Galaxy.libraries.folderToolbarView) {
Galaxy.libraries.folderToolbarView.$el.unbind("click");
}
Galaxy.libraries.folderToolbarView = new mod_foldertoolbar_view.FolderToolbarView(
{ id: id }
);
Galaxy.libraries.folderListView = new mod_folderlist_view.FolderListView(
{ id: id }
);
});
this.library_router.on('route:libraries_page', function( show_page ) {
if ( Galaxy.libraries.libraryToolbarView === null ){
Galaxy.libraries.libraryToolbarView = new mod_librarytoolbar_view.LibraryToolbarView();
Galaxy.libraries.libraryListView = new mod_librarylist_view.LibraryListView( { show_page: show_page } );
} else {
Galaxy.libraries.libraryListView.render( { show_page: show_page } );
this.library_router.on("route:folder_page", function(
id,
show_page
) {
if (Galaxy.libraries.folderToolbarView === null) {
Galaxy.libraries.folderToolbarView = new mod_foldertoolbar_view.FolderToolbarView(
{ id: id }
);
Galaxy.libraries.folderListView = new mod_folderlist_view.FolderListView(
{ id: id, show_page: show_page }
);
} else {
Galaxy.libraries.folderListView.render({
id: id,
show_page: parseInt(show_page)
});
}
});
this.library_router.on("route:download", function(
folder_id,
format
) {
if ($("#folder_list_body").find(":checked").length === 0) {
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.library_router.navigate(
"folders/" + folder_id,
{ trigger: false, replace: true }
);
}
});
this.library_router.on("route:dataset_detail", function(
folder_id,
dataset_id
) {
if (Galaxy.libraries.datasetView) {
Galaxy.libraries.datasetView.$el.unbind("click");
}
Galaxy.libraries.datasetView = new mod_library_dataset_view.LibraryDatasetView(
{
id: dataset_id,
show_version: false,
show_permissions: false
}
);
});
this.library_router.on("route:dataset_version", function(
folder_id,
dataset_id,
ldda_id
) {
if (Galaxy.libraries.datasetView) {
Galaxy.libraries.datasetView.$el.unbind("click");
}
Galaxy.libraries.datasetView = new mod_library_dataset_view.LibraryDatasetView(
{ id: dataset_id, ldda_id: ldda_id, show_version: true }
);
});
this.library_router.on("route:dataset_permissions", function(
folder_id,
dataset_id
) {
if (Galaxy.libraries.datasetView) {
Galaxy.libraries.datasetView.$el.unbind("click");
}
Galaxy.libraries.datasetView = new mod_library_dataset_view.LibraryDatasetView(
{ id: dataset_id, show_permissions: true }
);
});
this.library_router.on("route:library_permissions", function(
library_id
) {
if (Galaxy.libraries.libraryView) {
Galaxy.libraries.libraryView.$el.unbind("click");
}
Galaxy.libraries.libraryView = new mod_library_library_view.LibraryView(
{ id: library_id, show_permissions: true }
);
});
this.library_router.on("route:folder_permissions", function(
folder_id
) {
if (Galaxy.libraries.folderView) {
Galaxy.libraries.folderView.$el.unbind("click");
}
Galaxy.libraries.folderView = new mod_library_folder_view.FolderView(
{ id: folder_id, show_permissions: true }
);
});
this.library_router.on("route:import_datasets", function(
folder_id,
source
) {
if (
Galaxy.libraries.folderToolbarView &&
Galaxy.libraries.folderListView
) {
Galaxy.libraries.folderToolbarView.showImportModal({
source: source
});
} else {
Galaxy.libraries.folderToolbarView = new mod_foldertoolbar_view.FolderToolbarView(
{ id: folder_id }
);
Galaxy.libraries.folderListView = new mod_folderlist_view.FolderListView(
{ id: folder_id }
);
Galaxy.libraries.folderToolbarView.showImportModal({
source: source
});
}
});
Backbone.history.start({ pushState: false });
}
});
this.library_router.on( 'route:folder_content', function( id ) {
if (Galaxy.libraries.folderToolbarView){
Galaxy.libraries.folderToolbarView.$el.unbind( 'click' );
}
Galaxy.libraries.folderToolbarView = new mod_foldertoolbar_view.FolderToolbarView( { id: id } );
Galaxy.libraries.folderListView = new mod_folderlist_view.FolderListView( { id: id } );
});
this.library_router.on( 'route:folder_page', function( id, show_page ) {
if ( Galaxy.libraries.folderToolbarView === null ){
Galaxy.libraries.folderToolbarView = new mod_foldertoolbar_view.FolderToolbarView( {id: id} );
Galaxy.libraries.folderListView = new mod_folderlist_view.FolderListView( { id: id, show_page: show_page } );
} else {
Galaxy.libraries.folderListView.render( { id: id, show_page: parseInt( show_page ) } );
}
});
this.library_router.on( 'route:download', function( folder_id, format ) {
if ( $( '#folder_list_body' ).find( ':checked' ).length === 0 ) {
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.library_router.navigate( 'folders/' + folder_id, { trigger: false, replace: true } );
}
});
this.library_router.on( 'route:dataset_detail', function(folder_id, dataset_id){
if (Galaxy.libraries.datasetView){
Galaxy.libraries.datasetView.$el.unbind('click');
}
Galaxy.libraries.datasetView = new mod_library_dataset_view.LibraryDatasetView({id: dataset_id, show_version: false, show_permissions: false});
});
this.library_router.on( 'route:dataset_version', function(folder_id, dataset_id, ldda_id){
if (Galaxy.libraries.datasetView){
Galaxy.libraries.datasetView.$el.unbind('click');
}
Galaxy.libraries.datasetView = new mod_library_dataset_view.LibraryDatasetView({id: dataset_id, ldda_id: ldda_id, show_version: true});
});
this.library_router.on( 'route:dataset_permissions', function(folder_id, dataset_id){
if (Galaxy.libraries.datasetView){
Galaxy.libraries.datasetView.$el.unbind('click');
}
Galaxy.libraries.datasetView = new mod_library_dataset_view.LibraryDatasetView({id: dataset_id, show_permissions: true});
});
this.library_router.on( 'route:library_permissions', function(library_id){
if (Galaxy.libraries.libraryView){
Galaxy.libraries.libraryView.$el.unbind('click');
}
Galaxy.libraries.libraryView = new mod_library_library_view.LibraryView({id: library_id, show_permissions: true});
});
this.library_router.on( 'route:folder_permissions', function(folder_id){
if (Galaxy.libraries.folderView){
Galaxy.libraries.folderView.$el.unbind('click');
}
Galaxy.libraries.folderView = new mod_library_folder_view.FolderView({id: folder_id, show_permissions: true});
});
this.library_router.on( 'route:import_datasets', function( folder_id, source ){
if ( Galaxy.libraries.folderToolbarView && Galaxy.libraries.folderListView ){
Galaxy.libraries.folderToolbarView.showImportModal( { source:source } );
} else {
Galaxy.libraries.folderToolbarView = new mod_foldertoolbar_view.FolderToolbarView( { id: folder_id } );
Galaxy.libraries.folderListView = new mod_folderlist_view.FolderListView( { id: folder_id } );
Galaxy.libraries.folderToolbarView.showImportModal( { source: source } );
}
});
Backbone.history.start({pushState: false});
return {
GalaxyApp: GalaxyLibrary
};
}
});
return {
GalaxyApp: GalaxyLibrary
};
});
);
File diff suppressed because it is too large Load Diff
+42 -21
View File
@@ -34,8 +34,8 @@
* locale pieces into each other, then finally sets the context.defined value
* for the nls/fr-fr/colors bundle to be that mixed in locale.
*/
(function () {
'use strict';
(function() {
"use strict";
//regexp for reconstructing the master bundle name from parts of the regexp match
//nlsRegExp.exec("foo/bar/baz/nls/en-ca/foo") gives:
@@ -52,14 +52,14 @@
if (master[locale]) {
needed.push(locale);
if (master[locale] === true || master[locale] === 1) {
toLoad.push(prefix + locale + '/' + suffix);
toLoad.push(prefix + locale + "/" + suffix);
}
}
}
function addIfExists(req, locale, toLoad, prefix, suffix) {
var fullName = prefix + locale + '/' + suffix;
if (require._fileExists(req.toUrl(fullName + '.js'))) {
var fullName = prefix + locale + "/" + suffix;
if (require._fileExists(req.toUrl(fullName + ".js"))) {
toLoad.push(fullName);
}
}
@@ -74,9 +74,12 @@
function mixin(target, source, force) {
var prop;
for (prop in source) {
if (source.hasOwnProperty(prop) && (!target.hasOwnProperty(prop) || force)) {
if (
source.hasOwnProperty(prop) &&
(!target.hasOwnProperty(prop) || force)
) {
target[prop] = source[prop];
} else if (typeof source[prop] === 'object') {
} else if (typeof source[prop] === "object") {
if (!target[prop] && source[prop]) {
target[prop] = {};
}
@@ -85,15 +88,15 @@
}
}
define(['module'], function (module) {
define(["module"], function(module) {
var masterConfig = module.config ? module.config() : {};
return {
version: '2.0.4',
version: "2.0.4",
/**
* Called when a dependency needs to be loaded.
*/
load: function (name, req, onLoad, config) {
load: function(name, req, onLoad, config) {
config = config || {};
if (config.locale) {
@@ -108,7 +111,9 @@
parts = locale.split("-"),
toLoad = [],
value = {},
i, part, current = "";
i,
part,
current = "";
//If match[5] is blank, it means this is the top bundle definition,
//so it does not have to be handled. Locale-specific requests
@@ -124,9 +129,12 @@
locale = masterConfig.locale;
if (!locale) {
locale = masterConfig.locale =
typeof navigator === "undefined" ? "root" :
(navigator.language ||
navigator.userLanguage || "root").toLowerCase();
typeof navigator === "undefined"
? "root"
: (navigator.language ||
navigator.userLanguage ||
"root"
).toLowerCase();
}
parts = locale.split("-");
}
@@ -142,12 +150,12 @@
addIfExists(req, current, toLoad, prefix, suffix);
}
req(toLoad, function () {
req(toLoad, function() {
onLoad();
});
} else {
//First, fetch the master bundle, it knows what locales are available.
req([masterName], function (master) {
req([masterName], function(master) {
//Figure out the best fit
var needed = [],
part;
@@ -157,17 +165,30 @@
for (i = 0; i < parts.length; i++) {
part = parts[i];
current += (current ? "-" : "") + part;
addPart(current, master, needed, toLoad, prefix, suffix);
addPart(
current,
master,
needed,
toLoad,
prefix,
suffix
);
}
//Load all the parts missing.
req(toLoad, function () {
req(toLoad, function() {
var i, partBundle, part;
for (i = needed.length - 1; i > -1 && needed[i]; i--) {
for (
i = needed.length - 1;
i > -1 && needed[i];
i--
) {
part = needed[i];
partBundle = master[part];
if (partBundle === true || partBundle === 1) {
partBundle = req(prefix + part + '/' + suffix);
partBundle = req(
prefix + part + "/" + suffix
);
}
mixin(value, partBundle);
}
@@ -180,4 +201,4 @@
}
};
});
}());
})();
@@ -1,98 +1,106 @@
/** Real-time Communication feature **/
define(['mvc/ui/ui-modal'], function( Modal ) {
define(["mvc/ui/ui-modal"], function(Modal) {
var GenericNavView = Backbone.View.extend({
initialize: function() {
this.modal = null;
},
var GenericNavView = Backbone.View.extend({
/** makes bootstrap modal and iframe inside it */
makeModalIframe: function(e) {
// make modal
var host = window.Galaxy.config.communication_server_host,
port = window.Galaxy.config.communication_server_port,
username = escape(window.Galaxy.user.attributes.username),
persistent_communication_rooms = escape(
window.Galaxy.config.persistent_communication_rooms
),
query_string =
"?username=" +
username +
"&persistent_communication_rooms=" +
persistent_communication_rooms,
src = host + ":" + port + query_string,
$el_chat_modal_header = null,
$el_chat_modal_body = null,
iframe_template =
'<iframe class="f-iframe fade in communication-iframe" src="' +
src +
'"> </iframe>',
header_template =
'<i class="fa fa-comment" aria-hidden="true" title="Communicate with other users"></i>' +
'<i class="fa fa-expand expand-compress-modal" aria-hidden="true" title="Maximize"></i>' +
'<i class="fa fa-times close-modal" aria-hidden="true" title="Close"></i>',
frame_height = 350,
frame_width = 600,
class_names = "ui-modal chat-modal";
initialize: function ( ) {
this.modal = null;
},
/** makes bootstrap modal and iframe inside it */
makeModalIframe: function( e ) {
// make modal
var host = window.Galaxy.config.communication_server_host,
port = window.Galaxy.config.communication_server_port,
username = escape( window.Galaxy.user.attributes.username ),
persistent_communication_rooms = escape( window.Galaxy.config.persistent_communication_rooms ),
query_string = "?username=" + username + "&persistent_communication_rooms=" + persistent_communication_rooms,
src = host + ":" + port + query_string,
$el_chat_modal_header = null,
$el_chat_modal_body = null,
iframe_template = '<iframe class="f-iframe fade in communication-iframe" src="' + src + '"> </iframe>',
header_template = '<i class="fa fa-comment" aria-hidden="true" title="Communicate with other users"></i>' +
'<i class="fa fa-expand expand-compress-modal" aria-hidden="true" title="Maximize"></i>' +
'<i class="fa fa-times close-modal" aria-hidden="true" title="Close"></i>',
frame_height = 350,
frame_width = 600,
class_names = 'ui-modal chat-modal';
// deletes the chat modal if already present and create one
if( $( '.chat-modal' ).length > 0 ) {
$( '.chat-modal' ).remove();
}
// creates a modal
GenericNavView.modal = new Modal.View({
body : iframe_template,
height : frame_height,
width : frame_width,
closing_events : true,
title_separator : false,
cls : class_names
});
// shows modal
GenericNavView.modal.show();
$el_chat_modal_header = $( '.chat-modal .modal-header' );
$el_chat_modal_body = $( '.chat-modal .modal-body' );
// adjusts the css of bootstrap modal for chat
$el_chat_modal_header.addClass('modal-header-body');
$el_chat_modal_body.addClass('modal-header-body');
$el_chat_modal_header.find( 'h4' ).remove();
$el_chat_modal_header.removeAttr( 'min-height padding border' );
$el_chat_modal_header.append( header_template );
// click event of the close button for chat
$( '.close-modal' ).click(function( e ) {
$( '.chat-modal' ).css( 'display', 'none' );
});
// click event of expand and compress icon
$( '.expand-compress-modal' ).click(function( e ) {
if( $( '.expand-compress-modal' ).hasClass( 'fa-expand' ) ) {
$( '.chat-modal .modal-dialog' ).width( '1000px' );
$( '.chat-modal .modal-body' ).height( '575px' );
$( '.expand-compress-modal' ).removeClass( 'fa-expand' ).addClass( 'fa-compress' );
$( '.expand-compress-modal' ).attr('title', 'Minimize');
$( '.expand-compress-modal' ).css('margin-left', '96.2%');
// deletes the chat modal if already present and create one
if ($(".chat-modal").length > 0) {
$(".chat-modal").remove();
}
else {
$( '.chat-modal .modal-dialog' ).width( frame_width + 'px' );
$( '.chat-modal .modal-body' ).height( frame_height + 'px' );
$( '.expand-compress-modal' ).removeClass( 'fa-compress' ).addClass( 'fa-expand' );
$( '.expand-compress-modal' ).attr('title', 'Maximize');
$( '.expand-compress-modal' ).css('margin-left', '93.2%');
}
});
return this;
},
// creates a modal
GenericNavView.modal = new Modal.View({
body: iframe_template,
height: frame_height,
width: frame_width,
closing_events: true,
title_separator: false,
cls: class_names
});
/**renders the chat icon as a nav item*/
render: function() {
var self = this,
navItem = {};
navItem = {
id : 'show-chat-online',
icon : 'fa-comment-o',
tooltip : 'Chat online',
visible : false,
onclick : self.makeModalIframe
// shows modal
GenericNavView.modal.show();
$el_chat_modal_header = $(".chat-modal .modal-header");
$el_chat_modal_body = $(".chat-modal .modal-body");
// adjusts the css of bootstrap modal for chat
$el_chat_modal_header.addClass("modal-header-body");
$el_chat_modal_body.addClass("modal-header-body");
$el_chat_modal_header.find("h4").remove();
$el_chat_modal_header.removeAttr("min-height padding border");
$el_chat_modal_header.append(header_template);
// click event of the close button for chat
$(".close-modal").click(function(e) {
$(".chat-modal").css("display", "none");
});
// click event of expand and compress icon
$(".expand-compress-modal").click(function(e) {
if ($(".expand-compress-modal").hasClass("fa-expand")) {
$(".chat-modal .modal-dialog").width("1000px");
$(".chat-modal .modal-body").height("575px");
$(".expand-compress-modal")
.removeClass("fa-expand")
.addClass("fa-compress");
$(".expand-compress-modal").attr("title", "Minimize");
$(".expand-compress-modal").css("margin-left", "96.2%");
} else {
$(".chat-modal .modal-dialog").width(frame_width + "px");
$(".chat-modal .modal-body").height(frame_height + "px");
$(".expand-compress-modal")
.removeClass("fa-compress")
.addClass("fa-expand");
$(".expand-compress-modal").attr("title", "Maximize");
$(".expand-compress-modal").css("margin-left", "93.2%");
}
});
return this;
},
/**renders the chat icon as a nav item*/
render: function() {
var self = this,
navItem = {};
navItem = {
id: "show-chat-online",
icon: "fa-comment-o",
tooltip: "Chat online",
visible: false,
onclick: self.makeModalIframe
};
return navItem;
}
return navItem;
}
});
return {
GenericNavView: GenericNavView
};
});
return {
GenericNavView : GenericNavView
};
});
+111 -81
View File
@@ -1,92 +1,122 @@
define([
'utils/utils',
'layout/menu',
'layout/scratchbook',
'mvc/user/user-quotameter',
], function( Utils, Menu, Scratchbook, QuotaMeter ) {
define(
[
"utils/utils",
"layout/menu",
"layout/scratchbook",
"mvc/user/user-quotameter"
],
function(Utils, Menu, Scratchbook, QuotaMeter) {
/** Masthead **/
var View = Backbone.View.extend({
initialize: function(options) {
var self = this;
this.options = options;
this.setElement(this._template());
this.$navbarBrandLink = this.$(".navbar-brand-link");
this.$navbarBrandImage = this.$(".navbar-brand-image");
this.$navbarBrandTitle = this.$(".navbar-brand-title");
this.$navbarTabs = this.$(".navbar-tabs");
this.$quoteMeter = this.$(".quota-meter-container");
/** Masthead **/
var View = Backbone.View.extend({
initialize : function( options ) {
var self = this;
this.options = options;
this.setElement( this._template() );
this.$navbarBrandLink = this.$( '.navbar-brand-link' );
this.$navbarBrandImage = this.$( '.navbar-brand-image' );
this.$navbarBrandTitle = this.$( '.navbar-brand-title' );
this.$navbarTabs = this.$( '.navbar-tabs' );
this.$quoteMeter = this.$( '.quota-meter-container' );
// build tabs
this.collection = new Menu.Collection();
this.collection
.on("add", function(model) {
self.$navbarTabs.append(
new Menu.Tab({ model: model }).render().$el
);
})
.on("reset", function() {
self.$navbarTabs.empty();
})
.on("dispatch", function(callback) {
self.collection.each(function(m) {
callback(m);
});
})
.fetch(this.options);
// build tabs
this.collection = new Menu.Collection();
this.collection.on( 'add', function( model ) {
self.$navbarTabs.append( new Menu.Tab( { model : model } ).render().$el );
}).on( 'reset', function() {
self.$navbarTabs.empty();
}).on( 'dispatch', function( callback ) {
self.collection.each( function ( m ) { callback( m ) });
}).fetch( this.options );
// scratchbook
Galaxy.frame = this.frame = new Scratchbook({
collection: this.collection
});
// scratchbook
Galaxy.frame = this.frame = new Scratchbook( { collection: this.collection } );
// set up the quota meter (And fetch the current user data from trans)
// add quota meter to masthead
Galaxy.quotaMeter = this.quotaMeter = new QuotaMeter.UserQuotaMeter(
{
model: Galaxy.user,
el: this.$quoteMeter
}
);
// set up the quota meter (And fetch the current user data from trans)
// add quota meter to masthead
Galaxy.quotaMeter = this.quotaMeter = new QuotaMeter.UserQuotaMeter({
model : Galaxy.user,
el : this.$quoteMeter
});
// loop through beforeunload functions if the user attempts to unload the page
$(window)
.on("click", function(e) {
var $download_link = $(e.target).closest("a[download]");
if ($download_link.length == 1) {
if ($("iframe[id=download]").length === 0) {
$("body").append(
$("<iframe/>")
.attr("id", "download")
.hide()
);
}
$("iframe[id=download]").attr(
"src",
$download_link.attr("href")
);
e.preventDefault();
}
})
.on("beforeunload", function() {
var text = "";
self.collection.each(function(model) {
var q =
model.get("onbeforeunload") &&
model.get("onbeforeunload")();
q && (text += q + " ");
});
if (text !== "") {
return text;
}
});
},
// loop through beforeunload functions if the user attempts to unload the page
$( window ).on( 'click', function( e ) {
var $download_link = $( e.target ).closest( 'a[download]' );
if ( $download_link.length == 1 ) {
if( $( 'iframe[id=download]' ).length === 0 ) {
$( 'body' ).append( $( '<iframe/>' ).attr( 'id', 'download' ).hide() );
}
$( 'iframe[id=download]' ).attr( 'src', $download_link.attr( 'href' ) );
e.preventDefault();
}
}).on( 'beforeunload', function() {
var text = '';
self.collection.each( function( model ) {
var q = model.get( 'onbeforeunload' ) && model.get( 'onbeforeunload' )();
q && ( text += q + ' ' );
});
if ( text !== '' ) {
return text;
}
});
},
render: function() {
this.$navbarBrandTitle.html(
"Galaxy " +
((this.options.brand && "/ " + this.options.brand) ||
"")
);
this.$navbarBrandLink.attr("href", this.options.logo_url);
this.$navbarBrandImage.attr("src", this.options.logo_src);
this.quotaMeter.render();
return this;
},
render: function() {
this.$navbarBrandTitle.html( 'Galaxy ' + ( this.options.brand && '/ ' + this.options.brand || '' ) );
this.$navbarBrandLink.attr( 'href', this.options.logo_url );
this.$navbarBrandImage.attr( 'src', this.options.logo_src );
this.quotaMeter.render();
return this;
},
/** body template */
_template: function() {
return '<div id="masthead" class="navbar navbar-fixed-top navbar-inverse">' +
/** body template */
_template: function() {
return (
'<div id="masthead" class="navbar navbar-fixed-top navbar-inverse">' +
'<div class="navbar-header">' +
'<div class="navbar-tabs"/>' +
'</div>' +
'<div class="navbar-tabs"/>' +
"</div>" +
'<div class="navbar-brand">' +
'<a class="navbar-brand-link">' +
'<img class="navbar-brand-image"/>' +
'<span class="navbar-brand-title"/>' +
'</a>' +
'</div>' +
'<a class="navbar-brand-link">' +
'<img class="navbar-brand-image"/>' +
'<span class="navbar-brand-title"/>' +
"</a>" +
"</div>" +
'<div class="quota-meter-container"/>' +
'<div class="navbar-icons"/>' +
'</div>';
"</div>"
);
}
});
return {
View: View
};
}
});
return {
View: View
};
});
);
+507 -402
View File
@@ -1,424 +1,529 @@
/** Masthead Collection **/
define(['layout/generic-nav-view', 'mvc/webhooks', 'utils/localization', 'utils/utils'], function( GenericNav, Webhooks, _l, Utils ) {
var Collection = Backbone.Collection.extend({
model: Backbone.Model.extend({
defaults: {
visible : true,
target : '_parent'
}
}),
fetch: function( options ){
var self = this;
options = options || {};
this.reset();
//
// Chat server tab
//
var extendedNavItem = new GenericNav.GenericNavView();
this.add(extendedNavItem.render());
//
// Analyze data tab.
//
this.add({
id : 'analysis',
title : _l('Analyze Data'),
url : '',
tooltip : _l('Analysis home view')
});
//
// Workflow tab.
//
this.add({
id : 'workflow',
title : _l('Workflow'),
tooltip : _l('Chain tools into workflows'),
disabled : !Galaxy.user.id,
url : 'workflow'
});
//
// 'Shared Items' or Libraries tab.
//
this.add({
id : 'shared',
title : _l('Shared Data'),
url : 'library/index',
tooltip : _l('Access published resources'),
menu : [{
title : _l('Data Libraries'),
url : 'library/list'
},{
title : _l('Histories'),
url : 'histories/list_published'
},{
title : _l('Workflows'),
url : 'workflows/list_published'
},{
title : _l('Visualizations'),
url : 'visualizations/list_published'
},{
title : _l('Pages'),
url : 'pages/list_published'
}]
});
//
// Lab menu.
//
options.user_requests && this.add({
id : 'lab',
title : 'Lab',
menu : [{
title : 'Sequencing Requests',
url : 'requests/index'
},{
title : 'Find Samples',
url : 'requests/find_samples_index'
},{
title : 'Help',
url : options.lims_doc_url
}]
});
//
// Visualization tab.
//
this.add({
id : 'visualization',
title : _l('Visualization'),
url : 'visualizations/list',
tooltip : _l('Visualize datasets'),
disabled : !Galaxy.user.id,
menu : [{
title : _l('New Track Browser'),
url : 'visualization/trackster',
target : '_frame'
},{
title : _l('Saved Visualizations'),
url : 'visualizations/list',
target : '_frame'
},{
title : _l('Interactive Environments'),
url : 'visualization/gie_list',
target : 'galaxy_main'
define(
[
"layout/generic-nav-view",
"mvc/webhooks",
"utils/localization",
"utils/utils"
],
function(GenericNav, Webhooks, _l, Utils) {
var Collection = Backbone.Collection.extend({
model: Backbone.Model.extend({
defaults: {
visible: true,
target: "_parent"
}
]
});
}),
fetch: function(options) {
var self = this;
options = options || {};
this.reset();
//
// Webhooks
//
Webhooks.add({
url: 'api/webhooks/masthead/all',
callback: function(webhooks) {
$(document).ready(function() {
$.each(webhooks.models, function(index, model) {
var webhook = model.toJSON();
if (webhook.activate) {
var obj = {
id : webhook.name,
icon : webhook.config.icon,
url : webhook.config.url,
tooltip : webhook.config.tooltip,
onclick : webhook.config.function && new Function(webhook.config.function),
};
//
// Chat server tab
//
var extendedNavItem = new GenericNav.GenericNavView();
this.add(extendedNavItem.render());
// Galaxy.page is undefined for data libraries, workflows pages
if( Galaxy.page ) {
Galaxy.page.masthead.collection.add(obj);
}
else if( Galaxy.masthead ) {
Galaxy.masthead.collection.add(obj);
}
// Append masthead script and styles to Galaxy main
Utils.appendScriptStyle( webhook );
}
});
//
// Analyze data tab.
//
this.add({
id: "analysis",
title: _l("Analyze Data"),
url: "",
tooltip: _l("Analysis home view")
});
}
});
//
// Admin.
//
Galaxy.user.get( 'is_admin' ) && this.add({
id : 'admin',
title : _l('Admin'),
url : 'admin',
tooltip : _l('Administer this Galaxy'),
cls : 'admin-only'
});
//
// Workflow tab.
//
this.add({
id: "workflow",
title: _l("Workflow"),
tooltip: _l("Chain tools into workflows"),
disabled: !Galaxy.user.id,
url: "workflow"
});
//
// Help tab.
//
var helpTab = {
id : 'help',
title : _l('Help'),
tooltip : _l('Support, contact, and community'),
menu : [{
title : _l('Support'),
url : options.support_url,
target : '_blank'
},{
title : _l('Search'),
url : options.search_url,
target : '_blank'
},{
title : _l('Mailing Lists'),
url : options.mailing_lists,
target : '_blank'
},{
title : _l('Videos'),
url : options.screencasts_url,
target : '_blank'
},{
title : _l('Wiki'),
url : options.wiki_url,
target : '_blank'
},{
title : _l('How to Cite Galaxy'),
url : options.citation_url,
target : '_blank'
},{
title : _l('Interactive Tours'),
url : 'tours'
}]
};
options.terms_url && helpTab.menu.push({
title : _l('Terms and Conditions'),
url : options.terms_url,
target : '_blank'
});
options.biostar_url && helpTab.menu.unshift({
title : _l('Ask a question'),
url : 'biostar/biostar_question_redirect',
target : '_blank'
});
options.biostar_url && helpTab.menu.unshift({
title : _l('Galaxy Biostar'),
url : options.biostar_url_redirect,
target : '_blank'
});
this.add( helpTab );
//
// 'Shared Items' or Libraries tab.
//
this.add({
id: "shared",
title: _l("Shared Data"),
url: "library/index",
tooltip: _l("Access published resources"),
menu: [
{
title: _l("Data Libraries"),
url: "library/list"
},
{
title: _l("Histories"),
url: "histories/list_published"
},
{
title: _l("Workflows"),
url: "workflows/list_published"
},
{
title: _l("Visualizations"),
url: "visualizations/list_published"
},
{
title: _l("Pages"),
url: "pages/list_published"
}
]
});
//
// User tab.
//
var userTab = {};
if ( !Galaxy.user.id ){
if ( options.allow_user_creation ) {
userTab = {
id : 'user',
title : _l('Login or Register'),
cls : 'loggedout-only',
tooltip : _l('Account registration or login'),
menu : [{
title : _l('Login'),
url : 'user/login',
target : 'galaxy_main',
noscratchbook : true
}, {
title: _l('Register'),
url: 'user/create',
target: 'galaxy_main',
noscratchbook: true
//
// Lab menu.
//
options.user_requests &&
this.add({
id: "lab",
title: "Lab",
menu: [
{
title: "Sequencing Requests",
url: "requests/index"
},
{
title: "Find Samples",
url: "requests/find_samples_index"
},
{
title: "Help",
url: options.lims_doc_url
}
]
});
//
// Visualization tab.
//
this.add({
id: "visualization",
title: _l("Visualization"),
url: "visualizations/list",
tooltip: _l("Visualize datasets"),
disabled: !Galaxy.user.id,
menu: [
{
title: _l("New Track Browser"),
url: "visualization/trackster",
target: "_frame"
},
{
title: _l("Saved Visualizations"),
url: "visualizations/list",
target: "_frame"
},
{
title: _l("Interactive Environments"),
url: "visualization/gie_list",
target: "galaxy_main"
}
]
});
//
// Webhooks
//
Webhooks.add({
url: "api/webhooks/masthead/all",
callback: function(webhooks) {
$(document).ready(function() {
$.each(webhooks.models, function(index, model) {
var webhook = model.toJSON();
if (webhook.activate) {
var obj = {
id: webhook.name,
icon: webhook.config.icon,
url: webhook.config.url,
tooltip: webhook.config.tooltip,
onclick:
webhook.config.function &&
new Function(
webhook.config.function
)
};
// Galaxy.page is undefined for data libraries, workflows pages
if (Galaxy.page) {
Galaxy.page.masthead.collection.add(
obj
);
} else if (Galaxy.masthead) {
Galaxy.masthead.collection.add(obj);
}
// Append masthead script and styles to Galaxy main
Utils.appendScriptStyle(webhook);
}
});
});
}
});
//
// Admin.
//
Galaxy.user.get("is_admin") &&
this.add({
id: "admin",
title: _l("Admin"),
url: "admin",
tooltip: _l("Administer this Galaxy"),
cls: "admin-only"
});
//
// Help tab.
//
var helpTab = {
id: "help",
title: _l("Help"),
tooltip: _l("Support, contact, and community"),
menu: [
{
title: _l("Support"),
url: options.support_url,
target: "_blank"
},
{
title: _l("Search"),
url: options.search_url,
target: "_blank"
},
{
title: _l("Mailing Lists"),
url: options.mailing_lists,
target: "_blank"
},
{
title: _l("Videos"),
url: options.screencasts_url,
target: "_blank"
},
{
title: _l("Wiki"),
url: options.wiki_url,
target: "_blank"
},
{
title: _l("How to Cite Galaxy"),
url: options.citation_url,
target: "_blank"
},
{
title: _l("Interactive Tours"),
url: "tours"
}
]
};
} else {
userTab = {
id: 'user',
title: _l('Login'),
cls: 'loggedout-only',
tooltip: _l('Login'),
url: 'user/login',
target: 'galaxy_main',
noscratchbook: true
};
options.terms_url &&
helpTab.menu.push({
title: _l("Terms and Conditions"),
url: options.terms_url,
target: "_blank"
});
options.biostar_url &&
helpTab.menu.unshift({
title: _l("Ask a question"),
url: "biostar/biostar_question_redirect",
target: "_blank"
});
options.biostar_url &&
helpTab.menu.unshift({
title: _l("Galaxy Biostar"),
url: options.biostar_url_redirect,
target: "_blank"
});
this.add(helpTab);
//
// User tab.
//
var userTab = {};
if (!Galaxy.user.id) {
if (options.allow_user_creation) {
userTab = {
id: "user",
title: _l("Login or Register"),
cls: "loggedout-only",
tooltip: _l("Account registration or login"),
menu: [
{
title: _l("Login"),
url: "user/login",
target: "galaxy_main",
noscratchbook: true
},
{
title: _l("Register"),
url: "user/create",
target: "galaxy_main",
noscratchbook: true
}
]
};
} else {
userTab = {
id: "user",
title: _l("Login"),
cls: "loggedout-only",
tooltip: _l("Login"),
url: "user/login",
target: "galaxy_main",
noscratchbook: true
};
}
} else {
userTab = {
id: "user",
title: _l("User"),
cls: "loggedin-only",
tooltip: _l("Account and saved data"),
menu: [
{
title:
_l("Logged in as") +
" " +
Galaxy.user.get("email")
},
{
title: _l("Preferences"),
url: "user"
},
{
title: _l("Custom Builds"),
url: "custom_builds"
},
{
title: _l("Logout"),
url:
"user/logout?session_csrf_token=" +
Galaxy.session_csrf_token,
target: "_top",
divider: true
},
{
title: _l("Saved Histories"),
url: "histories/list",
target: "_top"
},
{
title: _l("Saved Datasets"),
url: "datasets/list",
target: "_top"
},
{
title: _l("Saved Pages"),
url: "pages/list",
target: "_top"
}
]
};
}
this.add(userTab);
var activeView = this.get(options.active_view);
activeView && activeView.set("active", true);
return new jQuery.Deferred().resolve().promise();
}
} else {
userTab = {
id : 'user',
title : _l('User'),
cls : 'loggedin-only',
tooltip : _l('Account and saved data'),
menu : [{
title : _l('Logged in as') + ' ' + Galaxy.user.get( 'email' )
},{
title : _l('Preferences'),
url : 'user'
},{
title : _l('Custom Builds'),
url : 'custom_builds'
},{
title : _l('Logout'),
url : 'user/logout?session_csrf_token=' + Galaxy.session_csrf_token,
target : '_top',
divider : true
},{
title : _l('Saved Histories'),
url : 'histories/list',
target : '_top'
},{
title : _l('Saved Datasets'),
url : 'datasets/list',
target : '_top'
},{
title : _l('Saved Pages'),
url : 'pages/list',
target : '_top'
}]
};
}
this.add( userTab );
var activeView = this.get( options.active_view );
activeView && activeView.set( 'active', true );
return new jQuery.Deferred().resolve().promise();
}
});
/** Masthead tab **/
var Tab = Backbone.View.extend({
initialize: function ( options ) {
this.model = options.model;
this.setElement( this._template() );
this.$dropdown = this.$( '.dropdown' );
this.$toggle = this.$( '.dropdown-toggle' );
this.$menu = this.$( '.dropdown-menu' );
this.$note = this.$( '.dropdown-note' );
this.listenTo( this.model, 'change', this.render, this );
},
events: {
'click .dropdown-toggle' : '_toggleClick'
},
render: function() {
var self = this;
$( '.tooltip' ).remove();
this.$el.attr( 'id', this.model.id )
.css( { visibility : this.model.get( 'visible' ) && 'visible' || 'hidden' } );
this.model.set( 'url', this._formatUrl( this.model.get( 'url' ) ) );
this.$note.html( this.model.get( 'note' ) || '' )
.removeClass().addClass( 'dropdown-note' )
.addClass( this.model.get( 'note_cls' ) )
.css( { 'display' : this.model.get( 'show_note' ) && 'block' || 'none' } );
this.$toggle.html( this.model.get( 'title' ) || '' )
.removeClass().addClass( 'dropdown-toggle' )
.addClass( this.model.get( 'cls' ) )
.addClass( this.model.get( 'icon' ) && 'dropdown-icon fa ' + this.model.get( 'icon' ) )
.addClass( this.model.get( 'toggle' ) && 'toggle' )
.attr( 'target', this.model.get( 'target' ) )
.attr( 'href', this.model.get( 'url' ) )
.attr( 'title', this.model.get( 'tooltip' ) )
.tooltip( 'destroy' );
this.model.get( 'tooltip' ) && this.$toggle.tooltip( { placement: 'bottom' } );
this.$dropdown.removeClass().addClass( 'dropdown' )
.addClass( this.model.get( 'disabled' ) && 'disabled' )
.addClass( this.model.get( 'active' ) && 'active' );
if ( this.model.get( 'menu' ) && this.model.get( 'show_menu' ) ) {
this.$menu.show();
$( '#dd-helper' ).show().off().on( 'click', function() {
$( '#dd-helper' ).hide();
self.model.set( 'show_menu', false );
});
} else {
self.$menu.hide();
$( '#dd-helper' ).hide();
}
this.$menu.empty().removeClass( 'dropdown-menu' );
if ( this.model.get( 'menu' ) ) {
_.each( this.model.get( 'menu' ), function( menuItem ) {
self.$menu.append( self._buildMenuItem( menuItem ) );
menuItem.divider && self.$menu.append( $( '<li/>' ).addClass( 'divider' ) );
});
self.$menu.addClass( 'dropdown-menu' );
self.$toggle.append( $( '<b/>' ).addClass( 'caret' ) );
}
return this;
},
/** Add new menu item */
_buildMenuItem: function ( options ) {
var self = this;
options = _.defaults( options || {}, {
title : '',
url : '',
target : '_parent',
noscratchbook : false
});
options.url = self._formatUrl( options.url );
return $( '<li/>' ).append(
$( '<a/>' ).attr( 'href', options.url )
.attr( 'target', options.target )
.html( options.title )
.on( 'click', function( e ) {
/** Masthead tab **/
var Tab = Backbone.View.extend({
initialize: function(options) {
this.model = options.model;
this.setElement(this._template());
this.$dropdown = this.$(".dropdown");
this.$toggle = this.$(".dropdown-toggle");
this.$menu = this.$(".dropdown-menu");
this.$note = this.$(".dropdown-note");
this.listenTo(this.model, "change", this.render, this);
},
events: {
"click .dropdown-toggle": "_toggleClick"
},
render: function() {
var self = this;
$(".tooltip").remove();
this.$el
.attr("id", this.model.id)
.css({
visibility:
(this.model.get("visible") && "visible") || "hidden"
});
this.model.set("url", this._formatUrl(this.model.get("url")));
this.$note
.html(this.model.get("note") || "")
.removeClass()
.addClass("dropdown-note")
.addClass(this.model.get("note_cls"))
.css({
display:
(this.model.get("show_note") && "block") || "none"
});
this.$toggle
.html(this.model.get("title") || "")
.removeClass()
.addClass("dropdown-toggle")
.addClass(this.model.get("cls"))
.addClass(
this.model.get("icon") &&
"dropdown-icon fa " + this.model.get("icon")
)
.addClass(this.model.get("toggle") && "toggle")
.attr("target", this.model.get("target"))
.attr("href", this.model.get("url"))
.attr("title", this.model.get("tooltip"))
.tooltip("destroy");
this.model.get("tooltip") &&
this.$toggle.tooltip({ placement: "bottom" });
this.$dropdown
.removeClass()
.addClass("dropdown")
.addClass(this.model.get("disabled") && "disabled")
.addClass(this.model.get("active") && "active");
if (this.model.get("menu") && this.model.get("show_menu")) {
this.$menu.show();
$("#dd-helper")
.show()
.off()
.on("click", function() {
$("#dd-helper").hide();
self.model.set("show_menu", false);
});
} else {
self.$menu.hide();
$("#dd-helper").hide();
}
this.$menu.empty().removeClass("dropdown-menu");
if (this.model.get("menu")) {
_.each(this.model.get("menu"), function(menuItem) {
self.$menu.append(self._buildMenuItem(menuItem));
menuItem.divider &&
self.$menu.append($("<li/>").addClass("divider"));
});
self.$menu.addClass("dropdown-menu");
self.$toggle.append($("<b/>").addClass("caret"));
}
return this;
},
/** Add new menu item */
_buildMenuItem: function(options) {
var self = this;
options = _.defaults(options || {}, {
title: "",
url: "",
target: "_parent",
noscratchbook: false
});
options.url = self._formatUrl(options.url);
return $("<li/>").append(
$("<a/>")
.attr("href", options.url)
.attr("target", options.target)
.html(options.title)
.on("click", function(e) {
e.preventDefault();
self.model.set( 'show_menu', false );
if (options.onclick){
self.model.set("show_menu", false);
if (options.onclick) {
options.onclick();
} else {
Galaxy.frame.add( options );
Galaxy.frame.add(options);
}
})
);
},
})
);
},
/** Handle click event */
_toggleClick: function( e ) {
var self = this;
var model = this.model;
e.preventDefault();
$( '.tooltip' ).hide();
model.trigger( 'dispatch', function( m ) {
model.id !== m.id && m.get( 'menu' ) && m.set( 'show_menu', false );
});
if ( !model.get( 'disabled' ) ) {
if ( !model.get( 'menu' ) ) {
model.get( 'onclick' ) ? model.get( 'onclick' )() : Galaxy.frame.add( model.attributes );
} else {
model.set( 'show_menu', true );
}
} else {
function buildLink( label, url ) {
return $( '<div/>' ).append( $( '<a/>' ).attr( 'href', Galaxy.root + url ).html( label ) ).html();
}
this.$toggle.popover && this.$toggle.popover( 'destroy' );
this.$toggle.popover({
html : true,
placement : 'bottom',
content : 'Please ' + buildLink( 'login', 'user/login?use_panels=True' ) + ' or ' +
buildLink( 'register', 'user/create?use_panels=True' ) + ' to use this feature.'
}).popover( 'show' );
setTimeout( function() { self.$toggle.popover( 'destroy' ) }, 5000 );
}
},
/** Handle click event */
_toggleClick: function(e) {
var self = this;
var model = this.model;
e.preventDefault();
$(".tooltip").hide();
model.trigger("dispatch", function(m) {
model.id !== m.id &&
m.get("menu") &&
m.set("show_menu", false);
});
if (!model.get("disabled")) {
if (!model.get("menu")) {
model.get("onclick")
? model.get("onclick")()
: Galaxy.frame.add(model.attributes);
} else {
model.set("show_menu", true);
}
} else {
function buildLink(label, url) {
return $("<div/>")
.append(
$("<a/>")
.attr("href", Galaxy.root + url)
.html(label)
)
.html();
}
this.$toggle.popover && this.$toggle.popover("destroy");
this.$toggle
.popover({
html: true,
placement: "bottom",
content:
"Please " +
buildLink(
"login",
"user/login?use_panels=True"
) +
" or " +
buildLink(
"register",
"user/create?use_panels=True"
) +
" to use this feature."
})
.popover("show");
setTimeout(function() {
self.$toggle.popover("destroy");
}, 5000);
}
},
/** Url formatting */
_formatUrl: function( url ) {
return typeof url == 'string' && url.indexOf( '//' ) === -1 && url.charAt( 0 ) != '/' ? Galaxy.root + url : url;
},
/** Url formatting */
_formatUrl: function(url) {
return typeof url == "string" &&
url.indexOf("//") === -1 &&
url.charAt(0) != "/"
? Galaxy.root + url
: url;
},
/** body tempate */
_template: function () {
return '<ul class="nav navbar-nav">' +
/** body tempate */
_template: function() {
return (
'<ul class="nav navbar-nav">' +
'<li class="dropdown">' +
'<a class="dropdown-toggle"/>' +
'<ul class="dropdown-menu"/>' +
'<div class="dropdown-note"/>' +
'</li>' +
'</ul>';
'<a class="dropdown-toggle"/>' +
'<ul class="dropdown-menu"/>' +
'<div class="dropdown-note"/>' +
"</li>" +
"</ul>"
);
}
});
return {
Collection: Collection,
Tab: Tab
};
}
});
return {
Collection : Collection,
Tab : Tab
};
});
);
+168 -139
View File
@@ -1,150 +1,179 @@
define([
'jquery',
], function (jQuery){
define(["jquery"], function(jQuery) {
"use strict";
// ============================================================================
//TODO: (the older version) unify with ui-modal (the newer version)
var $ = jQuery;
"use strict";
// ============================================================================
//TODO: (the older version) unify with ui-modal (the newer version)
var $ = jQuery;
// Modal dialog boxes
var Modal = function(options) {
this.$overlay = options.overlay;
this.$dialog = options.dialog;
this.$header = this.$dialog.find(".modal-header");
this.$body = this.$dialog.find(".modal-body");
this.$footer = this.$dialog.find(".modal-footer");
this.$backdrop = options.backdrop;
// Close button
this.$header.find(".close").on("click", $.proxy(this.hide, this));
};
// Modal dialog boxes
var Modal = function( options ) {
this.$overlay = options.overlay;
this.$dialog = options.dialog;
this.$header = this.$dialog.find( ".modal-header" );
this.$body = this.$dialog.find( ".modal-body" );
this.$footer = this.$dialog.find( ".modal-footer" );
this.$backdrop = options.backdrop;
// Close button
this.$header.find( ".close" ).on( "click", $.proxy( this.hide, this ) );
};
$.extend( Modal.prototype, {
setContent: function( options ) {
this.$header.hide();
// Title
if ( options.title ) {
this.$header.find( ".title" ).html( options.title );
this.$header.show();
}
if ( options.closeButton ) {
this.$header.find( ".close" ).show();
this.$header.show();
} else {
this.$header.find( ".close" ).hide();
}
// Buttons
this.$footer.hide();
var $buttons = this.$footer.find( ".buttons" ).html( "" );
if ( options.buttons ) {
$.each( options.buttons, function( name, value ) {
$buttons.append( $( '<button></button> ' ).text( name ).click( value ) ).append( " " );
});
this.$footer.show();
}
var $extraButtons = this.$footer.find( ".extra_buttons" ).html( "" );
if ( options.extra_buttons ) {
$.each( options.extra_buttons, function( name, value ) {
$extraButtons.append( $( '<button></button>' ).text( name ).click( value ) ).append( " " );
});
this.$footer.show();
}
// Body
var body = options.body;
if ( body == "progress" ) {
body = $("<div class='progress progress-striped active'><div class='progress-bar' style='width: 100%'></div></div>");
}
this.$body.html( body );
},
show: function( options, callback ) {
if ( ! this.$dialog.is( ":visible" ) ) {
if ( options.backdrop) {
this.$backdrop.addClass( "in" );
} else {
this.$backdrop.removeClass( "in" );
$.extend(Modal.prototype, {
setContent: function(options) {
this.$header.hide();
// Title
if (options.title) {
this.$header.find(".title").html(options.title);
this.$header.show();
}
this.$overlay.show();
this.$dialog.show();
this.$overlay.addClass("in");
// Fix min-width so that modal cannot shrink considerably if new content is loaded.
this.$body.css( "min-width", this.$body.width() );
// Set max-height so that modal does not exceed window size and is in middle of page.
// TODO: this could perhaps be handled better using CSS.
this.$body.css( "max-height",
$(window).height() -
this.$footer.outerHeight() -
this.$header.outerHeight() -
parseInt( this.$dialog.css( "padding-top" ), 10 ) -
parseInt( this.$dialog.css( "padding-bottom" ), 10 )
);
if (options.closeButton) {
this.$header.find(".close").show();
this.$header.show();
} else {
this.$header.find(".close").hide();
}
// Buttons
this.$footer.hide();
var $buttons = this.$footer.find(".buttons").html("");
if (options.buttons) {
$.each(options.buttons, function(name, value) {
$buttons
.append(
$("<button></button> ")
.text(name)
.click(value)
)
.append(" ");
});
this.$footer.show();
}
var $extraButtons = this.$footer.find(".extra_buttons").html("");
if (options.extra_buttons) {
$.each(options.extra_buttons, function(name, value) {
$extraButtons
.append(
$("<button></button>")
.text(name)
.click(value)
)
.append(" ");
});
this.$footer.show();
}
// Body
var body = options.body;
if (body == "progress") {
body = $(
"<div class='progress progress-striped active'><div class='progress-bar' style='width: 100%'></div></div>"
);
}
this.$body.html(body);
},
show: function(options, callback) {
if (!this.$dialog.is(":visible")) {
if (options.backdrop) {
this.$backdrop.addClass("in");
} else {
this.$backdrop.removeClass("in");
}
this.$overlay.show();
this.$dialog.show();
this.$overlay.addClass("in");
// Fix min-width so that modal cannot shrink considerably if new content is loaded.
this.$body.css("min-width", this.$body.width());
// Set max-height so that modal does not exceed window size and is in middle of page.
// TODO: this could perhaps be handled better using CSS.
this.$body.css(
"max-height",
$(window).height() -
this.$footer.outerHeight() -
this.$header.outerHeight() -
parseInt(this.$dialog.css("padding-top"), 10) -
parseInt(this.$dialog.css("padding-bottom"), 10)
);
}
// Callback on init
if (callback) {
callback();
}
},
hide: function() {
var modal = this;
modal.$dialog.fadeOut(function() {
modal.$overlay.hide();
modal.$backdrop.removeClass("in");
modal.$body.children().remove();
// Clear min-width to allow for modal to take size of new body.
modal.$body.css("min-width", undefined);
});
}
// Callback on init
if ( callback ) {
callback();
}
},
hide: function() {
var modal = this;
modal.$dialog.fadeOut( function() {
modal.$overlay.hide();
modal.$backdrop.removeClass( "in" );
modal.$body.children().remove();
// Clear min-width to allow for modal to take size of new body.
modal.$body.css( "min-width", undefined );
});
}
});
var modal;
$(function(){
modal = new Modal( { overlay: $("#top-modal"), dialog: $("#top-modal-dialog"), backdrop: $("#top-modal-backdrop") } );
});
// Backward compatibility
function hide_modal() {
modal.hide();
}
function show_modal( title, body, buttons, extra_buttons, init_fn ) {
modal.setContent( { title: title, body: body, buttons: buttons, extra_buttons: extra_buttons } );
modal.show( { backdrop: true }, init_fn );
}
function show_message( title, body, buttons, extra_buttons, init_fn ) {
modal.setContent( { title: title, body: body, buttons: buttons, extra_buttons: extra_buttons } );
modal.show( { backdrop: false }, init_fn );
}
function show_in_overlay( options ) {
var width = options.width || '600';
var height = options.height || '400';
var scroll = options.scroll || 'auto';
$("#overlay-background").bind( "click.overlay", function() {
hide_modal();
$("#overlay-background").unbind( "click.overlay" );
});
modal.setContent({
closeButton: true,
title: "&nbsp;",
body: $(
"<div style='margin: -5px;'><iframe style='margin: 0; padding: 0;' src='" + options.url +
"' width='" + width +
"' height='" + height +
"' scrolling='" + scroll +
"' frameborder='0'></iframe></div>"
)
var modal;
$(function() {
modal = new Modal({
overlay: $("#top-modal"),
dialog: $("#top-modal-dialog"),
backdrop: $("#top-modal-backdrop")
});
});
modal.show( { backdrop: true } );
}
// Backward compatibility
function hide_modal() {
modal.hide();
}
// ============================================================================
function show_modal(title, body, buttons, extra_buttons, init_fn) {
modal.setContent({
title: title,
body: body,
buttons: buttons,
extra_buttons: extra_buttons
});
modal.show({ backdrop: true }, init_fn);
}
function show_message(title, body, buttons, extra_buttons, init_fn) {
modal.setContent({
title: title,
body: body,
buttons: buttons,
extra_buttons: extra_buttons
});
modal.show({ backdrop: false }, init_fn);
}
function show_in_overlay(options) {
var width = options.width || "600";
var height = options.height || "400";
var scroll = options.scroll || "auto";
$("#overlay-background").bind("click.overlay", function() {
hide_modal();
$("#overlay-background").unbind("click.overlay");
});
modal.setContent({
closeButton: true,
title: "&nbsp;",
body: $(
"<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 });
}
// ============================================================================
return {
Modal : Modal,
hide_modal : hide_modal,
show_modal : show_modal,
show_message : show_message,
show_in_overlay : show_in_overlay,
Modal: Modal,
hide_modal: hide_modal,
show_modal: show_modal,
show_message: show_message,
show_in_overlay: show_in_overlay
};
});
+176 -156
View File
@@ -1,135 +1,142 @@
define( [ 'layout/masthead', 'layout/panel', 'mvc/ui/ui-modal', 'utils/utils' ], function( Masthead, Panel, Modal, Utils) {
var View = Backbone.View.extend({
el : 'body',
className : 'full-content',
_panelids : [ 'left', 'right' ],
define(
["layout/masthead", "layout/panel", "mvc/ui/ui-modal", "utils/utils"],
function(Masthead, Panel, Modal, Utils) {
var View = Backbone.View.extend({
el: "body",
className: "full-content",
_panelids: ["left", "right"],
initialize : function( options ) {
var self = this;
this.config = _.defaults( options.config || {}, {
message_box_visible : false,
message_box_content : '',
message_box_class : 'info',
show_inactivity_warning : false,
inactivity_box_content : ''
});
initialize: function(options) {
var self = this;
this.config = _.defaults(options.config || {}, {
message_box_visible: false,
message_box_content: "",
message_box_class: "info",
show_inactivity_warning: false,
inactivity_box_content: ""
});
// attach global objects, build mastheads
Galaxy.modal = this.modal = new Modal.View();
Galaxy.display = this.display = function( view ) {
if ( view.title ){
Utils.setWindowTitle( view.title );
view.allow_title_display = false;
} else {
Utils.setWindowTitle();
view.allow_title_display = true;
}
self.center.display( view );
};
Galaxy.router = this.router = options.Router && new options.Router( self, options );
this.masthead = new Masthead.View( this.config );
this.center = new Panel.CenterPanel();
// attach global objects, build mastheads
Galaxy.modal = this.modal = new Modal.View();
Galaxy.display = this.display = function(view) {
if (view.title) {
Utils.setWindowTitle(view.title);
view.allow_title_display = false;
} else {
Utils.setWindowTitle();
view.allow_title_display = true;
}
self.center.display(view);
};
Galaxy.router = this.router =
options.Router && new options.Router(self, options);
this.masthead = new Masthead.View(this.config);
this.center = new Panel.CenterPanel();
// build page template
this.$el.attr( 'scroll', 'no' );
this.$el.html( this._template() );
this.$( '#masthead' ).replaceWith( this.masthead.$el );
this.$( '#center' ).append( this.center.$el );
this.$el.append( this.masthead.frame.$el );
this.$el.append( this.modal.$el );
this.$messagebox = this.$( '#messagebox' );
this.$inactivebox = this.$( '#inactivebox' );
// build page template
this.$el.attr("scroll", "no");
this.$el.html(this._template());
this.$("#masthead").replaceWith(this.masthead.$el);
this.$("#center").append(this.center.$el);
this.$el.append(this.masthead.frame.$el);
this.$el.append(this.modal.$el);
this.$messagebox = this.$("#messagebox");
this.$inactivebox = this.$("#inactivebox");
// build panels
this.panels = {};
_.each( this._panelids, function( panel_id ) {
var panel_class_name = panel_id.charAt( 0 ).toUpperCase() + panel_id.slice( 1 );
var panel_class = options[ panel_class_name ];
if ( panel_class ) {
var panel_instance = new panel_class( self, options );
self[ panel_instance.toString() ] = panel_instance;
self.panels[ panel_id ] = new Panel.SidePanel({
id : panel_id,
el : self.$( '#' + panel_id ),
view : panel_instance
// build panels
this.panels = {};
_.each(this._panelids, function(panel_id) {
var panel_class_name =
panel_id.charAt(0).toUpperCase() + panel_id.slice(1);
var panel_class = options[panel_class_name];
if (panel_class) {
var panel_instance = new panel_class(self, options);
self[panel_instance.toString()] = panel_instance;
self.panels[panel_id] = new Panel.SidePanel({
id: panel_id,
el: self.$("#" + panel_id),
view: panel_instance
});
}
});
this.render();
// start the router
this.router &&
Backbone.history.start({
root: Galaxy.root,
pushState: true
});
}
});
this.render();
},
// start the router
this.router && Backbone.history.start({
root : Galaxy.root,
pushState : true,
});
},
render: function() {
// TODO: Remove this line after select2 update
$(".select2-hidden-accessible").remove();
this.masthead.render();
this.renderMessageBox();
this.renderInactivityBox();
this.renderPanels();
this._checkCommunicationServerOnline();
return this;
},
render : function() {
// TODO: Remove this line after select2 update
$( '.select2-hidden-accessible' ).remove();
this.masthead.render();
this.renderMessageBox();
this.renderInactivityBox();
this.renderPanels();
this._checkCommunicationServerOnline();
return this;
},
/** Render message box */
renderMessageBox : function() {
if ( this.config.message_box_visible ){
var content = this.config.message_box_content || '';
var level = this.config.message_box_class || 'info';
this.$el.addClass( 'has-message-box' );
this.$messagebox
.attr( 'class', 'panel-' + level + '-message' )
.html( content )
.toggle( !!content )
.show();
} else {
this.$el.removeClass( 'has-message-box' );
this.$messagebox.hide();
}
return this;
},
/** Render inactivity warning */
renderInactivityBox : function() {
if( this.config.show_inactivity_warning ){
var content = this.config.inactivity_box_content || '';
var verificationLink = $( '<a/>' ).attr( 'href', Galaxy.root + 'user/resend_verification' ).text( 'Resend verification' );
this.$el.addClass( 'has-inactivity-box' );
this.$inactivebox
.html( content + ' ' )
.append( verificationLink )
.toggle( !!content )
.show();
} else {
this.$el.removeClass( 'has-inactivity-box' );
this.$inactivebox.hide();
}
return this;
},
/** Render panels */
renderPanels : function() {
var self = this;
_.each( this._panelids, function( panel_id ) {
var panel = self.panels[ panel_id ];
if ( panel ) {
panel.render();
/** Render message box */
renderMessageBox: function() {
if (this.config.message_box_visible) {
var content = this.config.message_box_content || "";
var level = this.config.message_box_class || "info";
this.$el.addClass("has-message-box");
this.$messagebox
.attr("class", "panel-" + level + "-message")
.html(content)
.toggle(!!content)
.show();
} else {
self.$( '#center' ).css( panel_id, 0 );
self.$( '#' + panel_id ).hide();
this.$el.removeClass("has-message-box");
this.$messagebox.hide();
}
});
return this;
},
return this;
},
/** body template */
_template: function() {
return [
'<div id="everything">',
/** Render inactivity warning */
renderInactivityBox: function() {
if (this.config.show_inactivity_warning) {
var content = this.config.inactivity_box_content || "";
var verificationLink = $("<a/>")
.attr("href", Galaxy.root + "user/resend_verification")
.text("Resend verification");
this.$el.addClass("has-inactivity-box");
this.$inactivebox
.html(content + " ")
.append(verificationLink)
.toggle(!!content)
.show();
} else {
this.$el.removeClass("has-inactivity-box");
this.$inactivebox.hide();
}
return this;
},
/** Render panels */
renderPanels: function() {
var self = this;
_.each(this._panelids, function(panel_id) {
var panel = self.panels[panel_id];
if (panel) {
panel.render();
} else {
self.$("#center").css(panel_id, 0);
self.$("#" + panel_id).hide();
}
});
return this;
},
/** body template */
_template: function() {
return [
'<div id="everything">',
'<div id="background"/>',
'<div id="masthead"/>',
'<div id="messagebox"/>',
@@ -137,42 +144,55 @@ define( [ 'layout/masthead', 'layout/panel', 'mvc/ui/ui-modal', 'utils/utils' ],
'<div id="left" />',
'<div id="center" class="inbound" />',
'<div id="right" />',
'</div>',
'<div id="dd-helper" />',
].join('');
},
"</div>",
'<div id="dd-helper" />'
].join("");
},
toString : function() { return 'PageLayoutView' },
toString: function() {
return "PageLayoutView";
},
/** Check if the communication server is online and show the icon otherwise hide the icon */
_checkCommunicationServerOnline: function(){
var host = window.Galaxy.config.communication_server_host,
port = window.Galaxy.config.communication_server_port,
preferences = window.Galaxy.user.attributes.preferences,
$chat_icon_element = $( "#show-chat-online" );
/** Check if the user has deactivated the communication in it's personal settings */
if ( preferences && [ '1', 'true' ].indexOf( preferences.communication_server ) != -1 ) {
// See if the configured communication server is available
$.ajax({
url: host + ":" + port,
})
.success( function( data ) {
// enable communication only when a user is logged in
if( window.Galaxy.user.id !== null ) {
if( $chat_icon_element.css( "visibility") === "hidden" ) {
$chat_icon_element.css( "visibility", "visible" );
/** Check if the communication server is online and show the icon otherwise hide the icon */
_checkCommunicationServerOnline: function() {
var host = window.Galaxy.config.communication_server_host,
port = window.Galaxy.config.communication_server_port,
preferences = window.Galaxy.user.attributes.preferences,
$chat_icon_element = $("#show-chat-online");
/** Check if the user has deactivated the communication in it's personal settings */
if (
preferences &&
["1", "true"].indexOf(preferences.communication_server) !=
-1
) {
// See if the configured communication server is available
$.ajax({
url: host + ":" + port
})
.success(function(data) {
// enable communication only when a user is logged in
if (window.Galaxy.user.id !== null) {
if (
$chat_icon_element.css("visibility") ===
"hidden"
) {
$chat_icon_element.css(
"visibility",
"visible"
);
}
}
}
})
.error( function( data ) {
// hide the communication icon if the communication server is not available
$chat_icon_element.css( "visibility", "hidden" );
});
} else {
$chat_icon_element.css( "visibility", "hidden" );
})
.error(function(data) {
// hide the communication icon if the communication server is not available
$chat_icon_element.css("visibility", "hidden");
});
} else {
$chat_icon_element.css("visibility", "hidden");
}
}
},
});
});
return { View: View }
});
return { View: View };
}
);
+133 -100
View File
@@ -1,4 +1,8 @@
define( [ 'jquery', 'libs/underscore', 'libs/backbone' ], function( $, _, Backbone ) {
define(["jquery", "libs/underscore", "libs/backbone"], function(
$,
_,
Backbone
) {
"use strict";
var MIN_PANEL_WIDTH = 160,
@@ -6,33 +10,32 @@ define( [ 'jquery', 'libs/underscore', 'libs/backbone' ], function( $, _, Backbo
/** View for left/right panels used by Page view */
var SidePanel = Backbone.View.extend({
initialize: function( options ){
initialize: function(options) {
this.view = options.view;
this.hidden = false;
this.saved_size = null;
this.hiddenByTool = false;
},
$center : function() {
return this.$el.siblings( '#center' );
$center: function() {
return this.$el.siblings("#center");
},
$toggleButton : function() {
return this.$( '.unified-panel-footer > .panel-collapse' );
$toggleButton: function() {
return this.$(".unified-panel-footer > .panel-collapse");
},
render: function() {
var self = this;
var panel = this.view;
var components = this.view.model.attributes || {};
this.$el.html( this._templatePanel( this.id ) );
_.each( components.buttons, function( button ) {
self.$( '.panel-header-buttons' ).append( button.$el );
this.$el.html(this._templatePanel(this.id));
_.each(components.buttons, function(button) {
self.$(".panel-header-buttons").append(button.$el);
});
this.$el.addClass( components.cls );
this.$( '.panel-header-text' ).html( _.escape( components.title ) );
this.$( '.unified-panel-body' ).append( panel.$el );
this.$el.addClass(components.cls);
this.$(".panel-header-text").html(_.escape(components.title));
this.$(".unified-panel-body").append(panel.$el);
panel.render();
},
@@ -41,106 +44,119 @@ define( [ 'jquery', 'libs/underscore', 'libs/backbone' ], function( $, _, Backbo
return [
this._templateHeader(),
this._templateBody(),
this._templateFooter(),
].join('');
this._templateFooter()
].join("");
},
/** panel dom template. id is 'right' or 'left' */
_templateHeader: function( data ) {
_templateHeader: function(data) {
return [
'<div class="unified-panel-header" unselectable="on">',
'<div class="unified-panel-header-inner">',
'<div class="panel-header-buttons" style="float: right"/>',
'<div class="panel-header-text"/>',
'</div>',
'</div>',
].join('');
'<div class="unified-panel-header-inner">',
'<div class="panel-header-buttons" style="float: right"/>',
'<div class="panel-header-text"/>',
"</div>",
"</div>"
].join("");
},
/** panel dom template. id is 'right' or 'left' */
_templateBody: function( data ) {
_templateBody: function(data) {
return '<div class="unified-panel-body"/>';
},
/** panel dom template. id is 'right' or 'left' */
_templateFooter: function( data ) {
_templateFooter: function(data) {
return [
'<div class="unified-panel-footer">',
'<div class="panel-collapse ', _.escape( this.id ), '"/>',
'<div class="drag"/>',
'</div>',
].join('');
'<div class="panel-collapse ',
_.escape(this.id),
'"/>',
'<div class="drag"/>',
"</div>"
].join("");
},
events : {
'mousedown .unified-panel-footer > .drag' : '_mousedownDragHandler',
'click .unified-panel-footer > .panel-collapse' : 'toggle'
events: {
"mousedown .unified-panel-footer > .drag": "_mousedownDragHandler",
"click .unified-panel-footer > .panel-collapse": "toggle"
},
_mousedownDragHandler : function( ev ) {
_mousedownDragHandler: function(ev) {
var self = this,
draggingLeft = this.id === 'left',
draggingLeft = this.id === "left",
prevX = ev.pageX;
function move( e ){
function move(e) {
var delta = e.pageX - prevX;
prevX = e.pageX;
var oldWidth = self.$el.width(),
newWidth = draggingLeft?( oldWidth + delta ):( oldWidth - delta );
newWidth = draggingLeft
? oldWidth + delta
: oldWidth - delta;
// Limit range
newWidth = Math.min( MAX_PANEL_WIDTH, Math.max( MIN_PANEL_WIDTH, newWidth ) );
self.resize( newWidth );
newWidth = Math.min(
MAX_PANEL_WIDTH,
Math.max(MIN_PANEL_WIDTH, newWidth)
);
self.resize(newWidth);
}
// this is a page wide overlay that assists in capturing the move and release of the mouse
// if not provided, progress and end wouldn't fire if the mouse moved out of the drag button area
$( '#dd-helper' )
$("#dd-helper")
.show()
.on( 'mousemove', move )
.one( 'mouseup', function( e ){
$( this ).hide().off( 'mousemove', move );
.on("mousemove", move)
.one("mouseup", function(e) {
$(this)
.hide()
.off("mousemove", move);
});
},
// TODO: the following three could be simplified I think
// if panel is 'right' (this.id), move center right newSize number of pixels
resize : function( newSize ) {
this.$el.css( 'width', newSize );
this.$center().css( this.id, newSize );
resize: function(newSize) {
this.$el.css("width", newSize);
this.$center().css(this.id, newSize);
return this;
},
show : function(){
if( !this.hidden ) { return }
show: function() {
if (!this.hidden) {
return;
}
var self = this,
animation = {},
whichSide = this.id;
animation[ whichSide ] = 0;
animation[whichSide] = 0;
self.$el
.css( whichSide, -this.saved_size )
.css(whichSide, -this.saved_size)
.show()
.animate( animation, "fast", function(){
self.resize( self.saved_size );
.animate(animation, "fast", function() {
self.resize(self.saved_size);
});
self.hidden = false;
self.$toggleButton().removeClass( "hidden" );
self.$toggleButton().removeClass("hidden");
return this;
},
hide : function(){
if( this.hidden ) { return }
hide: function() {
if (this.hidden) {
return;
}
var animation = {},
whichSide = this.id;
this.saved_size = this.$el.width();
animation[ whichSide ] = -this.saved_size;
this.$el.animate( animation, "fast" );
this.$center().css( whichSide, 0 );
animation[whichSide] = -this.saved_size;
this.$el.animate(animation, "fast");
this.$center().css(whichSide, 0);
this.hidden = true;
this.$toggleButton().addClass( "hidden" );
this.$toggleButton().addClass("hidden");
return this;
},
toggle: function( ev ) {
toggle: function(ev) {
this.hidden ? this.show() : this.hide();
this.hiddenByTool = false;
return this;
@@ -149,15 +165,16 @@ define( [ 'jquery', 'libs/underscore', 'libs/backbone' ], function( $, _, Backbo
// ..............................................................
//TODO: only used in message.mako?
/** */
handle_minwidth_hint: function( hint ){
var space = this.$center().width() - ( this.hidden ? this.saved_size : 0 );
if( space < hint ){
if( !this.hidden ){
handle_minwidth_hint: function(hint) {
var space =
this.$center().width() - (this.hidden ? this.saved_size : 0);
if (space < hint) {
if (!this.hidden) {
this.toggle();
this.hiddenByTool = true;
}
} else {
if( this.hiddenByTool ){
if (this.hiddenByTool) {
this.toggle();
this.hiddenByTool = false;
}
@@ -166,77 +183,93 @@ define( [ 'jquery', 'libs/underscore', 'libs/backbone' ], function( $, _, Backbo
},
/** */
force_panel : function( op ){
if( op == 'show' ){ return this.show(); }
if( op == 'hide' ){ return this.hide(); }
force_panel: function(op) {
if (op == "show") {
return this.show();
}
if (op == "hide") {
return this.hide();
}
return self;
},
toString : function(){ return 'SidePanel(' + this.id + ')' }
toString: function() {
return "SidePanel(" + this.id + ")";
}
});
// ----------------------------------------------------------------------------
// TODO: side should be defined by page - not here
var LeftPanel = SidePanel.extend({
id : 'left',
id: "left"
});
var RightPanel = SidePanel.extend({
id : 'right',
id: "right"
});
/** Center panel with the ability to switch between iframe and view */
var CenterPanel = Backbone.View.extend({
initialize : function( options ){
this.setElement( $( this.template() ) );
this.$frame = this.$( '.center-frame' );
this.$panel = this.$( '.center-panel' );
this.$frame.on( 'load', _.bind( this._iframeChangeHandler, this ) );
initialize: function(options) {
this.setElement($(this.template()));
this.$frame = this.$(".center-frame");
this.$panel = this.$(".center-panel");
this.$frame.on("load", _.bind(this._iframeChangeHandler, this));
},
/** Display iframe if its target url changes, hide center panel */
_iframeChangeHandler : function( ev ) {
_iframeChangeHandler: function(ev) {
var iframe = ev.currentTarget;
var location = iframe.contentWindow && iframe.contentWindow.location;
if( location && location.host ) {
$( iframe ).show();
var location =
iframe.contentWindow && iframe.contentWindow.location;
if (location && location.host) {
$(iframe).show();
this.$panel.empty().hide();
Galaxy.trigger( 'center-frame:load', {
fullpath: location.pathname + location.search + location.hash,
Galaxy.trigger("center-frame:load", {
fullpath:
location.pathname + location.search + location.hash,
pathname: location.pathname,
search : location.search,
hash : location.hash
search: location.search,
hash: location.hash
});
}
},
/** Display a view in the center panel, hide iframe */
display: function( view ) {
var contentWindow = this.$frame[ 0 ].contentWindow || {};
var message = contentWindow.onbeforeunload && contentWindow.onbeforeunload();
if ( !message || confirm( message ) ) {
display: function(view) {
var contentWindow = this.$frame[0].contentWindow || {};
var message =
contentWindow.onbeforeunload && contentWindow.onbeforeunload();
if (!message || confirm(message)) {
contentWindow.onbeforeunload = undefined;
this.$frame.attr( 'src', 'about:blank' ).hide();
this.$panel.empty().scrollTop( 0 ).append( view.$el ).show();
Galaxy.trigger( 'center-panel:load', view );
this.$frame.attr("src", "about:blank").hide();
this.$panel
.empty()
.scrollTop(0)
.append(view.$el)
.show();
Galaxy.trigger("center-panel:load", view);
}
},
template: function() {
return '<div class="center-container">' +
'<iframe id="galaxy_main" name="galaxy_main" frameborder="0" class="center-frame" />' +
'<div class="center-panel" />' +
'</div>';
return (
'<div class="center-container">' +
'<iframe id="galaxy_main" name="galaxy_main" frameborder="0" class="center-frame" />' +
'<div class="center-panel" />' +
"</div>"
);
},
toString : function() { return 'CenterPanel' }
toString: function() {
return "CenterPanel";
}
});
return {
SidePanel : SidePanel,
LeftPanel : LeftPanel,
RightPanel : RightPanel,
CenterPanel : CenterPanel
SidePanel: SidePanel,
LeftPanel: LeftPanel,
RightPanel: RightPanel,
CenterPanel: CenterPanel
};
});
});
+29 -20
View File
@@ -1,49 +1,58 @@
var jQuery = require( 'jquery' ),
var jQuery = require("jquery"),
$ = jQuery,
QUERY_STRING = require( 'utils/query-string-parsing' ),
Ui = require( 'mvc/ui/ui-misc' );
QUERY_STRING = require("utils/query-string-parsing"),
Ui = require("mvc/ui/ui-misc");
var Router = Backbone.Router.extend({
// TODO: not many client routes at this point - fill and remove from server.
// since we're at root here, this may be the last to be routed entirely on the client.
initialize : function( page, options ){
initialize: function(page, options) {
this.page = page;
this.options = options;
},
/** helper to push a new navigation state */
push: function( url, data ) {
push: function(url, data) {
data = data || {};
data.__identifer = Math.random().toString( 36 ).substr( 2 );
if ( !$.isEmptyObject( data ) ) {
url += url.indexOf( '?' ) == -1 ? '?' : '&';
url += $.param( data , true );
data.__identifer = Math.random()
.toString(36)
.substr(2);
if (!$.isEmptyObject(data)) {
url += url.indexOf("?") == -1 ? "?" : "&";
url += $.param(data, true);
}
Galaxy.params = data;
this.navigate( url, { 'trigger': true } );
this.navigate(url, { trigger: true });
},
/** override to parse query string into obj and send to each route */
execute: function( callback, args, name ){
Galaxy.debug( 'router execute:', callback, args, name );
var queryObj = QUERY_STRING.parse( args.pop() );
args.push( queryObj );
if( callback ){
if ( this.authenticate( args, name ) ) {
callback.apply( this, args );
execute: function(callback, args, name) {
Galaxy.debug("router execute:", callback, args, name);
var queryObj = QUERY_STRING.parse(args.pop());
args.push(queryObj);
if (callback) {
if (this.authenticate(args, name)) {
callback.apply(this, args);
} else {
this.access_denied();
}
}
},
authenticate: function( args, name ) {
authenticate: function(args, name) {
return true;
},
access_denied: function() {
this.page.display( new Ui.Message( { status: 'danger', message: 'You must be logged in with proper credentials to make this request.', persistent: true } ) );
this.page.display(
new Ui.Message({
status: "danger",
message:
"You must be logged in with proper credentials to make this request.",
persistent: true
})
);
}
});
module.exports = Router
module.exports = Router;
+264 -166
View File
@@ -1,150 +1,236 @@
/** Frame manager uses the ui-frames to create the scratch book masthead icon and functionality **/
define([ 'mvc/ui/ui-frames' ], function( Frames ) {
return Backbone.View.extend({
initialize : function( options ) {
var self = this;
options = options || {};
this.frames = new Frames.View({ visible : false });
this.setElement( this.frames.$el );
this.buttonActive = options.collection.add({
id : 'enable-scratchbook',
icon : 'fa-th',
tooltip : _l( 'Enable/Disable Scratchbook' ),
onclick : function() {
self.active = !self.active;
self.buttonActive.set({
toggle : self.active,
show_note : self.active,
note_cls : self.active && 'fa fa-check'
});
!self.active && self.frames.hide();
},
onbeforeunload : function() {
if ( self.frames.length() > 0 ) {
return 'You opened ' + self.frames.length() + ' frame(s) which will be lost.';
}
}
});
this.buttonLoad = options.collection.add({
id : 'show-scratchbook',
icon : 'fa-eye',
tooltip : _l( 'Show/Hide Scratchbook' ),
show_note : true,
visible : false,
onclick : function( e ) {
self.frames.visible ? self.frames.hide() : self.frames.show();
}
});
this.frames.on( 'add remove', function() {
this.visible && this.length() == 0 && this.hide();
self.buttonLoad.set( { 'note': this.length(), 'visible': this.length() > 0 } );
}).on( 'show hide ', function() {
self.buttonLoad.set( { 'toggle': this.visible, 'icon': this.visible && 'fa-eye' || 'fa-eye-slash' } );
});
this.history_cache = {};
},
/** Add a dataset to the frames */
addDataset: function( dataset_id ) {
var self = this;
var current_dataset = null;
if ( Galaxy && Galaxy.currHistoryPanel ) {
var history_id = Galaxy.currHistoryPanel.collection.historyId;
this.history_cache[ history_id ] = { name: Galaxy.currHistoryPanel.model.get( 'name' ), dataset_ids: [] };
Galaxy.currHistoryPanel.collection.each( function( model ) {
!model.get( 'deleted' ) && model.get( 'visible' ) && self.history_cache[ history_id ].dataset_ids.push( model.get( 'id' ) );
});
}
var _findDataset = function( dataset, offset ) {
if ( dataset ) {
var history_details = self.history_cache[ dataset.get( 'history_id' ) ];
if ( history_details && history_details.dataset_ids ) {
var dataset_list = history_details.dataset_ids;
var pos = dataset_list.indexOf( dataset.get( 'id' ) );
if ( pos !== -1 && pos + offset >= 0 && pos + offset < dataset_list.length ) {
return dataset_list[ pos + offset ];
define(["mvc/ui/ui-frames"], function(Frames) {
return Backbone.View.extend({
initialize: function(options) {
var self = this;
options = options || {};
this.frames = new Frames.View({ visible: false });
this.setElement(this.frames.$el);
this.buttonActive = options.collection.add({
id: "enable-scratchbook",
icon: "fa-th",
tooltip: _l("Enable/Disable Scratchbook"),
onclick: function() {
self.active = !self.active;
self.buttonActive.set({
toggle: self.active,
show_note: self.active,
note_cls: self.active && "fa fa-check"
});
!self.active && self.frames.hide();
},
onbeforeunload: function() {
if (self.frames.length() > 0) {
return (
"You opened " +
self.frames.length() +
" frame(s) which will be lost."
);
}
}
}
};
var _loadDatasetOffset = function( dataset, offset, frame ) {
var new_dataset_id = _findDataset( dataset, offset );
if ( new_dataset_id ) {
self._loadDataset( new_dataset_id, function( new_dataset, config ) {
current_dataset = new_dataset;
frame.model.set( config );
});
} else {
frame.model.trigger( 'change' );
}
}
this._loadDataset( dataset_id, function( dataset, config ) {
current_dataset = dataset;
self.add( _.extend( { menu: [ { icon : 'fa fa-chevron-circle-left',
tooltip : 'Previous in History',
onclick : function( frame ) { _loadDatasetOffset( current_dataset, -1, frame ) },
disabled : function() { return !_findDataset( current_dataset, -1 ) } },
{ icon : 'fa fa-chevron-circle-right',
tooltip : 'Next in History',
onclick : function( frame ) { _loadDatasetOffset( current_dataset, 1, frame ) },
disabled : function() { return !_findDataset( current_dataset, 1 ) } } ] }, config ) )
});
},
_loadDataset: function( dataset_id, callback ) {
var self = this;
require([ 'mvc/dataset/data' ], function( DATA ) {
var dataset = new DATA.Dataset( { id : dataset_id } );
$.when( dataset.fetch() ).then( function() {
var is_tabular = _.find( [ 'tabular', 'interval' ] , function( data_type ) {
return dataset.get( 'data_type' ).indexOf( data_type ) !== -1;
});
var title = dataset.get( 'name' );
var history_details = self.history_cache[ dataset.get( 'history_id' ) ];
if ( history_details ) {
title = history_details.name + ': ' + title;
}
callback( dataset, is_tabular ? {
title : title,
url : null,
content : DATA.createTabularDatasetChunkedView({
model : new DATA.TabularDataset( dataset.toJSON() ),
embedded : true,
height : '100%'
}).$el
} : {
title : title,
url : Galaxy.root + 'datasets/' + dataset_id + '/display/?preview=True',
content : null
} );
});
});
},
this.buttonLoad = options.collection.add({
id: "show-scratchbook",
icon: "fa-eye",
tooltip: _l("Show/Hide Scratchbook"),
show_note: true,
visible: false,
onclick: function(e) {
self.frames.visible
? self.frames.hide()
: self.frames.show();
}
});
this.frames
.on("add remove", function() {
this.visible && this.length() == 0 && this.hide();
self.buttonLoad.set({
note: this.length(),
visible: this.length() > 0
});
})
.on("show hide ", function() {
self.buttonLoad.set({
toggle: this.visible,
icon: (this.visible && "fa-eye") || "fa-eye-slash"
});
});
this.history_cache = {};
},
/** Add a trackster visualization to the frames. */
addTrackster: function(viz_id) {
var self = this;
require(['viz/visualization', 'viz/trackster'], function(visualization, trackster) {
var viz = new visualization.Visualization({id: viz_id});
$.when( viz.fetch() ).then( function() {
var ui = new trackster.TracksterUI(Galaxy.root);
/** Add a dataset to the frames */
addDataset: function(dataset_id) {
var self = this;
var current_dataset = null;
if (Galaxy && Galaxy.currHistoryPanel) {
var history_id = Galaxy.currHistoryPanel.collection.historyId;
this.history_cache[history_id] = {
name: Galaxy.currHistoryPanel.model.get("name"),
dataset_ids: []
};
Galaxy.currHistoryPanel.collection.each(function(model) {
!model.get("deleted") &&
model.get("visible") &&
self.history_cache[history_id].dataset_ids.push(
model.get("id")
);
});
}
var _findDataset = function(dataset, offset) {
if (dataset) {
var history_details =
self.history_cache[dataset.get("history_id")];
if (history_details && history_details.dataset_ids) {
var dataset_list = history_details.dataset_ids;
var pos = dataset_list.indexOf(dataset.get("id"));
if (
pos !== -1 &&
pos + offset >= 0 &&
pos + offset < dataset_list.length
) {
return dataset_list[pos + offset];
}
}
}
};
var _loadDatasetOffset = function(dataset, offset, frame) {
var new_dataset_id = _findDataset(dataset, offset);
if (new_dataset_id) {
self._loadDataset(new_dataset_id, function(
new_dataset,
config
) {
current_dataset = new_dataset;
frame.model.set(config);
});
} else {
frame.model.trigger("change");
}
};
this._loadDataset(dataset_id, function(dataset, config) {
current_dataset = dataset;
self.add(
_.extend(
{
menu: [
{
icon: "fa fa-chevron-circle-left",
tooltip: "Previous in History",
onclick: function(frame) {
_loadDatasetOffset(
current_dataset,
-1,
frame
);
},
disabled: function() {
return !_findDataset(
current_dataset,
-1
);
}
},
{
icon: "fa fa-chevron-circle-right",
tooltip: "Next in History",
onclick: function(frame) {
_loadDatasetOffset(
current_dataset,
1,
frame
);
},
disabled: function() {
return !_findDataset(
current_dataset,
1
);
}
}
]
},
config
)
);
});
},
// Construct frame config based on dataset's type.
var frame_config = {
title: viz.get('name'),
type: 'other',
_loadDataset: function(dataset_id, callback) {
var self = this;
require(["mvc/dataset/data"], function(DATA) {
var dataset = new DATA.Dataset({ id: dataset_id });
$.when(dataset.fetch()).then(function() {
var is_tabular = _.find(["tabular", "interval"], function(
data_type
) {
return (
dataset.get("data_type").indexOf(data_type) !== -1
);
});
var title = dataset.get("name");
var history_details =
self.history_cache[dataset.get("history_id")];
if (history_details) {
title = history_details.name + ": " + title;
}
callback(
dataset,
is_tabular
? {
title: title,
url: null,
content: DATA.createTabularDatasetChunkedView(
{
model: new DATA.TabularDataset(
dataset.toJSON()
),
embedded: true,
height: "100%"
}
).$el
}
: {
title: title,
url:
Galaxy.root +
"datasets/" +
dataset_id +
"/display/?preview=True",
content: null
}
);
});
});
},
/** Add a trackster visualization to the frames. */
addTrackster: function(viz_id) {
var self = this;
require(["viz/visualization", "viz/trackster"], function(
visualization,
trackster
) {
var viz = new visualization.Visualization({ id: viz_id });
$.when(viz.fetch()).then(function() {
var ui = new trackster.TracksterUI(Galaxy.root);
// Construct frame config based on dataset's type.
var frame_config = {
title: viz.get("name"),
type: "other",
content: function(parent_elt) {
// Create view config.
var view_config = {
container: parent_elt,
name: viz.get('title'),
id: viz.id,
// FIXME: this will not work with custom builds b/c the dbkey needed to be encoded.
dbkey: viz.get('dbkey'),
stand_alone: false
},
latest_revision = viz.get('latest_revision'),
drawables = latest_revision.config.view.drawables;
container: parent_elt,
name: viz.get("title"),
id: viz.id,
// FIXME: this will not work with custom builds b/c the dbkey needed to be encoded.
dbkey: viz.get("dbkey"),
stand_alone: false
},
latest_revision = viz.get("latest_revision"),
drawables =
latest_revision.config.view.drawables;
// Set up datasets in drawables.
_.each(drawables, function(d) {
@@ -153,38 +239,50 @@ return Backbone.View.extend({
id: d.dataset_id
};
});
view = ui.create_visualization(view_config,
latest_revision.config.viewport,
latest_revision.config.view.drawables,
latest_revision.config.bookmarks,
false);
view = ui.create_visualization(
view_config,
latest_revision.config.viewport,
latest_revision.config.view.drawables,
latest_revision.config.bookmarks,
false
);
}
};
self.add(frame_config);
self.add(frame_config);
});
});
});
},
},
/** Add and display a new frame/window based on options. */
add: function( options ) {
if ( options.target == '_blank' ) {
window.open( options.url );
} else if ( options.target == '_top' || options.target == '_parent' || options.target == '_self' ) {
window.location = options.url;
} else if ( !this.active || options.noscratchbook ) {
var $galaxy_main = $( window.parent.document ).find( '#galaxy_main' );
if ( options.target == 'galaxy_main' || options.target == 'center' ) {
if ( $galaxy_main.length === 0 ) {
window.location = options.url + ( options.url.indexOf( '?' ) == -1 ? '?' : '&' ) + 'use_panels=True';
} else {
$galaxy_main.attr( 'src', options.url );
}
} else
/** Add and display a new frame/window based on options. */
add: function(options) {
if (options.target == "_blank") {
window.open(options.url);
} else if (
options.target == "_top" ||
options.target == "_parent" ||
options.target == "_self"
) {
window.location = options.url;
} else {
this.frames.add( options );
} else if (!this.active || options.noscratchbook) {
var $galaxy_main = $(window.parent.document).find(
"#galaxy_main"
);
if (
options.target == "galaxy_main" ||
options.target == "center"
) {
if ($galaxy_main.length === 0) {
window.location =
options.url +
(options.url.indexOf("?") == -1 ? "?" : "&") +
"use_panels=True";
} else {
$galaxy_main.attr("src", options.url);
}
} else window.location = options.url;
} else {
this.frames.add(options);
}
}
}
});
});
});
+82 -71
View File
@@ -1,79 +1,90 @@
define([
"mvc/base-mvc",
"utils/localization",
"ui/editable-text",
], function( baseMVC, _l ){
// =============================================================================
/** A view on any model that has a 'annotation' attribute
define(["mvc/base-mvc", "utils/localization", "ui/editable-text"], function(
baseMVC,
_l
) {
// =============================================================================
/** A view on any model that has a 'annotation' attribute
*/
var AnnotationEditor = Backbone.View
.extend( baseMVC.LoggableMixin )
.extend( baseMVC.HiddenUntilActivatedViewMixin ).extend({
var AnnotationEditor = Backbone.View
.extend(baseMVC.LoggableMixin)
.extend(baseMVC.HiddenUntilActivatedViewMixin)
.extend({
tagName: "div",
className: "annotation-display",
tagName : 'div',
className : 'annotation-display',
/** Set up listeners, parse options */
initialize: function(options) {
options = options || {};
this.tooltipConfig = options.tooltipConfig || {
placement: "bottom"
};
//console.debug( this, options );
// only listen to the model only for changes to annotations
this.listenTo(this.model, "change:annotation", function() {
this.render();
});
this.hiddenUntilActivated(options.$activator, options);
},
/** Set up listeners, parse options */
initialize : function( options ){
options = options || {};
this.tooltipConfig = options.tooltipConfig || { placement: 'bottom' };
//console.debug( this, options );
// only listen to the model only for changes to annotations
this.listenTo( this.model, 'change:annotation', function(){
this.render();
});
this.hiddenUntilActivated( options.$activator, options );
},
/** Build the DOM elements, call select to on the created input, and set up behaviors */
render: function() {
var view = this;
this.$el.html(this._template());
/** Build the DOM elements, call select to on the created input, and set up behaviors */
render : function(){
var view = this;
this.$el.html( this._template() );
//TODO: handle empties better
this.$annotation().make_text_editable({
use_textarea: true,
on_finish: function(newAnnotation) {
view.$annotation().text(newAnnotation);
view.model
.save(
{ annotation: newAnnotation },
{ silent: true }
)
.fail(function() {
view
.$annotation()
.text(view.model.previous("annotation"));
});
}
});
return this;
},
//TODO: handle empties better
this.$annotation().make_text_editable({
use_textarea: true,
on_finish: function( newAnnotation ){
view.$annotation().text( newAnnotation );
view.model.save({ annotation: newAnnotation }, { silent: true })
.fail( function(){
view.$annotation().text( view.model.previous( 'annotation' ) );
});
/** @returns {String} the html text used to build the view's DOM */
_template: function() {
var annotation = this.model.get("annotation");
return [
//TODO: make prompt optional
'<label class="prompt">',
_l("Annotation"),
"</label>",
// set up initial tags by adding as CSV to input vals (necc. to init select2)
'<div class="annotation">',
_.escape(annotation),
"</div>"
].join("");
},
/** @returns {jQuery} the main element for this view */
$annotation: function() {
return this.$el.find(".annotation");
},
/** shut down event listeners and remove this view's DOM */
remove: function() {
this.$annotation.off();
this.stopListening(this.model);
Backbone.View.prototype.remove.call(this);
},
/** string rep */
toString: function() {
return ["AnnotationEditor(", this.model + "", ")"].join("");
}
});
return this;
},
/** @returns {String} the html text used to build the view's DOM */
_template : function(){
var annotation = this.model.get( 'annotation' );
return [
//TODO: make prompt optional
'<label class="prompt">', _l( 'Annotation' ), '</label>',
// set up initial tags by adding as CSV to input vals (necc. to init select2)
'<div class="annotation">',
_.escape( annotation ),
'</div>'
].join( '' );
},
/** @returns {jQuery} the main element for this view */
$annotation : function(){
return this.$el.find( '.annotation' );
},
/** shut down event listeners and remove this view's DOM */
remove : function(){
this.$annotation.off();
this.stopListening( this.model );
Backbone.View.prototype.remove.call( this );
},
/** string rep */
toString : function(){ return [ 'AnnotationEditor(', this.model + '', ')' ].join(''); }
});
// =============================================================================
return {
AnnotationEditor : AnnotationEditor
};
// =============================================================================
return {
AnnotationEditor: AnnotationEditor
};
});
File diff suppressed because it is too large Load Diff
+179 -170
View File
@@ -1,196 +1,205 @@
define([
"mvc/base-mvc",
"utils/localization"
], function( baseMVC, _l ){
// =============================================================================
/** A view on any model that has a 'tags' attribute (a list of tag strings)
define(["mvc/base-mvc", "utils/localization"], function(baseMVC, _l) {
// =============================================================================
/** A view on any model that has a 'tags' attribute (a list of tag strings)
* Incorporates the select2 jQuery plugin for tags display/editing:
* http://ivaynberg.github.io/select2/
*/
var TagsEditor = Backbone.View
.extend( baseMVC.LoggableMixin )
.extend( baseMVC.HiddenUntilActivatedViewMixin ).extend({
var TagsEditor = Backbone.View
.extend(baseMVC.LoggableMixin)
.extend(baseMVC.HiddenUntilActivatedViewMixin)
.extend({
tagName: "div",
className: "tags-display",
select_width: "100%",
events: {},
tagName : 'div',
className : 'tags-display',
select_width : '100%',
events: {},
/** Set up listeners, parse options */
initialize: function(options) {
//console.debug( this, options );
// only listen to the model only for changes to tags - re-render
this.show_editor = false;
if (options.usePrompt === false) {
this.label = "";
} else {
this.label =
'<label class="prompt">' + _l("Tags") + "</label>";
}
this.workflow_mode = options.workflow_mode || false;
if (this.workflow_mode) {
this.events.click = "showEditor";
this.events.keydown = "keydownHandler";
}
this.hiddenUntilActivated(options.$activator, options);
},
/** Set up listeners, parse options */
initialize : function( options ){
//console.debug( this, options );
// only listen to the model only for changes to tags - re-render
this.show_editor = false;
if (options.usePrompt === false) {
this.label = '';
} else {
this.label = '<label class="prompt">' + _l( 'Tags' ) + '</label>';
}
this.workflow_mode = options.workflow_mode || false;
if (this.workflow_mode) {
this.events.click = 'showEditor';
this.events.keydown = 'keydownHandler';
}
this.hiddenUntilActivated( options.$activator, options );
},
/** Build the DOM elements, call select to on the created input, and set up behaviors */
render: function() {
var self = this;
if (this.workflow_mode) {
this.$el.html(this._workflowTemplate());
} else {
this.$el.html(this._defaultTemplate());
}
this.$input().select2({
placeholder: "Add tags",
width: this.workflow_mode ? this.width : this.select_width,
tags: function() {
// initialize possible tags in the dropdown based on all the tags the user has used so far
return self._getTagsUsed();
}
});
/** Build the DOM elements, call select to on the created input, and set up behaviors */
render : function(){
var self = this;
if (this.workflow_mode) {
this.$el.html(this._workflowTemplate());
} else {
this.$el.html(this._defaultTemplate());
}
this.$input().select2({
placeholder : 'Add tags',
width : this.workflow_mode ? this.width : this.select_width,
tags : function(){
// initialize possible tags in the dropdown based on all the tags the user has used so far
return self._getTagsUsed();
}
});
this._setUpBehaviors();
return this;
},
this._setUpBehaviors();
return this;
},
_hashToName: function(tag) {
if (tag.startsWith("#")) {
return "name:" + tag.slice(1);
}
return tag;
},
_hashToName: function(tag){
if (tag.startsWith('#')){
return 'name:' + tag.slice(1);
}
return tag;
},
_nameToHash: function(tag) {
if (tag.startsWith("name:")) {
tag = "#" + tag.slice(5);
}
return tag;
},
_nameToHash: function(tag){
if (tag.startsWith('name:')){
tag = '#' + tag.slice(5);
}
return tag;
},
/** @returns {String} the html text used to build the view's DOM */
_defaultTemplate: function() {
return [this.label, this._renderEditor()].join("");
},
/** @returns {String} the html text used to build the view's DOM */
_defaultTemplate : function(){
return [
this.label,
this._renderEditor()
].join( '' );
},
_workflowTemplate: function() {
// Shows labels by default, event handler controls whether we show tags or editor
return [
this.show_editor ? this._renderEditor() : this._renderTags()
].join(" ");
},
_workflowTemplate : function(){
// Shows labels by default, event handler controls whether we show tags or editor
return [
this.show_editor ? this._renderEditor() : this._renderTags(),
].join( ' ' );
},
keydownHandler: function(e) {
switch (e.which) {
// esc
case 27:
// hide the tag editor when pressing escape
this.hideEditor();
break;
}
},
keydownHandler : function (e) {
switch (e.which) {
// esc
case 27 :
// hide the tag editor when pressing escape
this.hideEditor();
break;
}
},
showEditor: function() {
this.show_editor = true;
this.render();
},
showEditor: function() {
this.show_editor = true;
this.render();
},
hideEditor: function() {
this.show_editor = false;
this.render();
},
hideEditor: function() {
this.show_editor = false;
this.render();
},
_renderEditor: function() {
// set up initial tags by adding as CSV to input vals (necc. to init select2)
return (
'<input class="tags-input" value="' +
this.tagsToCSV() +
'"/>'
);
},
_renderEditor: function(){
// set up initial tags by adding as CSV to input vals (necc. to init select2)
return '<input class="tags-input" value="' + this.tagsToCSV() + '"/>'
},
_renderTags: function() {
var tags = this.model.get("tags");
var addButton = "static/images/fugue/tag--plus.png";
var renderedArray = [];
_.each(tags, function(tag) {
tag = tag.indexOf("name:") == 0 ? tag.slice(5) : tag;
var renderString =
'<span class="label label-info">' + tag + "</span>";
renderedArray.push(renderString);
});
if (renderedArray.length === 0) {
// If there are no tags to render we just show the add-tag-button
renderedArray.push(
"<img src=" +
addButton +
' class="add-tag-button" title="Add tags"/>'
);
}
return renderedArray.join(" ");
},
_renderTags : function(){
var tags = this.model.get('tags');
var addButton = 'static/images/fugue/tag--plus.png';
var renderedArray = [];
_.each(tags, function(tag) {
tag = tag.indexOf("name:") == 0 ? tag.slice(5) : tag ;
var renderString = '<span class="label label-info">' + tag + '</span>';
renderedArray.push( renderString );
});
if (renderedArray.length === 0) {
// If there are no tags to render we just show the add-tag-button
renderedArray.push('<img src=' + addButton + ' class="add-tag-button" title="Add tags"/>');
}
return renderedArray.join(" ");
},
/** @returns {String} the sorted, comma-separated tags from the model */
tagsToCSV: function() {
var self = this;
var tagsArray = this.model.get("tags");
if (!_.isArray(tagsArray) || _.isEmpty(tagsArray)) {
return "";
}
return tagsArray
.map(function(tag) {
return _.escape(self._nameToHash(tag));
})
.sort()
.join(",");
},
/** @returns {String} the sorted, comma-separated tags from the model */
tagsToCSV : function(){
var self = this;
var tagsArray = this.model.get( 'tags' );
if( !_.isArray( tagsArray ) || _.isEmpty( tagsArray ) ){
return '';
}
return tagsArray.map( function( tag ){
return _.escape( self._nameToHash( tag ) );
}).sort().join( ',' );
},
/** @returns {jQuery} the input for this view */
$input: function() {
return this.$el.find("input.tags-input");
},
/** @returns {jQuery} the input for this view */
$input : function(){
return this.$el.find( 'input.tags-input' );
},
/** @returns {String[]} all tags used by the current user */
_getTagsUsed: function() {
//TODO: global
var self = this;
return _.map(Galaxy.user.get("tags_used"), self._nameToHash);
},
/** @returns {String[]} all tags used by the current user */
_getTagsUsed : function(){
//TODO: global
var self = this;
return _.map(Galaxy.user.get( 'tags_used' ), self._nameToHash);
},
/** set up any event listeners on the view's DOM (mostly handled by select2) */
_setUpBehaviors: function() {
var self = this;
this.$input().on("change", function(event) {
// Modify any 'hashtag' 'nametags'
event.val = _.map(event.val, self._hashToName);
// save the model's tags in either remove or added event
self.model.save({ tags: event.val });
// if it's new, add the tag to the users tags
if (event.added) {
//??: solve weird behavior in FF on test.galaxyproject.org where
// event.added.text is string object: 'String{ 0="o", 1="n", 2="e" }'
self._addNewTagToTagsUsed(event.added.text + "");
}
});
},
/** set up any event listeners on the view's DOM (mostly handled by select2) */
_setUpBehaviors : function(){
var self = this;
this.$input().on( 'change', function( event ){
// Modify any 'hashtag' 'nametags'
event.val = _.map(event.val, self._hashToName);
// save the model's tags in either remove or added event
self.model.save({ tags: event.val });
// if it's new, add the tag to the users tags
if( event.added ){
//??: solve weird behavior in FF on test.galaxyproject.org where
// event.added.text is string object: 'String{ 0="o", 1="n", 2="e" }'
self._addNewTagToTagsUsed( event.added.text + '' );
}
});
},
/** add a new tag (if not already there) to the list of all tags used by the user
/** add a new tag (if not already there) to the list of all tags used by the user
* @param {String} newTag the tag to add to the list of used
*/
_addNewTagToTagsUsed : function( newTag ){
//TODO: global
var tagsUsed = Galaxy.user.get( 'tags_used' );
if( !_.contains( tagsUsed, newTag ) ){
tagsUsed.push( newTag );
tagsUsed.sort();
Galaxy.user.set( 'tags_used', tagsUsed );
}
},
_addNewTagToTagsUsed: function(newTag) {
//TODO: global
var tagsUsed = Galaxy.user.get("tags_used");
if (!_.contains(tagsUsed, newTag)) {
tagsUsed.push(newTag);
tagsUsed.sort();
Galaxy.user.set("tags_used", tagsUsed);
}
},
/** shut down event listeners and remove this view's DOM */
remove : function(){
this.$input.off();
this.stopListening( this.model );
Backbone.View.prototype.remove.call( this );
},
/** shut down event listeners and remove this view's DOM */
remove: function() {
this.$input.off();
this.stopListening(this.model);
Backbone.View.prototype.remove.call(this);
},
/** string rep */
toString : function(){ return [ 'TagsEditor(', this.model + '', ')' ].join(''); }
});
/** string rep */
toString: function() {
return ["TagsEditor(", this.model + "", ")"].join("");
}
});
return {
TagsEditor : TagsEditor,
};
return {
TagsEditor: TagsEditor
};
});
+105 -103
View File
@@ -4,9 +4,9 @@
*/
define(["libs/bootstrap-tour"], function(BootstrapTour) {
var gxy_root = typeof Galaxy === "undefined" ? "/" : Galaxy.root;
var gxy_root = typeof Galaxy === "undefined" ? "/" : Galaxy.root;
var tourpage_template = `<h2>Galaxy Tours</h2>
var tourpage_template = `<h2>Galaxy Tours</h2>
<p>This page presents a list of interactive tours available on this Galaxy server.
Select any tour to get started (and remember, you can click 'End Tour' at any time).</p>
<ul>
@@ -20,108 +20,110 @@ Select any tour to get started (and remember, you can click 'End Tour' at any ti
<% }); %>
</ul>`;
var tour_opts = {
storage: window.sessionStorage,
onEnd: function() {
sessionStorage.removeItem("activeGalaxyTour");
},
delay: 150, // Attempts to make it look natural
orphan: true
};
var tour_opts = {
storage: window.sessionStorage,
onEnd: function() {
sessionStorage.removeItem("activeGalaxyTour");
},
delay: 150, // Attempts to make it look natural
orphan: true
};
var hooked_tour_from_data = function(data) {
_.each(data.steps, function(step) {
if (step.preclick) {
step.onShow = function() {
_.each(step.preclick, function(preclick) {
// TODO: click delay between clicks
$(preclick).click();
});
};
}
if (step.postclick) {
step.onHide = function() {
_.each(step.postclick, function(postclick) {
// TODO: click delay between clicks
$(postclick).click();
});
};
}
if (step.textinsert) {
// Have to manually trigger a change here, for some
// elements which have additional logic, like the
// upload input box
step.onShown = function() {
$(step.element).val(step.textinsert).trigger("change");
};
}
});
return data;
};
var TourItem = Backbone.Model.extend({
urlRoot: gxy_root + "api/tours"
});
var Tours = Backbone.Collection.extend({
url: gxy_root + "api/tours",
model: TourItem
});
var giveTour = function(tour_id) {
var url = gxy_root + "api/tours/" + tour_id;
$.getJSON(url, function(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({
title: "Tours",
// initialize
initialize: function() {
var self = this;
this.setElement("<div/>");
this.model = new Tours();
this.model.fetch({
success: function() {
self.render();
},
error: function(){
// Do something.
console.error("Failed to fetch tours.");
}
});
},
render: function() {
var tpl = _.template(tourpage_template);
this.$el
.html(tpl({ tours: this.model.models }))
.on("click", ".tourItem", function(e) {
e.preventDefault();
giveTour($(this).data("tour.id"));
var hooked_tour_from_data = function(data) {
_.each(data.steps, function(step) {
if (step.preclick) {
step.onShow = function() {
_.each(step.preclick, function(preclick) {
// TODO: click delay between clicks
$(preclick).click();
});
};
}
if (step.postclick) {
step.onHide = function() {
_.each(step.postclick, function(postclick) {
// TODO: click delay between clicks
$(postclick).click();
});
};
}
if (step.textinsert) {
// Have to manually trigger a change here, for some
// elements which have additional logic, like the
// upload input box
step.onShown = function() {
$(step.element)
.val(step.textinsert)
.trigger("change");
};
}
});
}
});
return data;
};
return {
ToursView: ToursView,
hooked_tour_from_data: hooked_tour_from_data,
tour_opts: tour_opts,
giveTour: giveTour
};
var TourItem = Backbone.Model.extend({
urlRoot: gxy_root + "api/tours"
});
var Tours = Backbone.Collection.extend({
url: gxy_root + "api/tours",
model: TourItem
});
var giveTour = function(tour_id) {
var url = gxy_root + "api/tours/" + tour_id;
$.getJSON(url, function(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({
title: "Tours",
// initialize
initialize: function() {
var self = this;
this.setElement("<div/>");
this.model = new Tours();
this.model.fetch({
success: function() {
self.render();
},
error: function() {
// Do something.
console.error("Failed to fetch tours.");
}
});
},
render: function() {
var tpl = _.template(tourpage_template);
this.$el
.html(tpl({ tours: this.model.models }))
.on("click", ".tourItem", function(e) {
e.preventDefault();
giveTour($(this).data("tour.id"));
});
}
});
return {
ToursView: ToursView,
hooked_tour_from_data: hooked_tour_from_data,
tour_opts: tour_opts,
giveTour: giveTour
};
});
+13 -8
View File
@@ -2,7 +2,6 @@
Webhooks
**/
define([], function() {
var WebhookModel = Backbone.Model.extend({
defaults: {
activate: false
@@ -14,15 +13,15 @@ define([], function() {
});
var WebhookView = Backbone.View.extend({
el: '#webhook-view',
el: "#webhook-view",
initialize: function(options) {
var me = this;
var toolId = options.toolId || '';
var toolVersion = options.toolVersion || '';
var toolId = options.toolId || "";
var toolVersion = options.toolVersion || "";
this.$el.attr('tool_id', toolId);
this.$el.attr('tool_version', toolVersion);
this.$el.attr("tool_id", toolId);
this.$el.attr("tool_version", toolVersion);
this.model = new WebhookModel();
this.model.urlRoot = options.urlRoot;
@@ -37,8 +36,14 @@ define([], function() {
var webhook = this.model.toJSON();
this.$el.html('<div id="' + webhook.name + '"></div>');
if (webhook.styles) $('<style/>', {type: 'text/css'}).text(webhook.styles).appendTo('head');
if (webhook.script) $('<script/>', {type: 'text/javascript'}).text(webhook.script).appendTo('head');
if (webhook.styles)
$("<style/>", { type: "text/css" })
.text(webhook.styles)
.appendTo("head");
if (webhook.script)
$("<script/>", { type: "text/javascript" })
.text(webhook.script)
.appendTo("head");
return this;
}
+218 -377
View File
@@ -1,406 +1,247 @@
/** en/main localization hash - for use with requirejs' i18n plugin */
define({
root : {
// ----------------------------------------------------------------------------- masthead
"Analyze Data":
false,
"Workflow":
false,
"Shared Data":
false,
"Data Libraries":
false,
"Histories":
false,
"Workflows":
false,
"Visualizations":
false,
"Pages":
false,
"Visualization":
false,
"New Track Browser":
false,
"Saved Visualizations":
false,
"Interactive Environments":
false,
"Admin":
false,
"Help":
false,
"Support":
false,
"Search":
false,
"Mailing Lists":
false,
"Videos":
false,
"Wiki":
false,
"How to Cite Galaxy":
false,
"Interactive Tours":
false,
"User":
false,
"Login":
false,
"Register":
false,
"Login or Register":
false,
"Logged in as":
false,
"Preferences":
false,
"Custom Builds":
false,
"Logout":
false,
"Saved Histories":
false,
"Saved Datasets":
false,
"Saved Pages":
false,
root: {
// ----------------------------------------------------------------------------- masthead
"Analyze Data": false,
Workflow: false,
"Shared Data": false,
"Data Libraries": false,
Histories: false,
Workflows: false,
Visualizations: false,
Pages: false,
Visualization: false,
"New Track Browser": false,
"Saved Visualizations": false,
"Interactive Environments": false,
Admin: false,
Help: false,
Support: false,
Search: false,
"Mailing Lists": false,
Videos: false,
Wiki: false,
"How to Cite Galaxy": false,
"Interactive Tours": false,
User: false,
Login: false,
Register: false,
"Login or Register": false,
"Logged in as": false,
Preferences: false,
"Custom Builds": false,
Logout: false,
"Saved Histories": false,
"Saved Datasets": false,
"Saved Pages": false,
//Tooltip
"Account and saved data" :
false,
"Account registration or login" :
false,
"Support, contact, and community" :
false,
"Administer this Galaxy" :
false,
"Visualize datasets" :
false,
"Access published resources" :
false,
"Chain tools into workflows" :
false,
"Analysis home view" :
false,
//Tooltip
"Account and saved data": false,
"Account registration or login": false,
"Support, contact, and community": false,
"Administer this Galaxy": false,
"Visualize datasets": false,
"Access published resources": false,
"Chain tools into workflows": false,
"Analysis home view": false,
// ---------------------------------------------------------------------------- histories
// ---- history/options-menu
"History Lists":
false,
// Saved histories is defined above.
// "Saved Histories":
// false,
"Histories Shared with Me":
false,
"Current History":
false,
"Create New":
false,
"Copy History":
false,
"Share or Publish":
false,
"Show Structure":
false,
"Extract Workflow":
false,
// Delete is defined elsewhere, but is also in this menu.
// "Delete":
// false,
"Delete Permanently":
false,
"Dataset Actions":
false,
"Copy Datasets":
false,
"Dataset Security":
false,
"Resume Paused Jobs":
false,
"Collapse Expanded Datasets":
false,
"Unhide Hidden Datasets":
false,
"Delete Hidden Datasets":
false,
"Purge Deleted Datasets":
false,
"Downloads":
false,
"Export Tool Citations":
false,
"Export History to File":
false,
"Other Actions":
false,
"Import from File":
false,
"Webhooks":
false,
// ---------------------------------------------------------------------------- histories
// ---- history/options-menu
"History Lists": false,
// Saved histories is defined above.
// "Saved Histories":
// false,
"Histories Shared with Me": false,
"Current History": false,
"Create New": false,
"Copy History": false,
"Share or Publish": false,
"Show Structure": false,
"Extract Workflow": false,
// Delete is defined elsewhere, but is also in this menu.
// "Delete":
// false,
"Delete Permanently": false,
"Dataset Actions": false,
"Copy Datasets": false,
"Dataset Security": false,
"Resume Paused Jobs": false,
"Collapse Expanded Datasets": false,
"Unhide Hidden Datasets": false,
"Delete Hidden Datasets": false,
"Purge Deleted Datasets": false,
Downloads: false,
"Export Tool Citations": false,
"Export History to File": false,
"Other Actions": false,
"Import from File": false,
Webhooks: false,
// ---- history-model
// ---- history-view
"This history is empty" :
false,
"No matching datasets found" :
false,
"An error occurred while getting updates from the server" :
false,
"Please contact a Galaxy administrator if the problem persists" :
false,
//TODO:
//"An error was encountered while <% where %>" :
//false,
"search datasets" :
false,
"You are currently viewing a deleted history!" :
false,
"You are over your disk quota" :
false,
"Tool execution is on hold until your disk usage drops below your allocated quota" :
false,
"All" :
false,
"None" :
false,
"For all selected" :
false,
// ---- history-model
// ---- history-view
"This history is empty": false,
"No matching datasets found": false,
"An error occurred while getting updates from the server": false,
"Please contact a Galaxy administrator if the problem persists": false,
//TODO:
//"An error was encountered while <% where %>" :
//false,
"search datasets": false,
"You are currently viewing a deleted history!": false,
"You are over your disk quota": false,
"Tool execution is on hold until your disk usage drops below your allocated quota": false,
All: false,
None: false,
"For all selected": false,
// ---- history-view-edit
"Edit history tags" :
false,
"Edit history Annotation" :
false,
"Click to rename history" :
false,
// multi operations
"Operations on multiple datasets" :
false,
"Hide datasets" :
false,
"Unhide datasets" :
false,
"Delete datasets" :
false,
"Undelete datasets" :
false,
"Permanently delete datasets" :
false,
"This will permanently remove the data in your datasets. Are you sure?" :
false,
// ---- history-view-edit
"Edit history tags": false,
"Edit history Annotation": false,
"Click to rename history": false,
// multi operations
"Operations on multiple datasets": false,
"Hide datasets": false,
"Unhide datasets": false,
"Delete datasets": false,
"Undelete datasets": false,
"Permanently delete datasets": false,
"This will permanently remove the data in your datasets. Are you sure?": false,
// ---- history-view-annotated
"Dataset" :
false,
"Annotation" :
false,
// ---- history-view-annotated
Dataset: false,
Annotation: false,
// ---- history-view-edit-current
"This history is empty. Click 'Get Data' on the left tool menu to start" :
false,
"You must be logged in to create histories" :
false,
//TODO:
//"You can <% loadYourOwn %> or <% externalSource %>" :
//false,
//"load your own data" :
//false,
//"get data from an external source" :
//false,
// ---- history-view-edit-current
"This history is empty. Click 'Get Data' on the left tool menu to start": false,
"You must be logged in to create histories": false,
//TODO:
//"You can <% loadYourOwn %> or <% externalSource %>" :
//false,
//"load your own data" :
//false,
//"get data from an external source" :
//false,
// these aren't in zh/ginga.po and the template doesn't localize
//"Include Deleted Datasets" :
//false,
//"Include Hidden Datasets" :
//false,
// these aren't in zh/ginga.po and the template doesn't localize
//"Include Deleted Datasets" :
//false,
//"Include Hidden Datasets" :
//false,
// ---------------------------------------------------------------------------- datasets
// ---- hda-model
"Unable to purge dataset": false,
// ---------------------------------------------------------------------------- datasets
// ---- hda-model
"Unable to purge dataset" :
false,
// ---- 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": false,
// download button
Download: false,
"Download dataset": false,
"Additional files": false,
// info/show_params
"View details": false,
// ---- 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" :
false,
// download button
"Download" :
false,
"Download dataset" :
false,
"Additional files" :
false,
// info/show_params
"View details" :
false,
// dataset states
// state: new
"This is a new dataset and not all of its data are available yet": false,
// state: noPermission
"You do not have permission to view this dataset": false,
// state: discarded
"The job creating this dataset was cancelled before completion": false,
// state: queued
"This job is waiting to run": false,
// state: upload
"This dataset is currently uploading": false,
// state: setting_metadata
"Metadata is being auto-detected": false,
// state: running
"This job is currently running": false,
// state: paused
'This job is paused. Use the "Resume Paused Jobs" in the history menu to resume': false,
// state: error
"An error occurred with this dataset": false,
// state: empty
"No data": false,
// state: failed_metadata
"An error occurred setting the metadata for this dataset": false,
// dataset states
// state: new
"This is a new dataset and not all of its data are available yet" :
false,
// state: noPermission
"You do not have permission to view this dataset" :
false,
// state: discarded
"The job creating this dataset was cancelled before completion" :
false,
// state: queued
"This job is waiting to run" :
false,
// state: upload
"This dataset is currently uploading" :
false,
// state: setting_metadata
"Metadata is being auto-detected" :
false,
// state: running
"This job is currently running" :
false,
// state: paused
"This job is paused. Use the \"Resume Paused Jobs\" in the history menu to resume" :
false,
// state: error
"An error occurred with this dataset" :
false,
// state: empty
"No data" :
false,
// state: failed_metadata
"An error occurred setting the metadata for this dataset" :
false,
// ajax error prefix
"There was an error getting the data for this dataset": false,
// ajax error prefix
"There was an error getting the data for this dataset" :
false,
// purged'd/del'd msg
"This dataset has been deleted and removed from disk": false,
"This dataset has been deleted": false,
"This dataset has been hidden": false,
// purged'd/del'd msg
"This dataset has been deleted and removed from disk" :
false,
"This dataset has been deleted" :
false,
"This dataset has been hidden" :
false,
format: false,
database: false,
"format" :
false,
"database" :
false,
// ---- hda-edit
"Edit attributes": false,
"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,
// ---- hda-edit
"Edit attributes" :
false,
"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,
Delete: false,
"Dataset is already deleted": false,
"Delete" :
false,
"Dataset is already deleted" :
false,
"View or report this error": false,
"View or report this error" :
false,
"Run this job again": false,
"Run this job again" :
false,
Visualize: false,
"Visualize in": false,
"Visualize" :
false,
"Visualize in" :
false,
"Undelete it": false,
"Permanently remove it from disk": false,
"Unhide it": false,
"Undelete it" :
false,
"Permanently remove it from disk" :
false,
"Unhide it" :
false,
"You may be able to": false,
"set it manually or retry auto-detection": false,
"You may be able to" :
false,
"set it manually or retry auto-detection" :
false,
"Edit dataset tags": false,
"Edit dataset annotation": false,
"Edit dataset tags" :
false,
"Edit dataset annotation" :
false,
"Tool Help": false,
"Tool Help" :
false,
// ---------------------------------------------------------------------------- admin
"Search Tool Shed": false,
"Monitor installing repositories": false,
"Manage installed tools": false,
"Reset metadata": false,
"Download local tool": false,
"Tool lineage": false,
"Reload a tool's configuration": false,
"Review tool migration stages": false,
"View Tool Error Logs": false,
"Manage Display Whitelist": false,
"Manage Tool Dependencies": false,
Users: false,
Groups: false,
"API keys": false,
"Impersonate a user": false,
Data: false,
Quotas: false,
Roles: false,
"Local data": false,
"Form Definitions": false,
// ---------------------------------------------------------------------------- admin
"Search Tool Shed":
false,
"Monitor installing repositories":
false,
"Manage installed tools":
false,
"Reset metadata":
false,
"Download local tool":
false,
"Tool lineage":
false,
"Reload a tool's configuration":
false,
"Review tool migration stages":
false,
"View Tool Error Logs":
false,
"Manage Display Whitelist":
false,
"Manage Tool Dependencies":
false,
"Users":
false,
"Groups":
false,
"API keys":
false,
"Impersonate a user":
false,
"Data":
false,
"Quotas":
false,
"Roles":
false,
"Local data":
false,
"Form Definitions":
false,
// ---------------------------------------------------------------------------- Scratchbook
"Enable/Disable Scratchbook": false,
"Show/Hide Scratchbook": false,
// ---------------------------------------------------------------------------- Scratchbook
"Enable/Disable Scratchbook":
false,
"Show/Hide Scratchbook":
false,
// ---------------------------------------------------------------------------- misc. MVC
Tags: false,
"Edit annotation": false
// ---------------------------------------------------------------------------- misc. MVC
"Tags" :
false,
"Edit annotation" :
false,
// ----------------------------------------------------------------------------
},
'ja' : true,
'fr' : true,
'zh' : true
// ----------------------------------------------------------------------------
},
ja: true,
fr: true,
zh: true
});
+82 -59
View File
@@ -4,23 +4,23 @@
// HACK: add these to global scope until we stop asking for them there...
// Via webpack: these are required here automatically by the provider plugin
// Via script tag: these are redundant (identities) since they're already global
window[ 'jQuery' ] = jQuery; // a weird form to prevent webpack from sub'ing 'window.jQuery' in the provider plugin
window["jQuery"] = jQuery; // a weird form to prevent webpack from sub'ing 'window.jQuery' in the provider plugin
window.$ = jQuery;
window._ = _;
window.Backbone = Backbone;
// console.debug('globals loaded:', window.jQuery, window.Backbone, '...');
// these are galaxy globals not defined in the provider (although they could be - but why encourage that?)
window.panels = require( 'layout/panel' );
window.panels = require("layout/panel");
// using extend bc there are multiple fns/objs to decorate the window with
_.extend( window, require( 'layout/modal' ) );
window.async_save_text = require( 'utils/async-save-text' );
var POPUPMENU = require( 'ui/popupmenu' );
_.extend(window, require("layout/modal"));
window.async_save_text = require("utils/async-save-text");
var POPUPMENU = require("ui/popupmenu");
window.make_popupmenu = POPUPMENU.make_popupmenu;
window.make_popup_menus = POPUPMENU.make_popup_menus;
window.init_tag_click_function = require( 'ui/autocom_tagging' );
var TOURS = require( 'mvc/tours' );
var QUERY_STRING = require( 'utils/query-string-parsing' );
window.init_tag_click_function = require("ui/autocom_tagging");
var TOURS = require("mvc/tours");
var QUERY_STRING = require("utils/query-string-parsing");
// console.debug( 'galaxy globals loaded' );
// ============================================================================
@@ -28,15 +28,15 @@ var QUERY_STRING = require( 'utils/query-string-parsing' );
// ============================================================================
// Replace select box with a text input box + autocomplete.
function replace_big_select_inputs(min_length, max_length, select_elts) {
function refresh_select2( element ) {
function refresh_select2(element) {
var select_elt = $(element);
var options = { placeholder:'Click to select',
closeOnSelect: !select_elt.is("[MULTIPLE]"),
dropdownAutoWidth : true,
containerCssClass: 'select2-minwidth'
};
return element.select2( options );
var options = {
placeholder: "Click to select",
closeOnSelect: !select_elt.is("[MULTIPLE]"),
dropdownAutoWidth: true,
containerCssClass: "select2-minwidth"
};
return element.select2(options);
}
// To do replace, the select2 plugin must be loaded.
@@ -52,13 +52,13 @@ function replace_big_select_inputs(min_length, max_length, select_elts) {
max_length = 3000;
}
select_elts = select_elts || $('select');
select_elts = select_elts || $("select");
select_elts.each( function() {
var select_elt = $(this).not('[multiple]');
select_elts.each(function() {
var select_elt = $(this).not("[multiple]");
// Make sure that options is within range.
var num_options = select_elt.find('option').length;
if ( (num_options < min_length) || (num_options > max_length) ) {
var num_options = select_elt.find("option").length;
if (num_options < min_length || num_options > max_length) {
return;
}
@@ -73,23 +73,30 @@ function replace_big_select_inputs(min_length, max_length, select_elts) {
*
* - should we still sort dbkey fields here?
*/
refresh_select2( select_elt );
refresh_select2(select_elt);
});
}
// Initialize refresh events.
function init_refresh_on_change () {
function init_refresh_on_change() {
$("select[refresh_on_change='true']")
.off('change')
.off("change")
.change(function() {
var select_field = $(this),
select_val = select_field.val(),
refresh = false,
ref_on_change_vals = select_field.attr("refresh_on_change_values");
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");
if ($.inArray(select_val, ref_on_change_vals) === -1 && $.inArray(last_selected_value, ref_on_change_vals) === -1) {
ref_on_change_vals = ref_on_change_vals.split(",");
var last_selected_value = select_field.attr(
"last_selected_value"
);
if (
$.inArray(select_val, ref_on_change_vals) === -1 &&
$.inArray(last_selected_value, ref_on_change_vals) === -1
) {
return;
}
}
@@ -100,16 +107,23 @@ function init_refresh_on_change () {
// checkboxes refresh on change
$(":checkbox[refresh_on_change='true']")
.off('click')
.click( function() {
.off("click")
.click(function() {
var select_field = $(this),
select_val = select_field.val(),
refresh = false,
ref_on_change_vals = select_field.attr("refresh_on_change_values");
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");
if ($.inArray(select_val, ref_on_change_vals) === -1 && $.inArray(last_selected_value, ref_on_change_vals) === -1) {
ref_on_change_vals = ref_on_change_vals.split(",");
var last_selected_value = select_field.attr(
"last_selected_value"
);
if (
$.inArray(select_val, ref_on_change_vals) === -1 &&
$.inArray(last_selected_value, ref_on_change_vals) === -1
) {
return;
}
}
@@ -118,23 +132,23 @@ function init_refresh_on_change () {
});
// Links with confirmation
$( "a[confirm]" )
.off('click')
.click( function() {
return confirm( $(this).attr("confirm") );
$("a[confirm]")
.off("click")
.click(function() {
return confirm($(this).attr("confirm"));
});
}
// used globally in grid-view
window.init_refresh_on_change = init_refresh_on_change;
$(document).ready( function() {
$(document).ready(function() {
// Refresh events for form fields.
init_refresh_on_change();
// Tooltips
if ( $.fn.tooltip ) {
if ($.fn.tooltip) {
// Put tooltips below items in panel header so that they do not overlap masthead.
$(".unified-panel-header [title]").tooltip( { placement: 'bottom' } );
$(".unified-panel-header [title]").tooltip({ placement: "bottom" });
// default tooltip initialization, it will follow the data-placement tag for tooltip location
// and fallback to 'top' if not present
@@ -148,15 +162,14 @@ $(document).ready( function() {
// If galaxy_main frame does not exist and link targets galaxy_main,
// add use_panels=True and set target to self.
$("a").click( function() {
$("a").click(function() {
var anchor = $(this);
var galaxy_main_exists = (parent.frames && parent.frames.galaxy_main);
if ( ( anchor.attr( "target" ) == "galaxy_main" ) && ( !galaxy_main_exists ) ) {
var galaxy_main_exists = parent.frames && parent.frames.galaxy_main;
if (anchor.attr("target") == "galaxy_main" && !galaxy_main_exists) {
var href = anchor.attr("href");
if (href.indexOf("?") == -1) {
href += "?";
}
else {
} else {
href += "&";
}
href += "use_panels=True";
@@ -166,18 +179,23 @@ $(document).ready( function() {
return anchor;
});
var et = JSON.parse(sessionStorage.getItem('activeGalaxyTour'));
if (et){
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){
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));
var tour = new Tour(
_.extend(
{
steps: et.steps
},
TOURS.tour_opts
)
);
tour.init();
tour.restart();
}
@@ -185,18 +203,23 @@ $(document).ready( function() {
}
function onloadWebhooks() {
if (Galaxy.root !== undefined){
if (Galaxy.root !== undefined) {
// Load all webhooks with the type 'onload'
$.getJSON( Galaxy.root + 'api/webhooks/onload/all', function(webhooks) {
$.getJSON(Galaxy.root + "api/webhooks/onload/all", function(
webhooks
) {
_.each(webhooks, function(webhook) {
if (webhook.activate && webhook.script) {
$('<script/>', {type: 'text/javascript'}).text(webhook.script).appendTo('head');
$('<style/>', {type: 'text/css'}).text(webhook.styles).appendTo('head');
$("<script/>", { type: "text/javascript" })
.text(webhook.script)
.appendTo("head");
$("<style/>", { type: "text/css" })
.text(webhook.styles)
.appendTo("head");
}
});
});
}
else {
} else {
setTimeout(onloadWebhooks, 100);
}
}
+43 -30
View File
@@ -9,18 +9,18 @@
* these should be configurable via options because they all need different things.
* So, analysis-polyfills.js, reports-polyfills.js (or analysis/polyfills)
*/
'use strict';
"use strict";
/*globals window, clearTimeout */
// ------------------------------------------------------------------ polyfills
// console protection needed in some versions of IE (at this point (IE>=9), shouldn't be needed)
window.console = window.console || {
log : function(){},
debug : function(){},
info : function(){},
warn : function(){},
error : function(){},
assert : function(){}
log: function() {},
debug: function() {},
info: function() {},
warn: function() {},
error: function() {},
assert: function() {}
};
// phantomjs: does not have the native extend fn assign
@@ -28,19 +28,21 @@
// requestAnimationFrame polyfill
var lastTime = 0;
var vendors = ['ms', 'moz', 'webkit', 'o'];
for(var x = 0; x < vendors.length && !window.requestAnimationFrame; ++x) {
window.requestAnimationFrame = window[vendors[x]+'RequestAnimationFrame'];
window.cancelRequestAnimationFrame = window[vendors[x]+
'CancelRequestAnimationFrame'];
var vendors = ["ms", "moz", "webkit", "o"];
for (var x = 0; x < vendors.length && !window.requestAnimationFrame; ++x) {
window.requestAnimationFrame =
window[vendors[x] + "RequestAnimationFrame"];
window.cancelRequestAnimationFrame =
window[vendors[x] + "CancelRequestAnimationFrame"];
}
if (!window.requestAnimationFrame)
window.requestAnimationFrame = function(callback, element) {
var currTime = new Date().getTime();
var timeToCall = Math.max(0, 16 - (currTime - lastTime));
var id = window.setTimeout(function() { callback(currTime + timeToCall); },
timeToCall);
var id = window.setTimeout(function() {
callback(currTime + timeToCall);
}, timeToCall);
lastTime = currTime + timeToCall;
return id;
};
@@ -48,30 +50,41 @@
if (!window.cancelAnimationFrame)
window.cancelAnimationFrame = function(id) {
clearTimeout(id);
};
};
// ------------------------------------------------------------------ can't/won't polyfill
var features = [
{ name: 'canvas', compatible: function(){ return window.CanvasRenderingContext2D; } },
{ name: 'sessionStorage', compatible: function(){
try {
return window.sessionStorage.length >= 0;
} catch( err ){}
return false;
}},
{
name: "canvas",
compatible: function() {
return window.CanvasRenderingContext2D;
}
},
{
name: "sessionStorage",
compatible: function() {
try {
return window.sessionStorage.length >= 0;
} catch (err) {}
return false;
}
}
];
// build a list of feature names for features that were not found
var incompatibilities = features
.filter( function( feature ){ return !feature.compatible(); })
.map( function( feature ){ return feature.name; });
.filter(function(feature) {
return !feature.compatible();
})
.map(function(feature) {
return feature.name;
});
// if there are needed features missing, follow the index link to the static incompat warning
if( !!incompatibilities.length ){
var root = document.querySelectorAll( 'link[rel="index"]' ).item( 0 );
if( root ){
window.location = root.href + 'static/incompatible-browser.html';
if (!!incompatibilities.length) {
var root = document.querySelectorAll('link[rel="index"]').item(0);
if (root) {
window.location = root.href + "static/incompatible-browser.html";
}
console.log( 'incompatible browser:\n' + incompatibilities.join( '\n' ) );
console.log("incompatible browser:\n" + incompatibilities.join("\n"));
}
})();
+349 -224
View File
@@ -4,8 +4,8 @@ function days_in_month(month, year) {
function date_by_subtracting_days(date, days) {
return new Date(
date.getFullYear(),
date.getMonth(),
date.getFullYear(),
date.getMonth(),
date.getDate() - days,
date.getHours(),
date.getMinutes(),
@@ -16,8 +16,8 @@ function date_by_subtracting_days(date, days) {
function date_by_subtracting_hours(date, hours) {
return new Date(
date.getFullYear(),
date.getMonth(),
date.getFullYear(),
date.getMonth(),
date.getDate(),
date.getHours() - hours,
date.getMinutes(),
@@ -34,7 +34,8 @@ function get_utc_time_hours() {
date.getUTCMonth(),
date.getUTCDate(),
date.getUTCHours(),
0, 0
0,
0
);
}
@@ -46,35 +47,35 @@ function refresh() {
// This is commented out until we make Reports more responsive.
// setTimeout(refresh, 60000); //1 minute = 60000 ms
function create_chart( inp_data, name, time, title ) {
function create_chart(inp_data, name, time, title) {
// Initialize starting variables
var data = inp_data;
var hours_array = []
var now = get_utc_time_hours()
for(var i = 0; i < 24; i++) {
var hours_array = [];
var now = get_utc_time_hours();
for (var i = 0; i < 24; i++) {
hours_array.push(date_by_subtracting_hours(now, i));
}
var days_array = []
var now = get_utc_time_hours()
for(var i = 0; i < 30; i++) {
var days_array = [];
var now = get_utc_time_hours();
for (var i = 0; i < 30; i++) {
days_array.push(date_by_subtracting_days(now, i));
}
var margin = {top: 60, right: 30, bottom: 50, left: 60};
var margin = { top: 60, right: 30, bottom: 50, left: 60 };
var width = 300;
var barWidth = 0;
if(time == "hours") {
if (time == "hours") {
barWidth = width / 24;
} else if(time == "days") {
} else if (time == "days") {
barWidth = width / 30;
}
var chart_width = width + margin.left + margin.right;
var chart_zoom = 1.75;
var height = 150;
if(d3.max(data) != 0) {
if (d3.max(data) != 0) {
var zoom = height / d3.max(data);
} else {
var zoom = 1.0;
@@ -85,26 +86,30 @@ function create_chart( inp_data, name, time, title ) {
function click() {
var classes = d3.select(this).attr("class");
classes = classes.split(" ");
d3.selectAll("." + classes[0]).filter("." + classes[1])
d3
.selectAll("." + classes[0])
.filter("." + classes[1])
.style("cursor", "zoom-in")
.transition()
.duration(750)
.attr("height", chart_height)
.attr("width", chart_width);
.duration(750)
.attr("height", chart_height)
.attr("width", chart_width);
d3.select(this)
d3
.select(this)
.style("cursor", "default")
.transition()
.duration(750)
.attr("height", chart_height*chart_zoom)
.attr("width", chart_width*chart_zoom);
.duration(750)
.attr("height", chart_height * chart_zoom)
.attr("width", chart_width * chart_zoom);
}
// Initialize all chart containers to have the correct height
$(".charts").css("height", chart_height * (chart_zoom));
$(".charts").css("height", chart_height * chart_zoom);
// Create the chart object
var chart = d3.select("#" + name)
var chart = d3
.select("#" + name)
.attr("width", chart_width)
.attr("height", chart_height)
.attr("preserveAspectRatio", "xMidYMin")
@@ -112,57 +117,82 @@ function create_chart( inp_data, name, time, title ) {
.on("click", click);
// Create bars on the chart and assosciate data with it
var bar = chart.selectAll("g")
var bar = chart
.selectAll("g")
.data(data)
.enter().append("g")
.attr("transform", function(d, i) {
// Place the bar in the correct place
var curr_margin = +margin.left;
curr_margin += +(i * barWidth);
return "translate(" + curr_margin + "," + margin.top + ")";
})
.on("mouseenter", function(d) {
// Show tool tip
var i = 1;
var size = d;
.enter()
.append("g")
.attr("transform", function(d, i) {
// Place the bar in the correct place
var curr_margin = +margin.left;
curr_margin += +(i * barWidth);
return "translate(" + curr_margin + "," + margin.top + ")";
})
.on("mouseenter", function(d) {
// Show tool tip
var i = 1;
var size = d;
while( size >= 10) {
size = size / 10;
i++;
}
while (size >= 10) {
size = size / 10;
i++;
}
var wdth = (i * 4) + 10;
d3.select(d3.event.path[1]).select(".tool_tip")
.select("text")
.attr("transform", "translate( " + (margin.left - 5) + ", " + ((height - (d * zoom)) + +margin.top + 10) + " )" )
.attr("visibility", "visible")
.text(d);
var wdth = i * 4 + 10;
d3
.select(d3.event.path[1])
.select(".tool_tip")
.select("text")
.attr(
"transform",
"translate( " +
(margin.left - 5) +
", " +
(height - d * zoom + +margin.top + 10) +
" )"
)
.attr("visibility", "visible")
.text(d);
d3.select(d3.event.path[1]).select(".tool_tip")
.attr("width", wdth + "px")
.attr("height", "15px")
.select("rect")
.attr("transform", "translate( " + ((+margin.left) - wdth) + ", " + ((height - (d * zoom)) + +margin.top) + " )" )
.attr("width", wdth + "px")
.attr("height", "15px")
.attr("fill","#ebd9b2");
})
.on("mouseleave", function(d) {
// Remove tool tip
d3.select(d3.event.path[1]).select(".tool_tip")
.select("text")
.attr("visibility", "hidden");
d3
.select(d3.event.path[1])
.select(".tool_tip")
.attr("width", wdth + "px")
.attr("height", "15px")
.select("rect")
.attr(
"transform",
"translate( " +
(+margin.left - wdth) +
", " +
(height - d * zoom + +margin.top) +
" )"
)
.attr("width", wdth + "px")
.attr("height", "15px")
.attr("fill", "#ebd9b2");
})
.on("mouseleave", function(d) {
// Remove tool tip
d3
.select(d3.event.path[1])
.select(".tool_tip")
.select("text")
.attr("visibility", "hidden");
d3.select(d3.event.path[1]).select(".tool_tip")
.select("rect")
.attr("width", "0")
.attr("height", "0")
.attr("fill","")
.text(d);
});
d3
.select(d3.event.path[1])
.select(".tool_tip")
.select("rect")
.attr("width", "0")
.attr("height", "0")
.attr("fill", "")
.text(d);
});
// Add a title to the chart
chart.append("g")
chart
.append("g")
.append("text")
.attr("class", "title")
.attr("text-anchor", "end")
@@ -172,62 +202,76 @@ function create_chart( inp_data, name, time, title ) {
.text(title);
// Add an x axis line to the chart
chart.append("g")
chart
.append("g")
.attr("class", "axis")
.append("path")
.attr("class", "x")
.attr("d", function(d) {
var m_x = margin.left;
var m_y = +margin.top + height;
var l_x = m_x + width;
var l_y = m_y;
.attr("class", "x")
.attr("d", function(d) {
var m_x = margin.left;
var m_y = +margin.top + height;
var l_x = m_x + width;
var l_y = m_y;
return "M" + m_x + " " + m_y + " L " + l_x + " " + l_y;
});
return "M" + m_x + " " + m_y + " L " + l_x + " " + l_y;
});
// Declare how high the y axis goes
var y = d3.scale.linear()
.range([height, 0]);
var y = d3.scale.linear().range([height, 0]);
// Create a yAxis object
var yAxis = d3.svg.axis()
var yAxis = d3.svg
.axis()
.scale(y)
.orient("left")
.tickFormat( function(d) { return d3.round( d*d3.max(data), 0 ) });
.tickFormat(function(d) {
return d3.round(d * d3.max(data), 0);
});
// Put the y axis on the chart
chart.append("g")
chart
.append("g")
.attr("class", "y axis")
.attr("id", ("y_" + name))
.attr("id", "y_" + name)
.attr("text-anchor", "end")
.attr("transform", "translate( " + margin.left + "," + margin.top + ")")
.call(yAxis)
.select(".domain");
.select(".domain");
// Put a title for y axis on chart
chart.append("g")
chart
.append("g")
.append("text")
.attr("class", "ax_title")
.attr("transform", function(e) {
var axis = d3.select("#y_" + name).node()
var left_pad = +margin.left - axis.getBoundingClientRect().width - 5;
var top_pad = +margin.top + (axis.getBoundingClientRect().height/2) - 30
var trans = "translate(" + left_pad + "," + top_pad + ")rotate(-90)";
.attr("class", "ax_title")
.attr("transform", function(e) {
var axis = d3.select("#y_" + name).node();
var left_pad =
+margin.left - axis.getBoundingClientRect().width - 5;
var top_pad =
+margin.top + axis.getBoundingClientRect().height / 2 - 30;
var trans =
"translate(" + left_pad + "," + top_pad + ")rotate(-90)";
return trans;
})
.text("Number of Jobs");
return trans;
})
.text("Number of Jobs");
// Add color to the chart's bars
bar.append("rect")
.attr("y", function(d) { return height - (d * zoom); })
.attr("height", function(d) { return (d * zoom); })
bar
.append("rect")
.attr("y", function(d) {
return height - d * zoom;
})
.attr("height", function(d) {
return d * zoom;
})
.attr("width", barWidth - 1);
// Append x axis
if(time == "hours") {
if (time == "hours") {
// Append hour lines
bar.append("line")
bar
.append("line")
.attr("x1", 0)
.attr("y1", 0)
.attr("x2", 0)
@@ -236,17 +280,18 @@ function create_chart( inp_data, name, time, title ) {
.attr("stroke-width", 1)
.attr("pointer-events", "none")
.attr("transform", function(d, i) {
return "translate( " + (barWidth/2) + ", " + height + ")"
return "translate( " + barWidth / 2 + ", " + height + ")";
});
// Append hour numbers
bar.append("text")
bar
.append("text")
.attr("fill", "rgb(0,0,0)")
.attr("transform", "translate( 10, " + (height + 10) + " )")
.text(function(d, i) {
var time = "0000"
var time = "0000";
if( hours_array[i].getHours() < 10 ) {
if (hours_array[i].getHours() < 10) {
time = "0" + String(hours_array[i].getHours());
} else {
time = hours_array[i].getHours();
@@ -258,21 +303,22 @@ function create_chart( inp_data, name, time, title ) {
// Append day lines
var curr_day = "";
var first = false;
bar.append("line")
bar
.append("line")
.attr("x1", 0)
.attr("y1", 0)
.attr("x2", 0)
.attr("y2", function(d, i) {
var _y2 = 0;
if(hours_array[i].getDate() != curr_day) {
if(!first) {
if (hours_array[i].getDate() != curr_day) {
if (!first) {
_y2 = 27;
first = true;
} else {
_y2 = 20;
}
curr_day = hours_array[i].getDate();
}
@@ -289,16 +335,19 @@ function create_chart( inp_data, name, time, title ) {
curr_day = "";
curr_day_text = "";
first = false;
bar.append("text")
bar
.append("text")
.attr("fill", "rgb(0,0,0)")
.attr("pointer-events", "none")
.text(function(d, i) {
var time = "";
var locale = "en-us";
if(hours_array[i].getDate() != curr_day_text) {
time = String(hours_array[i].toLocaleString(locale, { month: "long" }));
time += " " + String(hours_array[i].getDate())
if (hours_array[i].getDate() != curr_day_text) {
time = String(
hours_array[i].toLocaleString(locale, { month: "long" })
);
time += " " + String(hours_array[i].getDate());
curr_day_text = hours_array[i].getDate();
}
@@ -307,24 +356,30 @@ function create_chart( inp_data, name, time, title ) {
})
.attr("transform", function(d, i) {
var text_height = height;
var this_width = d3.select(this).node().getBBox().width;
var this_width = d3
.select(this)
.node()
.getBBox().width;
if(hours_array[i].getDate() != curr_day) {
if(!first) {
if (hours_array[i].getDate() != curr_day) {
if (!first) {
text_height += 26;
first = true;
} else {
text_height += 18;
}
curr_day = hours_array[i].getDate();
}
return "translate( " + (this_width + 2) + ", " + (text_height) + " )"
return (
"translate( " + (this_width + 2) + ", " + text_height + " )"
);
});
} else if(time == "days") {
} else if (time == "days") {
// Append day lines
bar.append("line")
bar
.append("line")
.attr("x1", 0)
.attr("y1", 0)
.attr("x2", 0)
@@ -333,17 +388,18 @@ function create_chart( inp_data, name, time, title ) {
.attr("stroke-width", 1)
.attr("pointer-events", "none")
.attr("transform", function(d, i) {
return "translate( " + (barWidth/2) + ", " + height + ")"
return "translate( " + barWidth / 2 + ", " + height + ")";
});
// Append day numbers
bar.append("text")
bar
.append("text")
.attr("fill", "rgb(0,0,0)")
.attr("transform", "translate( 9, " + (height + 10) + " )")
.text(function(d, i) {
var time = "0000"
var time = "0000";
if( days_array[i].getDate() < 10 ) {
if (days_array[i].getDate() < 10) {
time = "0" + String(days_array[i].getDate());
} else {
time = days_array[i].getDate();
@@ -355,21 +411,22 @@ function create_chart( inp_data, name, time, title ) {
// Append month lines
var curr_month = "";
var first = false;
bar.append("line")
bar
.append("line")
.attr("x1", 0)
.attr("y1", 0)
.attr("x2", 0)
.attr("y2", function(d, i) {
var _y2 = 0;
if(days_array[i].getMonth() != curr_month) {
if(!first) {
if (days_array[i].getMonth() != curr_month) {
if (!first) {
_y2 = 27;
first = true;
} else {
_y2 = 20;
}
curr_month = days_array[i].getMonth();
}
@@ -386,16 +443,19 @@ function create_chart( inp_data, name, time, title ) {
curr_month = "";
curr_month_text = "";
first = false;
bar.append("text")
bar
.append("text")
.attr("fill", "rgb(0,100,0)")
.attr("pointer-events", "none")
.text(function(d, i) {
var time = "";
var locale = "en-us";
if(days_array[i].getMonth() != curr_month_text) {
time = String(days_array[i].toLocaleString(locale, { month: "long" }));
time += " " + String(days_array[i].getFullYear())
if (days_array[i].getMonth() != curr_month_text) {
time = String(
days_array[i].toLocaleString(locale, { month: "long" })
);
time += " " + String(days_array[i].getFullYear());
curr_month_text = days_array[i].getMonth();
}
@@ -404,33 +464,39 @@ function create_chart( inp_data, name, time, title ) {
})
.attr("transform", function(d, i) {
var text_height = height;
var this_width = d3.select(this).node().getBBox().width;
var this_width = d3
.select(this)
.node()
.getBBox().width;
if(days_array[i].getMonth() != curr_month) {
if(!first) {
if (days_array[i].getMonth() != curr_month) {
if (!first) {
text_height += 26;
first = true;
} else {
text_height += 18;
}
curr_month = days_array[i].getMonth();
}
return "translate( " + (this_width + 2) + ", " + (text_height) + " )"
return (
"translate( " + (this_width + 2) + ", " + text_height + " )"
);
});
}
// Put an invisible tool tip on the chart
chart.append("g")
chart
.append("g")
.attr("class", "tool_tip")
.append("rect");
chart.select(".tool_tip")
.append("text");
chart.select(".tool_tip").append("text");
// Initialize initial zoomed charts
if(name == "jc_dy_chart" || name == "jc_hr_chart") {
d3.select("#" + name)
if (name == "jc_dy_chart" || name == "jc_hr_chart") {
d3
.select("#" + name)
.attr("height", chart_height * chart_zoom)
.attr("width", chart_width * chart_zoom)
.style("cursor", "default");
@@ -439,13 +505,13 @@ function create_chart( inp_data, name, time, title ) {
//============================================================================================================
function create_histogram( inp_data, name, title ) {
function create_histogram(inp_data, name, title) {
// Initialize initial variables
// inp_data is an array of numbers that are the amount of minutes per run
var data = inp_data;
var chart_zoom = 1.75;
var margin = {top: 60, right: 30, bottom: 50, left: 60};
var margin = { top: 60, right: 30, bottom: 50, left: 60 };
var height = 150;
var chart_height = height + margin.top + margin.bottom;
@@ -455,22 +521,22 @@ function create_histogram( inp_data, name, title ) {
// Cereate x axis metadata
// Used for x axis, histogram creation, and bar initialization
var x = d3.scale.linear()
var x = d3.scale
.linear()
.domain([0, d3.max(data)])
.range([0, width]);
// Generate a histogram using twenty uniformly-spaced bins.
var data = d3.layout.histogram()
.bins(x.ticks(20))(data);
var data = d3.layout.histogram().bins(x.ticks(20))(data);
// Create an array of the sizes of the bars
var lengths = [];
for(var i = 0; i < data.length; i ++) {
lengths.push(data[i].length)
for (var i = 0; i < data.length; i++) {
lengths.push(data[i].length);
}
// Find the amount needed to magnify the bars
if(d3.max(data) != 0) {
if (d3.max(data) != 0) {
var zoom = height / d3.max(lengths);
} else {
var zoom = 1.0;
@@ -478,46 +544,56 @@ function create_histogram( inp_data, name, title ) {
// Create y axis metadata
// Used for y axis and bar initialization
var y = d3.scale.linear()
.domain([0, d3.max(data, function(d) { return d.y; })])
var y = d3.scale
.linear()
.domain([
0,
d3.max(data, function(d) {
return d.y;
})
])
.range([height, 0]);
// Function for zooming in and out of charts
function click() {
var classes = d3.select(this).attr("class");
classes = classes.split(" ");
d3.selectAll("." + classes[0]).filter("." + classes[1])
d3
.selectAll("." + classes[0])
.filter("." + classes[1])
.style("cursor", "zoom-in")
.transition()
.duration(750)
.attr("height", chart_height)
.attr("width", chart_width);
.duration(750)
.attr("height", chart_height)
.attr("width", chart_width);
d3.select(this)
d3
.select(this)
.style("cursor", "default")
.transition()
.duration(750)
.attr("height", chart_height*chart_zoom)
.attr("width", chart_width*chart_zoom);
.duration(750)
.attr("height", chart_height * chart_zoom)
.attr("width", chart_width * chart_zoom);
}
// Formatter for x axis times (converting minutes to HH:MM).
var formatMinutes = function(d) {
var hours = Math.floor( d / 60 ),
minutes = Math.floor(d - (hours * 60))
var hours = Math.floor(d / 60),
minutes = Math.floor(d - hours * 60);
if(hours < 10) {
hours = "0" + hours
}
if(minutes < 10) {
minutes = "0" + minutes
}
if (hours < 10) {
hours = "0" + hours;
}
if (minutes < 10) {
minutes = "0" + minutes;
}
return hours + ":" + minutes;
};
return hours + ":" + minutes;
};
// Create a chart object
var chart = d3.select("#" + name)
var chart = d3
.select("#" + name)
.attr("viewBox", "0 0 " + chart_width + " " + chart_height)
.attr("width", chart_width)
.attr("height", chart_height)
@@ -525,7 +601,8 @@ function create_histogram( inp_data, name, title ) {
.on("click", click);
// Put title on chart
chart.append("g")
chart
.append("g")
.append("text")
.attr("class", "title")
.attr("transform", function(e) {
@@ -534,121 +611,169 @@ function create_histogram( inp_data, name, title ) {
.text(title);
// Put bars on chart
var bar = chart.selectAll(".bar")
var bar = chart
.selectAll(".bar")
.data(data)
.enter().append("g")
.enter()
.append("g")
.attr("class", "bar")
.attr("transform", function(d) {
return "translate(" + (+x(d.x) + +margin.left) + "," + (+y(d.y) + +margin.top) + ")"; })
return (
"translate(" +
(+x(d.x) + +margin.left) +
"," +
(+y(d.y) + +margin.top) +
")"
);
})
.on("mouseenter", function(d) {
// Show tool tip
var i = 0,
size = d.length;
while( size >= 1) {
while (size >= 1) {
size = size / 10;
i++;
}
var wdth = (i * 4) + 10;
d3.select(d3.event.path[1]).select(".tool_tip")
var wdth = i * 4 + 10;
d3
.select(d3.event.path[1])
.select(".tool_tip")
.select("text")
.attr("transform", "translate( " + (margin.left - 5) + ", " + (height - (d.length * zoom) + +margin.top + 10) + " )" )
.attr("visibility", "visible")
.text(d.length);
.attr(
"transform",
"translate( " +
(margin.left - 5) +
", " +
(height - d.length * zoom + +margin.top + 10) +
" )"
)
.attr("visibility", "visible")
.text(d.length);
d3.select(d3.event.path[1]).select(".tool_tip")
d3
.select(d3.event.path[1])
.select(".tool_tip")
.attr("width", wdth + "px")
.attr("height", "15px")
.select("rect")
.attr("transform", "translate( " + ((+margin.left) - wdth) + ", " + (height - (d.length * zoom) + +margin.top) + " )")
.attr("width", wdth + "px")
.attr("height", "15px")
.attr("fill","#ebd9b2");
.attr(
"transform",
"translate( " +
(+margin.left - wdth) +
", " +
(height - d.length * zoom + +margin.top) +
" )"
)
.attr("width", wdth + "px")
.attr("height", "15px")
.attr("fill", "#ebd9b2");
})
.on("mouseleave", function(d) {
// Remove tool tip
d3.select(d3.event.path[1]).select(".tool_tip")
d3
.select(d3.event.path[1])
.select(".tool_tip")
.select("text")
.attr("visibility", "hidden");
.attr("visibility", "hidden");
d3.select(d3.event.path[1]).select(".tool_tip")
d3
.select(d3.event.path[1])
.select(".tool_tip")
.select("rect")
.attr("width", "0")
.attr("height", "0")
.attr("fill","")
.attr("width", "0")
.attr("height", "0")
.attr("fill", "");
});
// Create bar width
var bar_x;
if(data[0] == undefined) {
if (data[0] == undefined) {
bar_x = 1;
} else {
bar_x = x(data[0].dx);
}
// Add color to bar
bar.append("rect")
bar
.append("rect")
.attr("x", 1)
.attr("width", bar_x - 1)
.attr("height", function(d) { return height - y(d.y); });
.attr("height", function(d) {
return height - y(d.y);
});
// Create x axis
var xAxis = d3.svg.axis()
var xAxis = d3.svg
.axis()
.scale(x)
.orient("bottom")
.tickFormat(formatMinutes);
// Add x axis to chart
chart.append("g")
chart
.append("g")
.attr("class", "x axis")
.attr("id", "x_" + name)
.attr("transform", "translate( " + margin.left + "," + (+height + +margin.top) + ")")
.attr(
"transform",
"translate( " + margin.left + "," + (+height + +margin.top) + ")"
)
.call(xAxis);
// Add a title to the x axis
chart.append("g")
chart
.append("g")
.append("text")
.attr("class", "ax_title")
.attr("transform", function(e) {
var axis = d3.select("#x_" + name).node()
var left_pad = +margin.left + (axis.getBoundingClientRect().width/2) + 30;
var top_pad = +margin.top + height + axis.getBoundingClientRect().height + 10
var trans = "translate(" + left_pad + "," + top_pad + ")";
.attr("class", "ax_title")
.attr("transform", function(e) {
var axis = d3.select("#x_" + name).node();
var left_pad =
+margin.left + axis.getBoundingClientRect().width / 2 + 30;
var top_pad =
+margin.top + height + axis.getBoundingClientRect().height + 10;
var trans = "translate(" + left_pad + "," + top_pad + ")";
return trans;
})
.text("ETA - hrs:mins");
return trans;
})
.text("ETA - hrs:mins");
// Create y axis
var yAxis = d3.svg.axis()
var yAxis = d3.svg
.axis()
.scale(y)
.orient("left");
// Add y axis to chart
chart.append("g")
chart
.append("g")
.attr("class", "y axis")
.attr("id", ("y_" + name))
.attr("id", "y_" + name)
.attr("transform", "translate( " + margin.left + "," + margin.top + ")")
.call(yAxis);
// Add a title to the y axis
chart.append("g")
chart
.append("g")
.append("text")
.attr("class", "ax_title")
.attr("transform", function(e) {
var axis = d3.select("#y_" + name).node()
var left_pad = +margin.left - axis.getBoundingClientRect().width - 5;
var top_pad = +margin.top + (axis.getBoundingClientRect().height/2) - 30
var trans = "translate(" + left_pad + "," + top_pad + ")rotate(-90)";
.attr("class", "ax_title")
.attr("transform", function(e) {
var axis = d3.select("#y_" + name).node();
var left_pad =
+margin.left - axis.getBoundingClientRect().width - 5;
var top_pad =
+margin.top + axis.getBoundingClientRect().height / 2 - 30;
var trans =
"translate(" + left_pad + "," + top_pad + ")rotate(-90)";
return trans;
})
.text("Number of Jobs");
return trans;
})
.text("Number of Jobs");
// Put an invisible tool tip on the chart
chart.append("g")
chart
.append("g")
.attr("class", "tool_tip")
.append("rect");
chart.select(".tool_tip")
.append("text");
chart.select(".tool_tip").append("text");
}
+379 -352
View File
@@ -1,391 +1,418 @@
// from: https://raw.githubusercontent.com/umdjs/umd/master/jqueryPlugin.js
// Uses AMD or browser globals to create a jQuery plugin.
(function (factory) {
if (typeof define === 'function' && define.amd) {
(function(factory) {
if (typeof define === "function" && define.amd) {
//TODO: So...this turns out to be an all or nothing thing. If I load jQuery in the define below, it will
// (of course) wipe the old jquery *and all the plugins loaded into it*. So the define below *is still
// relying on jquery being loaded globally* in order to preserve plugins.
define([ 'jquery' ], factory);
define(["jquery"], factory);
} else {
// Browser globals
factory(jQuery);
}
})(function(jQuery) {
"use_strict";
}(function ( jQuery ) {
'use_strict';
var $ = jQuery;
var $ = jQuery;
// ============================================================================
/**
// ============================================================================
/**
* JQuery extension for tagging with autocomplete.
* @author: Jeremy Goecks
* @require: jquery.autocomplete plugin
*/
//
// Initialize "tag click functions" for tags.
//
function init_tag_click_function(tag_elt, click_func) {
$(tag_elt).find('.tag-name').each( function() {
$(this).click( function() {
var tag_str = $(this).text();
var tag_name_and_value = tag_str.split(":");
click_func(tag_name_and_value[0], tag_name_and_value[1]);
return true;
});
});
}
jQuery.fn.autocomplete_tagging = function(options) {
var defaults = {
get_toggle_link_text_fn: function(tags) {
var text = "";
var num_tags = _.size(tags);
if (num_tags > 0) {
text = num_tags + (num_tags > 1 ? " Tags" : " Tag");
} else {
text = "Add tags";
}
return text;
},
tag_click_fn : function (name, value) {},
editable: true,
input_size: 20,
in_form: false,
tags : {},
use_toggle_link: true,
item_id: "",
add_tag_img: "",
add_tag_img_rollover: "",
delete_tag_img: "",
ajax_autocomplete_tag_url: "",
ajax_retag_url: "",
ajax_delete_tag_url: "",
ajax_add_tag_url: ""
};
var settings = jQuery.extend(defaults, options);
//
// Initalize object's elements.
// Initialize "tag click functions" for tags.
//
// Get elements for this object. For this_obj, assume the last element with the id is the "this"; this is somewhat of a hack to address the problem
// that there may be two tagging elements for a single item if there are both community and individual tags for an element.
var this_obj = $(this);
var tag_area = this_obj.find('.tag-area');
var toggle_link = this_obj.find('.toggle-link');
var tag_input_field = this_obj.find('.tag-input');
var add_tag_button = this_obj.find('.add-tag-button');
// Initialize toggle link.
toggle_link.click( function() {
// Take special actions depending on whether toggle is showing or hiding link.
var after_toggle_fn;
if (tag_area.is(":hidden")) {
after_toggle_fn = function() {
// If there are no tags, go right to editing mode by generating a click on the area.
var num_tags = $(this).find('.tag-button').length;
if (num_tags === 0) {
tag_area.click();
}
};
} else {
after_toggle_fn = function() {
tag_area.blur();
};
}
tag_area.slideToggle("fast", after_toggle_fn);
return $(this);
});
// Initialize tag input field.
if (settings.editable) {
tag_input_field.hide();
}
tag_input_field.keyup( function(e) {
if ( e.keyCode === 27 ) {
// Escape key
$(this).trigger( "blur" );
} else if (
( e.keyCode === 13 ) || // Return Key
( e.keyCode === 188 ) || // Comma
( e.keyCode === 32 ) // Space
) {
//
// Check input.
//
var new_value = this.value;
// Suppress space after a ":"
if ( new_value.indexOf(": ", new_value.length - 2) !== -1) {
this.value = new_value.substring(0, new_value.length-1);
return false;
}
// Remove trigger keys from input.
if ( (e.keyCode === 188) || (e.keyCode === 32) ) {
new_value = new_value.substring( 0 , new_value.length - 1 );
}
// Trim whitespace.
new_value = $.trim(new_value);
// Too short?
if (new_value.length < 2) {
return false;
}
//
// New tag OK - apply it.
//
this.value = ""; // Reset text field now that tag is being added
// Add button for tag after all other tag buttons.
var new_tag_button = build_tag_button(new_value);
var tag_buttons = tag_area.children(".tag-button");
if (tag_buttons.length !== 0) {
var last_tag_button = tag_buttons.slice(tag_buttons.length-1);
last_tag_button.after(new_tag_button);
} else {
tag_area.prepend(new_tag_button);
}
// Add tag to internal list.
var tag_name_and_value = new_value.split(":");
settings.tags[tag_name_and_value[0]] = tag_name_and_value[1];
// Update toggle link text.
var new_text = settings.get_toggle_link_text_fn(settings.tags);
toggle_link.text(new_text);
// Commit tag to server.
var zz = $(this);
$.ajax({
url: settings.ajax_add_tag_url,
data: { new_tag: new_value },
error: function() {
// Failed. Roll back changes and show alert.
new_tag_button.remove();
delete settings.tags[tag_name_and_value[0]];
var new_text = settings.get_toggle_link_text_fn(settings.tags);
toggle_link.text(new_text);
alert( "Add tag failed" );
},
success: function() {
// Flush autocomplete cache because it's not out of date.
// TODO: in the future, we could remove the particular item
// that was chosen from the cache rather than flush it.
zz.data('autocompleter').cacheFlush();
}
function init_tag_click_function(tag_elt, click_func) {
$(tag_elt)
.find(".tag-name")
.each(function() {
$(this).click(function() {
var tag_str = $(this).text();
var tag_name_and_value = tag_str.split(":");
click_func(tag_name_and_value[0], tag_name_and_value[1]);
return true;
});
});
}
return false;
}
});
jQuery.fn.autocomplete_tagging = function(options) {
var defaults = {
get_toggle_link_text_fn: function(tags) {
var text = "";
var num_tags = _.size(tags);
if (num_tags > 0) {
text = num_tags + (num_tags > 1 ? " Tags" : " Tag");
} else {
text = "Add tags";
}
return text;
},
tag_click_fn: function(name, value) {},
editable: true,
input_size: 20,
in_form: false,
tags: {},
use_toggle_link: true,
item_id: "",
add_tag_img: "",
add_tag_img_rollover: "",
delete_tag_img: "",
ajax_autocomplete_tag_url: "",
ajax_retag_url: "",
ajax_delete_tag_url: "",
ajax_add_tag_url: ""
};
// Add autocomplete to input.
var format_item_func = function(key, row_position, num_rows, value, search_term) {
var tag_name_and_value = value.split(":");
return (tag_name_and_value.length === 1 ? tag_name_and_value[0] : tag_name_and_value[1]);
};
var autocomplete_options = { selectFirst: false, formatItem: format_item_func,
autoFill: false, highlight: false };
tag_input_field.autocomplete_verheul(settings.ajax_autocomplete_tag_url, autocomplete_options);
var settings = jQuery.extend(defaults, options);
//
// Initalize object's elements.
//
// Initialize delete tag images for current tags.
this_obj.find('.delete-tag-img').each(function() {
init_delete_tag_image( $(this) );
});
// Get elements for this object. For this_obj, assume the last element with the id is the "this"; this is somewhat of a hack to address the problem
// that there may be two tagging elements for a single item if there are both community and individual tags for an element.
var this_obj = $(this);
var tag_area = this_obj.find(".tag-area");
var toggle_link = this_obj.find(".toggle-link");
var tag_input_field = this_obj.find(".tag-input");
var add_tag_button = this_obj.find(".add-tag-button");
// Initialize tag click function.
init_tag_click_function($(this), settings.tag_click_fn);
// Initialize "add tag" button.
add_tag_button.click( function() {
$(this).hide();
// Clicking on button is the same as clicking on the tag area.
tag_area.click();
return false;
});
//
// Set up tag area interactions; these are needed only if tags are editable.
//
if (settings.editable) {
// When the tag area blurs, go to "view tag" mode.
tag_area.bind("blur", function(e) {
if (_.size(settings.tags) > 0) {
add_tag_button.show();
tag_input_field.hide();
tag_area.removeClass("active-tag-area");
// tag_area.addClass("tooltip");
} else {
// No tags, so do nothing to ensure that input is still visible.
}
});
// On click, enable user to add tags.
tag_area.click( function(e) {
var is_active = $(this).hasClass("active-tag-area");
// If a "delete image" object was pressed and area is inactive, do nothing.
if ($(e.target).hasClass("delete-tag-img") && !is_active) {
return false;
}
// If a "tag name" object was pressed and area is inactive, do nothing.
if ($(e.target).hasClass("tag-name") && !is_active) {
return false;
}
// Remove tooltip.
// $(this).removeClass("tooltip");
// Hide add tag button, show tag_input field. Change background to show
// area is active.
$(this).addClass("active-tag-area");
add_tag_button.hide();
tag_input_field.show();
tag_input_field.focus();
// Add handler to document that will call blur when the tag area is blurred;
// a tag area is blurred when a user clicks on an element outside the area.
var handle_document_click = function(e) {
var check_click = function(tag_area, target) {
var tag_area_id = tag_area.attr("id");
// Blur the tag area if the element clicked on is not in the tag area.
if (target !== tag_area) {
tag_area.blur();
$(window).unbind("click.tagging_blur");
$(this).addClass("tooltip");
// Initialize toggle link.
toggle_link.click(function() {
// Take special actions depending on whether toggle is showing or hiding link.
var after_toggle_fn;
if (tag_area.is(":hidden")) {
after_toggle_fn = function() {
// If there are no tags, go right to editing mode by generating a click on the area.
var num_tags = $(this).find(".tag-button").length;
if (num_tags === 0) {
tag_area.click();
}
};
check_click(tag_area, $(e.target));
};
// TODO: we should attach the click handler to all frames in order to capture
// clicks outside the frame that this element is in.
//window.parent.document.onclick = handle_document_click;
//var temp = $(window.parent.document.body).contents().find("iframe").html();
//alert(temp);
//$(document).parent().click(handle_document_click);
$(window).bind("click.tagging_blur", handle_document_click);
} else {
after_toggle_fn = function() {
tag_area.blur();
};
}
tag_area.slideToggle("fast", after_toggle_fn);
return $(this);
});
// Initialize tag input field.
if (settings.editable) {
tag_input_field.hide();
}
tag_input_field.keyup(function(e) {
if (e.keyCode === 27) {
// Escape key
$(this).trigger("blur");
} else if (
e.keyCode === 13 || // Return Key
e.keyCode === 188 || // Comma
e.keyCode === 32 // Space
) {
//
// Check input.
//
var new_value = this.value;
// Suppress space after a ":"
if (new_value.indexOf(": ", new_value.length - 2) !== -1) {
this.value = new_value.substring(0, new_value.length - 1);
return false;
}
// Remove trigger keys from input.
if (e.keyCode === 188 || e.keyCode === 32) {
new_value = new_value.substring(0, new_value.length - 1);
}
// Trim whitespace.
new_value = $.trim(new_value);
// Too short?
if (new_value.length < 2) {
return false;
}
//
// New tag OK - apply it.
//
this.value = ""; // Reset text field now that tag is being added
// Add button for tag after all other tag buttons.
var new_tag_button = build_tag_button(new_value);
var tag_buttons = tag_area.children(".tag-button");
if (tag_buttons.length !== 0) {
var last_tag_button = tag_buttons.slice(
tag_buttons.length - 1
);
last_tag_button.after(new_tag_button);
} else {
tag_area.prepend(new_tag_button);
}
// Add tag to internal list.
var tag_name_and_value = new_value.split(":");
settings.tags[tag_name_and_value[0]] = tag_name_and_value[1];
// Update toggle link text.
var new_text = settings.get_toggle_link_text_fn(settings.tags);
toggle_link.text(new_text);
// Commit tag to server.
var zz = $(this);
$.ajax({
url: settings.ajax_add_tag_url,
data: { new_tag: new_value },
error: function() {
// Failed. Roll back changes and show alert.
new_tag_button.remove();
delete settings.tags[tag_name_and_value[0]];
var new_text = settings.get_toggle_link_text_fn(
settings.tags
);
toggle_link.text(new_text);
alert("Add tag failed");
},
success: function() {
// Flush autocomplete cache because it's not out of date.
// TODO: in the future, we could remove the particular item
// that was chosen from the cache rather than flush it.
zz.data("autocompleter").cacheFlush();
}
});
return false;
}
});
// Add autocomplete to input.
var format_item_func = function(
key,
row_position,
num_rows,
value,
search_term
) {
var tag_name_and_value = value.split(":");
return tag_name_and_value.length === 1
? tag_name_and_value[0]
: tag_name_and_value[1];
};
var autocomplete_options = {
selectFirst: false,
formatItem: format_item_func,
autoFill: false,
highlight: false
};
tag_input_field.autocomplete_verheul(
settings.ajax_autocomplete_tag_url,
autocomplete_options
);
// Initialize delete tag images for current tags.
this_obj.find(".delete-tag-img").each(function() {
init_delete_tag_image($(this));
});
// Initialize tag click function.
init_tag_click_function($(this), settings.tag_click_fn);
// Initialize "add tag" button.
add_tag_button.click(function() {
$(this).hide();
// Clicking on button is the same as clicking on the tag area.
tag_area.click();
return false;
});
}
// If using toggle link, hide the tag area. Otherwise, show the tag area.
if (settings.use_toggle_link) {
tag_area.hide();
}
//
// Helper functions.
//
//
// Collapse tag name + value into a single string.
//
function build_tag_str(tag_name, tag_value) {
return tag_name + ( tag_value ? ":" + tag_value : "");
}
// Initialize a "delete tag image": when click, delete tag from UI and send delete request to server.
function init_delete_tag_image(delete_img) {
$(delete_img).mouseenter( function () {
$(this).attr("src", settings.delete_tag_img_rollover);
});
$(delete_img).mouseleave( function () {
$(this).attr("src", settings.delete_tag_img);
});
$(delete_img).click( function () {
// Tag button is image's parent.
var tag_button = $(this).parent();
// Get tag name, value.
var tag_name_elt = tag_button.find(".tag-name").eq(0);
var tag_str = tag_name_elt.text();
var tag_name_and_value = tag_str.split(":");
var tag_name = tag_name_and_value[0];
var tag_value = tag_name_and_value[1];
var prev_button = tag_button.prev();
tag_button.remove();
// Remove tag from local list for consistency.
delete settings.tags[tag_name];
// Update toggle link text.
var new_text = settings.get_toggle_link_text_fn(settings.tags);
toggle_link.text(new_text);
// Delete tag.
$.ajax({
url: settings.ajax_delete_tag_url,
data: { tag_name: tag_name },
error: function() {
// Failed. Roll back changes and show alert.
settings.tags[tag_name] = tag_value;
if (prev_button.hasClass("tag-button")) {
prev_button.after(tag_button);
} else {
tag_area.prepend(tag_button);
}
alert( "Remove tag failed" );
toggle_link.text(settings.get_toggle_link_text_fn(settings.tags));
// TODO: no idea why it's necessary to set this up again.
delete_img.mouseenter( function () {
$(this).attr("src", settings.delete_tag_img_rollover);
});
delete_img.mouseleave( function () {
$(this).attr("src", settings.delete_tag_img);
});
},
success: function() {}
//
// Set up tag area interactions; these are needed only if tags are editable.
//
if (settings.editable) {
// When the tag area blurs, go to "view tag" mode.
tag_area.bind("blur", function(e) {
if (_.size(settings.tags) > 0) {
add_tag_button.show();
tag_input_field.hide();
tag_area.removeClass("active-tag-area");
// tag_area.addClass("tooltip");
} else {
// No tags, so do nothing to ensure that input is still visible.
}
});
return true;
});
}
// On click, enable user to add tags.
tag_area.click(function(e) {
var is_active = $(this).hasClass("active-tag-area");
//
// Function that builds a tag button.
//
function build_tag_button(tag_str) {
// Build "delete tag" image.
var delete_img = $("<img/>").attr("src", settings.delete_tag_img).addClass("delete-tag-img");
init_delete_tag_image(delete_img);
// If a "delete image" object was pressed and area is inactive, do nothing.
if ($(e.target).hasClass("delete-tag-img") && !is_active) {
return false;
}
// Build tag button.
var tag_name_elt = $("<span>").text(tag_str).addClass("tag-name");
tag_name_elt.click( function() {
var tag_name_and_value = tag_str.split(":");
settings.tag_click_fn(tag_name_and_value[0], tag_name_and_value[1]);
return true;
});
// If a "tag name" object was pressed and area is inactive, do nothing.
if ($(e.target).hasClass("tag-name") && !is_active) {
return false;
}
var tag_button = $("<span></span>").addClass("tag-button");
tag_button.append(tag_name_elt);
// Allow delete only if element is editable.
if (settings.editable) {
tag_button.append(delete_img);
// Remove tooltip.
// $(this).removeClass("tooltip");
// Hide add tag button, show tag_input field. Change background to show
// area is active.
$(this).addClass("active-tag-area");
add_tag_button.hide();
tag_input_field.show();
tag_input_field.focus();
// Add handler to document that will call blur when the tag area is blurred;
// a tag area is blurred when a user clicks on an element outside the area.
var handle_document_click = function(e) {
var check_click = function(tag_area, target) {
var tag_area_id = tag_area.attr("id");
// Blur the tag area if the element clicked on is not in the tag area.
if (target !== tag_area) {
tag_area.blur();
$(window).unbind("click.tagging_blur");
$(this).addClass("tooltip");
}
};
check_click(tag_area, $(e.target));
};
// TODO: we should attach the click handler to all frames in order to capture
// clicks outside the frame that this element is in.
//window.parent.document.onclick = handle_document_click;
//var temp = $(window.parent.document.body).contents().find("iframe").html();
//alert(temp);
//$(document).parent().click(handle_document_click);
$(window).bind("click.tagging_blur", handle_document_click);
return false;
});
}
return tag_button;
}
// If using toggle link, hide the tag area. Otherwise, show the tag area.
if (settings.use_toggle_link) {
tag_area.hide();
}
};
//
// Helper functions.
//
// ============================================================================
//
// Collapse tag name + value into a single string.
//
function build_tag_str(tag_name, tag_value) {
return tag_name + (tag_value ? ":" + tag_value : "");
}
// Initialize a "delete tag image": when click, delete tag from UI and send delete request to server.
function init_delete_tag_image(delete_img) {
$(delete_img).mouseenter(function() {
$(this).attr("src", settings.delete_tag_img_rollover);
});
$(delete_img).mouseleave(function() {
$(this).attr("src", settings.delete_tag_img);
});
$(delete_img).click(function() {
// Tag button is image's parent.
var tag_button = $(this).parent();
// Get tag name, value.
var tag_name_elt = tag_button.find(".tag-name").eq(0);
var tag_str = tag_name_elt.text();
var tag_name_and_value = tag_str.split(":");
var tag_name = tag_name_and_value[0];
var tag_value = tag_name_and_value[1];
var prev_button = tag_button.prev();
tag_button.remove();
// Remove tag from local list for consistency.
delete settings.tags[tag_name];
// Update toggle link text.
var new_text = settings.get_toggle_link_text_fn(settings.tags);
toggle_link.text(new_text);
// Delete tag.
$.ajax({
url: settings.ajax_delete_tag_url,
data: { tag_name: tag_name },
error: function() {
// Failed. Roll back changes and show alert.
settings.tags[tag_name] = tag_value;
if (prev_button.hasClass("tag-button")) {
prev_button.after(tag_button);
} else {
tag_area.prepend(tag_button);
}
alert("Remove tag failed");
toggle_link.text(
settings.get_toggle_link_text_fn(settings.tags)
);
// TODO: no idea why it's necessary to set this up again.
delete_img.mouseenter(function() {
$(this).attr(
"src",
settings.delete_tag_img_rollover
);
});
delete_img.mouseleave(function() {
$(this).attr("src", settings.delete_tag_img);
});
},
success: function() {}
});
return true;
});
}
//
// Function that builds a tag button.
//
function build_tag_button(tag_str) {
// Build "delete tag" image.
var delete_img = $("<img/>")
.attr("src", settings.delete_tag_img)
.addClass("delete-tag-img");
init_delete_tag_image(delete_img);
// Build tag button.
var tag_name_elt = $("<span>")
.text(tag_str)
.addClass("tag-name");
tag_name_elt.click(function() {
var tag_name_and_value = tag_str.split(":");
settings.tag_click_fn(
tag_name_and_value[0],
tag_name_and_value[1]
);
return true;
});
var tag_button = $("<span></span>").addClass("tag-button");
tag_button.append(tag_name_elt);
// Allow delete only if element is editable.
if (settings.editable) {
tag_button.append(delete_img);
}
return tag_button;
}
};
// ============================================================================
return init_tag_click_function;
}));
});
+106 -94
View File
@@ -1,119 +1,131 @@
// from: https://raw.githubusercontent.com/umdjs/umd/master/jqueryPlugin.js
// Uses AMD or browser globals to create a jQuery plugin.
(function (factory) {
if (typeof define === 'function' && define.amd) {
(function(factory) {
if (typeof define === "function" && define.amd) {
//TODO: So...this turns out to be an all or nothing thing. If I load jQuery in the define below, it will
// (of course) wipe the old jquery *and all the plugins loaded into it*. So the define below *is still
// relying on jquery being loaded globally* in order to preserve plugins.
define([ 'jquery' ], factory);
define(["jquery"], factory);
} else {
// Browser globals
factory(jQuery);
}
})(function(jQuery) {
"use_strict";
}(function ( jQuery ) {
'use_strict';
var $ = jQuery;
var $ = jQuery;
// ============================================================================
/**
// ============================================================================
/**
* Make an element with text editable: (a) when user clicks on text, a textbox/area
* is provided for editing; (b) when enter key pressed, element's text is set and on_finish
* is called.
*/
$.fn.make_text_editable = function(config_dict) {
// Get config options.
var num_cols = ("num_cols" in config_dict ? config_dict.num_cols : 30),
num_rows = ("num_rows" in config_dict ? config_dict.num_rows : 4),
use_textarea = ("use_textarea" in config_dict ? config_dict.use_textarea : false),
on_finish = ("on_finish" in config_dict ? config_dict.on_finish : null),
help_text = ("help_text" in config_dict ? config_dict.help_text : null);
$.fn.make_text_editable = function(config_dict) {
// Get config options.
var num_cols = "num_cols" in config_dict ? config_dict.num_cols : 30,
num_rows = "num_rows" in config_dict ? config_dict.num_rows : 4,
use_textarea =
"use_textarea" in config_dict
? config_dict.use_textarea
: false,
on_finish =
"on_finish" in config_dict ? config_dict.on_finish : null,
help_text =
"help_text" in config_dict ? config_dict.help_text : null;
// Add element behavior.
var container = $(this);
container.addClass("editable-text").click(function(e) {
// If there's already an input element, editing is active, so do nothing.
if ($(this).children(":input").length > 0) {
return;
}
container.removeClass("editable-text");
// Handler for setting element text.
var set_text = function(new_text) {
container.find(":input").remove();
if (new_text !== "") {
container.text(new_text);
} else {
// No text; need a line so that there is a click target.
container.html("<br>");
// Add element behavior.
var container = $(this);
container.addClass("editable-text").click(function(e) {
// If there's already an input element, editing is active, so do nothing.
if ($(this).children(":input").length > 0) {
return;
}
container.addClass("editable-text");
if (on_finish) {
on_finish(new_text);
}
};
container.removeClass("editable-text");
// Create input element(s) for editing.
var cur_text = ("cur_text" in config_dict ? config_dict.cur_text : container.text() ),
input_elt, button_elt;
// Handler for setting element text.
var set_text = function(new_text) {
container.find(":input").remove();
if (use_textarea) {
input_elt = $("<textarea/>")
.attr({ rows: num_rows, cols: num_cols }).text($.trim(cur_text))
.keyup(function(e) {
if (e.keyCode === 27) {
// Escape key.
set_text(cur_text);
}
});
button_elt = $("<button/>").text("Done").click(function() {
set_text(input_elt.val());
// Return false so that click does not propogate to container.
return false;
});
}
else {
input_elt = $("<input type='text'/>").attr({ value: $.trim(cur_text), size: num_cols })
.blur(function() {
set_text(cur_text);
}).keyup(function(e) {
if (e.keyCode === 27) {
// Escape key.
$(this).trigger("blur");
} else if (e.keyCode === 13) {
// Enter key.
set_text($(this).val());
if (new_text !== "") {
container.text(new_text);
} else {
// No text; need a line so that there is a click target.
container.html("<br>");
}
container.addClass("editable-text");
// Do not propogate event to avoid unwanted side effects.
e.stopPropagation();
});
if (on_finish) {
on_finish(new_text);
}
};
// Create input element(s) for editing.
var cur_text =
"cur_text" in config_dict
? config_dict.cur_text
: container.text(),
input_elt,
button_elt;
if (use_textarea) {
input_elt = $("<textarea/>")
.attr({ rows: num_rows, cols: num_cols })
.text($.trim(cur_text))
.keyup(function(e) {
if (e.keyCode === 27) {
// Escape key.
set_text(cur_text);
}
});
button_elt = $("<button/>")
.text("Done")
.click(function() {
set_text(input_elt.val());
// Return false so that click does not propogate to container.
return false;
});
} else {
input_elt = $("<input type='text'/>")
.attr({ value: $.trim(cur_text), size: num_cols })
.blur(function() {
set_text(cur_text);
})
.keyup(function(e) {
if (e.keyCode === 27) {
// Escape key.
$(this).trigger("blur");
} else if (e.keyCode === 13) {
// Enter key.
set_text($(this).val());
}
// Do not propogate event to avoid unwanted side effects.
e.stopPropagation();
});
}
// Replace text with input object(s) and focus & select.
container.text("");
container.append(input_elt);
if (button_elt) {
container.append(button_elt);
}
input_elt.focus();
input_elt.select();
// Do not propogate to elements below b/c that blurs input and prevents it from being used.
e.stopPropagation();
});
// Add help text if there some.
if (help_text) {
container.attr("title", help_text).tooltip();
}
// Replace text with input object(s) and focus & select.
container.text("");
container.append(input_elt);
if (button_elt) {
container.append(button_elt);
}
input_elt.focus();
input_elt.select();
return container;
};
// Do not propogate to elements below b/c that blurs input and prevents it from being used.
e.stopPropagation();
});
// Add help text if there some.
if (help_text) {
container.attr("title", help_text).tooltip();
}
return container;
};
// ============================================================================
}));
// ============================================================================
});
+34 -23
View File
@@ -1,12 +1,11 @@
(function (root, factory) {
if (typeof define === 'function' && define.amd) {
(function(root, factory) {
if (typeof define === "function" && define.amd) {
define([], factory);
} else {
root.faIconButton = factory();
}
}(this, function () {
//============================================================================
})(this, function() {
//============================================================================
/** Returns a jQuery object containing a clickable font-awesome button.
* options:
* tooltipConfig : option map for bootstrap tool tip
@@ -18,31 +17,43 @@
* faIcon : which font awesome icon to use
* onclick : function to call when the button is clicked
*/
var faIconButton = function( options ){
var faIconButton = function(options) {
options = options || {};
options.tooltipConfig = options.tooltipConfig || { placement: 'bottom' };
options.tooltipConfig = options.tooltipConfig || {
placement: "bottom"
};
options.classes = [ 'icon-btn' ].concat( options.classes || [] );
if( options.disabled ){
options.classes.push( 'disabled' );
options.classes = ["icon-btn"].concat(options.classes || []);
if (options.disabled) {
options.classes.push("disabled");
}
var html = [
'<a class="', options.classes.join( ' ' ), '"',
(( options.title )?( ' title="' + options.title + '"' ):( '' )),
(( !options.disabled && options.target )? ( ' target="' + options.target + '"' ):( '' )),
' href="', (( !options.disabled && options.href )?( options.href ):( 'javascript:void(0);' )), '">',
// could go with something less specific here - like 'html'
'<span class="fa ', options.faIcon, '"></span>',
'</a>'
].join( '' );
var $button = $( html ).tooltip( options.tooltipConfig );
if( _.isFunction( options.onclick ) ){
$button.click( options.onclick );
'<a class="',
options.classes.join(" "),
'"',
options.title ? ' title="' + options.title + '"' : "",
!options.disabled && options.target
? ' target="' + options.target + '"'
: "",
' href="',
!options.disabled && options.href
? options.href
: "javascript:void(0);",
'">',
// could go with something less specific here - like 'html'
'<span class="fa ',
options.faIcon,
'"></span>',
"</a>"
].join("");
var $button = $(html).tooltip(options.tooltipConfig);
if (_.isFunction(options.onclick)) {
$button.click(options.onclick);
}
return $button;
};
//============================================================================
//============================================================================
return faIconButton;
}));
});
+126 -101
View File
@@ -1,17 +1,16 @@
// from: https://raw.githubusercontent.com/umdjs/umd/master/jqueryPlugin.js
// Uses AMD or browser globals to create a jQuery plugin.
(function (factory) {
if (typeof define === 'function' && define.amd) {
(function(factory) {
if (typeof define === "function" && define.amd) {
// AMD. Register as an anonymous module.
define(['jquery'], factory);
define(["jquery"], factory);
} else {
// Browser globals
factory(jQuery);
}
}(function ($) {
//==============================================================================
/**
})(function($) {
//==============================================================================
/**
* Template function that produces a bootstrap dropdown to replace the
* vanilla HTML select input. Pass in an array of options and an initial selection:
* $( '.my-div' ).append( dropDownSelect( [ 'option1', 'option2' ], 'option2' );
@@ -23,48 +22,58 @@
* var userChoice = $( '.my-div .dropdown-select .dropdown-select-selected' ).text();
*
*/
function dropDownSelect( options, selected ){
// replacement for vanilla select element using bootstrap dropdowns instead
selected = selected || (( !_.isEmpty( options ) )?( options[0] ):( '' ));
var $select = $([
'<div class="dropdown-select btn-group">',
'<button type="button" class="btn btn-default">',
'<span class="dropdown-select-selected">' + selected + '</span>',
'</button>',
'</div>'
].join( '\n' ));
function dropDownSelect(options, selected) {
// replacement for vanilla select element using bootstrap dropdowns instead
selected = selected || (!_.isEmpty(options) ? options[0] : "");
var $select = $(
[
'<div class="dropdown-select btn-group">',
'<button type="button" class="btn btn-default">',
'<span class="dropdown-select-selected">' +
selected +
"</span>",
"</button>",
"</div>"
].join("\n")
);
// if there's only one option, do not style/create as buttons, dropdown - use simple span
// otherwise, a dropdown displaying the current selection
if( options && options.length > 1 ){
$select.find( 'button' )
.addClass( 'dropdown-toggle' ).attr( 'data-toggle', 'dropdown' )
.append( ' <span class="caret"></span>' );
$select.append([
'<ul class="dropdown-menu" role="menu">',
_.map( options, function( option ){
return [
'<li><a href="javascript:void(0)">', option, '</a></li>'
].join( '' );
}).join( '\n' ),
'</ul>'
].join( '\n' ));
}
// if there's only one option, do not style/create as buttons, dropdown - use simple span
// otherwise, a dropdown displaying the current selection
if (options && options.length > 1) {
$select
.find("button")
.addClass("dropdown-toggle")
.attr("data-toggle", "dropdown")
.append(' <span class="caret"></span>');
$select.append(
[
'<ul class="dropdown-menu" role="menu">',
_.map(options, function(option) {
return [
'<li><a href="javascript:void(0)">',
option,
"</a></li>"
].join("");
}).join("\n"),
"</ul>"
].join("\n")
);
}
// trigger 'change.dropdown-select' when a new selection is made using the dropdown
function selectThis( event ){
var $this = $( this ),
$select = $this.parents( '.dropdown-select' ),
newSelection = $this.text();
$select.find( '.dropdown-select-selected' ).text( newSelection );
$select.trigger( 'change.dropdown-select', newSelection );
}
// trigger 'change.dropdown-select' when a new selection is made using the dropdown
function selectThis(event) {
var $this = $(this),
$select = $this.parents(".dropdown-select"),
newSelection = $this.text();
$select.find(".dropdown-select-selected").text(newSelection);
$select.trigger("change.dropdown-select", newSelection);
}
$select.find( 'a' ).click( selectThis );
return $select;
}
$select.find("a").click(selectThis);
return $select;
}
//==============================================================================
//==============================================================================
/**
* Creates a three part bootstrap button group (key, op, value) meant to
* allow the user control of filters (e.g. { key: 'name', op: 'contains', value: 'my_history' })
@@ -91,39 +100,47 @@
* $( '.my-div' ).filterControl( 'val' )
*
*/
function FilterControl( element, options ){
return this.init( element, options );
function FilterControl(element, options) {
return this.init(element, options);
}
/** the data key that this object will be stored under in the DOM element */
FilterControl.prototype.DATA_KEY = 'filter-control';
FilterControl.prototype.DATA_KEY = "filter-control";
/** parses options, sets up instance vars, and does initial render */
FilterControl.prototype.init = function _init( element, options ){
options = options || { filters: [] };
this.$element = $( element ).addClass( 'filter-control btn-group' );
this.options = jQuery.extend( true, {}, this.defaults, options );
FilterControl.prototype.init = function _init(element, options) {
options = options || { filters: [] };
this.$element = $(element).addClass("filter-control btn-group");
this.options = jQuery.extend(true, {}, this.defaults, options);
this.currFilter = this.options.filters[0];
return this.render();
};
return this.render();
};
/** render (or re-render) the controls on the element */
FilterControl.prototype.render = function _render(){
this.$element.empty()
.append([ this._renderKeySelect(), this._renderOpSelect(), this._renderValueInput() ]);
FilterControl.prototype.render = function _render() {
this.$element
.empty()
.append([
this._renderKeySelect(),
this._renderOpSelect(),
this._renderValueInput()
]);
return this;
};
/** render the key dropDownSelect, bind a change event to it, and return it */
FilterControl.prototype._renderKeySelect = function __renderKeySelect(){
FilterControl.prototype._renderKeySelect = function __renderKeySelect() {
var filterControl = this;
var keys = this.options.filters.map( function( filter ){
var keys = this.options.filters.map(function(filter) {
return filter.key;
});
this.$keySelect = dropDownSelect( keys, this.currFilter.key )
.addClass( 'filter-control-key' )
.on( 'change.dropdown-select', function( event, selection ){
filterControl.currFilter = _.findWhere( filterControl.options.filters, { key: selection });
this.$keySelect = dropDownSelect(keys, this.currFilter.key)
.addClass("filter-control-key")
.on("change.dropdown-select", function(event, selection) {
filterControl.currFilter = _.findWhere(
filterControl.options.filters,
{ key: selection }
);
// when the filter/key changes, re-render the control entirely
filterControl.render()._triggerChange();
});
@@ -131,74 +148,82 @@
};
/** render the op dropDownSelect, bind a change event to it, and return it */
FilterControl.prototype._renderOpSelect = function __renderOpSelect(){
FilterControl.prototype._renderOpSelect = function __renderOpSelect() {
var filterControl = this,
ops = this.currFilter.ops;
//TODO: search for currOp in avail. ops: use that for selected if there; otherwise: first op
this.$opSelect = dropDownSelect( ops, ops[0] )
.addClass( 'filter-control-op' )
.on( 'change.dropdown-select', function( event, selection ){
this.$opSelect = dropDownSelect(ops, ops[0])
.addClass("filter-control-op")
.on("change.dropdown-select", function(event, selection) {
filterControl._triggerChange();
});
return this.$opSelect;
};
/** render the value control, bind a change event to it, and return it */
FilterControl.prototype._renderValueInput = function __renderValueInput(){
FilterControl.prototype._renderValueInput = function __renderValueInput() {
var filterControl = this;
// if a values attribute is prov. on the filter - make this a dropdown; otherwise, use an input
if( this.currFilter.values ){
this.$valueSelect = dropDownSelect( this.currFilter.values, this.currFilter.values[0] )
.on( 'change.dropdown-select', function( event, selection ){
filterControl._triggerChange();
});
if (this.currFilter.values) {
this.$valueSelect = dropDownSelect(
this.currFilter.values,
this.currFilter.values[0]
).on("change.dropdown-select", function(event, selection) {
filterControl._triggerChange();
});
} else {
//TODO: allow setting a value type (mainly for which html5 input to use: range, number, etc.)
this.$valueSelect = $( '<input/>' ).addClass( 'form-control' )
.on( 'change', function( event, value ){
this.$valueSelect = $("<input/>")
.addClass("form-control")
.on("change", function(event, value) {
filterControl._triggerChange();
});
}
this.$valueSelect.addClass( 'filter-control-value' );
this.$valueSelect.addClass("filter-control-value");
return this.$valueSelect;
};
/** return the current state/setting for the filter as a three key object: key, op, value */
FilterControl.prototype.val = function _val(){
var key = this.$element.find( '.filter-control-key .dropdown-select-selected' ).text(),
op = this.$element.find( '.filter-control-op .dropdown-select-selected' ).text(),
FilterControl.prototype.val = function _val() {
var key = this.$element
.find(".filter-control-key .dropdown-select-selected")
.text(),
op = this.$element
.find(".filter-control-op .dropdown-select-selected")
.text(),
// handle either a dropdown or plain input
$value = this.$element.find( '.filter-control-value' ),
value = ( $value.hasClass( 'dropdown-select' ) )?( $value.find( '.dropdown-select-selected' ).text() )
:( $value.val() );
$value = this.$element.find(".filter-control-value"),
value = $value.hasClass("dropdown-select")
? $value.find(".dropdown-select-selected").text()
: $value.val();
return { key: key, op: op, value: value };
};
// single point of change for change event
FilterControl.prototype._triggerChange = function __triggerChange(){
this.$element.trigger( 'change.filter-control', this.val() );
FilterControl.prototype._triggerChange = function __triggerChange() {
this.$element.trigger("change.filter-control", this.val());
};
// as jq plugin
jQuery.fn.extend({
filterControl : function $filterControl( options ){
var nonOptionsArgs = jQuery.makeArray( arguments ).slice( 1 );
return this.map( function(){
var $this = $( this ),
data = $this.data( FilterControl.prototype.DATA_KEY );
filterControl: function $filterControl(options) {
var nonOptionsArgs = jQuery.makeArray(arguments).slice(1);
return this.map(function() {
var $this = $(this),
data = $this.data(FilterControl.prototype.DATA_KEY);
if( jQuery.type( options ) === 'object' ){
data = new FilterControl( $this, options );
$this.data( FilterControl.prototype.DATA_KEY, data );
}
if( data && jQuery.type( options ) === 'string' ){
var fn = data[ options ];
if( jQuery.type( fn ) === 'function' ){
return fn.apply( data, nonOptionsArgs );
}
}
return this;
if (jQuery.type(options) === "object") {
data = new FilterControl($this, options);
$this.data(FilterControl.prototype.DATA_KEY, data);
}
if (data && jQuery.type(options) === "string") {
var fn = data[options];
if (jQuery.type(fn) === "function") {
return fn.apply(data, nonOptionsArgs);
}
}
return this;
});
}
});
}));
});
+24 -22
View File
@@ -1,37 +1,39 @@
(function (factory) {
if (typeof define === 'function' && define.amd) {
(function(factory) {
if (typeof define === "function" && define.amd) {
define([], factory);
} else {
// Browser globals
factory(jQuery);
}
}(function () {
//=============================================================================
})(function() {
//=============================================================================
jQuery.fn.extend({
hoverhighlight : function $hoverhighlight( scope, color ){
scope = scope || 'body';
if( !this.length ){ return this; }
hoverhighlight: function $hoverhighlight(scope, color) {
scope = scope || "body";
if (!this.length) {
return this;
}
$( this ).each( function(){
var $this = $( this ),
targetSelector = $this.data( 'target' );
$(this).each(function() {
var $this = $(this),
targetSelector = $this.data("target");
if( targetSelector ){
$this.mouseover( function( ev ){
$( targetSelector, scope ).css({
background: color
if (targetSelector) {
$this
.mouseover(function(ev) {
$(targetSelector, scope).css({
background: color
});
})
.mouseout(function(ev) {
$(targetSelector).css({
background: ""
});
});
})
.mouseout( function( ev ){
$( targetSelector ).css({
background: ''
});
});
}
});
return this;
}
});
}));
});
+68 -56
View File
@@ -1,5 +1,5 @@
(function (root, factory) {
if (typeof define === 'function' && define.amd) {
(function(root, factory) {
if (typeof define === "function" && define.amd) {
// AMD. Register as an anonymous module.
define([], factory);
} else {
@@ -7,92 +7,104 @@
root.LoadingIndicator = factory();
}
//============================================================================
}(this, function () {
//============================================================================
})(this, function() {
//TODO: too specific to history panel
function LoadingIndicator( $where, options ){
function LoadingIndicator($where, options) {
var self = this;
// defaults
options = jQuery.extend({
cover : false
}, options || {} );
options = jQuery.extend(
{
cover: false
},
options || {}
);
function render(){
function render() {
var html = [
'<div class="loading-indicator">',
'<div class="loading-indicator-text">',
'<span class="fa fa-spinner fa-spin fa-lg"></span>',
'<span class="loading-indicator-message">loading...</span>',
'</div>',
'</div>'
].join( '\n' );
'<div class="loading-indicator-text">',
'<span class="fa fa-spinner fa-spin fa-lg"></span>',
'<span class="loading-indicator-message">loading...</span>',
"</div>",
"</div>"
].join("\n");
var $indicator = $( html ).hide().css( options.css || {
position : 'fixed'
}),
$text = $indicator.children( '.loading-indicator-text' );
var $indicator = $(html)
.hide()
.css(
options.css || {
position: "fixed"
}
),
$text = $indicator.children(".loading-indicator-text");
if( options.cover ){
if (options.cover) {
$indicator.css({
'z-index' : 2,
top : $where.css( 'top' ),
bottom : $where.css( 'bottom' ),
left : $where.css( 'left' ),
right : $where.css( 'right' ),
opacity : 0.5,
'background-color': 'white',
'text-align': 'center'
"z-index": 2,
top: $where.css("top"),
bottom: $where.css("bottom"),
left: $where.css("left"),
right: $where.css("right"),
opacity: 0.5,
"background-color": "white",
"text-align": "center"
});
$text = $indicator.children( '.loading-indicator-text' ).css({
'margin-top' : '20px'
$text = $indicator.children(".loading-indicator-text").css({
"margin-top": "20px"
});
} else {
$text = $indicator.children( '.loading-indicator-text' ).css({
margin : '12px 0px 0px 10px',
opacity : '0.85',
color : 'grey'
$text = $indicator.children(".loading-indicator-text").css({
margin: "12px 0px 0px 10px",
opacity: "0.85",
color: "grey"
});
$text.children( '.loading-indicator-message' ).css({
margin : '0px 8px 0px 0px',
'font-style' : 'italic'
$text.children(".loading-indicator-message").css({
margin: "0px 8px 0px 0px",
"font-style": "italic"
});
}
return $indicator;
}
self.show = function( msg, speed, callback ){
msg = msg || 'loading...';
speed = speed || 'fast';
self.show = function(msg, speed, callback) {
msg = msg || "loading...";
speed = speed || "fast";
// remove previous
$where.parent().find( '.loading-indicator' ).remove();
$where
.parent()
.find(".loading-indicator")
.remove();
// since position is fixed - we insert as sibling
self.$indicator = render().insertBefore( $where );
self.message( msg );
self.$indicator.fadeIn( speed, callback );
self.$indicator = render().insertBefore($where);
self.message(msg);
self.$indicator.fadeIn(speed, callback);
return self;
};
self.message = function( msg ){
self.$indicator.find( 'i' ).text( msg );
self.message = function(msg) {
self.$indicator.find("i").text(msg);
};
self.hide = function( speed, callback ){
speed = speed || 'fast';
if( self.$indicator && self.$indicator.length ){
self.$indicator.fadeOut( speed, function(){
self.hide = function(speed, callback) {
speed = speed || "fast";
if (self.$indicator && self.$indicator.length) {
self.$indicator.fadeOut(speed, function() {
self.$indicator.remove();
if( callback ){ callback(); }
if (callback) {
callback();
}
});
} else {
if( callback ){ callback(); }
if (callback) {
callback();
}
}
return self;
};
return self;
}
//============================================================================
//============================================================================
return LoadingIndicator;
}));
});
+108 -96
View File
@@ -1,7 +1,7 @@
// from: https://raw.githubusercontent.com/umdjs/umd/master/jqueryPlugin.js
// Uses AMD or browser globals to create a jQuery plugin.
(function (factory) {
if (typeof define === 'function' && define.amd) {
(function(factory) {
if (typeof define === "function" && define.amd) {
//TODO: So...this turns out to be an all or nothing thing. If I load jQuery in the define below, it will
// (of course) wipe the old jquery *and all the plugins loaded into it*. So the define below *is still
// relying on jquery being loaded globally* in order to preserve plugins.
@@ -10,9 +10,7 @@
// Browser globals
factory(jQuery);
}
}(function () {
})(function() {
/** Multi 'mode' button (or any element really) that changes the html
* contents of itself when clicked. Pass in an ordered list of
* objects with 'html' and (optional) onclick functions.
@@ -53,139 +51,153 @@
* $( '.myElement' ).modeButton( 'callModeFn', 'bler' );
*/
/** constructor */
function ModeButton( element, options ){
this.currModeIndex = 0;
return this._init( element, options );
function ModeButton(element, options) {
this.currModeIndex = 0;
return this._init(element, options);
}
/** html5 data key to store this object inside an element */
ModeButton.prototype.DATA_KEY = 'mode-button';
ModeButton.prototype.DATA_KEY = "mode-button";
/** default options */
ModeButton.prototype.defaults = {
switchModesOnClick : true
};
ModeButton.prototype.defaults = {
switchModesOnClick: true
};
// ---- private interface
/** set up options, intial mode, and the click handler */
ModeButton.prototype._init = function _init( element, options ){
ModeButton.prototype._init = function _init(element, options) {
//console.debug( 'ModeButton._init:', element, options );
options = options || {};
this.$element = $( element );
this.options = $.extend( true, {}, this.defaults, options );
if( !options.modes ){
throw new Error( 'ModeButton requires a "modes" array' );
options = options || {};
this.$element = $(element);
this.options = $.extend(true, {}, this.defaults, options);
if (!options.modes) {
throw new Error('ModeButton requires a "modes" array');
}
var modeButton = this;
this.$element.click( function _ModeButtonClick( event ){
// call the curr mode fn
modeButton.callModeFn();
// inc the curr mode index
if( modeButton.options.switchModesOnClick ){ modeButton._incModeIndex(); }
// set the element html
$( this ).html( modeButton.options.modes[ modeButton.currModeIndex ].html );
});
return this.reset();
};
var modeButton = this;
this.$element.click(function _ModeButtonClick(event) {
// call the curr mode fn
modeButton.callModeFn();
// inc the curr mode index
if (modeButton.options.switchModesOnClick) {
modeButton._incModeIndex();
}
// set the element html
$(this).html(
modeButton.options.modes[modeButton.currModeIndex].html
);
});
return this.reset();
};
/** increment the mode index to the next in the array, looping back to zero if at the last */
ModeButton.prototype._incModeIndex = function _incModeIndex(){
this.currModeIndex += 1;
if( this.currModeIndex >= this.options.modes.length ){
this.currModeIndex = 0;
}
return this;
};
ModeButton.prototype._incModeIndex = function _incModeIndex() {
this.currModeIndex += 1;
if (this.currModeIndex >= this.options.modes.length) {
this.currModeIndex = 0;
}
return this;
};
/** get the mode index in the modes array for the given key (mode name) */
ModeButton.prototype._getModeIndex = function _getModeIndex( modeKey ){
for( var i=0; i<this.options.modes.length; i+=1 ){
if( this.options.modes[ i ].mode === modeKey ){ return i; }
}
throw new Error( 'mode not found: ' + modeKey );
};
ModeButton.prototype._getModeIndex = function _getModeIndex(modeKey) {
for (var i = 0; i < this.options.modes.length; i += 1) {
if (this.options.modes[i].mode === modeKey) {
return i;
}
}
throw new Error("mode not found: " + modeKey);
};
/** set the current mode to the one with the given index and set button html */
ModeButton.prototype._setModeByIndex = function _setModeByIndex( index ){
var newMode = this.options.modes[ index ];
if( !newMode ){
throw new Error( 'mode index not found: ' + index );
ModeButton.prototype._setModeByIndex = function _setModeByIndex(index) {
var newMode = this.options.modes[index];
if (!newMode) {
throw new Error("mode index not found: " + index);
}
this.currModeIndex = index;
if( newMode.html ){
this.$element.html( newMode.html );
if (newMode.html) {
this.$element.html(newMode.html);
}
return this;
};
return this;
};
// ---- public interface
/** get the current mode object (not just the mode name) */
ModeButton.prototype.currentMode = function currentMode(){
return this.options.modes[ this.currModeIndex ];
};
ModeButton.prototype.currentMode = function currentMode() {
return this.options.modes[this.currModeIndex];
};
/** return the mode key of the current mode */
ModeButton.prototype.current = function current(){
ModeButton.prototype.current = function current() {
// sugar for returning mode name
return this.currentMode().mode;
};
return this.currentMode().mode;
};
/** get the mode with the given modeKey or the current mode if modeKey is undefined */
ModeButton.prototype.getMode = function getMode( modeKey ){
if( !modeKey ){ return this.currentMode(); }
return this.options.modes[( this._getModeIndex( modeKey ) )];
};
ModeButton.prototype.getMode = function getMode(modeKey) {
if (!modeKey) {
return this.currentMode();
}
return this.options.modes[this._getModeIndex(modeKey)];
};
/** T/F if the button has the given mode */
ModeButton.prototype.hasMode = function hasMode( modeKey ){
ModeButton.prototype.hasMode = function hasMode(modeKey) {
try {
return !!this.getMode( modeKey );
} catch( err ){}
return !!this.getMode(modeKey);
} catch (err) {}
return false;
};
};
/** set the current mode to the mode with the given name */
ModeButton.prototype.setMode = function setMode( modeKey ){
return this._setModeByIndex( this._getModeIndex( modeKey ) );
};
ModeButton.prototype.setMode = function setMode(modeKey) {
return this._setModeByIndex(this._getModeIndex(modeKey));
};
/** reset to the initial mode */
ModeButton.prototype.reset = function reset(){
this.currModeIndex = 0;
if( this.options.initialMode ){
this.currModeIndex = this._getModeIndex( this.options.initialMode );
}
return this._setModeByIndex( this.currModeIndex );
};
ModeButton.prototype.reset = function reset() {
this.currModeIndex = 0;
if (this.options.initialMode) {
this.currModeIndex = this._getModeIndex(this.options.initialMode);
}
return this._setModeByIndex(this.currModeIndex);
};
/** manually call the click handler of the given mode */
ModeButton.prototype.callModeFn = function callModeFn( modeKey ){
var modeFn = this.getMode( modeKey ).onclick;
if( modeFn && $.type( modeFn === 'function' ) ){
ModeButton.prototype.callModeFn = function callModeFn(modeKey) {
var modeFn = this.getMode(modeKey).onclick;
if (modeFn && $.type(modeFn === "function")) {
// call with the element as context (std jquery pattern)
return modeFn.call( this.$element.get(0) );
}
return undefined;
};
return modeFn.call(this.$element.get(0));
}
return undefined;
};
// as jq plugin
$.fn.modeButton = function $modeButton( options ){
if( !this.length ){ return this; }
$.fn.modeButton = function $modeButton(options) {
if (!this.length) {
return this;
}
//TODO: does map still work with jq multi selection (i.e. $( '.class-for-many-btns' ).modeButton)?
if( $.type( options ) === 'object' ){
return this.map( function(){
var $this = $( this );
$this.data( 'mode-button', new ModeButton( $this, options ) );
if ($.type(options) === "object") {
return this.map(function() {
var $this = $(this);
$this.data("mode-button", new ModeButton($this, options));
return this;
});
}
var $first = $( this[0] ),
button = $first.data( 'mode-button' );
var $first = $(this[0]),
button = $first.data("mode-button");
if( !button ){
throw new Error( 'modeButton needs an options object or string name of a function' );
if (!button) {
throw new Error(
"modeButton needs an options object or string name of a function"
);
}
if( button && $.type( options ) === 'string' ){
if (button && $.type(options) === "string") {
var fnName = options;
if( button && $.type( button[ fnName ] ) === 'function' ){
return button[ fnName ].apply( button, $.makeArray( arguments ).slice( 1 ) );
if (button && $.type(button[fnName]) === "function") {
return button[fnName].apply(
button,
$.makeArray(arguments).slice(1)
);
}
}
return button;
};
}));
});
+124 -89
View File
@@ -1,58 +1,58 @@
// from: https://raw.githubusercontent.com/umdjs/umd/master/jqueryPlugin.js
// Uses AMD or browser globals to create a jQuery plugin.
(function (factory) {
if (typeof define === 'function' && define.amd) {
(function(factory) {
if (typeof define === "function" && define.amd) {
// AMD. Register as an anonymous module.
define(['jquery'], factory);
define(["jquery"], factory);
} else {
// Browser globals
factory(jQuery);
}
}(function ($) {
})(function($) {
/** Builds (twitter bootstrap styled) pagination controls.
* If the totalDataSize is not null, a horizontal list of page buttons is displayed.
* If totalDataSize is null, two links ('Prev' and 'Next) are displayed.
* When pages are changed, a 'pagination.page-change' event is fired
* sending the event and the (0-based) page requested.
*/
function Pagination( element, options ){
function Pagination(element, options) {
/** the total number of pages */
this.numPages = null;
/** the current, active page */
this.currPage = 0;
return this.init( element, options );
this.currPage = 0;
return this.init(element, options);
}
/** data key under which this object will be stored in the element */
Pagination.prototype.DATA_KEY = 'pagination';
Pagination.prototype.DATA_KEY = "pagination";
/** default options */
Pagination.prototype.defaults = {
/** which page to begin at */
startingPage : 0,
startingPage: 0,
/** number of data per page */
perPage : 20,
perPage: 20,
/** the total number of data (null == unknown) */
totalDataSize : null,
totalDataSize: null,
/** size of current data on current page */
currDataSize : null
};
currDataSize: null
};
/** init the control, calc numPages if possible, and render
* @param {jQuery} the element that will contain the pagination control
* @param {Object} options a map containing overrides to the pagination default options
*/
Pagination.prototype.init = function _init( $element, options ){
options = options || {};
this.$element = $element;
this.options = jQuery.extend( true, {}, this.defaults, options );
Pagination.prototype.init = function _init($element, options) {
options = options || {};
this.$element = $element;
this.options = jQuery.extend(true, {}, this.defaults, options);
this.currPage = this.options.startingPage;
if( this.options.totalDataSize !== null ){
this.numPages = Math.ceil( this.options.totalDataSize / this.options.perPage );
if (this.options.totalDataSize !== null) {
this.numPages = Math.ceil(
this.options.totalDataSize / this.options.perPage
);
// limit currPage by numPages
if( this.currPage >= this.numPages ){
if (this.currPage >= this.numPages) {
this.currPage = this.numPages - 1;
}
}
@@ -60,161 +60,196 @@
//console.debug( JSON.stringify( this.options ) );
// bind to data of element
this.$element.data( Pagination.prototype.DATA_KEY, this );
this.$element.data(Pagination.prototype.DATA_KEY, this);
this._render();
return this;
};
return this;
};
/** helper to create a simple li + a combo */
function _make$Li( contents ){
return $([
'<li><a href="javascript:void(0);">', contents, '</a></li>'
].join( '' ));
function _make$Li(contents) {
return $(
['<li><a href="javascript:void(0);">', contents, "</a></li>"].join(
""
)
);
}
/** render previous and next pagination buttons */
Pagination.prototype._render = function __render(){
Pagination.prototype._render = function __render() {
// no data - no pagination
if( this.options.totalDataSize === 0 ){ return this; }
if (this.options.totalDataSize === 0) {
return this;
}
// only one page
if( this.numPages === 1 ){ return this; }
if (this.numPages === 1) {
return this;
}
// when the number of pages are known, render each page as a link
if( this.numPages > 0 ){
if (this.numPages > 0) {
this._renderPages();
this._scrollToActivePage();
// when the number of pages is not known, render previous or next
// when the number of pages is not known, render previous or next
} else {
this._renderPrevNext();
}
return this;
return this;
};
/** render previous and next pagination buttons */
Pagination.prototype._renderPrevNext = function __renderPrevNext(){
Pagination.prototype._renderPrevNext = function __renderPrevNext() {
var pagination = this,
$prev = _make$Li( 'Prev' ),
$next = _make$Li( 'Next' ),
$paginationContainer = $( '<ul/>' ).addClass( 'pagination pagination-prev-next' );
$prev = _make$Li("Prev"),
$next = _make$Li("Next"),
$paginationContainer = $("<ul/>").addClass(
"pagination pagination-prev-next"
);
// disable if it either end
if( this.currPage === 0 ){
$prev.addClass( 'disabled' );
if (this.currPage === 0) {
$prev.addClass("disabled");
} else {
$prev.click( function(){ pagination.prevPage(); });
$prev.click(function() {
pagination.prevPage();
});
}
if( ( this.numPages && this.currPage === ( this.numPages - 1 ) )
|| ( this.options.currDataSize && this.options.currDataSize < this.options.perPage ) ){
$next.addClass( 'disabled' );
if (
(this.numPages && this.currPage === this.numPages - 1) ||
(this.options.currDataSize &&
this.options.currDataSize < this.options.perPage)
) {
$next.addClass("disabled");
} else {
$next.click( function(){ pagination.nextPage(); });
$next.click(function() {
pagination.nextPage();
});
}
this.$element.html( $paginationContainer.append([ $prev, $next ]) );
this.$element.html($paginationContainer.append([$prev, $next]));
//console.debug( this.$element, this.$element.html() );
return this.$element;
};
/** render page links for each possible page (if we can) */
Pagination.prototype._renderPages = function __renderPages(){
Pagination.prototype._renderPages = function __renderPages() {
// it's better to scroll the control and let the user see all pages
// than to force her/him to change pages in order to find the one they want (as traditional << >> does)
var pagination = this,
$scrollingContainer = $( '<div>' ).addClass( 'pagination-scroll-container' ),
$paginationContainer = $( '<ul/>' ).addClass( 'pagination pagination-page-list' ),
page$LiClick = function( ev ){
pagination.goToPage( $( this ).data( 'page' ) );
$scrollingContainer = $("<div>").addClass(
"pagination-scroll-container"
),
$paginationContainer = $("<ul/>").addClass(
"pagination pagination-page-list"
),
page$LiClick = function(ev) {
pagination.goToPage($(this).data("page"));
};
for( var i=0; i<this.numPages; i+=1 ){
for (var i = 0; i < this.numPages; i += 1) {
// add html5 data tag 'page' for later click event handler use
var $pageLi = _make$Li( i + 1 ).attr( 'data-page', i ).click( page$LiClick );
var $pageLi = _make$Li(i + 1)
.attr("data-page", i)
.click(page$LiClick);
// highlight the current page
if( i === this.currPage ){
$pageLi.addClass( 'active' );
if (i === this.currPage) {
$pageLi.addClass("active");
}
//console.debug( '\t', $pageLi );
$paginationContainer.append( $pageLi );
$paginationContainer.append($pageLi);
}
return this.$element.html( $scrollingContainer.html( $paginationContainer ) );
return this.$element.html(
$scrollingContainer.html($paginationContainer)
);
};
/** scroll scroll-container (if any) to show the active page */
Pagination.prototype._scrollToActivePage = function __scrollToActivePage(){
Pagination.prototype._scrollToActivePage = function __scrollToActivePage() {
// scroll to show active page in center of scrollable area
var $container = this.$element.find( '.pagination-scroll-container' );
var $container = this.$element.find(".pagination-scroll-container");
// no scroll container : don't scroll
if( !$container.length ){ return this; }
if (!$container.length) {
return this;
}
var $activePage = this.$element.find( 'li.active' ),
var $activePage = this.$element.find("li.active"),
midpoint = $container.width() / 2;
//console.debug( $container, $activePage, midpoint );
$container.scrollLeft( $container.scrollLeft() + $activePage.position().left - midpoint );
$container.scrollLeft(
$container.scrollLeft() + $activePage.position().left - midpoint
);
return this;
};
/** go to a certain page */
Pagination.prototype.goToPage = function goToPage( page ){
if( page <= 0 ){ page = 0; }
if( this.numPages && page >= this.numPages ){ page = this.numPages - 1; }
if( page === this.currPage ){ return this; }
Pagination.prototype.goToPage = function goToPage(page) {
if (page <= 0) {
page = 0;
}
if (this.numPages && page >= this.numPages) {
page = this.numPages - 1;
}
if (page === this.currPage) {
return this;
}
//console.debug( '\t going to page ' + page )
this.currPage = page;
this.$element.trigger( 'pagination.page-change', this.currPage );
this.$element.trigger("pagination.page-change", this.currPage);
//console.info( 'pagination:page-change', this.currPage );
this._render();
return this;
};
/** go to the previous page */
Pagination.prototype.prevPage = function prevPage(){
return this.goToPage( this.currPage - 1 );
Pagination.prototype.prevPage = function prevPage() {
return this.goToPage(this.currPage - 1);
};
/** go to the next page */
Pagination.prototype.nextPage = function nextPage(){
return this.goToPage( this.currPage + 1 );
Pagination.prototype.nextPage = function nextPage() {
return this.goToPage(this.currPage + 1);
};
/** return the current page */
Pagination.prototype.page = function page(){
Pagination.prototype.page = function page() {
return this.currPage;
};
// alternate constructor invocation
Pagination.create = function _create( $element, options ){
return new Pagination( $element, options );
Pagination.create = function _create($element, options) {
return new Pagination($element, options);
};
// as jq plugin
jQuery.fn.extend({
pagination : function $pagination( options ){
var nonOptionsArgs = jQuery.makeArray( arguments ).slice( 1 );
pagination: function $pagination(options) {
var nonOptionsArgs = jQuery.makeArray(arguments).slice(1);
// if passed an object - use that as an options map to create pagination for each selected
if( jQuery.type( options ) === 'object' ){
return this.map( function(){
Pagination.create( $( this ), options );
if (jQuery.type(options) === "object") {
return this.map(function() {
Pagination.create($(this), options);
return this;
});
}
// (other invocations only work on the first element in selected)
var $firstElement = $( this[0] ),
previousControl = $firstElement.data( Pagination.prototype.DATA_KEY );
var $firstElement = $(this[0]),
previousControl = $firstElement.data(
Pagination.prototype.DATA_KEY
);
// if a pagination control was found for this element, either...
if( previousControl ){
if (previousControl) {
// invoke a function on the pagination object if passed a string (the function name)
if( jQuery.type( options ) === 'string' ){
var fn = previousControl[ options ];
if( jQuery.type( fn ) === 'function' ){
return fn.apply( previousControl, nonOptionsArgs );
if (jQuery.type(options) === "string") {
var fn = previousControl[options];
if (jQuery.type(fn) === "function") {
return fn.apply(previousControl, nonOptionsArgs);
}
// if passed nothing, return the previously set control
// if passed nothing, return the previously set control
} else {
return previousControl;
}
@@ -223,4 +258,4 @@
return undefined;
}
});
}));
});
+206 -135
View File
@@ -1,15 +1,14 @@
// from: https://raw.githubusercontent.com/umdjs/umd/master/jqueryPlugin.js
// Uses AMD or browser globals to create a jQuery plugin.
(function (factory) {
if (typeof define === 'function' && define.amd) {
(function(factory) {
if (typeof define === "function" && define.amd) {
// AMD. Register as an anonymous module.
define(['jquery'], factory);
define(["jquery"], factory);
} else {
// Browser globals
factory(jQuery);
}
}(function ($) {
})(function($) {
//==============================================================================
/** Column selection using the peek display as the control.
* Adds rows to the bottom of the peek with clickable areas in each cell
@@ -57,120 +56,152 @@
/** option defaults */
var defaults = {
/** does this control allow renaming headers? */
renameColumns : false,
renameColumns: false,
/** does this control allow renaming headers? */
columnNames : [],
columnNames: [],
/** the comment character used by the peek's datatype */
commentChar : '#',
commentChar: "#",
/** should comment rows be shown or hidden in the peek */
hideCommentRows : false,
hideCommentRows: false,
/** should a column of row control prompts be used */
includePrompts : true,
includePrompts: true,
/** what is the content of the top left cell (often a title) */
topLeftContent : 'Columns:'
topLeftContent: "Columns:"
},
/** class added to the pre.peek element (to allow css on just the control) */
PEEKCONTROL_CLASS = 'peek-column-selector',
PEEKCONTROL_CLASS = "peek-column-selector",
/** the string of the event fired when a control row changes */
CHANGE_EVENT = 'peek-column-selector.change',
CHANGE_EVENT = "peek-column-selector.change",
/** the string of the event fired when a column is renamed */
RENAME_EVENT = 'peek-column-selector.rename',
RENAME_EVENT = "peek-column-selector.rename",
/** class added to the control rows */
ROW_CLASS = 'control',
ROW_CLASS = "control",
/** class added to the left-hand cells that serve as row prompts */
PROMPT_CLASS = 'control-prompt',
PROMPT_CLASS = "control-prompt",
/** class added to selected _cells_/tds */
SELECTED_CLASS = 'selected',
SELECTED_CLASS = "selected",
/** class added to disabled/un-clickable cells/tds */
DISABLED_CLASS = 'disabled',
DISABLED_CLASS = "disabled",
/** class added to the clickable surface within a cell to select it */
BUTTON_CLASS = 'button',
BUTTON_CLASS = "button",
/** class added to peek table header (th) cells to indicate they can be clicked and are renamable */
RENAMABLE_HEADER_CLASS = 'renamable-header',
RENAMABLE_HEADER_CLASS = "renamable-header",
/** the data key used for each cell to store the column index ('data-...') */
COLUMN_INDEX_DATA_KEY = 'column-index',
COLUMN_INDEX_DATA_KEY = "column-index",
/** renamable header data key used to store the column name (w/o the number and dot: '1.Bler') */
COLUMN_NAME_DATA_KEY = 'column-name';
COLUMN_NAME_DATA_KEY = "column-name";
//TODO: not happy with pure functional here - rows should polymorph (multi, single, etc.)
//TODO: needs clean up, move handlers to outer scope
// ........................................................................
/** validate the control data sent in for each row */
function validateControl( control ){
if( control.disabled && jQuery.type( control.disabled ) !== 'array' ){
throw new Error( '"disabled" must be defined as an array of indeces: ' + JSON.stringify( control ) );
function validateControl(control) {
if (control.disabled && jQuery.type(control.disabled) !== "array") {
throw new Error(
'"disabled" must be defined as an array of indeces: ' +
JSON.stringify(control)
);
}
if( control.multiselect && control.selected && jQuery.type( control.selected ) !== 'array' ){
throw new Error( 'Mulitselect rows need an array for "selected": ' + JSON.stringify( control ) );
if (
control.multiselect &&
control.selected &&
jQuery.type(control.selected) !== "array"
) {
throw new Error(
'Mulitselect rows need an array for "selected": ' +
JSON.stringify(control)
);
}
if( !control.label || !control.id ){
throw new Error( 'Peek controls need a label and id for each control row: ' + JSON.stringify( control ) );
if (!control.label || !control.id) {
throw new Error(
"Peek controls need a label and id for each control row: " +
JSON.stringify(control)
);
}
if( control.disabled && control.disabled.indexOf( control.selected ) !== -1 ){
throw new Error( 'Selected column is in the list of disabled columns: ' + JSON.stringify( control ) );
if (
control.disabled &&
control.disabled.indexOf(control.selected) !== -1
) {
throw new Error(
"Selected column is in the list of disabled columns: " +
JSON.stringify(control)
);
}
return control;
}
/** build the inner control surface (i.e. button-like) */
function buildButton( control, columnIndex ){
return $( '<div/>' ).addClass( BUTTON_CLASS ).text( control.label );
function buildButton(control, columnIndex) {
return $("<div/>")
.addClass(BUTTON_CLASS)
.text(control.label);
}
/** build the basic (shared) cell structure */
function buildControlCell( control, columnIndex ){
var $td = $( '<td/>' )
.html( buildButton( control, columnIndex ) )
.attr( 'data-' + COLUMN_INDEX_DATA_KEY, columnIndex );
function buildControlCell(control, columnIndex) {
var $td = $("<td/>")
.html(buildButton(control, columnIndex))
.attr("data-" + COLUMN_INDEX_DATA_KEY, columnIndex);
// disable if index in disabled array
if( control.disabled && control.disabled.indexOf( columnIndex ) !== -1 ){
$td.addClass( DISABLED_CLASS );
if (control.disabled && control.disabled.indexOf(columnIndex) !== -1) {
$td.addClass(DISABLED_CLASS);
}
return $td;
}
/** set the text of the control based on selected/un */
function setSelectedText( $cell, control, columnIndex ){
var $button = $cell.children( '.' + BUTTON_CLASS );
if( $cell.hasClass( SELECTED_CLASS ) ){
$button.html( ( control.selectedText !== undefined )?( control.selectedText ):( control.label ) );
function setSelectedText($cell, control, columnIndex) {
var $button = $cell.children("." + BUTTON_CLASS);
if ($cell.hasClass(SELECTED_CLASS)) {
$button.html(
control.selectedText !== undefined
? control.selectedText
: control.label
);
} else {
$button.html( ( control.unselectedText !== undefined )?( control.unselectedText ):( control.label ) );
$button.html(
control.unselectedText !== undefined
? control.unselectedText
: control.label
);
}
}
/** build a cell for a row that only allows one selection */
function buildSingleSelectCell( control, columnIndex ){
function buildSingleSelectCell(control, columnIndex) {
// only one selection - selected is single index
var $cell = buildControlCell( control, columnIndex );
if( control.selected === columnIndex ){
$cell.addClass( SELECTED_CLASS );
var $cell = buildControlCell(control, columnIndex);
if (control.selected === columnIndex) {
$cell.addClass(SELECTED_CLASS);
}
setSelectedText( $cell, control, columnIndex );
setSelectedText($cell, control, columnIndex);
// only add the handler to non-disabled controls
if( !$cell.hasClass( DISABLED_CLASS ) ){
$cell.click( function selectClick( ev ){
var $cell = $( this );
if (!$cell.hasClass(DISABLED_CLASS)) {
$cell.click(function selectClick(ev) {
var $cell = $(this);
// don't re-select or fire event if already selected
if( !$cell.hasClass( SELECTED_CLASS ) ){
if (!$cell.hasClass(SELECTED_CLASS)) {
// only one can be selected - remove selected on all others, add it here
var $otherSelected = $cell.parent().children( '.' + SELECTED_CLASS ).removeClass( SELECTED_CLASS );
$otherSelected.each( function(){
setSelectedText( $( this ), control, columnIndex );
var $otherSelected = $cell
.parent()
.children("." + SELECTED_CLASS)
.removeClass(SELECTED_CLASS);
$otherSelected.each(function() {
setSelectedText($(this), control, columnIndex);
});
$cell.addClass( SELECTED_CLASS );
setSelectedText( $cell, control, columnIndex );
$cell.addClass(SELECTED_CLASS);
setSelectedText($cell, control, columnIndex);
// fire the event from the table itself, passing the id and index of selected
var eventData = {},
key = $cell.parent().attr( 'id' ),
val = $cell.data( COLUMN_INDEX_DATA_KEY );
eventData[ key ] = val;
$cell.parents( '.peek' ).trigger( CHANGE_EVENT, eventData );
key = $cell.parent().attr("id"),
val = $cell.data(COLUMN_INDEX_DATA_KEY);
eventData[key] = val;
$cell.parents(".peek").trigger(CHANGE_EVENT, eventData);
}
});
}
@@ -178,129 +209,169 @@
}
/** build a cell for a row that allows multiple selections */
function buildMultiSelectCell( control, columnIndex ){
var $cell = buildControlCell( control, columnIndex );
function buildMultiSelectCell(control, columnIndex) {
var $cell = buildControlCell(control, columnIndex);
// multiple selection - selected is an array
if( control.selected && control.selected.indexOf( columnIndex ) !== -1 ){
$cell.addClass( SELECTED_CLASS );
if (control.selected && control.selected.indexOf(columnIndex) !== -1) {
$cell.addClass(SELECTED_CLASS);
}
setSelectedText( $cell, control, columnIndex );
setSelectedText($cell, control, columnIndex);
// only add the handler to non-disabled controls
if( !$cell.hasClass( DISABLED_CLASS ) ){
$cell.click( function multiselectClick( ev ){
var $cell = $( this );
if (!$cell.hasClass(DISABLED_CLASS)) {
$cell.click(function multiselectClick(ev) {
var $cell = $(this);
// can be more than one selected - toggle selected on this cell
$cell.toggleClass( SELECTED_CLASS );
setSelectedText( $cell, control, columnIndex );
var selectedColumnIndeces = $cell.parent().find( '.' + SELECTED_CLASS ).map( function( i, e ){
return $( e ).data( COLUMN_INDEX_DATA_KEY );
});
$cell.toggleClass(SELECTED_CLASS);
setSelectedText($cell, control, columnIndex);
var selectedColumnIndeces = $cell
.parent()
.find("." + SELECTED_CLASS)
.map(function(i, e) {
return $(e).data(COLUMN_INDEX_DATA_KEY);
});
// fire the event from the table itself, passing the id and index of selected
var eventData = {},
key = $cell.parent().attr( 'id' ),
val = jQuery.makeArray( selectedColumnIndeces );
eventData[ key ] = val;
$cell.parents( '.peek' ).trigger( CHANGE_EVENT, eventData );
key = $cell.parent().attr("id"),
val = jQuery.makeArray(selectedColumnIndeces);
eventData[key] = val;
$cell.parents(".peek").trigger(CHANGE_EVENT, eventData);
});
}
return $cell;
}
/** iterate over columns in peek and create a control for each */
function buildControlCells( count, control ){
function buildControlCells(count, control) {
var $cells = [];
// build a control for each column - using a build fn based on control
for( var columnIndex=0; columnIndex<count; columnIndex+=1 ){
$cells.push( control.multiselect? buildMultiSelectCell( control, columnIndex )
: buildSingleSelectCell( control, columnIndex ) );
for (var columnIndex = 0; columnIndex < count; columnIndex += 1) {
$cells.push(
control.multiselect
? buildMultiSelectCell(control, columnIndex)
: buildSingleSelectCell(control, columnIndex)
);
}
return $cells;
}
/** build a row of controls for the peek */
function buildControlRow( cellCount, control, includePrompts ){
var $controlRow = $( '<tr/>' ).attr( 'id', control.id ).addClass( ROW_CLASS );
if( includePrompts ){
var $promptCell = $( '<td/>' ).addClass( PROMPT_CLASS ).text( control.label + ':' );
$controlRow.append( $promptCell );
function buildControlRow(cellCount, control, includePrompts) {
var $controlRow = $("<tr/>")
.attr("id", control.id)
.addClass(ROW_CLASS);
if (includePrompts) {
var $promptCell = $("<td/>")
.addClass(PROMPT_CLASS)
.text(control.label + ":");
$controlRow.append($promptCell);
}
$controlRow.append( buildControlCells( cellCount, control ) );
$controlRow.append(buildControlCells(cellCount, control));
return $controlRow;
}
// ........................................................................
/** add to the peek, using options for configuration, return the peek */
function peekColumnSelector( options ){
options = jQuery.extend( true, {}, defaults, options );
function peekColumnSelector(options) {
options = jQuery.extend(true, {}, defaults, options);
var $peek = $( this ).addClass( PEEKCONTROL_CLASS ),
$peektable = $peek.find( 'table' ),
var $peek = $(this).addClass(PEEKCONTROL_CLASS),
$peektable = $peek.find("table"),
// get the size of the tables - width and height, number of comment rows
columnCount = $peektable.find( 'th' ).length,
rowCount = $peektable.find( 'tr' ).length,
columnCount = $peektable.find("th").length,
rowCount = $peektable.find("tr").length,
// get the rows containing text starting with the comment char (also make them grey)
$commentRows = $peektable.find( 'td[colspan]' ).map( function( e, i ){
var $this = $( this );
if( $this.text() && $this.text().match( new RegExp( '^' + options.commentChar ) ) ){
return $( this ).css( 'color', 'grey' ).parent().get(0);
$commentRows = $peektable.find("td[colspan]").map(function(e, i) {
var $this = $(this);
if (
$this.text() &&
$this.text().match(new RegExp("^" + options.commentChar))
) {
return $(this)
.css("color", "grey")
.parent()
.get(0);
}
return null;
});
// should comment rows in the peek be hidden?
if( options.hideCommentRows ){
if (options.hideCommentRows) {
$commentRows.hide();
rowCount -= $commentRows.length;
}
//console.debug( 'rowCount:', rowCount, 'columnCount:', columnCount, '$commentRows:', $commentRows );
// should a first column of control prompts be added?
if( options.includePrompts ){
var $topLeft = $( '<th/>' ).addClass( 'top-left' ).text( options.topLeftContent )
.attr( 'rowspan', rowCount );
$peektable.find( 'tr' ).first().prepend( $topLeft );
if (options.includePrompts) {
var $topLeft = $("<th/>")
.addClass("top-left")
.text(options.topLeftContent)
.attr("rowspan", rowCount);
$peektable
.find("tr")
.first()
.prepend($topLeft);
}
// save either the options column name or the parsed text of each column header in html5 data attr and text
var $headers = $peektable.find( 'th:not(.top-left)' ).each( function( i, e ){
var $this = $( this ),
// can be '1.name' or '1'
text = $this.text().replace( /^\d+\.*/, '' ),
name = options.columnNames[ i ] || text;
$this.attr( 'data-' + COLUMN_NAME_DATA_KEY, name )
.text( ( i + 1 ) + (( name )?( '.' + name ):( '' )) );
});
var $headers = $peektable
.find("th:not(.top-left)")
.each(function(i, e) {
var $this = $(this),
// can be '1.name' or '1'
text = $this.text().replace(/^\d+\.*/, ""),
name = options.columnNames[i] || text;
$this
.attr("data-" + COLUMN_NAME_DATA_KEY, name)
.text(i + 1 + (name ? "." + name : ""));
});
// allow renaming of columns when the header is clicked
if( options.renameColumns ){
$headers.addClass( RENAMABLE_HEADER_CLASS )
.click( function renameColumn(){
if (options.renameColumns) {
$headers
.addClass(RENAMABLE_HEADER_CLASS)
.click(function renameColumn() {
// prompt for new name
var $this = $( this ),
index = $this.index() + ( options.includePrompts? 0: 1 ),
prevName = $this.data( COLUMN_NAME_DATA_KEY ),
newColumnName = prompt( 'New column name:', prevName );
if( newColumnName !== null && newColumnName !== prevName ){
var $this = $(this),
index =
$this.index() + (options.includePrompts ? 0 : 1),
prevName = $this.data(COLUMN_NAME_DATA_KEY),
newColumnName = prompt("New column name:", prevName);
if (newColumnName !== null && newColumnName !== prevName) {
// set the new text and data
$this.text( index + ( newColumnName?( '.' + newColumnName ):'' ) )
.data( COLUMN_NAME_DATA_KEY, newColumnName )
.attr( 'data-', COLUMN_NAME_DATA_KEY, newColumnName );
$this
.text(
index +
(newColumnName ? "." + newColumnName : "")
)
.data(COLUMN_NAME_DATA_KEY, newColumnName)
.attr("data-", COLUMN_NAME_DATA_KEY, newColumnName);
// fire event for new column names
var columnNames = jQuery.makeArray(
$this.parent().children( 'th:not(.top-left)' ).map( function(){
return $( this ).data( COLUMN_NAME_DATA_KEY );
}));
$this.parents( '.peek' ).trigger( RENAME_EVENT, columnNames );
$this
.parent()
.children("th:not(.top-left)")
.map(function() {
return $(this).data(COLUMN_NAME_DATA_KEY);
})
);
$this
.parents(".peek")
.trigger(RENAME_EVENT, columnNames);
}
});
}
// build a row for each control
options.controls.forEach( function( control, i ){
validateControl( control );
var $controlRow = buildControlRow( columnCount, control, options.includePrompts );
$peektable.find( 'tbody' ).append( $controlRow );
options.controls.forEach(function(control, i) {
validateControl(control);
var $controlRow = buildControlRow(
columnCount,
control,
options.includePrompts
);
$peektable.find("tbody").append($controlRow);
});
return this;
}
@@ -308,10 +379,10 @@
// ........................................................................
// as jq plugin
jQuery.fn.extend({
peekColumnSelector : function $peekColumnSelector( options ){
return this.map( function(){
return peekColumnSelector.call( this, options );
peekColumnSelector: function $peekColumnSelector(options) {
return this.map(function() {
return peekColumnSelector.call(this, options);
});
}
});
}));
});
+145 -121
View File
@@ -1,83 +1,109 @@
define([ 'jquery' ], function( jQuery ){
'use_strict';
define(["jquery"], function(jQuery) {
"use_strict";
var $ = jQuery;
var $ = jQuery;
// ============================================================================
// TODO: unify popup menus and/or eliminate this
/**
// ============================================================================
// TODO: unify popup menus and/or eliminate this
/**
* Sets up popupmenu rendering and binds options functions to the appropriate links.
* initial_options is a dict with text describing the option pointing to either (a) a
* function to perform; or (b) another dict with two required keys, 'url' and 'action' (the
* function to perform. (b) is useful for exposing the underlying URL of the option.
*/
function make_popupmenu(button_element, initial_options) {
/* Use the $.data feature to store options with the link element.
function make_popupmenu(button_element, initial_options) {
/* Use the $.data feature to store options with the link element.
This allows options to be changed at a later time
*/
var element_menu_exists = (button_element.data("menu_options"));
button_element.data("menu_options", initial_options);
var element_menu_exists = button_element.data("menu_options");
button_element.data("menu_options", initial_options);
// If element already has menu, nothing else to do since HTML and actions are already set.
if (element_menu_exists) { return; }
// If element already has menu, nothing else to do since HTML and actions are already set.
if (element_menu_exists) {
return;
}
button_element.bind("click.show_popup", function(e) {
// Close existing visible menus
$(".popmenu-wrapper").remove();
button_element.bind("click.show_popup", function(e) {
// Close existing visible menus
$(".popmenu-wrapper").remove();
// Need setTimeouts so clicks don't interfere with each other
setTimeout( function() {
// Dynamically generate the wrapper holding all the selectable options of the menu.
var menu_element = $( "<ul class='dropdown-menu' id='" + button_element.attr('id') + "-menu'></ul>" );
var options = button_element.data("menu_options");
if (_.size(options) <= 0) {
$("<li>No Options.</li>").appendTo(menu_element);
}
$.each( options, function( k, v ) {
if (v) {
// Action can be either an anonymous function and a mapped dict.
var action = v.action || v;
menu_element.append( $("<li></li>").append( $("<a>").attr("href", v.url).html(k).click(action) ) );
} else {
menu_element.append( $("<li></li>").addClass( "head" ).append( $("<a href='#'></a>").html(k) ) );
// Need setTimeouts so clicks don't interfere with each other
setTimeout(function() {
// Dynamically generate the wrapper holding all the selectable options of the menu.
var menu_element = $(
"<ul class='dropdown-menu' id='" +
button_element.attr("id") +
"-menu'></ul>"
);
var options = button_element.data("menu_options");
if (_.size(options) <= 0) {
$("<li>No Options.</li>").appendTo(menu_element);
}
});
var wrapper = $( "<div class='popmenu-wrapper' style='position: absolute;left: 0; top: -1000;'></div>" )
.append( menu_element ).appendTo( "body" );
var x = e.pageX - wrapper.width() / 2 ;
x = Math.min( x, $(document).scrollLeft() + $(window).width() - $(wrapper).width() - 5 );
x = Math.max( x, $(document).scrollLeft() + 5 );
wrapper.css({
top: e.pageY,
left: x
});
}, 10);
setTimeout( function() {
// Bind click event to current window and all frames to remove any visible menus
// Bind to document object instead of window object for IE compat
var close_popup = function(el) {
$(el).bind("click.close_popup", function() {
$(".popmenu-wrapper").remove();
el.unbind("click.close_popup");
$.each(options, function(k, v) {
if (v) {
// Action can be either an anonymous function and a mapped dict.
var action = v.action || v;
menu_element.append(
$("<li></li>").append(
$("<a>")
.attr("href", v.url)
.html(k)
.click(action)
)
);
} else {
menu_element.append(
$("<li></li>")
.addClass("head")
.append($("<a href='#'></a>").html(k))
);
}
});
};
close_popup( $(window.document) ); // Current frame
close_popup( $(window.top.document) ); // Parent frame
for (var frame_id = window.top.frames.length; frame_id--;) { // Sibling frames
var frame = $(window.top.frames[frame_id].document);
close_popup(frame);
}
}, 50);
var wrapper = $(
"<div class='popmenu-wrapper' style='position: absolute;left: 0; top: -1000;'></div>"
)
.append(menu_element)
.appendTo("body");
return false;
});
var x = e.pageX - wrapper.width() / 2;
x = Math.min(
x,
$(document).scrollLeft() +
$(window).width() -
$(wrapper).width() -
5
);
x = Math.max(x, $(document).scrollLeft() + 5);
}
wrapper.css({
top: e.pageY,
left: x
});
}, 10);
/**
setTimeout(function() {
// Bind click event to current window and all frames to remove any visible menus
// Bind to document object instead of window object for IE compat
var close_popup = function(el) {
$(el).bind("click.close_popup", function() {
$(".popmenu-wrapper").remove();
el.unbind("click.close_popup");
});
};
close_popup($(window.document)); // Current frame
close_popup($(window.top.document)); // Parent frame
for (var frame_id = window.top.frames.length; frame_id--; ) {
// Sibling frames
var frame = $(window.top.frames[frame_id].document);
close_popup(frame);
}
}, 50);
return false;
});
}
/**
* Convert two seperate (often adjacent) divs into galaxy popupmenu
* - div 1 contains a number of anchors which become the menu options
* - div 1 should have a 'popupmenu' attribute
@@ -88,71 +114,69 @@ function make_popupmenu(button_element, initial_options) {
* but (since that selector searches the dom on the page), you can send a parent in
* NOTE: make_popup_menus, and make_popupmenu are horrible names
*/
function make_popup_menus( parent ) {
// find all popupmenu menu divs (divs that contains anchors to be converted to menu options)
// either in the parent or the document if no parent passed
parent = parent || document;
$( parent ).find( "div[popupmenu]" ).each( function() {
var options = {};
var menu = $(this);
function make_popup_menus(parent) {
// find all popupmenu menu divs (divs that contains anchors to be converted to menu options)
// either in the parent or the document if no parent passed
parent = parent || document;
$(parent)
.find("div[popupmenu]")
.each(function() {
var options = {};
var menu = $(this);
// find each anchor in the menu, convert them into an options map: { a.text : click_function }
menu.find( "a" ).each( function() {
var link = $(this),
link_dom = link.get(0),
confirmtext = link_dom.getAttribute( "confirm" ),
href = link_dom.getAttribute( "href" ),
target = link_dom.getAttribute( "target" );
// find each anchor in the menu, convert them into an options map: { a.text : click_function }
menu.find("a").each(function() {
var link = $(this),
link_dom = link.get(0),
confirmtext = link_dom.getAttribute("confirm"),
href = link_dom.getAttribute("href"),
target = link_dom.getAttribute("target");
// no href - no function (gen. a label)
if (!href) {
options[ link.text() ] = null;
} else {
options[ link.text() ] = {
url: href,
action: function( event ) {
// if theres confirm text, send the dialog
if ( !confirmtext || confirm( confirmtext ) ) {
// link.click() doesn't use target for some reason,
// so manually do it here.
if (target) {
window.open(href, target);
return false;
// no href - no function (gen. a label)
if (!href) {
options[link.text()] = null;
} else {
options[link.text()] = {
url: href,
action: function(event) {
// if theres confirm text, send the dialog
if (!confirmtext || confirm(confirmtext)) {
// link.click() doesn't use target for some reason,
// so manually do it here.
if (target) {
window.open(href, target);
return false;
} else {
// For all other links, do the default action.
link.click();
}
} else {
event.preventDefault();
}
}
// For all other links, do the default action.
else {
link.click();
}
} else {
event.preventDefault();
}
};
}
};
}
});
// locate the element with the id corresponding to the menu's popupmenu attr
var box = $( parent ).find( "#" + menu.attr( 'popupmenu' ) );
});
// locate the element with the id corresponding to the menu's popupmenu attr
var box = $(parent).find("#" + menu.attr("popupmenu"));
// For menus with clickable link text, make clicking on the link go through instead
// of activating the popup menu
box.find("a").bind("click", function(e) {
e.stopPropagation(); // Stop bubbling so clicking on the link goes through
return true;
});
// For menus with clickable link text, make clicking on the link go through instead
// of activating the popup menu
box.find("a").bind("click", function(e) {
e.stopPropagation(); // Stop bubbling so clicking on the link goes through
return true;
});
// attach the click events and menu box building to the box element
make_popupmenu(box, options);
box.addClass("popup");
menu.remove();
});
}
// attach the click events and menu box building to the box element
make_popupmenu(box, options);
box.addClass("popup");
menu.remove();
});
}
// ============================================================================
// ============================================================================
return {
make_popupmenu : make_popupmenu,
make_popup_menus : make_popup_menus
make_popupmenu: make_popupmenu,
make_popup_menus: make_popup_menus
};
});
+47 -33
View File
@@ -1,11 +1,11 @@
// This is an extension to jQuery UI draggable
// When dragging move the parent element ("panel") relative to its parent
// ("viewport") so that the draggable is always visible.
// ("viewport") so that the draggable is always visible.
$.ui.plugin.add("draggable", "scrollPanel", {
drag: function(e, ui) {
var instance = $(this).data("draggable");
clearTimeout( instance.timeout );
clearTimeout(instance.timeout);
var o = ui.options,
element = instance.element,
panel = o.panel,
@@ -21,68 +21,82 @@ $.ui.plugin.add("draggable", "scrollPanel", {
close_dist = 5,
nudge = 23,
// Legal panel range
p_min_x = - ( panel_w - viewport_w ),
p_min_y = - ( panel_h - viewport_h ),
p_min_x = -(panel_w - viewport_w),
p_min_y = -(panel_h - viewport_h),
p_max_x = 0,
p_max_y = 0,
// Visible
min_vis_x = - panel_pos.left,
min_vis_x = -panel_pos.left,
max_vis_x = min_vis_x + viewport_w,
min_vis_y = - panel_pos.top,
min_vis_y = -panel_pos.top,
max_vis_y = min_vis_y + viewport_h,
// Mouse
mouse_x = ui.position.left + instance.offset.click.left;
mouse_y = ui.position.top + instance.offset.click.top;
mouse_y = ui.position.top + instance.offset.click.top;
// Move it
if ( ( panel_pos.left < p_max_x ) && ( mouse_x - close_dist < min_vis_x ) ) {
var t = Math.min( nudge, p_max_x - panel_pos.left );
panel.css( "left", panel_pos.left + t );
if (panel_pos.left < p_max_x && mouse_x - close_dist < min_vis_x) {
var t = Math.min(nudge, p_max_x - panel_pos.left);
panel.css("left", panel_pos.left + t);
moved = true;
instance.offset.parent.left += t;
ui.position.left -= t
ui.position.left -= t;
}
if ( ( ! moved ) && ( panel_pos.left > p_min_x ) && ( mouse_x + close_dist > max_vis_x ) ) {
var t = Math.min( nudge, panel_pos.left - p_min_x );
panel.css( "left", panel_pos.left - t );
if (
!moved &&
panel_pos.left > p_min_x &&
mouse_x + close_dist > max_vis_x
) {
var t = Math.min(nudge, panel_pos.left - p_min_x);
panel.css("left", panel_pos.left - t);
moved = true;
instance.offset.parent.left -= t;
ui.position.left += t;
ui.position.left += t;
}
if ( ( ! moved ) && ( panel_pos.top < p_max_y ) && ( mouse_y - close_dist < min_vis_y ) ) {
var t = Math.min( nudge, p_max_y - panel_pos.top );
panel.css( "top", panel_pos.top + t );
if (
!moved &&
panel_pos.top < p_max_y &&
mouse_y - close_dist < min_vis_y
) {
var t = Math.min(nudge, p_max_y - panel_pos.top);
panel.css("top", panel_pos.top + t);
// Firefox sometimes moves by less, so we need to check. Yuck.
var amount_moved = panel.position().top - panel_pos.top;
instance.offset.parent.top += amount_moved;
ui.position.top -= amount_moved;
moved = true;
}
if ( ( ! moved ) && ( panel_pos.top > p_min_y ) && ( mouse_y + close_dist > max_vis_y ) ) {
var t = Math.min( nudge, panel_pos.top - p_min_x );
panel.css( "top", ( panel_pos.top - t ) + "px" );
if (
!moved &&
panel_pos.top > p_min_y &&
mouse_y + close_dist > max_vis_y
) {
var t = Math.min(nudge, panel_pos.top - p_min_x);
panel.css("top", panel_pos.top - t + "px");
// Firefox sometimes moves by less, so we need to check. Yuck.
var amount_moved = panel_pos.top - panel.position().top;
var amount_moved = panel_pos.top - panel.position().top;
instance.offset.parent.top -= amount_moved;
ui.position.top += amount_moved;
moved = true;
}
// Still contain in panel
ui.position.left = Math.max( ui.position.left, 0 );
ui.position.top = Math.max( ui.position.top, 0 );
ui.position.left = Math.min( ui.position.left, panel_w - element_w );
ui.position.top = Math.min( ui.position.top, panel_h - element_h );
ui.position.left = Math.max(ui.position.left, 0);
ui.position.top = Math.max(ui.position.top, 0);
ui.position.left = Math.min(ui.position.left, panel_w - element_w);
ui.position.top = Math.min(ui.position.top, panel_h - element_h);
// Update offsets
if ( moved ) {
$.ui.ddmanager.prepareOffsets( instance, e );
if (moved) {
$.ui.ddmanager.prepareOffsets(instance, e);
}
// Keep moving even if mouse doesn't move
if ( moved ) {
if (moved) {
instance.old_e = e;
instance.timeout = setTimeout( function() { instance.mouseMove( e ) }, 50 );
instance.timeout = setTimeout(function() {
instance.mouseMove(e);
}, 50);
}
},
stop: function( e, ui ) {
stop: function(e, ui) {
var instance = $(this).data("draggable");
clearTimeout( instance.timeout );
clearTimeout(instance.timeout);
}
});
});
+115 -78
View File
@@ -1,7 +1,7 @@
// from: https://raw.githubusercontent.com/umdjs/umd/master/jqueryPlugin.js
// Uses AMD or browser globals to create a jQuery plugin.
(function (factory) {
if (typeof define === 'function' && define.amd) {
(function(factory) {
if (typeof define === "function" && define.amd) {
//TODO: So...this turns out to be an all or nothing thing. If I load jQuery in the define below, it will
// (of course) wipe the old jquery *and all the plugins loaded into it*. So the define below *is still
// relying on jquery being loaded globally* in order to preserve plugins.
@@ -10,9 +10,12 @@
// Browser globals
factory(jQuery);
}
}(function () {
var _l = window._l || function( s ){ return s; };
})(function() {
var _l =
window._l ||
function(s) {
return s;
};
//TODO: consolidate with tool menu functionality, use there
@@ -24,130 +27,164 @@
* of characters, a callback is called. Pressing ESC when the input
* is focused will clear the input and call a separate callback.
*/
function searchInput( parentNode, options ){
var KEYCODE_ESC = 27,
KEYCODE_RETURN = 13,
$parentNode = $( parentNode ),
firstSearch = true,
function searchInput(parentNode, options) {
var KEYCODE_ESC = 27,
KEYCODE_RETURN = 13,
$parentNode = $(parentNode),
firstSearch = true,
defaults = {
initialVal : '',
name : 'search',
placeholder : 'search',
classes : '',
onclear : function(){},
onfirstsearch : null,
onsearch : function( inputVal ){},
minSearchLen : 0,
escWillClear : true,
oninit : function(){}
initialVal: "",
name: "search",
placeholder: "search",
classes: "",
onclear: function() {},
onfirstsearch: null,
onsearch: function(inputVal) {},
minSearchLen: 0,
escWillClear: true,
oninit: function() {}
};
// .................................................................... input rendering and events
// visually clear the search, trigger an event, and call the callback
function clearSearchInput( event ){
var $input = $( this ).parent().children( 'input' );
$input.val( '' ).trigger( 'searchInput.clear' ).blur();
function clearSearchInput(event) {
var $input = $(this)
.parent()
.children("input");
$input
.val("")
.trigger("searchInput.clear")
.blur();
options.onclear();
}
// search for searchTerms, trigger an event, call the appropo callback (based on whether this is the first)
function search( event, searchTerms ){
if( !searchTerms ){
function search(event, searchTerms) {
if (!searchTerms) {
return clearSearchInput();
}
$( this ).trigger( 'search.search', searchTerms );
if( typeof options.onfirstsearch === 'function' && firstSearch ){
$(this).trigger("search.search", searchTerms);
if (typeof options.onfirstsearch === "function" && firstSearch) {
firstSearch = false;
options.onfirstsearch( searchTerms );
options.onfirstsearch(searchTerms);
} else {
options.onsearch( searchTerms );
options.onsearch(searchTerms);
}
}
// .................................................................... input rendering and events
function inputTemplate(){
function inputTemplate() {
// class search-query is bootstrap 2.3 style that now lives in base.less
return [ '<input type="text" name="', options.name, '" placeholder="', options.placeholder, '" ',
'class="search-query ', options.classes, '" ', '/>' ].join( '' );
return [
'<input type="text" name="',
options.name,
'" placeholder="',
options.placeholder,
'" ',
'class="search-query ',
options.classes,
'" ',
"/>"
].join("");
}
// the search input that responds to keyboard events and displays the search value
function $input(){
return $( inputTemplate() )
// select all text on a focus
.focus( function( event ){
$( this ).select();
})
// attach behaviors to esc, return if desired, search on some min len string
.keyup( function( event ){
event.preventDefault();
event.stopPropagation();
function $input() {
return ($(inputTemplate())
// select all text on a focus
.focus(function(event) {
$(this).select();
})
// attach behaviors to esc, return if desired, search on some min len string
.keyup(function(event) {
event.preventDefault();
event.stopPropagation();
// esc key will clear if desired
if( event.which === KEYCODE_ESC && options.escWillClear ){
clearSearchInput.call( this, event );
} else {
var searchTerms = $( this ).val();
// return key or the search string len > minSearchLen (if not 0) triggers search
if( ( event.which === KEYCODE_RETURN )
|| ( options.minSearchLen && searchTerms.length >= options.minSearchLen ) ){
search.call( this, event, searchTerms );
// esc key will clear if desired
if (
event.which === KEYCODE_ESC &&
options.escWillClear
) {
clearSearchInput.call(this, event);
} else {
var searchTerms = $(this).val();
// return key or the search string len > minSearchLen (if not 0) triggers search
if (
event.which === KEYCODE_RETURN ||
(options.minSearchLen &&
searchTerms.length >= options.minSearchLen)
) {
search.call(this, event, searchTerms);
}
}
}
})
.val( options.initialVal );
})
.val(options.initialVal) );
}
// .................................................................... clear button rendering and events
// a button for clearing the search bar, placed on the right hand side
function $clearBtn(){
return $([ '<span class="search-clear fa fa-times-circle" ',
'title="', _l( 'clear search (esc)' ), '"></span>' ].join('') )
.tooltip({ placement: 'bottom' })
.click( function( event ){
clearSearchInput.call( this, event );
});
function $clearBtn() {
return $(
[
'<span class="search-clear fa fa-times-circle" ',
'title="',
_l("clear search (esc)"),
'"></span>'
].join("")
)
.tooltip({ placement: "bottom" })
.click(function(event) {
clearSearchInput.call(this, event);
});
}
// .................................................................... loadingIndicator rendering
// a button for clearing the search bar, placed on the right hand side
function $loadingIndicator(){
return $([ '<span class="search-loading fa fa-spinner fa-spin" ',
'title="', _l( 'loading...' ), '"></span>' ].join('') )
.hide().tooltip({ placement: 'bottom' });
function $loadingIndicator() {
return $(
[
'<span class="search-loading fa fa-spinner fa-spin" ',
'title="',
_l("loading..."),
'"></span>'
].join("")
)
.hide()
.tooltip({ placement: "bottom" });
}
// .................................................................... commands
// visually swap the load, clear buttons
function toggleLoadingIndicator(){
$parentNode.find( '.search-loading' ).toggle();
$parentNode.find( '.search-clear' ).toggle();
function toggleLoadingIndicator() {
$parentNode.find(".search-loading").toggle();
$parentNode.find(".search-clear").toggle();
}
// .................................................................... init
// string command (not constructor)
if( jQuery.type( options ) === 'string' ){
if( options === 'toggle-loading' ){
if (jQuery.type(options) === "string") {
if (options === "toggle-loading") {
toggleLoadingIndicator();
}
return $parentNode;
}
// initial render
if( jQuery.type( options ) === 'object' ){
options = jQuery.extend( true, {}, defaults, options );
if (jQuery.type(options) === "object") {
options = jQuery.extend(true, {}, defaults, options);
}
//NOTE: prepended
return $parentNode.addClass( 'search-input' ).prepend([ $input(), $clearBtn(), $loadingIndicator() ]);
return $parentNode
.addClass("search-input")
.prepend([$input(), $clearBtn(), $loadingIndicator()]);
}
// as jq plugin
jQuery.fn.extend({
searchInput : function $searchInput( options ){
return this.each( function(){
return searchInput( this, options );
searchInput: function $searchInput(options) {
return this.each(function() {
return searchInput(this, options);
});
}
});
}));
});
+38 -35
View File
@@ -1,42 +1,45 @@
define([
], function(){
//==============================================================================
var LOGGING_FNS = [ 'log', 'debug', 'info', 'warn', 'error', 'metric' ];
/** adds logging functions to an obj.prototype (or obj directly) adding a namespace for filtering
define([], function() {
//==============================================================================
var LOGGING_FNS = ["log", "debug", "info", "warn", "error", "metric"];
/** adds logging functions to an obj.prototype (or obj directly) adding a namespace for filtering
* @param {Object} obj
* @param {String} namespace
*/
function addLogging( obj, namespace ){
var addTo = ( obj.prototype !== undefined )?( obj.prototype ):( obj );
if( namespace !== undefined ){
addTo._logNamespace = namespace;
}
//yagni?: without this, may not capture Galaxy.config.debug and add Galaxy.logger properly
// if( window.Galaxy && window.Galaxy.config && window.Galaxy.config.debug ){
// addTo.logger = window.Galaxy.logger;
// }
function addLogging(obj, namespace) {
var addTo = obj.prototype !== undefined ? obj.prototype : obj;
if (namespace !== undefined) {
addTo._logNamespace = namespace;
}
//yagni?: without this, may not capture Galaxy.config.debug and add Galaxy.logger properly
// if( window.Galaxy && window.Galaxy.config && window.Galaxy.config.debug ){
// addTo.logger = window.Galaxy.logger;
// }
// give the object each
LOGGING_FNS.forEach( function( logFn ){
addTo[ logFn ] = function(){
if( !this.logger ){
// give the object each
LOGGING_FNS.forEach(function(logFn) {
addTo[logFn] = function() {
if (!this.logger) {
return undefined;
}
if (this.logger.emit) {
return this.logger.emit(
logFn,
this._logNamespace,
arguments
);
}
if (this.logger[logFn]) {
//TODO:! there has to be a way to get the lineno/file into this
// http://stackoverflow.com/questions/13815640/a-proper-wrapper-for-console-log-with-correct-line-number
// http://www.paulirish.com/2009/log-a-lightweight-wrapper-for-consolelog/
return this.logger[logFn].apply(this.logger, arguments);
}
return undefined;
}
if( this.logger.emit ){
return this.logger.emit( logFn, this._logNamespace, arguments );
}
if( this.logger[ logFn ] ){
//TODO:! there has to be a way to get the lineno/file into this
// http://stackoverflow.com/questions/13815640/a-proper-wrapper-for-console-log-with-correct-line-number
// http://www.paulirish.com/2009/log-a-lightweight-wrapper-for-consolelog/
return this.logger[ logFn ].apply( this.logger, arguments );
}
return undefined;
};
});
return obj;
}
};
});
return obj;
}
//==============================================================================
return addLogging;
//==============================================================================
return addLogging;
});
+146 -142
View File
@@ -1,8 +1,7 @@
define([
], function(){
//ASSUMES: jquery
//=============================================================================
/** @class AjaxQueue
define([], function() {
//ASSUMES: jquery
//=============================================================================
/** @class AjaxQueue
* Class that allows queueing functions that return jQuery promises (such
* as ajax calls). Each function waits for the previous to complete before
* being called
@@ -10,118 +9,121 @@ define([
* @constructor accepts a list of functions and automatically begins
* processing them
*/
function AjaxQueue( initialFunctions ){
//TODO: possibly rename to DeferredQueue
var self = this;
/** the main deferred for the entire queue - note: also sends notifications of progress */
self.deferred = jQuery.Deferred();
/** the queue array of functions */
self.queue = [];
/** cache the response from each deferred call - error or success */
self.responses = [];
/** total number of fn's to process */
self.numToProcess = 0;
/** is the queue processing/waiting for any calls to return? */
self.running = false;
function AjaxQueue(initialFunctions) {
//TODO: possibly rename to DeferredQueue
var self = this;
/** the main deferred for the entire queue - note: also sends notifications of progress */
self.deferred = jQuery.Deferred();
/** the queue array of functions */
self.queue = [];
/** cache the response from each deferred call - error or success */
self.responses = [];
/** total number of fn's to process */
self.numToProcess = 0;
/** is the queue processing/waiting for any calls to return? */
self.running = false;
self.init( initialFunctions || [] );
self.start();
self.init(initialFunctions || []);
self.start();
return self;
}
/** add all fns in initialFunctions (if any) to the queue */
AjaxQueue.prototype.init = function init( initialFunctions ){
var self = this;
initialFunctions.forEach( function( fn ){
self.add( fn );
});
};
/** add a fn to the queue */
AjaxQueue.prototype.add = function add( fn ){
//console.debug( 'AjaxQueue.prototype.add:', fn );
var self = this,
index = this.queue.length;
this.numToProcess += 1;
this.queue.push( function(){
var fnIndex = index,
xhr = fn();
// if successful, notify using the deferred to allow tracking progress
xhr.done( function( response ){
self.deferred.notify({ curr: fnIndex, total: self.numToProcess, response: response });
});
// (regardless of previous error or success) if not last ajax call, shift and call the next
// if last fn, resolve deferred
xhr.always( function( response ){
self.responses.push( response );
if( self.queue.length ){
self.queue.shift()();
} else {
self.stop();
}
});
});
return this;
};
/** start processing the queue */
AjaxQueue.prototype.start = function start(){
if( this.queue.length ){
this.running = true;
this.queue.shift()();
return self;
}
return this;
};
/** stop the queue
/** add all fns in initialFunctions (if any) to the queue */
AjaxQueue.prototype.init = function init(initialFunctions) {
var self = this;
initialFunctions.forEach(function(fn) {
self.add(fn);
});
};
/** add a fn to the queue */
AjaxQueue.prototype.add = function add(fn) {
//console.debug( 'AjaxQueue.prototype.add:', fn );
var self = this,
index = this.queue.length;
this.numToProcess += 1;
this.queue.push(function() {
var fnIndex = index,
xhr = fn();
// if successful, notify using the deferred to allow tracking progress
xhr.done(function(response) {
self.deferred.notify({
curr: fnIndex,
total: self.numToProcess,
response: response
});
});
// (regardless of previous error or success) if not last ajax call, shift and call the next
// if last fn, resolve deferred
xhr.always(function(response) {
self.responses.push(response);
if (self.queue.length) {
self.queue.shift()();
} else {
self.stop();
}
});
});
return this;
};
/** start processing the queue */
AjaxQueue.prototype.start = function start() {
if (this.queue.length) {
this.running = true;
this.queue.shift()();
}
return this;
};
/** stop the queue
* @param {boolean} causeFail cause an error/fail on the main deferred
* @param {String} msg message to send when rejecting the main deferred
*/
AjaxQueue.prototype.stop = function stop( causeFail, msg ){
//TODO: doesn't abort current call
this.running = false;
this.queue = [];
if( causeFail ){
//TODO: spliced args instead
this.deferred.reject( msg );
} else {
this.deferred.resolve( this.responses );
}
this.numToProcess = 0;
this.deferred = jQuery.Deferred();
return this;
};
AjaxQueue.prototype.stop = function stop(causeFail, msg) {
//TODO: doesn't abort current call
this.running = false;
this.queue = [];
if (causeFail) {
//TODO: spliced args instead
this.deferred.reject(msg);
} else {
this.deferred.resolve(this.responses);
}
this.numToProcess = 0;
this.deferred = jQuery.Deferred();
return this;
};
// only a handful of the deferred interface for now - possible YAGNI
/** implement done from the jq deferred interface */
AjaxQueue.prototype.done = function done( fn ){
return this.deferred.done( fn );
};
/** implement fail from the jq deferred interface */
AjaxQueue.prototype.fail = function fail( fn ){
return this.deferred.fail( fn );
};
/** implement always from the jq deferred interface */
AjaxQueue.prototype.always = function always( fn ){
return this.deferred.always( fn );
};
/** implement progress from the jq deferred interface */
AjaxQueue.prototype.progress = function progress( fn ){
return this.deferred.progress( fn );
};
// only a handful of the deferred interface for now - possible YAGNI
/** implement done from the jq deferred interface */
AjaxQueue.prototype.done = function done(fn) {
return this.deferred.done(fn);
};
/** implement fail from the jq deferred interface */
AjaxQueue.prototype.fail = function fail(fn) {
return this.deferred.fail(fn);
};
/** implement always from the jq deferred interface */
AjaxQueue.prototype.always = function always(fn) {
return this.deferred.always(fn);
};
/** implement progress from the jq deferred interface */
AjaxQueue.prototype.progress = function progress(fn) {
return this.deferred.progress(fn);
};
/** shortcut constructor / fire and forget
/** shortcut constructor / fire and forget
* @returns {Deferred} the queue's main deferred
*/
AjaxQueue.create = function create( initialFunctions ){
return new AjaxQueue( initialFunctions ).deferred;
};
AjaxQueue.create = function create(initialFunctions) {
return new AjaxQueue(initialFunctions).deferred;
};
//=============================================================================
/** @class NamedAjaxQueue
//=============================================================================
/** @class NamedAjaxQueue
* @augments AjaxQueue
* Allows associating a name with a deferring fn and prevents adding deferring
* fns if the name has already been used. Useful to prevent build up of duplicate
@@ -131,49 +133,51 @@ AjaxQueue.create = function create( initialFunctions ){
* { name: some unique id,
* fn: the deferring fn or ajax call }
*/
function NamedAjaxQueue( initialFunctions ){
var self = this;
self.names = {};
AjaxQueue.call( this, initialFunctions );
return self;
}
NamedAjaxQueue.prototype = new AjaxQueue();
NamedAjaxQueue.prototype.constructor = NamedAjaxQueue;
/** add the obj.fn to the queue if obj.name hasn't been used before */
NamedAjaxQueue.prototype.add = function add( obj ){
//console.debug( 'NamedAjaxQueue.adding:', obj )
//console.debug( 'NamedAjaxQueue.prototype.add:', obj );
if( !( obj.hasOwnProperty( 'name' ) && obj.hasOwnProperty( 'fn' ) ) ){
throw new Error( 'NamedAjaxQueue.add requires an object with both "name" and "fn": ' + JSON.stringify( obj ) );
function NamedAjaxQueue(initialFunctions) {
var self = this;
self.names = {};
AjaxQueue.call(this, initialFunctions);
return self;
}
if( this.names.hasOwnProperty( obj.name ) ){
//console.warn( 'name has been used:', obj.name );
return;
}
this.names[ obj.name ] = true;
//console.debug( '\t names: ', this.names )
return AjaxQueue.prototype.add.call( this, obj.fn );
//console.debug( '\t queue: ', this.queue.length );
};
NamedAjaxQueue.prototype = new AjaxQueue();
NamedAjaxQueue.prototype.constructor = NamedAjaxQueue;
/** override to remove names */
NamedAjaxQueue.prototype.clear = function clear(){
this.names = {};
return this;
};
/** add the obj.fn to the queue if obj.name hasn't been used before */
NamedAjaxQueue.prototype.add = function add(obj) {
//console.debug( 'NamedAjaxQueue.adding:', obj )
//console.debug( 'NamedAjaxQueue.prototype.add:', obj );
if (!(obj.hasOwnProperty("name") && obj.hasOwnProperty("fn"))) {
throw new Error(
'NamedAjaxQueue.add requires an object with both "name" and "fn": ' +
JSON.stringify(obj)
);
}
if (this.names.hasOwnProperty(obj.name)) {
//console.warn( 'name has been used:', obj.name );
return;
}
this.names[obj.name] = true;
//console.debug( '\t names: ', this.names )
return AjaxQueue.prototype.add.call(this, obj.fn);
//console.debug( '\t queue: ', this.queue.length );
};
/** shortcut constructor / fire and forget
/** override to remove names */
NamedAjaxQueue.prototype.clear = function clear() {
this.names = {};
return this;
};
/** shortcut constructor / fire and forget
* @returns {Deferred} the queue's main deferred
*/
NamedAjaxQueue.create = function create( initialFunctions ){
return new NamedAjaxQueue( initialFunctions ).deferred;
};
NamedAjaxQueue.create = function create(initialFunctions) {
return new NamedAjaxQueue(initialFunctions).deferred;
};
//=============================================================================
//=============================================================================
return {
AjaxQueue : AjaxQueue,
NamedAjaxQueue : NamedAjaxQueue
AjaxQueue: AjaxQueue,
NamedAjaxQueue: NamedAjaxQueue
};
});
+94 -77
View File
@@ -1,89 +1,106 @@
define([ 'jquery' ], function( jQuery ){
'use_strict';
define(["jquery"], function(jQuery) {
"use_strict";
var $ = jQuery;
// ============================================================================
/**
var $ = jQuery;
// ============================================================================
/**
* Edit and save text asynchronously.
*/
function async_save_text( click_to_edit_elt, text_elt_id, save_url,
text_parm_name, num_cols, use_textarea, num_rows, on_start, on_finish ) {
// Set defaults if necessary.
if (num_cols === undefined) {
num_cols = 30;
}
if (num_rows === undefined) {
num_rows = 4;
}
// Set up input element.
$("#" + click_to_edit_elt).click(function() {
// Check if this is already active
if ( $("#renaming-active").length > 0) {
return;
function async_save_text(
click_to_edit_elt,
text_elt_id,
save_url,
text_parm_name,
num_cols,
use_textarea,
num_rows,
on_start,
on_finish
) {
// Set defaults if necessary.
if (num_cols === undefined) {
num_cols = 30;
}
var text_elt = $("#" + text_elt_id),
old_text = text_elt.text(),
t;
if (use_textarea) {
t = $("<textarea></textarea>").attr({ rows: num_rows, cols: num_cols }).text( $.trim(old_text) );
} else {
t = $("<input type='text'></input>").attr({ value: $.trim(old_text), size: num_cols });
if (num_rows === undefined) {
num_rows = 4;
}
t.attr("id", "renaming-active");
t.blur( function() {
$(this).remove();
text_elt.show();
if (on_finish) {
on_finish(t);
// Set up input element.
$("#" + click_to_edit_elt).click(function() {
// Check if this is already active
if ($("#renaming-active").length > 0) {
return;
}
});
t.keyup( function( e ) {
if ( e.keyCode === 27 ) {
// Escape key
$(this).trigger( "blur" );
} else if ( e.keyCode === 13 ) {
// Enter key submits
var ajax_data = {};
ajax_data[text_parm_name] = $(this).val();
$(this).trigger( "blur" );
$.ajax({
url: save_url,
data: ajax_data,
error: function() {
alert( "Text editing for elt " + text_elt_id + " failed" );
// TODO: call finish or no? For now, let's not because error occurred.
},
success: function(processed_text) {
// Set new text and call finish method.
if (processed_text !== "") {
text_elt.text(processed_text);
} else {
text_elt.html("<em>None</em>");
}
if (on_finish) {
on_finish(t);
}
}
var text_elt = $("#" + text_elt_id),
old_text = text_elt.text(),
t;
if (use_textarea) {
t = $("<textarea></textarea>")
.attr({ rows: num_rows, cols: num_cols })
.text($.trim(old_text));
} else {
t = $("<input type='text'></input>").attr({
value: $.trim(old_text),
size: num_cols
});
}
t.attr("id", "renaming-active");
t.blur(function() {
$(this).remove();
text_elt.show();
if (on_finish) {
on_finish(t);
}
});
t.keyup(function(e) {
if (e.keyCode === 27) {
// Escape key
$(this).trigger("blur");
} else if (e.keyCode === 13) {
// Enter key submits
var ajax_data = {};
ajax_data[text_parm_name] = $(this).val();
$(this).trigger("blur");
$.ajax({
url: save_url,
data: ajax_data,
error: function() {
alert(
"Text editing for elt " +
text_elt_id +
" failed"
);
// TODO: call finish or no? For now, let's not because error occurred.
},
success: function(processed_text) {
// Set new text and call finish method.
if (processed_text !== "") {
text_elt.text(processed_text);
} else {
text_elt.html("<em>None</em>");
}
if (on_finish) {
on_finish(t);
}
}
});
}
});
if (on_start) {
on_start(t);
}
// Replace text with input object and focus & select.
text_elt.hide();
t.insertAfter(text_elt);
t.focus();
t.select();
return;
});
}
if (on_start) {
on_start(t);
}
// Replace text with input object and focus & select.
text_elt.hide();
t.insertAfter(text_elt);
t.focus();
t.select();
return;
});
}
// ============================================================================
// ============================================================================
return async_save_text;
});
+393 -253
View File
@@ -1,308 +1,448 @@
define(['libs/underscore', 'viz/trackster/util', 'utils/config'], function(_, util_mod, config_mod) {
/**
define(["libs/underscore", "viz/trackster/util", "utils/config"], function(
_,
util_mod,
config_mod
) {
/**
* A configuration setting. Currently key is used as id.
*/
var ConfigSetting = Backbone.Model.extend({
var ConfigSetting = Backbone.Model.extend({
initialize: function(options) {
// Use key as id for now.
var key = this.get("key");
this.set("id", key);
initialize: function(options) {
// Use key as id for now.
var key = this.get('key');
this.set('id', key);
// Set defaults based on key.
var defaults = _.find([
{ key: 'name', label: 'Name', type: 'text', default_value: '' },
{ key: 'color', label: 'Color', type: 'color', default_value: null },
{ key: 'min_value', label: 'Min Value', type: 'float', default_value: null },
{ key: 'max_value', label: 'Max Value', type: 'float', default_value: null },
{ key: 'mode', type: 'string', default_value: this.mode, hidden: true },
{ key: 'height', type: 'int', default_value: 32, hidden: true },
{ key: 'pos_color', label: 'Positive Color', type: 'color', default_value: "#FF8C00" },
{ key: 'neg_color', label: 'Negative Color', type: 'color', default_value: "#4169E1" },
{ key: 'block_color', label: 'Block color', type: 'color', default_value: null },
{ key: 'label_color', label: 'Label color', type: 'color', default_value: 'black' },
{ key: 'show_insertions', label: 'Show insertions', type: 'bool', default_value: false },
{ key: 'show_counts', label: 'Show summary counts', type: 'bool', default_value: true },
{ key: 'reverse_strand_color', label: 'Antisense strand color', type: 'color', default_value: null },
{ key: 'show_differences', label: 'Show differences only', type: 'bool', default_value: true }
], function(s) { return s.key === key; });
if (defaults) {
this.set(_.extend({}, defaults, options));
}
if (this.get('value') === undefined && this.get('default_value') !== undefined) {
// Use default to set value (if present).
this.set_value(this.get('default_value'));
// If no default value for color config, set random color.
if (!this.get('value') && this.get('type') === 'color') {
// For color setting, set random color.
this.set('value', util_mod.get_random_color());
// Set defaults based on key.
var defaults = _.find(
[
{
key: "name",
label: "Name",
type: "text",
default_value: ""
},
{
key: "color",
label: "Color",
type: "color",
default_value: null
},
{
key: "min_value",
label: "Min Value",
type: "float",
default_value: null
},
{
key: "max_value",
label: "Max Value",
type: "float",
default_value: null
},
{
key: "mode",
type: "string",
default_value: this.mode,
hidden: true
},
{
key: "height",
type: "int",
default_value: 32,
hidden: true
},
{
key: "pos_color",
label: "Positive Color",
type: "color",
default_value: "#FF8C00"
},
{
key: "neg_color",
label: "Negative Color",
type: "color",
default_value: "#4169E1"
},
{
key: "block_color",
label: "Block color",
type: "color",
default_value: null
},
{
key: "label_color",
label: "Label color",
type: "color",
default_value: "black"
},
{
key: "show_insertions",
label: "Show insertions",
type: "bool",
default_value: false
},
{
key: "show_counts",
label: "Show summary counts",
type: "bool",
default_value: true
},
{
key: "reverse_strand_color",
label: "Antisense strand color",
type: "color",
default_value: null
},
{
key: "show_differences",
label: "Show differences only",
type: "bool",
default_value: true
}
],
function(s) {
return s.key === key;
}
);
if (defaults) {
this.set(_.extend({}, defaults, options));
}
}
},
/**
if (
this.get("value") === undefined &&
this.get("default_value") !== undefined
) {
// Use default to set value (if present).
this.set_value(this.get("default_value"));
// If no default value for color config, set random color.
if (!this.get("value") && this.get("type") === "color") {
// For color setting, set random color.
this.set("value", util_mod.get_random_color());
}
}
},
/**
* Cast and set value. This should be instead of
* setting.set('value', new_value)
*/
set_value: function(value, options) {
var type = this.get('type');
set_value: function(value, options) {
var type = this.get("type");
if (type === 'float') {
value = parseFloat(value);
if (type === "float") {
value = parseFloat(value);
} else if (type === "int") {
value = parseInt(value, 10);
}
// TODO: handle casting from string to bool?
this.set({ value: value }, options);
}
else if (type === 'int') {
value = parseInt(value, 10);
}
// TODO: handle casting from string to bool?
this.set({value: value}, options);
}
});
/**
* Collection of config settings.
*/
var ConfigSettingCollection = Backbone.Collection.extend({
model: ConfigSetting,
});
/**
* Collection of config settings.
*/
var ConfigSettingCollection = Backbone.Collection.extend(
{
model: ConfigSetting,
/**
* Save settings as a dictionary of key-value pairs.
* This function is needed for backwards compatibility.
*/
to_key_value_dict: function() {
var rval = {};
this.each(function(setting) {
rval[setting.get('key')] = setting.get('value');
});
to_key_value_dict: function() {
var rval = {};
this.each(function(setting) {
rval[setting.get("key")] = setting.get("value");
});
return rval;
},
return rval;
},
/**
/**
* Returns value for a given key. Returns undefined if there is no setting with the specified key.
*/
get_value: function(key) {
var s = this.get(key);
if (s) {
return s.get('value');
}
get_value: function(key) {
var s = this.get(key);
if (s) {
return s.get("value");
}
return undefined;
},
return undefined;
},
/**
/**
* Set value for a setting.
*/
set_value: function(key, value, options) {
var s = this.get(key);
if (s) {
return s.set_value(value, options);
}
set_value: function(key, value, options) {
var s = this.get(key);
if (s) {
return s.set_value(value, options);
}
return undefined;
},
return undefined;
},
/**
/**
* Set default value for a setting.
*/
set_default_value: function(key, default_value) {
var s = this.get(key);
if (s) {
return s.set('default_value', default_value);
}
set_default_value: function(key, default_value) {
var s = this.get(key);
if (s) {
return s.set("default_value", default_value);
}
return undefined;
}
},
{
/**
return undefined;
}
},
{
/**
* Utility function that creates a ConfigSettingsCollection from a set of models
* and a saved_values dictionary.
*/
from_models_and_saved_values: function(models, saved_values) {
// If there are saved values, copy models and update with saved values.
if (saved_values) {
models = _.map(models, function(m) {
return _.extend({}, m, { value: saved_values[m.key] });
});
from_models_and_saved_values: function(models, saved_values) {
// If there are saved values, copy models and update with saved values.
if (saved_values) {
models = _.map(models, function(m) {
return _.extend({}, m, { value: saved_values[m.key] });
});
}
return new ConfigSettingCollection(models);
}
}
);
return new ConfigSettingCollection(models);
}
});
/**
/**
* Viewer for config settings collection.
*/
var ConfigSettingCollectionView = Backbone.View.extend({
className: 'config-settings-view',
var ConfigSettingCollectionView = Backbone.View.extend({
className: "config-settings-view",
/**
/**
* Renders form for editing configuration settings.
*/
render: function() {
var container = this.$el;
render: function() {
var container = this.$el;
this.collection.each(function(param, index) {
// Hidden params have no representation in the form
if (param.get('hidden')) { return; }
this.collection.each(function(param, index) {
// Hidden params have no representation in the form
if (param.get("hidden")) {
return;
}
// Build row for param.
var id = 'param_' + index,
type = param.get('type'),
value = param.get('value');
var row = $("<div class='form-row' />").appendTo(container);
row.append($('<label />').attr("for", id ).text(param.get('label') + ":" ));
// Draw parameter as checkbox
if ( type === 'bool' ) {
row.append( $('<input type="checkbox" />').attr("id", id ).attr("name", id ).attr( 'checked', value ) );
}
// Draw parameter as textbox
else if ( type === 'text' ) {
row.append( $('<input type="text"/>').attr("id", id ).val(value).click( function() { $(this).select(); }));
}
// Draw parameter as select area
else if ( type === 'select' ) {
var select = $('<select />').attr("id", id);
_.each(param.get('options'), function(option) {
$("<option/>").text( option.label ).attr( "value", option.value ).appendTo( select );
});
select.val( value );
row.append( select );
}
// Draw parameter as color picker
else if ( type === 'color' ) {
var
container_div = $("<div/>").appendTo(row),
input = $('<input />').attr("id", id ).attr("name", id ).val( value ).css("float", "left")
.appendTo(container_div).click(function(e) {
// Hide other pickers.
$(".tooltip").removeClass( "in" );
// Show input's color picker.
var tip = $(this).siblings(".tooltip").addClass( "in" );
tip.css( {
// left: $(this).position().left + ( $(input).width() / 2 ) - 60,
// top: $(this).position().top + $(this.height)
left: $(this).position().left + $(this).width() + 5,
top: $(this).position().top - ( $(tip).height() / 2 ) + ( $(this).height() / 2 )
} ).show();
// Click management:
// Keep showing tip if clicking in tip.
tip.click(function(e) {
e.stopPropagation();
});
// Hide tip if clicking outside of tip.
$(document).bind( "click.color-picker", function() {
tip.hide();
$(document).unbind( "click.color-picker" );
});
// No propagation to avoid triggering document click (and tip hiding) above.
e.stopPropagation();
}),
// Icon for setting a new random color; behavior set below.
new_color_icon = $("<a href='javascript:void(0)'/>").addClass("icon-button arrow-circle").appendTo(container_div)
.attr("title", "Set new random color").tooltip(),
// Color picker in tool tip style.
tip = $( "<div class='tooltip right' style='position: absolute;' />" ).appendTo(container_div).hide(),
// Inner div for padding purposes
tip_inner = $("<div class='tooltip-inner' style='text-align: inherit'></div>").appendTo(tip),
tip_arrow = $("<div class='tooltip-arrow'></div>").appendTo(tip),
farb_obj = $.farbtastic(tip_inner, { width: 100, height: 100, callback: input, color: value });
// Clear floating.
container_div.append( $("<div/>").css("clear", "both"));
// Use function to fix farb_obj value.
(function(fixed_farb_obj) {
new_color_icon.click(function() {
fixed_farb_obj.setColor(util_mod.get_random_color());
// Build row for param.
var id = "param_" + index,
type = param.get("type"),
value = param.get("value");
var row = $("<div class='form-row' />").appendTo(container);
row.append(
$("<label />")
.attr("for", id)
.text(param.get("label") + ":")
);
// Draw parameter as checkbox
if (type === "bool") {
row.append(
$('<input type="checkbox" />')
.attr("id", id)
.attr("name", id)
.attr("checked", value)
);
} else if (type === "text") {
// Draw parameter as textbox
row.append(
$('<input type="text"/>')
.attr("id", id)
.val(value)
.click(function() {
$(this).select();
})
);
} else if (type === "select") {
// Draw parameter as select area
var select = $("<select />").attr("id", id);
_.each(param.get("options"), function(option) {
$("<option/>")
.text(option.label)
.attr("value", option.value)
.appendTo(select);
});
})(farb_obj);
select.val(value);
row.append(select);
} else if (type === "color") {
// Draw parameter as color picker
var container_div = $("<div/>").appendTo(row),
input = $("<input />")
.attr("id", id)
.attr("name", id)
.val(value)
.css("float", "left")
.appendTo(container_div)
.click(function(e) {
// Hide other pickers.
$(".tooltip").removeClass("in");
}
else {
row.append( $('<input />').attr("id", id ).attr("name", id ).val( value ) );
}
// Help text
if ( param.help ) {
row.append( $("<div class='help'/>").text( param.help ) );
}
});
// Show input's color picker.
var tip = $(this)
.siblings(".tooltip")
.addClass("in");
tip
.css({
// left: $(this).position().left + ( $(input).width() / 2 ) - 60,
// top: $(this).position().top + $(this.height)
left:
$(this).position().left +
$(this).width() +
5,
top:
$(this).position().top -
$(tip).height() / 2 +
$(this).height() / 2
})
.show();
return this;
},
// Click management:
/**
// Keep showing tip if clicking in tip.
tip.click(function(e) {
e.stopPropagation();
});
// Hide tip if clicking outside of tip.
$(document).bind(
"click.color-picker",
function() {
tip.hide();
$(document).unbind(
"click.color-picker"
);
}
);
// No propagation to avoid triggering document click (and tip hiding) above.
e.stopPropagation();
}),
// Icon for setting a new random color; behavior set below.
new_color_icon = $("<a href='javascript:void(0)'/>")
.addClass("icon-button arrow-circle")
.appendTo(container_div)
.attr("title", "Set new random color")
.tooltip(),
// Color picker in tool tip style.
tip = $(
"<div class='tooltip right' style='position: absolute;' />"
)
.appendTo(container_div)
.hide(),
// Inner div for padding purposes
tip_inner = $(
"<div class='tooltip-inner' style='text-align: inherit'></div>"
).appendTo(tip),
tip_arrow = $(
"<div class='tooltip-arrow'></div>"
).appendTo(tip),
farb_obj = $.farbtastic(tip_inner, {
width: 100,
height: 100,
callback: input,
color: value
});
// Clear floating.
container_div.append($("<div/>").css("clear", "both"));
// Use function to fix farb_obj value.
(function(fixed_farb_obj) {
new_color_icon.click(function() {
fixed_farb_obj.setColor(
util_mod.get_random_color()
);
});
})(farb_obj);
} else {
row.append(
$("<input />")
.attr("id", id)
.attr("name", id)
.val(value)
);
}
// Help text
if (param.help) {
row.append($("<div class='help'/>").text(param.help));
}
});
return this;
},
/**
* Render view in modal.
*/
render_in_modal: function(title) {
// Set up handlers for cancel, ok button and for handling esc key.
var self = this,
cancel_fn = function() { Galaxy.modal.hide(); $(window).unbind("keypress.check_enter_esc"); },
ok_fn = function() {
Galaxy.modal.hide();
$(window).unbind("keypress.check_enter_esc");
self.update_from_form();
},
check_enter_esc = function(e) {
if ((e.keyCode || e.which) === 27) { // Escape key
cancel_fn();
} else if ((e.keyCode || e.which) === 13) { // Enter key
ok_fn();
}
};
render_in_modal: function(title) {
// Set up handlers for cancel, ok button and for handling esc key.
var self = this,
cancel_fn = function() {
Galaxy.modal.hide();
$(window).unbind("keypress.check_enter_esc");
},
ok_fn = function() {
Galaxy.modal.hide();
$(window).unbind("keypress.check_enter_esc");
self.update_from_form();
},
check_enter_esc = function(e) {
if ((e.keyCode || e.which) === 27) {
// Escape key
cancel_fn();
} else if ((e.keyCode || e.which) === 13) {
// Enter key
ok_fn();
}
};
// Set keypress handler.
$(window).bind("keypress.check_enter_esc", check_enter_esc);
// Set keypress handler.
$(window).bind("keypress.check_enter_esc", check_enter_esc);
// Show modal.
if (this.$el.children().length === 0) {
this.render();
}
Galaxy.modal.show({
title: title || "Configure",
body: this.$el,
buttons: {
"Cancel": cancel_fn,
"OK": ok_fn
// Show modal.
if (this.$el.children().length === 0) {
this.render();
}
});
},
Galaxy.modal.show({
title: title || "Configure",
body: this.$el,
buttons: {
Cancel: cancel_fn,
OK: ok_fn
}
});
},
/**
/**
* Update settings with new values entered via form.
*/
update_from_form: function() {
var self = this;
this.collection.each(function(setting, index) {
if ( !setting.get('hidden') ) {
// Set value from view.
var id = 'param_' + index;
var value = self.$el.find( '#' + id ).val();
if ( setting.get('type') === 'bool' ) {
value = self.$el.find( '#' + id ).is( ':checked' );
update_from_form: function() {
var self = this;
this.collection.each(function(setting, index) {
if (!setting.get("hidden")) {
// Set value from view.
var id = "param_" + index;
var value = self.$el.find("#" + id).val();
if (setting.get("type") === "bool") {
value = self.$el.find("#" + id).is(":checked");
}
setting.set_value(value);
}
setting.set_value(value);
}
});
}
});
return {
ConfigSetting: ConfigSetting,
ConfigSettingCollection: ConfigSettingCollection,
ConfigSettingCollectionView: ConfigSettingCollectionView
};
});
}
});
return {
ConfigSetting: ConfigSetting,
ConfigSettingCollection: ConfigSettingCollection,
ConfigSettingCollectionView: ConfigSettingCollectionView
};
});
+56 -44
View File
@@ -1,58 +1,70 @@
/**
* This class defines a queue to ensure that multiple deferred callbacks are executed sequentially.
*/
define(['utils/utils'], function( Utils ) {
return Backbone.Model.extend({
initialize: function(){
this.active = {};
this.last = null;
},
define(["utils/utils"], function(Utils) {
return Backbone.Model.extend({
initialize: function() {
this.active = {};
this.last = null;
},
/** Adds a callback to the queue. Upon execution a deferred object is parsed to the callback i.e. callback( deferred ).
/** Adds a callback to the queue. Upon execution a deferred object is parsed to the callback i.e. callback( deferred ).
* If the callback does not take any arguments, the deferred is resolved instantly.
*/
execute: function( callback ) {
var self = this;
var id = Utils.uid();
var has_deferred = callback.length > 0;
execute: function(callback) {
var self = this;
var id = Utils.uid();
var has_deferred = callback.length > 0;
// register process
this.active[ id ] = true;
// register process
this.active[id] = true;
// deferred process
var process = $.Deferred();
process.promise().always(function() {
delete self.active[ id ];
has_deferred && Galaxy.emit.debug( 'deferred::execute()', this.state().charAt(0).toUpperCase() + this.state().slice(1) + ' ' + id );
});
// deferred process
var process = $.Deferred();
process.promise().always(function() {
delete self.active[id];
has_deferred &&
Galaxy.emit.debug(
"deferred::execute()",
this.state()
.charAt(0)
.toUpperCase() +
this.state().slice(1) +
" " +
id
);
});
// deferred queue
$.when( this.last ).always(function() {
if ( self.active[ id ] ) {
has_deferred && Galaxy.emit.debug( 'deferred::execute()', 'Running ' + id );
callback( process );
!has_deferred && process.resolve();
} else {
process.reject();
// deferred queue
$.when(this.last).always(function() {
if (self.active[id]) {
has_deferred &&
Galaxy.emit.debug(
"deferred::execute()",
"Running " + id
);
callback(process);
!has_deferred && process.resolve();
} else {
process.reject();
}
});
this.last = process.promise();
},
/** Resets the promise queue. All currently queued but unexecuted callbacks/promises will be rejected.
*/
reset: function() {
Galaxy.emit.debug("deferred::execute()", "Reset");
for (var i in this.active) {
this.active[i] = false;
}
});
this.last = process.promise();
},
},
/** Resets the promise queue. All currently queued but unexecuted callbacks/promises will be rejected.
/** Returns true if all processes are done.
*/
reset: function() {
Galaxy.emit.debug('deferred::execute()', 'Reset');
for ( var i in this.active ) {
this.active[ i ] = false;
ready: function() {
return $.isEmptyObject(this.active);
}
},
/** Returns true if all processes are done.
*/
ready: function() {
return $.isEmptyObject( this.active );
}
});
});
});
File diff suppressed because it is too large Load Diff
+43 -36
View File
@@ -1,7 +1,6 @@
define([
], function(){
//=============================================================================
/**
define([], function() {
//=============================================================================
/**
(Imported for edit distance algorith. From: https://gist.github.com/andrei-m/982927)
Copyright (c) 2011 Andrei Mackenzie
@@ -18,43 +17,51 @@ WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEM
COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
// Compute the edit distance between the two given strings
//exports.getEditDistance = function(a, b){
function levenshteinDistance(a, b){
if(a.length === 0){ return b.length; }
if(b.length === 0){ return a.length; }
// Compute the edit distance between the two given strings
//exports.getEditDistance = function(a, b){
function levenshteinDistance(a, b) {
if (a.length === 0) {
return b.length;
}
if (b.length === 0) {
return a.length;
}
var matrix = [];
var matrix = [];
// increment along the first column of each row
var i;
for(i = 0; i <= b.length; i++){
matrix[i] = [i];
}
// increment along the first column of each row
var i;
for (i = 0; i <= b.length; i++) {
matrix[i] = [i];
}
// increment each column in the first row
var j;
for(j = 0; j <= a.length; j++){
matrix[0][j] = j;
}
// increment each column in the first row
var j;
for (j = 0; j <= a.length; j++) {
matrix[0][j] = j;
}
// Fill in the rest of the matrix
for(i = 1; i <= b.length; i++){
for(j = 1; j <= a.length; j++){
if(b.charAt(i-1) === a.charAt(j-1)){
matrix[i][j] = matrix[i-1][j-1];
} else {
matrix[i][j] = Math.min(matrix[i-1][j-1] + 1, // substitution
Math.min(matrix[i][j-1] + 1, // insertion
matrix[i-1][j] + 1)); // deletion
}
// Fill in the rest of the matrix
for (i = 1; i <= b.length; i++) {
for (j = 1; j <= a.length; j++) {
if (b.charAt(i - 1) === a.charAt(j - 1)) {
matrix[i][j] = matrix[i - 1][j - 1];
} else {
matrix[i][j] = Math.min(
matrix[i - 1][j - 1] + 1, // substitution
Math.min(
matrix[i][j - 1] + 1, // insertion
matrix[i - 1][j] + 1
)
); // deletion
}
}
}
//console.debug( '\t\t levenshteinDistance', a, b, matrix[b.length][a.length] );
return matrix[b.length][a.length];
}
}
//console.debug( '\t\t levenshteinDistance', a, b, matrix[b.length][a.length] );
return matrix[b.length][a.length];
}
//=============================================================================
//=============================================================================
return levenshteinDistance;
});
+40 -38
View File
@@ -1,50 +1,52 @@
define([
'i18n!nls/locale'
], function( localeStrings ){
define(["i18n!nls/locale"], function(localeStrings) {
// =============================================================================
/** Simple string replacement localization. Language data from galaxy/scripts/nls */
// =============================================================================
/** Simple string replacement localization. Language data from galaxy/scripts/nls */
// support both requirejs and webpack from the same file
// if loaded via webpack, it will be a different format than via requirejs - convert
if (localeStrings.hasOwnProperty("__root")) {
//console.debug( 'amdi18n+webpack localization for ' + locale + ' loaded' );
var locale =
typeof navigator === "undefined"
? "__root"
: (navigator.language ||
navigator.userLanguage ||
"__root"
).toLowerCase();
localeStrings =
localeStrings["__" + locale] ||
localeStrings["__" + locale.split("-")[0]] ||
localeStrings.__root;
// support both requirejs and webpack from the same file
// if loaded via webpack, it will be a different format than via requirejs - convert
if( localeStrings.hasOwnProperty( '__root' ) ){
//console.debug( 'amdi18n+webpack localization for ' + locale + ' loaded' );
var locale = (
typeof navigator === 'undefined' ? '__root'
: ( navigator.language || navigator.userLanguage || '__root' ).toLowerCase()
);
localeStrings = localeStrings[ '__' + locale ] || localeStrings[ '__' + locale.split('-')[0] ] ||localeStrings.__root;
// } else {
// console.debug( 'i18n+requirejs localization for ' + locale + ' loaded' );
}
// TODO: when this is no longer necessary remove this, i18n.js, and the resolveModule in config
// } else {
// console.debug( 'i18n+requirejs localization for ' + locale + ' loaded' );
}
// TODO: when this is no longer necessary remove this, i18n.js, and the resolveModule in config
// -----------------------------------------------------------------------------
/** Attempt to get a localized string for strToLocalize. If not found, return
// -----------------------------------------------------------------------------
/** Attempt to get a localized string for strToLocalize. If not found, return
* the original strToLocalize.
* @param {String} strToLocalize the string to localize
* @returns either the localized string if found or strToLocalize if not found
*/
var localize = function( strToLocalize ){
// console.debug( 'amdi18n.localize:', strToLocalize, '->', localeStrings[ strToLocalize ] || strToLocalize );
var localize = function(strToLocalize) {
// console.debug( 'amdi18n.localize:', strToLocalize, '->', localeStrings[ strToLocalize ] || strToLocalize );
// //TODO: conditional compile on DEBUG flag
// // cache strings that need to be localized but haven't been?
// if( localize.cacheNonLocalized && !localeStrings.hasOwnProperty( strToLocalize ) ){
// // console.debug( 'localization NOT found:', strToLocalize );
// // add nonCached as hash directly to this function
// localize.nonLocalized = localize.nonLocalized || {};
// localize.nonLocalized[ locale ] = localize.nonLocalized[ locale ] || {};
// localize.nonLocalized[ locale ][ strToLocalize ] = false;
// }
// //TODO: conditional compile on DEBUG flag
// // cache strings that need to be localized but haven't been?
// if( localize.cacheNonLocalized && !localeStrings.hasOwnProperty( strToLocalize ) ){
// // console.debug( 'localization NOT found:', strToLocalize );
// // add nonCached as hash directly to this function
// localize.nonLocalized = localize.nonLocalized || {};
// localize.nonLocalized[ locale ] = localize.nonLocalized[ locale ] || {};
// localize.nonLocalized[ locale ][ strToLocalize ] = false;
// }
// return the localized version from the closure if it's there, the strToLocalize if not
return localeStrings[ strToLocalize ] || strToLocalize;
};
localize.cacheNonLocalized = false;
// return the localized version from the closure if it's there, the strToLocalize if not
return localeStrings[strToLocalize] || strToLocalize;
};
localize.cacheNonLocalized = false;
// =============================================================================
// =============================================================================
return localize;
});
+472 -392
View File
@@ -1,7 +1,6 @@
define([
], function(){
/*global window, jQuery, console */
/*=============================================================================
define([], function() {
/*global window, jQuery, console */
/*=============================================================================
TODO:
while anon: logs saved to 'logs-null' - this will never post
unless we manually do so at/after login
@@ -9,7 +8,7 @@ TODO:
wire up _delayPost and test
=============================================================================*/
/** @class MetricsLogger
/** @class MetricsLogger
*
* Object to cache, output, and post log/metric messages to the server.
* Meant to be attached to the Galaxy object.
@@ -29,447 +28,528 @@ TODO:
* > panel.metric( 'something weird with window', { window : window })
* !'Metrics logger could not stringify logArguments: ...'
*/
function MetricsLogger( options ){
options = options || {};
var self = this;
function MetricsLogger(options) {
options = options || {};
var self = this;
///** get the current user's id from bootstrapped data or options */
self.userId = ( window.bootstrapped && window.bootstrapped.user )? window.bootstrapped.user.id: null;
self.userId = self.userId || options.userId || null;
///** get the current user's id from bootstrapped data or options */
self.userId =
window.bootstrapped && window.bootstrapped.user
? window.bootstrapped.user.id
: null;
self.userId = self.userId || options.userId || null;
/** the (optional) console to emit logs to */
self.consoleLogger = options.consoleLogger || null;
/** the (optional) console to emit logs to */
self.consoleLogger = options.consoleLogger || null;
self._init( options );
return self;
}
//----------------------------------------------------------------------------- defaults and constants
// see: python std lib, logging
MetricsLogger.ALL = 0;
MetricsLogger.LOG = 0;
MetricsLogger.DEBUG = 10;
MetricsLogger.INFO = 20;
MetricsLogger.WARN = 30;
MetricsLogger.ERROR = 40;
// metrics levels here?
//MetricsLogger.MinorEvent = 45;
//MetricsLogger.MajorEvent = 50;
MetricsLogger.METRIC = 50;
MetricsLogger.NONE = 100;
/** default options - override these through the constructor */
MetricsLogger.defaultOptions = {
/** if an incoming message has a level >= this, it will be cached - can also be a string (e.g. 'debug') */
logLevel : MetricsLogger.NONE,
/** if an incoming message has a level >= this, it will be output to the console */
consoleLevel : MetricsLogger.NONE,
/** the default 'namespace' or label associated with an incoming message (if none is passed) */
defaultNamespace : 'Galaxy',
/** the namespaces output to the console (all namespaces will be output if this is falsy)
* note: applies only to the console (not the event/metrics log/cache)
*/
consoleNamespaceWhitelist : null,
/** the prefix attached to client-side logs to distinguish them in the metrics db */
clientPrefix : 'client.',
/** the maximum number of messages the cache should hold; if exceeded older messages are removed first */
maxCacheSize : 3000,
/** the number of messages accumulate before posting to the server; should be <= maxCacheSize */
postSize : 1000,
/** T/F whether to add a timestamp to incoming cached messages */
addTime : true,
/** string to prefix to userid for cache web storage */
cacheKeyPrefix : 'logs-',
/** the relative url to post messages to */
postUrl : '/api/metrics',
/** delay before trying post again after two failures */
delayPostInMs : 1000 * 60 * 10,
/** an (optional) function that should return an object; used to send additional data with the metrics */
getPingData : undefined,
/** an (optional) function that will handle the servers response after successfully posting messages */
onServerResponse : undefined
};
//----------------------------------------------------------------------------- set up
/** initialize the logger with options, set up instance vars and cache, and add onpageunload to window */
MetricsLogger.prototype._init = function _init( options ){
var self = this;
self.options = {};
for( var k in MetricsLogger.defaultOptions ){
if( MetricsLogger.defaultOptions.hasOwnProperty( k ) ){
self.options[ k ] = ( options.hasOwnProperty( k ) )?( options[ k ] ):( MetricsLogger.defaultOptions[ k ] );
}
}
self.options.logLevel = self._parseLevel( self.options.logLevel );
self.options.consoleLevel = self._parseLevel( self.options.consoleLevel );
//self._emitToConsole( 'debug', 'MetricsLogger', 'MetricsLogger.options:', self.options );
/** is the logger currently sending? */
self._sending = false;
/** the setTimeout id if the logger POST has failed more than once */
self._waiting = null;
/** the current number of entries to send in a POST */
self._postSize = self.options.postSize;
self._initCache();
return self;
};
/** initialize the cache */
MetricsLogger.prototype._initCache = function _initCache(){
try {
this.cache = new LoggingCache({
maxSize : this.options.maxCacheSize,
key : this.options.cacheKeyPrefix + this.userId
});
} catch( err ){
this._emitToConsole( 'warn', 'MetricsLogger', [ 'Could not intitialize logging cache:', err ] );
this.options.logLevel = MetricsLogger.NONE;
}
};
/** return the numeric log level if level in 'none, debug, log, info, warn, error' */
MetricsLogger.prototype._parseLevel = function _parseLevel( level ){
var type = typeof level;
if( type === 'number' ){ return level; }
if( type === 'string' ){
var upper = level.toUpperCase();
if( MetricsLogger.hasOwnProperty( upper ) ){
return MetricsLogger[ upper ];
}
}
throw new Error( 'Unknown log level: ' + level );
};
//----------------------------------------------------------------------------- main entry point
/** record a log/message's arguments to the cache and/or the console based on level and namespace */
MetricsLogger.prototype.emit = function emit( level, namespace, logArguments ){
//this._emitToConsole( 'debug', 'MetricsLogger', [ 'emit:', level, namespace, logArguments ]);
var self = this;
namespace = namespace || self.options.defaultNamespace;
if( !level || !logArguments ){
self._init(options);
return self;
}
// add to cache if proper level
//TODO: respect do not track?
//if( !navigator.doNotTrack && level >= self.options.logLevel ){
level = self._parseLevel( level );
if( level >= self.options.logLevel ){
self._addToCache( level, namespace, logArguments );
}
// also emit to consoleLogger if proper level for that
if( self.consoleLogger && level >= self.options.consoleLevel ){
self._emitToConsole( level, namespace, logArguments );
}
return self;
};
//----------------------------------------------------------------------------- cache
/** add a message to the cache and if messages.length is high enough post them to the server */
MetricsLogger.prototype._addToCache = function _addToCache( level, namespace, logArguments ){
this._emitToConsole( 'debug', 'MetricsLogger',
[ '_addToCache:', arguments, this.options.addTime, this.cache.length() ]);
//this._emitToConsole( 'debug', 'MetricsLogger', [ '\t logArguments:', logArguments ]);
var self = this;
// try add to the cache and if we've got _postSize number of entries, attempt to post them to the server
try {
var newLength = self.cache.add( self._buildEntry( level, namespace, logArguments ) );
if( newLength >= self._postSize ){
self._postCache();
//----------------------------------------------------------------------------- defaults and constants
// see: python std lib, logging
MetricsLogger.ALL = 0;
MetricsLogger.LOG = 0;
MetricsLogger.DEBUG = 10;
MetricsLogger.INFO = 20;
MetricsLogger.WARN = 30;
MetricsLogger.ERROR = 40;
// metrics levels here?
//MetricsLogger.MinorEvent = 45;
//MetricsLogger.MajorEvent = 50;
MetricsLogger.METRIC = 50;
MetricsLogger.NONE = 100;
/** default options - override these through the constructor */
MetricsLogger.defaultOptions = {
/** if an incoming message has a level >= this, it will be cached - can also be a string (e.g. 'debug') */
logLevel: MetricsLogger.NONE,
/** if an incoming message has a level >= this, it will be output to the console */
consoleLevel: MetricsLogger.NONE,
/** the default 'namespace' or label associated with an incoming message (if none is passed) */
defaultNamespace: "Galaxy",
/** the namespaces output to the console (all namespaces will be output if this is falsy)
* note: applies only to the console (not the event/metrics log/cache)
*/
consoleNamespaceWhitelist: null,
/** the prefix attached to client-side logs to distinguish them in the metrics db */
clientPrefix: "client.",
/** the maximum number of messages the cache should hold; if exceeded older messages are removed first */
maxCacheSize: 3000,
/** the number of messages accumulate before posting to the server; should be <= maxCacheSize */
postSize: 1000,
/** T/F whether to add a timestamp to incoming cached messages */
addTime: true,
/** string to prefix to userid for cache web storage */
cacheKeyPrefix: "logs-",
/** the relative url to post messages to */
postUrl: "/api/metrics",
/** delay before trying post again after two failures */
delayPostInMs: 1000 * 60 * 10,
/** an (optional) function that should return an object; used to send additional data with the metrics */
getPingData: undefined,
/** an (optional) function that will handle the servers response after successfully posting messages */
onServerResponse: undefined
};
//----------------------------------------------------------------------------- set up
/** initialize the logger with options, set up instance vars and cache, and add onpageunload to window */
MetricsLogger.prototype._init = function _init(options) {
var self = this;
self.options = {};
for (var k in MetricsLogger.defaultOptions) {
if (MetricsLogger.defaultOptions.hasOwnProperty(k)) {
self.options[k] = options.hasOwnProperty(k)
? options[k]
: MetricsLogger.defaultOptions[k];
}
}
// discard entry if an error occurs, but warn if level set to do so
} catch( err ){
self._emitToConsole( 'warn', 'MetricsLogger',
[ 'Metrics logger could not stringify logArguments:', namespace, logArguments ] );
self._emitToConsole( 'error', 'MetricsLogger', [ err ] );
self.options.logLevel = self._parseLevel(self.options.logLevel);
self.options.consoleLevel = self._parseLevel(self.options.consoleLevel);
//self._emitToConsole( 'debug', 'MetricsLogger', 'MetricsLogger.options:', self.options );
}
return self;
};
/** is the logger currently sending? */
self._sending = false;
/** the setTimeout id if the logger POST has failed more than once */
self._waiting = null;
/** the current number of entries to send in a POST */
self._postSize = self.options.postSize;
/** build a log cache entry object from the given level, namespace, and arguments (optionally adding timestamp */
MetricsLogger.prototype._buildEntry = function _buildEntry( level, namespace, logArguments ){
this._emitToConsole( 'debug', 'MetricsLogger', [ '_buildEntry:', arguments ]);
var entry = {
level : level,
namespace : this.options.clientPrefix + namespace,
args : logArguments
self._initCache();
return self;
};
/** initialize the cache */
MetricsLogger.prototype._initCache = function _initCache() {
try {
this.cache = new LoggingCache({
maxSize: this.options.maxCacheSize,
key: this.options.cacheKeyPrefix + this.userId
});
} catch (err) {
this._emitToConsole("warn", "MetricsLogger", [
"Could not intitialize logging cache:",
err
]);
this.options.logLevel = MetricsLogger.NONE;
}
};
/** return the numeric log level if level in 'none, debug, log, info, warn, error' */
MetricsLogger.prototype._parseLevel = function _parseLevel(level) {
var type = typeof level;
if (type === "number") {
return level;
}
if (type === "string") {
var upper = level.toUpperCase();
if (MetricsLogger.hasOwnProperty(upper)) {
return MetricsLogger[upper];
}
}
throw new Error("Unknown log level: " + level);
};
//----------------------------------------------------------------------------- main entry point
/** record a log/message's arguments to the cache and/or the console based on level and namespace */
MetricsLogger.prototype.emit = function emit(
level,
namespace,
logArguments
) {
//this._emitToConsole( 'debug', 'MetricsLogger', [ 'emit:', level, namespace, logArguments ]);
var self = this;
namespace = namespace || self.options.defaultNamespace;
if (!level || !logArguments) {
return self;
}
// add to cache if proper level
//TODO: respect do not track?
//if( !navigator.doNotTrack && level >= self.options.logLevel ){
level = self._parseLevel(level);
if (level >= self.options.logLevel) {
self._addToCache(level, namespace, logArguments);
}
// also emit to consoleLogger if proper level for that
if (self.consoleLogger && level >= self.options.consoleLevel) {
self._emitToConsole(level, namespace, logArguments);
}
return self;
};
//----------------------------------------------------------------------------- cache
/** add a message to the cache and if messages.length is high enough post them to the server */
MetricsLogger.prototype._addToCache = function _addToCache(
level,
namespace,
logArguments
) {
this._emitToConsole("debug", "MetricsLogger", [
"_addToCache:",
arguments,
this.options.addTime,
this.cache.length()
]);
//this._emitToConsole( 'debug', 'MetricsLogger', [ '\t logArguments:', logArguments ]);
var self = this;
// try add to the cache and if we've got _postSize number of entries, attempt to post them to the server
try {
var newLength = self.cache.add(
self._buildEntry(level, namespace, logArguments)
);
if (newLength >= self._postSize) {
self._postCache();
}
// discard entry if an error occurs, but warn if level set to do so
} catch (err) {
self._emitToConsole("warn", "MetricsLogger", [
"Metrics logger could not stringify logArguments:",
namespace,
logArguments
]);
self._emitToConsole("error", "MetricsLogger", [err]);
}
return self;
};
/** build a log cache entry object from the given level, namespace, and arguments (optionally adding timestamp */
MetricsLogger.prototype._buildEntry = function _buildEntry(
level,
namespace,
logArguments
) {
this._emitToConsole("debug", "MetricsLogger", [
"_buildEntry:",
arguments
]);
var entry = {
level: level,
namespace: this.options.clientPrefix + namespace,
args: logArguments
};
if( this.options.addTime ){
entry.time = new Date().toISOString();
}
return entry;
};
if (this.options.addTime) {
entry.time = new Date().toISOString();
}
return entry;
};
/** post _postSize messages from the cache to the server, removing them if successful
/** post _postSize messages from the cache to the server, removing them if successful
* if the post fails, wait until maxCacheSize is accumulated instead and try again then
* in addition to the messages from the cache ('metrics'), any info from getPingData (if set) will be sent
* onServerResponse will be called (if set) with any response from the server
*/
MetricsLogger.prototype._postCache = function _postCache( options ){
options = options || {};
this._emitToConsole( 'info', 'MetricsLogger', [ '_postCache', options, this._postSize ]);
MetricsLogger.prototype._postCache = function _postCache(options) {
options = options || {};
this._emitToConsole("info", "MetricsLogger", [
"_postCache",
options,
this._postSize
]);
// short circuit if we're already sending
if( !this.options.postUrl || this._sending ){
return jQuery.when({});
}
// short circuit if we're already sending
if (!this.options.postUrl || this._sending) {
return jQuery.when({});
}
var self = this,
postSize = options.count || self._postSize,
// do not splice - remove after *successful* post
entries = self.cache.get( postSize ),
entriesLength = entries.length,
// use the optional getPingData to add any extra info we may want to send
postData = ( typeof self.options.getPingData === 'function' )?( self.options.getPingData() ):( {} );
//console.debug( postSize, entriesLength );
var self = this,
postSize = options.count || self._postSize,
// do not splice - remove after *successful* post
entries = self.cache.get(postSize),
entriesLength = entries.length,
// use the optional getPingData to add any extra info we may want to send
postData =
typeof self.options.getPingData === "function"
? self.options.getPingData()
: {};
//console.debug( postSize, entriesLength );
// add the metrics and send
postData.metrics = JSON.stringify( entries );
//console.debug( postData.metrics );
self._sending = true;
return jQuery.post( self.options.postUrl, postData )
.always( function(){
self._sending = false;
})
.fail( function( xhr, status, message ){
// if we failed the previous time, set the next post target to the max num of entries
self._postSize = self.options.maxCacheSize;
//TODO:??
// log this failure to explain any gap in metrics
self.emit( 'error', 'MetricsLogger', [ '_postCache error:',
xhr.readyState, xhr.status, xhr.responseJSON || xhr.responseText ]);
//TODO: still doesn't solve the problem that when cache == max, post will be tried on every emit
//TODO: see _delayPost
})
.done( function( response ){
if( typeof self.options.onServerResponse === 'function' ){
self.options.onServerResponse( response );
}
// only remove if post successful
self.cache.remove( entriesLength );
//console.debug( 'removed entries:', entriesLength, 'size now:', self.cache.length() );
// if we succeeded, reset the post target to the normal num of entries
self._postSize = self.options.postSize;
});
// return the xhr promise
};
// add the metrics and send
postData.metrics = JSON.stringify(entries);
//console.debug( postData.metrics );
self._sending = true;
return jQuery
.post(self.options.postUrl, postData)
.always(function() {
self._sending = false;
})
.fail(function(xhr, status, message) {
// if we failed the previous time, set the next post target to the max num of entries
self._postSize = self.options.maxCacheSize;
//TODO:??
// log this failure to explain any gap in metrics
self.emit("error", "MetricsLogger", [
"_postCache error:",
xhr.readyState,
xhr.status,
xhr.responseJSON || xhr.responseText
]);
//TODO: still doesn't solve the problem that when cache == max, post will be tried on every emit
//TODO: see _delayPost
})
.done(function(response) {
if (typeof self.options.onServerResponse === "function") {
self.options.onServerResponse(response);
}
// only remove if post successful
self.cache.remove(entriesLength);
//console.debug( 'removed entries:', entriesLength, 'size now:', self.cache.length() );
// if we succeeded, reset the post target to the normal num of entries
self._postSize = self.options.postSize;
});
// return the xhr promise
};
/** set _waiting to true and, after delayPostInMs, set it back to false */
MetricsLogger.prototype._delayPost = function _delayPost(){
//TODO: this won't work between pages
var self = this;
self._waiting = setTimeout( function(){
self._waiting = null;
}, self.options.delayPostInMs );
};
/** set _waiting to true and, after delayPostInMs, set it back to false */
MetricsLogger.prototype._delayPost = function _delayPost() {
//TODO: this won't work between pages
var self = this;
self._waiting = setTimeout(function() {
self._waiting = null;
}, self.options.delayPostInMs);
};
//----------------------------------------------------------------------------- console
/** output message to console based on level and consoleLogger type */
MetricsLogger.prototype._emitToConsole = function _emitToConsole(
level,
namespace,
logArguments
) {
//console.debug( '_emitToConsole:', level, namespace, logArguments );
var self = this,
whitelist = self.options.consoleNamespaceWhitelist;
if (!self.consoleLogger) {
return self;
}
// if a whitelist for namespaces is set, bail if this namespace is not in the list
if (whitelist && whitelist.indexOf(namespace) === -1) {
return self;
}
//----------------------------------------------------------------------------- console
/** output message to console based on level and consoleLogger type */
MetricsLogger.prototype._emitToConsole = function _emitToConsole( level, namespace, logArguments ){
//console.debug( '_emitToConsole:', level, namespace, logArguments );
var self = this,
whitelist = self.options.consoleNamespaceWhitelist;
if( !self.consoleLogger ){ return self; }
// if a whitelist for namespaces is set, bail if this namespace is not in the list
if( whitelist && whitelist.indexOf( namespace ) === -1 ){
var args = Array.prototype.slice.call(logArguments, 0);
args.unshift(namespace);
//TODO: script location and/or source maps?
//TODO: branch on navigator.userAgent == AIIEEE - it only has log
if (
level >= MetricsLogger.METRIC &&
typeof self.consoleLogger.info === "function"
) {
return self.consoleLogger.info.apply(self.consoleLogger, args);
} else if (
level >= MetricsLogger.ERROR &&
typeof self.consoleLogger.error === "function"
) {
return self.consoleLogger.error.apply(self.consoleLogger, args);
} else if (
level >= MetricsLogger.WARN &&
typeof self.consoleLogger.warn === "function"
) {
self.consoleLogger.warn.apply(self.consoleLogger, args);
} else if (
level >= MetricsLogger.INFO &&
typeof self.consoleLogger.info === "function"
) {
self.consoleLogger.info.apply(self.consoleLogger, args);
} else if (
level >= MetricsLogger.DEBUG &&
typeof self.consoleLogger.debug === "function"
) {
self.consoleLogger.debug.apply(self.consoleLogger, args);
} else if (typeof self.consoleLogger.log === "function") {
self.consoleLogger.log.apply(self.consoleLogger, args);
}
return self;
}
};
var args = Array.prototype.slice.call( logArguments, 0 );
args.unshift( namespace );
//TODO: script location and/or source maps?
//TODO: branch on navigator.userAgent == AIIEEE - it only has log
if( level >= MetricsLogger.METRIC && typeof( self.consoleLogger.info ) === 'function' ){
return self.consoleLogger.info.apply( self.consoleLogger, args );
//----------------------------------------------------------------------------- shortcuts
// generic functions when logging from non-namespaced object (e.g. templates)
/** log to default namespace */
MetricsLogger.prototype.log = function log() {
this.emit(
1,
this.options.defaultNamespace,
Array.prototype.slice.call(arguments, 0)
);
};
} else if( level >= MetricsLogger.ERROR && typeof( self.consoleLogger.error ) === 'function' ){
return self.consoleLogger.error.apply( self.consoleLogger, args );
} else if( level >= MetricsLogger.WARN && typeof( self.consoleLogger.warn ) === 'function' ){
self.consoleLogger.warn.apply( self.consoleLogger, args );
} else if( level >= MetricsLogger.INFO && typeof( self.consoleLogger.info ) === 'function' ){
self.consoleLogger.info.apply( self.consoleLogger, args );
} else if( level >= MetricsLogger.DEBUG && typeof( self.consoleLogger.debug ) === 'function' ){
self.consoleLogger.debug.apply( self.consoleLogger, args );
} else if( typeof( self.consoleLogger.log ) === 'function' ){
self.consoleLogger.log.apply( self.consoleLogger, args );
}
return self;
};
/** debug to default namespace */
MetricsLogger.prototype.debug = function debug() {
this.emit(
MetricsLogger.DEBUG,
this.options.defaultNamespace,
Array.prototype.slice.call(arguments, 0)
);
};
//----------------------------------------------------------------------------- shortcuts
// generic functions when logging from non-namespaced object (e.g. templates)
/** log to default namespace */
MetricsLogger.prototype.log = function log(){
this.emit( 1, this.options.defaultNamespace,
Array.prototype.slice.call( arguments, 0 ) );
};
/** info to default namespace */
MetricsLogger.prototype.info = function info() {
this.emit(
MetricsLogger.INFO,
this.options.defaultNamespace,
Array.prototype.slice.call(arguments, 0)
);
};
/** debug to default namespace */
MetricsLogger.prototype.debug = function debug(){
this.emit( MetricsLogger.DEBUG, this.options.defaultNamespace,
Array.prototype.slice.call( arguments, 0 ) );
};
/** warn to default namespace */
MetricsLogger.prototype.warn = function warn() {
this.emit(
MetricsLogger.WARN,
this.options.defaultNamespace,
Array.prototype.slice.call(arguments, 0)
);
};
/** info to default namespace */
MetricsLogger.prototype.info = function info(){
this.emit( MetricsLogger.INFO, this.options.defaultNamespace,
Array.prototype.slice.call( arguments, 0 ) );
};
/** error to default namespace */
MetricsLogger.prototype.error = function error() {
this.emit(
MetricsLogger.ERROR,
this.options.defaultNamespace,
Array.prototype.slice.call(arguments, 0)
);
};
/** warn to default namespace */
MetricsLogger.prototype.warn = function warn(){
this.emit( MetricsLogger.WARN, this.options.defaultNamespace,
Array.prototype.slice.call( arguments, 0 ) );
};
/** metric to default namespace */
MetricsLogger.prototype.metric = function metric() {
this.emit(
MetricsLogger.METRIC,
this.options.defaultNamespace,
Array.prototype.slice.call(arguments, 0)
);
};
/** error to default namespace */
MetricsLogger.prototype.error = function error(){
this.emit( MetricsLogger.ERROR, this.options.defaultNamespace,
Array.prototype.slice.call( arguments, 0 ) );
};
/** metric to default namespace */
MetricsLogger.prototype.metric = function metric(){
this.emit( MetricsLogger.METRIC, this.options.defaultNamespace,
Array.prototype.slice.call( arguments, 0 ) );
};
/* ============================================================================
/* ============================================================================
TODO:
need a performance pass - the JSON un/parsing is a bit much
============================================================================ */
/** @class LoggingCache
/** @class LoggingCache
* Simple implementation of cache wrapping an array.
*
* Formats an entry before it's cached and only keeps options.maxSize number
* of entries. Older entries are deleted first.
*/
function LoggingCache( options ){
var self = this;
return self._init( options || {} );
}
/** default options */
LoggingCache.defaultOptions = {
/** maximum number of entries to keep before discarding oldest */
maxSize : 5000
};
/** initialize with options */
LoggingCache.prototype._init = function _init( options ){
if( !this._hasStorage() ){
//TODO: fall back to jstorage
throw new Error( 'LoggingCache needs localStorage' );
function LoggingCache(options) {
var self = this;
return self._init(options || {});
}
if( !options.key ){
throw new Error( 'LoggingCache needs key for localStorage' );
}
this.key = options.key;
this._initStorage();
this.maxSize = options.maxSize || LoggingCache.defaultOptions.maxSize;
return this;
};
/** default options */
LoggingCache.defaultOptions = {
/** maximum number of entries to keep before discarding oldest */
maxSize: 5000
};
/** tests for localStorage fns */
LoggingCache.prototype._hasStorage = function _hasStorage(){
//TODO: modernizr
var test = 'test';
try {
localStorage.setItem( test, test );
localStorage.removeItem( test );
return true;
} catch( e ){
return false;
}
};
/** initialize with options */
LoggingCache.prototype._init = function _init(options) {
if (!this._hasStorage()) {
//TODO: fall back to jstorage
throw new Error("LoggingCache needs localStorage");
}
if (!options.key) {
throw new Error("LoggingCache needs key for localStorage");
}
this.key = options.key;
this._initStorage();
/** if no localStorage set for key, initialize to empty array */
LoggingCache.prototype._initStorage = function _initStorage(){
if( localStorage.getItem( this.key ) === null ){
return this.empty();
}
return this;
};
this.maxSize = options.maxSize || LoggingCache.defaultOptions.maxSize;
return this;
};
/** add an entry to the cache, removing the oldest beforehand if size >= maxSize */
LoggingCache.prototype.add = function add( entry ){
var self = this,
_cache = self._fetchAndParse(),
overage = ( _cache.length + 1 ) - self.maxSize;
if( overage > 0 ){
_cache.splice( 0, overage );
}
_cache.push( entry );
self._unparseAndStore( _cache );
return _cache.length;
};
/** tests for localStorage fns */
LoggingCache.prototype._hasStorage = function _hasStorage() {
//TODO: modernizr
var test = "test";
try {
localStorage.setItem(test, test);
localStorage.removeItem(test);
return true;
} catch (e) {
return false;
}
};
/** get the entries from localStorage and parse them */
LoggingCache.prototype._fetchAndParse = function _fetchAndParse(){
var self = this;
return JSON.parse( localStorage.getItem( self.key ) );
};
/** if no localStorage set for key, initialize to empty array */
LoggingCache.prototype._initStorage = function _initStorage() {
if (localStorage.getItem(this.key) === null) {
return this.empty();
}
return this;
};
/** stringify the entries and put them in localStorage */
LoggingCache.prototype._unparseAndStore = function _unparseAndStore( entries ){
var self = this;
return localStorage.setItem( self.key, JSON.stringify( entries ) );
};
/** add an entry to the cache, removing the oldest beforehand if size >= maxSize */
LoggingCache.prototype.add = function add(entry) {
var self = this,
_cache = self._fetchAndParse(),
overage = _cache.length + 1 - self.maxSize;
if (overage > 0) {
_cache.splice(0, overage);
}
_cache.push(entry);
self._unparseAndStore(_cache);
return _cache.length;
};
///** process the entry before caching */
//LoggingCache.prototype._preprocessEntry = function _preprocessEntry( entry ){
// return JSON.stringify( entry );
//};
/** get the entries from localStorage and parse them */
LoggingCache.prototype._fetchAndParse = function _fetchAndParse() {
var self = this;
return JSON.parse(localStorage.getItem(self.key));
};
/** return the length --- oh, getters where are you? */
LoggingCache.prototype.length = function length(){
return this._fetchAndParse().length;
};
/** stringify the entries and put them in localStorage */
LoggingCache.prototype._unparseAndStore = function _unparseAndStore(
entries
) {
var self = this;
return localStorage.setItem(self.key, JSON.stringify(entries));
};
/** get count number of entries starting with the oldest */
LoggingCache.prototype.get = function get( count ){
return this._fetchAndParse().slice( 0, count );
};
///** process the entry before caching */
//LoggingCache.prototype._preprocessEntry = function _preprocessEntry( entry ){
// return JSON.stringify( entry );
//};
/** remove count number of entries starting with the oldest */
LoggingCache.prototype.remove = function remove( count ){
var _cache = this._fetchAndParse(),
removed = _cache.splice( 0, count );
this._unparseAndStore( _cache );
return removed;
};
/** return the length --- oh, getters where are you? */
LoggingCache.prototype.length = function length() {
return this._fetchAndParse().length;
};
/** empty/clear the entire cache */
LoggingCache.prototype.empty = function empty(){
localStorage.setItem( this.key, '[]' );
return this;
};
/** get count number of entries starting with the oldest */
LoggingCache.prototype.get = function get(count) {
return this._fetchAndParse().slice(0, count);
};
/** stringify count number of entries (but do not remove) */
LoggingCache.prototype.stringify = function stringify( count ){
return JSON.stringify( this.get( count ) );
};
/** remove count number of entries starting with the oldest */
LoggingCache.prototype.remove = function remove(count) {
var _cache = this._fetchAndParse(),
removed = _cache.splice(0, count);
this._unparseAndStore(_cache);
return removed;
};
/** outputs entire cache to console */
LoggingCache.prototype.print = function print(){
// popup? (really, carl? a popup?) - easier to copy/paste
console.log( JSON.stringify( this._fetchAndParse(), null, ' ' ) );
};
/** empty/clear the entire cache */
LoggingCache.prototype.empty = function empty() {
localStorage.setItem(this.key, "[]");
return this;
};
/** stringify count number of entries (but do not remove) */
LoggingCache.prototype.stringify = function stringify(count) {
return JSON.stringify(this.get(count));
};
//=============================================================================
/** outputs entire cache to console */
LoggingCache.prototype.print = function print() {
// popup? (really, carl? a popup?) - easier to copy/paste
console.log(JSON.stringify(this._fetchAndParse(), null, " "));
};
//=============================================================================
return {
MetricsLogger : MetricsLogger,
LoggingCache : LoggingCache
MetricsLogger: MetricsLogger,
LoggingCache: LoggingCache
};
});
+38 -28
View File
@@ -1,30 +1,40 @@
define([], function(){
// Alphanumeric/natural sort fn
function naturalSort(a, b) {
// setup temp-scope variables for comparison evauluation
var re = /(-?[0-9\.]+)/g,
x = a.toString().toLowerCase() || '',
y = b.toString().toLowerCase() || '',
nC = String.fromCharCode(0),
xN = x.replace( re, nC + '$1' + nC ).split(nC),
yN = y.replace( re, nC + '$1' + nC ).split(nC),
xD = (new Date(x)).getTime(),
yD = xD ? (new Date(y)).getTime() : null;
// natural sorting of dates
if ( yD ) {
if ( xD < yD ) { return -1; }
else if ( xD > yD ) { return 1; }
define([], function() {
// Alphanumeric/natural sort fn
function naturalSort(a, b) {
// setup temp-scope variables for comparison evauluation
var re = /(-?[0-9\.]+)/g,
x = a.toString().toLowerCase() || "",
y = b.toString().toLowerCase() || "",
nC = String.fromCharCode(0),
xN = x.replace(re, nC + "$1" + nC).split(nC),
yN = y.replace(re, nC + "$1" + nC).split(nC),
xD = new Date(x).getTime(),
yD = xD ? new Date(y).getTime() : null;
// natural sorting of dates
if (yD) {
if (xD < yD) {
return -1;
} else if (xD > yD) {
return 1;
}
}
// natural sorting through split numeric strings and default strings
var oFxNcL, oFyNcL;
for (
var cLoc = 0, numS = Math.max(xN.length, yN.length);
cLoc < numS;
cLoc++
) {
oFxNcL = parseFloat(xN[cLoc]) || xN[cLoc];
oFyNcL = parseFloat(yN[cLoc]) || yN[cLoc];
if (oFxNcL < oFyNcL) {
return -1;
} else if (oFxNcL > oFyNcL) {
return 1;
}
}
return 0;
}
// natural sorting through split numeric strings and default strings
var oFxNcL, oFyNcL;
for ( var cLoc = 0, numS = Math.max(xN.length, yN.length); cLoc < numS; cLoc++ ) {
oFxNcL = parseFloat(xN[cLoc]) || xN[cLoc];
oFyNcL = parseFloat(yN[cLoc]) || yN[cLoc];
if (oFxNcL < oFyNcL) { return -1; }
else if (oFxNcL > oFyNcL) { return 1; }
}
return 0;
}
return naturalSort;
})
return naturalSort;
});
@@ -1,35 +1,39 @@
define([], function(){
// ============================================================================
function get( key, queryString ){
queryString = queryString || window.location.search.substr( 1 );
var keyRegex = new RegExp( key + '=([^&#$]+)', 'g' );
var matches = queryString.match( keyRegex );
if( !matches || !matches.length ){
return undefined;
define([], function() {
// ============================================================================
function get(key, queryString) {
queryString = queryString || window.location.search.substr(1);
var keyRegex = new RegExp(key + "=([^&#$]+)", "g");
var matches = queryString.match(keyRegex);
if (!matches || !matches.length) {
return undefined;
}
matches = _.map(matches, function(match) {
return decodeURIComponent(
match.substr(key.length + 1).replace(/\+/g, " ")
);
});
if (matches.length === 1) {
return matches[0];
}
return matches;
}
matches = _.map( matches, function( match ) {
return decodeURIComponent( match.substr( key.length + 1 ).replace( /\+/g, ' ' ) );
} );
if( matches.length === 1 ){
return matches[ 0 ];
function parse(queryString) {
if (!queryString) {
return {};
}
var parsed = {},
split = queryString.split("&");
split.forEach(function(pairString) {
var pair = pairString.split("=");
parsed[pair[0]] = decodeURI(pair[1]);
});
return parsed;
}
return matches;
}
function parse( queryString ){
if( !queryString ){ return {}; }
var parsed = {},
split = queryString.split( '&' );
split.forEach( function( pairString ){
var pair = pairString.split( '=' );
parsed[ pair[0] ] = decodeURI( pair[1] );
});
return parsed;
}
// ============================================================================
// ============================================================================
return {
get : get,
parse : parse,
get: get,
parse: parse
};
});
+125 -80
View File
@@ -1,27 +1,31 @@
/*
galaxy upload plugins - requires FormData and XMLHttpRequest
*/
;(function($){
(function($) {
// add event properties
jQuery.event.props.push("dataTransfer");
/**
Posts file data to the API
*/
$.uploadpost = function (config) {
$.uploadpost = function(config) {
// parse options
var cnf = $.extend({}, {
data : {},
success : function() {},
error : function() {},
progress : function() {},
url : null,
maxfilesize : 2048,
error_filesize : 'File exceeds 2GB. Please use a FTP client.',
error_default : 'Please make sure the file is available.',
error_server : 'Upload request failed.',
error_login : 'Uploads require you to log in.'
}, config);
var cnf = $.extend(
{},
{
data: {},
success: function() {},
error: function() {},
progress: function() {},
url: null,
maxfilesize: 2048,
error_filesize: "File exceeds 2GB. Please use a FTP client.",
error_default: "Please make sure the file is available.",
error_server: "Upload request failed.",
error_login: "Uploads require you to log in."
},
config
);
// link data
var data = cnf.data;
@@ -54,10 +58,10 @@
// prepare request
var xhr = new XMLHttpRequest();
xhr.open('POST', cnf.url, true);
xhr.setRequestHeader('Accept', 'application/json');
xhr.setRequestHeader('Cache-Control', 'no-cache');
xhr.setRequestHeader('X-Requested-With', 'XMLHttpRequest');
xhr.open("POST", cnf.url, true);
xhr.setRequestHeader("Accept", "application/json");
xhr.setRequestHeader("Cache-Control", "no-cache");
xhr.setRequestHeader("X-Requested-With", "XMLHttpRequest");
// captures state changes
xhr.onreadystatechange = function() {
@@ -85,24 +89,32 @@
} else if (!text) {
text = cnf.error_default;
}
cnf.error(text + ' (' + xhr.status + '). ' + extra_info);
cnf.error(text + " (" + xhr.status + "). " + extra_info);
} else {
cnf.success(response);
}
}
}
};
// prepare upload progress
xhr.upload.addEventListener('progress', function(e) {
if (e.lengthComputable) {
cnf.progress(Math.round((e.loaded * 100) / e.total));
}
}, false);
xhr.upload.addEventListener(
"progress",
function(e) {
if (e.lengthComputable) {
cnf.progress(Math.round(e.loaded * 100 / e.total));
}
},
false
);
// send request
Galaxy.emit.debug('uploadbox::uploadpost()', 'Posting following data.', cnf);
Galaxy.emit.debug(
"uploadbox::uploadpost()",
"Posting following data.",
cnf
);
xhr.send(form);
}
};
/**
Handles the upload events drag/drop etc.
@@ -110,60 +122,76 @@
$.fn.uploadinput = function(options) {
// initialize
var el = this;
var opts = $.extend({}, {
ondragover : function() {},
ondragleave : function() {},
onchange : function() {},
multiple : false
}, options);
var opts = $.extend(
{},
{
ondragover: function() {},
ondragleave: function() {},
onchange: function() {},
multiple: false
},
options
);
// append hidden upload field
var $input = $('<input type="file" style="display: none" ' + (opts.multiple && 'multiple' || '') + '/>');
el.append($input.change(function (e) {
opts.onchange(e.target.files);
$(this).val('');
}));
var $input = $(
'<input type="file" style="display: none" ' +
((opts.multiple && "multiple") || "") +
"/>"
);
el.append(
$input.change(function(e) {
opts.onchange(e.target.files);
$(this).val("");
})
);
// drag/drop events
el.on('drop', function (e) {
el.on("drop", function(e) {
opts.ondragleave(e);
if(e.dataTransfer) {
if (e.dataTransfer) {
opts.onchange(e.dataTransfer.files);
e.preventDefault();
}
});
el.on('dragover', function (e) {
el.on("dragover", function(e) {
e.preventDefault();
opts.ondragover(e);
});
el.on('dragleave', function (e) {
el.on("dragleave", function(e) {
e.stopPropagation();
opts.ondragleave(e);
});
// exports
return {
dialog: function () {
$input.trigger('click');
dialog: function() {
$input.trigger("click");
}
}
}
};
};
/**
Handles the upload queue and events such as drag/drop etc.
*/
$.fn.uploadbox = function(options) {
// parse options
var opts = $.extend({}, {
dragover : function() {},
dragleave : function() {},
announce : function(d) {},
initialize : function(d) {},
progress : function(d, m) {},
success : function(d, m) {},
error : function(d, m) { alert(m); },
complete : function() {}
}, options);
var opts = $.extend(
{},
{
dragover: function() {},
dragleave: function() {},
announce: function(d) {},
initialize: function(d) {},
progress: function(d, m) {},
success: function(d, m) {},
error: function(d, m) {
alert(m);
},
complete: function() {}
},
options
);
// file queue
var queue = {};
@@ -178,10 +206,12 @@
// element
var uploadinput = $(this).uploadinput({
multiple : true,
onchange : function(files) { add(files); },
ondragover : options.ondragover,
ondragleave : options.ondragleave
multiple: true,
onchange: function(files) {
add(files);
},
ondragover: options.ondragover,
ondragleave: options.ondragleave
});
// add new files to upload queue
@@ -189,9 +219,12 @@
if (files && files.length && !queue_running) {
var index = undefined;
_.each(files, function(file, key) {
if (file.mode !== 'new' && _.filter(queue, function(f) {
return f.name === file.name && f.size === file.size;
}).length) {
if (
file.mode !== "new" &&
_.filter(queue, function(f) {
return f.name === file.name && f.size === file.size;
}).length
) {
file.duplicate = true;
}
});
@@ -238,15 +271,23 @@
var file = queue[index];
// remove from queue
remove(index)
remove(index);
// create and submit data
$.uploadpost({
url : opts.url,
data : opts.initialize(index),
success : function(message) { opts.success(index, message); process();},
error : function(message) { opts.error(index, message); process();},
progress : function(percentage) { opts.progress(index, percentage); }
url: opts.url,
data: opts.initialize(index),
success: function(message) {
opts.success(index, message);
process();
},
error: function(message) {
opts.error(index, message);
process();
},
progress: function(percentage) {
opts.progress(index, percentage);
}
});
}
@@ -287,20 +328,24 @@
// verify browser compatibility
function compatible() {
return window.File && window.FormData && window.XMLHttpRequest && window.FileList;
return (
window.File &&
window.FormData &&
window.XMLHttpRequest &&
window.FileList
);
}
// export functions
return {
'select' : select,
'add' : add,
'remove' : remove,
'start' : start,
'stop' : stop,
'reset' : reset,
'configure' : configure,
'compatible' : compatible
select: select,
add: add,
remove: remove,
start: start,
stop: stop,
reset: reset,
configure: configure,
compatible: compatible
};
}
};
})(jQuery);
+183 -112
View File
@@ -2,47 +2,59 @@
* Galaxy utilities comprises small functions, which at this point
* do not require their own classes/files
*/
define( ['utils/localization'], function(_l) {
define(["utils/localization"], function(_l) {
/** Builds a basic iframe */
function iframe( src ) {
return '<iframe src="' + src + '" frameborder="0" style="width: 100%; height: 100%;"/>';
function iframe(src) {
return (
'<iframe src="' +
src +
'" frameborder="0" style="width: 100%; height: 100%;"/>'
);
}
/** Traverse through json */
function deepeach( dict, callback ) {
for( var i in dict ) {
var d = dict[ i ];
if( _.isObject( d ) ) {
var new_dict = callback( d );
new_dict && ( dict[ i ] = new_dict );
deepeach( d, callback );
function deepeach(dict, callback) {
for (var i in dict) {
var d = dict[i];
if (_.isObject(d)) {
var new_dict = callback(d);
new_dict && (dict[i] = new_dict);
deepeach(d, callback);
}
}
}
/** Identifies urls and replaces them with anchors */
function linkify( inputText ) {
function linkify(inputText) {
var replacedText, replacePattern1, replacePattern2, replacePattern3;
// URLs starting with http://, https://, or ftp://
replacePattern1 = /(\b(https?|ftp):\/\/[-A-Z0-9+&@#\/%?=~_|!:,.;]*[-A-Z0-9+&@#\/%=~_|])/gim;
replacedText = inputText.replace(replacePattern1, '<a href="$1" target="_blank">$1</a>');
replacedText = inputText.replace(
replacePattern1,
'<a href="$1" target="_blank">$1</a>'
);
// URLs starting with "www." (without // before it, or it'd re-link the ones done above).
replacePattern2 = /(^|[^\/])(www\.[\S]+(\b|$))/gim;
replacedText = replacedText.replace(replacePattern2, '$1<a href="http://$2" target="_blank">$2</a>');
replacedText = replacedText.replace(
replacePattern2,
'$1<a href="http://$2" target="_blank">$2</a>'
);
// Change email addresses to mailto:: links.
replacePattern3 = /(([a-zA-Z0-9\-\_\.])+@[a-zA-Z\_]+?(\.[a-zA-Z]{2,6})+)/gim;
replacedText = replacedText.replace(replacePattern3, '<a href="mailto:$1">$1</a>');
replacedText = replacedText.replace(
replacePattern3,
'<a href="mailto:$1">$1</a>'
);
return replacedText;
}
/** Clone */
function clone( obj ) {
return JSON.parse( JSON.stringify( obj ) || null );
function clone(obj) {
return JSON.parse(JSON.stringify(obj) || null);
}
/**
@@ -50,9 +62,15 @@ define( ['utils/localization'], function(_l) {
* @param{String} text - Content to be validated
*/
function isJSON(text) {
return /^[\],:{}\s]*$/.test(text.replace(/\\["\\\/bfnrtu]/g, '@').
replace(/"[^"\\\n\r]*"|true|false|null|-?\d+(?:\.\d*)?(?:[eE][+\-]?\d+)?/g, ']').
replace(/(?:^|:|,)(?:\s*\[)+/g, ''));
return /^[\],:{}\s]*$/.test(
text
.replace(/\\["\\\/bfnrtu]/g, "@")
.replace(
/"[^"\\\n\r]*"|true|false|null|-?\d+(?:\.\d*)?(?:[eE][+\-]?\d+)?/g,
"]"
)
.replace(/(?:^|:|,)(?:\s*\[)+/g, "")
);
}
/**
@@ -60,7 +78,9 @@ define( ['utils/localization'], function(_l) {
* @param{String} content - Content to be sanitized
*/
function sanitize(content) {
return $('<div/>').text(content).html();
return $("<div/>")
.text(content)
.html();
}
/**
@@ -68,15 +88,19 @@ define( ['utils/localization'], function(_l) {
* usually used for selectable options
* @param{String} value - Value or list to be validated
*/
function isEmpty ( value ) {
if ( !( value instanceof Array ) ) {
value = [ value ];
function isEmpty(value) {
if (!(value instanceof Array)) {
value = [value];
}
if ( value.length === 0 ) {
if (value.length === 0) {
return true;
}
for( var i in value ) {
if ( [ '__null__', '__undefined__', null, undefined ].indexOf( value[ i ] ) > -1 ) {
for (var i in value) {
if (
["__null__", "__undefined__", null, undefined].indexOf(
value[i]
) > -1
) {
return true;
}
}
@@ -87,16 +111,16 @@ define( ['utils/localization'], function(_l) {
* Convert list to pretty string
* @param{String} lst - List of strings to be converted in human readable list sentence
*/
function textify( lst ) {
if ( $.isArray( lst ) ) {
var lst = lst.toString().replace( /,/g, ', ' );
var pos = lst.lastIndexOf( ', ' );
if ( pos != -1 ) {
lst = lst.substr( 0, pos ) + ' or ' + lst.substr( pos + 2 );
function textify(lst) {
if ($.isArray(lst)) {
var lst = lst.toString().replace(/,/g, ", ");
var pos = lst.lastIndexOf(", ");
if (pos != -1) {
lst = lst.substr(0, pos) + " or " + lst.substr(pos + 2);
}
return lst;
}
return '';
return "";
}
/**
@@ -106,21 +130,23 @@ define( ['utils/localization'], function(_l) {
* @param{Function} error - Callback on error
* @param{Boolean} cache - Use cached data if available
*/
function get (options) {
function get(options) {
top.__utils__get__ = top.__utils__get__ || {};
var cache_key = JSON.stringify( options );
var cache_key = JSON.stringify(options);
if (options.cache && top.__utils__get__[cache_key]) {
options.success && options.success(top.__utils__get__[cache_key]);
window.console.debug('utils.js::get() - Fetching from cache [' + options.url + '].');
window.console.debug(
"utils.js::get() - Fetching from cache [" + options.url + "]."
);
} else {
request({
url : options.url,
data : options.data,
success : function(response) {
url: options.url,
data: options.data,
success: function(response) {
top.__utils__get__[cache_key] = response;
options.success && options.success(response);
},
error : function(response, status) {
error: function(response, status) {
options.error && options.error(response, status);
}
});
@@ -135,49 +161,52 @@ define( ['utils/localization'], function(_l) {
* @param{Function} success - Callback on success
* @param{Function} error - Callback on error
*/
function request (options) {
function request(options) {
// prepare ajax
var ajaxConfig = {
contentType : 'application/json',
type : options.type || 'GET',
data : options.data || {},
url : options.url
contentType: "application/json",
type: options.type || "GET",
data: options.data || {},
url: options.url
};
// encode data into url
if ( ajaxConfig.type == 'GET' || ajaxConfig.type == 'DELETE' ) {
if ( !$.isEmptyObject(ajaxConfig.data) ) {
ajaxConfig.url += ajaxConfig.url.indexOf('?') == -1 ? '?' : '&';
if (ajaxConfig.type == "GET" || ajaxConfig.type == "DELETE") {
if (!$.isEmptyObject(ajaxConfig.data)) {
ajaxConfig.url += ajaxConfig.url.indexOf("?") == -1 ? "?" : "&";
ajaxConfig.url += $.param(ajaxConfig.data, true);
}
ajaxConfig.data = null;
} else {
ajaxConfig.dataType = 'json';
ajaxConfig.url = ajaxConfig.url;
ajaxConfig.data = JSON.stringify(ajaxConfig.data);
ajaxConfig.dataType = "json";
ajaxConfig.url = ajaxConfig.url;
ajaxConfig.data = JSON.stringify(ajaxConfig.data);
}
// make request
$.ajax(ajaxConfig).done(function(response) {
if (typeof response === 'string') {
try {
response = response.replace('Infinity,', '"Infinity",');
response = jQuery.parseJSON(response);
} catch (e) {
console.debug(e);
$.ajax(ajaxConfig)
.done(function(response) {
if (typeof response === "string") {
try {
response = response.replace("Infinity,", '"Infinity",');
response = jQuery.parseJSON(response);
} catch (e) {
console.debug(e);
}
}
}
options.success && options.success(response);
}).fail(function(response) {
var response_text = null;
try {
response_text = jQuery.parseJSON(response.responseText);
} catch (e) {
response_text = response.responseText;
}
options.error && options.error(response_text, response.status);
}).always(function() {
options.complete && options.complete();
});
options.success && options.success(response);
})
.fail(function(response) {
var response_text = null;
try {
response_text = jQuery.parseJSON(response.responseText);
} catch (e) {
response_text = response.responseText;
}
options.error && options.error(response_text, response.status);
})
.always(function() {
options.complete && options.complete();
});
}
/**
@@ -185,9 +214,9 @@ define( ['utils/localization'], function(_l) {
* @param{String} classname - CSS class
* @param{String} name - CSS property
*/
function cssGetAttribute (classname, name) {
function cssGetAttribute(classname, name) {
var el = $('<div class="' + classname + '"></div>');
el.appendTo(':eq(0)');
el.appendTo(":eq(0)");
var value = el.css(name);
el.remove();
return value;
@@ -197,9 +226,11 @@ define( ['utils/localization'], function(_l) {
* Load a CSS file
* @param{String} url - Url of CSS file
*/
function cssLoadFile (url) {
function cssLoadFile(url) {
if (!$('link[href^="' + url + '"]').length) {
$('<link href="' + Galaxy.root + url + '" rel="stylesheet">').appendTo('head');
$(
'<link href="' + Galaxy.root + url + '" rel="stylesheet">'
).appendTo("head");
}
}
@@ -208,7 +239,7 @@ define( ['utils/localization'], function(_l) {
* @param{Object} options - Target dictionary
* @param{Object} optionsDefault - Source dictionary
*/
function merge (options, optionsDefault) {
function merge(options, optionsDefault) {
if (options) {
return _.defaults(options, optionsDefault);
} else {
@@ -216,18 +247,17 @@ define( ['utils/localization'], function(_l) {
}
}
/**
* Round floaing point 'number' to 'numPlaces' number of decimal places.
* @param{Object} number a floaing point number
* @param{Object} numPlaces number of decimal places
*/
function roundToDecimalPlaces( number, numPlaces ){
function roundToDecimalPlaces(number, numPlaces) {
var placesMultiplier = 1;
for( var i=0; i<numPlaces; i++ ){
for (var i = 0; i < numPlaces; i++) {
placesMultiplier *= 10;
}
return Math.round( number * placesMultiplier ) / placesMultiplier;
return Math.round(number * placesMultiplier) / placesMultiplier;
}
// calculate on import
@@ -240,65 +270,106 @@ define( ['utils/localization'], function(_l) {
* @param{Integer} size - Size in bytes
* @param{Boolean} normal_font - Switches font between normal and bold
*/
function bytesToString (size, normal_font, numberPlaces) {
numberPlaces = numberPlaces !== undefined? numberPlaces: 1;
function bytesToString(size, normal_font, numberPlaces) {
numberPlaces = numberPlaces !== undefined ? numberPlaces : 1;
// identify unit
var unit = "";
if (size >= tb){ size = size / tb; unit = 'TB'; } else
if (size >= gb){ size = size / gb; unit = 'GB'; } else
if (size >= mb){ size = size / mb; unit = 'MB'; } else
if (size >= kb){ size = size / kb; unit = 'KB'; } else
if (size > 0){ unit = 'b'; }
else { return normal_font? '0 b': '<strong>-</strong>'; }
// return formatted string
var rounded = unit == 'b'? size: roundToDecimalPlaces( size, numberPlaces );
if (normal_font) {
return rounded + ' ' + unit;
if (size >= tb) {
size = size / tb;
unit = "TB";
} else if (size >= gb) {
size = size / gb;
unit = "GB";
} else if (size >= mb) {
size = size / mb;
unit = "MB";
} else if (size >= kb) {
size = size / kb;
unit = "KB";
} else if (size > 0) {
unit = "b";
} else {
return '<strong>' + rounded + '</strong> ' + unit;
return normal_font ? "0 b" : "<strong>-</strong>";
}
// return formatted string
var rounded =
unit == "b" ? size : roundToDecimalPlaces(size, numberPlaces);
if (normal_font) {
return rounded + " " + unit;
} else {
return "<strong>" + rounded + "</strong> " + unit;
}
}
/** Create a unique id */
function uid(){
function uid() {
top.__utils__uid__ = top.__utils__uid__ || 0;
return 'uid-' + top.__utils__uid__++;
return "uid-" + top.__utils__uid__++;
}
/** Create a time stamp */
function time() {
var d = new Date();
var hours = (d.getHours() < 10 ? "0" : "") + d.getHours();
var minutes = (d.getMinutes() < 10 ? "0" : "") + d.getMinutes()
return datetime = d.getDate() + "/"
+ (d.getMonth() + 1) + "/"
+ d.getFullYear() + ", "
+ hours + ":"
+ minutes;
var minutes = (d.getMinutes() < 10 ? "0" : "") + d.getMinutes();
return (datetime =
d.getDate() +
"/" +
(d.getMonth() + 1) +
"/" +
d.getFullYear() +
", " +
hours +
":" +
minutes);
}
/** Append script and style tags to Galaxy main application */
function appendScriptStyle( data ) {
function appendScriptStyle(data) {
// create a script tag inside head tag
if( data.script && data.script !== "" ) {
$( '<script/>', { type: 'text/javascript' } ).text( data.script ).appendTo( 'head' );
if (data.script && data.script !== "") {
$("<script/>", { type: "text/javascript" })
.text(data.script)
.appendTo("head");
}
// create a style tag inside head tag
if( data.styles && data.styles !== "" ) {
$( '<style/>', { type: 'text/css' } ).text( data.styles ).appendTo( 'head' );
if (data.styles && data.styles !== "") {
$("<style/>", { type: "text/css" })
.text(data.styles)
.appendTo("head");
}
}
/** Get querystrings from url */
function getQueryString( key ) {
return decodeURIComponent( window.location.search.replace(new RegExp("^(?:.*[&\\?]" + encodeURIComponent( key ).replace(/[\.\+\*]/g, "\\$&") + "(?:\\=([^&]*))?)?.*$", "i"), "$1") );
function getQueryString(key) {
return decodeURIComponent(
window.location.search.replace(
new RegExp(
"^(?:.*[&\\?]" +
encodeURIComponent(key).replace(/[\.\+\*]/g, "\\$&") +
"(?:\\=([^&]*))?)?.*$",
"i"
),
"$1"
)
);
}
function setWindowTitle(title){
function setWindowTitle(title) {
if (title) {
window.document.title = "Galaxy " + (window.Galaxy.config.brand ? " | " + window.Galaxy.config.brand : '') + " | " + _l(title);
window.document.title =
"Galaxy " +
(window.Galaxy.config.brand
? " | " + window.Galaxy.config.brand
: "") +
" | " +
_l(title);
} else {
window.document.title = "Galaxy " + (window.Galaxy.config.brand ? " | " + window.Galaxy.config.brand : '');
window.document.title =
"Galaxy " +
(window.Galaxy.config.brand
? " | " + window.Galaxy.config.brand
: "");
}
}
+23 -14
View File
@@ -1,11 +1,11 @@
define( ["viz/visualization", "libs/bbi/bigwig"],
function(visualization, bigwig) {
define(["viz/visualization", "libs/bbi/bigwig"], function(
visualization,
bigwig
) {
/**
* Data manager for BBI datasets/files, including BigWig and BigBed.
*/
var BBIDataManager = visualization.GenomeDataManager.extend({
/**
* Load data from server and manage data entries. Adds a Deferred to manager
* for region; when data becomes available, replaces Deferred with data.
@@ -15,11 +15,21 @@ define( ["viz/visualization", "libs/bbi/bigwig"],
var deferred = $.Deferred();
this.set_data(region, deferred);
var url = Galaxy.root + 'datasets/' + this.get('dataset').id + '/display',
var url =
Galaxy.root +
"datasets/" +
this.get("dataset").id +
"/display",
self = this;
var promise = new $.Deferred();
$.when(bigwig.makeBwg(url)).then(function(bb, err) {
$.when(bb.readWigData(region.get("chrom"), region.get("start"), region.get("end"))).then(function(data) {
var promise = new $.Deferred();
$.when(bigwig.makeBwg(url)).then(function(bb, err) {
$.when(
bb.readWigData(
region.get("chrom"),
region.get("start"),
region.get("end")
)
).then(function(data) {
// Transform data into "bigwig" format for LinePainter. "bigwig" format is an array of 2-element arrays
// where each element is [position, score]; unlike real bigwig format, no gaps are allowed.
var result = [],
@@ -46,10 +56,10 @@ define( ["viz/visualization", "libs/bbi/bigwig"],
});
var entry = {
data: result,
region: region,
dataset_type: 'bigwig'
};
data: result,
region: region,
dataset_type: "bigwig"
};
self.set_data(region, entry);
deferred.resolve(entry);
@@ -57,11 +67,10 @@ define( ["viz/visualization", "libs/bbi/bigwig"],
});
return deferred;
},
}
});
return {
BBIDataManager: BBIDataManager
};
});
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
+115 -79
View File
@@ -1,100 +1,136 @@
define( ["libs/underscore"], function(_) {
/**
define(["libs/underscore"], function(_) {
/**
* View for track/group header.
*/
var TrackHeaderView = Backbone.View.extend({
className: 'track-header',
var TrackHeaderView = Backbone.View.extend({
className: "track-header",
initialize: function() {
// Watch and update name changes.
this.model.config.get('name').on('change:value', this.update_name, this);
this.render();
},
initialize: function() {
// Watch and update name changes.
this.model.config
.get("name")
.on("change:value", this.update_name, this);
this.render();
},
render: function() {
this.$el.append($("<div/>").addClass(this.model.drag_handle_class));
this.$el.append($("<div/>").addClass("track-name")
.text(this.model.config.get_value('name')));
render: function() {
this.$el.append($("<div/>").addClass(this.model.drag_handle_class));
this.$el.append(
$("<div/>")
.addClass("track-name")
.text(this.model.config.get_value("name"))
);
// Icons container.
this.action_icons = {};
this.render_action_icons();
// Icons container.
this.action_icons = {};
this.render_action_icons();
// Suppress double clicks in header so that they do not impact viz under header.
this.$el.dblclick( function(e) { e.stopPropagation(); } );
// Needed for floating elts in header.
this.$el.append( $("<div style='clear: both'/>") );
},
// Suppress double clicks in header so that they do not impact viz under header.
this.$el.dblclick(function(e) {
e.stopPropagation();
});
update_name: function() {
this.$el.find('.track-name').text(this.model.config.get_value('name'));
},
// Needed for floating elts in header.
this.$el.append($("<div style='clear: both'/>"));
},
render_action_icons: function() {
var self = this;
this.icons_div = $("<div/>").addClass('track-icons').hide().appendTo(this.$el);
_.each(this.model.action_icons_def, function(icon_dict) {
self.add_action_icon(icon_dict.name, icon_dict.title, icon_dict.css_class,
icon_dict.on_click_fn, icon_dict.prepend, icon_dict.hide);
});
update_name: function() {
this.$el
.find(".track-name")
.text(this.model.config.get_value("name"));
},
// Set up behavior for modes popup.
this.set_display_modes(this.model.display_modes);
},
render_action_icons: function() {
var self = this;
this.icons_div = $("<div/>")
.addClass("track-icons")
.hide()
.appendTo(this.$el);
_.each(this.model.action_icons_def, function(icon_dict) {
self.add_action_icon(
icon_dict.name,
icon_dict.title,
icon_dict.css_class,
icon_dict.on_click_fn,
icon_dict.prepend,
icon_dict.hide
);
});
/**
// Set up behavior for modes popup.
this.set_display_modes(this.model.display_modes);
},
/**
* Add an action icon to this object. Appends icon unless prepend flag is specified.
*/
add_action_icon: function(name, title, css_class, on_click_fn, prepend, hide) {
var self = this;
this.action_icons[name] = $("<a/>").attr("title", title)
.addClass("icon-button").addClass(css_class).tooltip()
.click( function() { on_click_fn(self.model); } )
.appendTo(this.icons_div);
if (hide) {
this.action_icons[name].hide();
}
},
add_action_icon: function(
name,
title,
css_class,
on_click_fn,
prepend,
hide
) {
var self = this;
this.action_icons[name] = $("<a/>")
.attr("title", title)
.addClass("icon-button")
.addClass(css_class)
.tooltip()
.click(function() {
on_click_fn(self.model);
})
.appendTo(this.icons_div);
if (hide) {
this.action_icons[name].hide();
}
},
/**
/**
* Set track's modes and update mode icon popup.
*/
set_display_modes: function(new_modes, init_mode) {
if (!new_modes) { return; }
set_display_modes: function(new_modes, init_mode) {
if (!new_modes) {
return;
}
// HACK: move this out of view and into track.
// HACK: move this out of view and into track.
// Set modes, init mode.
this.model.display_modes = new_modes;
this.model.mode = (init_mode || this.model.config.get_value('mode') || this.model.display_modes[0]);
this.action_icons.mode_icon.attr("title", "Set display mode (now: " + this.mode + ")");
// Set modes, init mode.
this.model.display_modes = new_modes;
this.model.mode =
init_mode ||
this.model.config.get_value("mode") ||
this.model.display_modes[0];
// Setup popup menu for changing modes.
var self = this,
track = this.model,
mode_mapping = {};
for (var i = 0, len = track.display_modes.length; i < len; i++) {
var mode = track.display_modes[i];
mode_mapping[mode] = function(mode) {
return function() {
track.change_mode(mode);
// HACK: the popup menu messes with the track's hover event, so manually show/hide
// icons div for now.
//self.icons_div.show();
//track.container_div.mouseleave(function() { track.icons_div.hide(); } );
};
}(mode);
this.action_icons.mode_icon.attr(
"title",
"Set display mode (now: " + this.mode + ")"
);
// Setup popup menu for changing modes.
var self = this,
track = this.model,
mode_mapping = {};
for (var i = 0, len = track.display_modes.length; i < len; i++) {
var mode = track.display_modes[i];
mode_mapping[mode] = (function(mode) {
return function() {
track.change_mode(mode);
// HACK: the popup menu messes with the track's hover event, so manually show/hide
// icons div for now.
//self.icons_div.show();
//track.container_div.mouseleave(function() { track.icons_div.hide(); } );
};
})(mode);
}
make_popupmenu(this.action_icons.mode_icon, mode_mapping);
}
});
make_popupmenu(this.action_icons.mode_icon, mode_mapping);
}
return {
TrackHeaderView: TrackHeaderView
};
});
return {
TrackHeaderView: TrackHeaderView
};
});
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
+1 -1
View File
@@ -1 +1 @@
{"version":3,"sources":["layout/router.js"],"names":["jQuery","require","$","Ui","Router","extend","initialize","page","options","this","push","url","data","__identifer","Math","toString","substr","random","isEmptyObject","param","navigate","trigger","execute","callback","args","name","Galaxy","debug","QUERY_STRING","queryObj","apply","authenticate","access_denied","display","Message","status","message","persistent","module","exports"],"mappings":"aAAA,IAAIA,OAASC,QAAS,UAClBC,EAAIF,OADJA,aAASC,QAAS,8BAClBC,GAAIF,QADR,kBAGIG,OAAKF,SAASG,OAAAC,QAGdC,WAAA,SAAAC,EAAAC,GACAC,KAAAF,KAAAA,EACAD,KAAAA,QAAaE,GAMbE,KAAM,SAAUC,EAAKC,IADrBA,EAAAA,OACMC,YAAAC,KAAeF,SAAOG,SAAA,IAAAC,OAAA,GACxBJ,EAAOA,cAAPA,KACAA,IAAiCG,GAA5BF,EAAAA,QAAmBI,KAASF,IAAd,IACdJ,GAAGO,EAAAA,MAAFN,GAAN,IAEID,OAAAA,OAASQ,EACZV,KAAAW,SAAAT,GAAAU,SAAA,KAMLC,QAAS,SAAUC,EAAUC,EAAMC,GADnCC,OAAAC,MAAA,kBAAAJ,EAAAC,EAAAC,GACAH,IAAAA,EAASM,aAAUL,MAAUC,EAAMC,OAC/BC,EAAAA,KAAOC,GACHE,IACJpB,KAAWoB,aAAXL,EAAAC,GACIF,EAAUO,MAAArB,KAAAe,GAEND,KAAAA,kBAKXQ,aAhC+B,SAAAP,EAAAC,GAmC5B,OAAO,GACVO,cApC+B,WAuC5BvB,KAAKF,KAAK0B,QAAS,IAAI9B,GAAG+B,SAAWC,OAAQ,SAAUC,QAAS,sEAAuEC,YAAY,QAvCvHC,OAApCC,QAAAnC","file":"../../scripts/layout/router.js","sourcesContent":["var jQuery = require( 'jquery' ),\n $ = jQuery,\n QUERY_STRING = require( 'utils/query-string-parsing' ),\n Ui = require( 'mvc/ui/ui-misc' );\n\nvar Router = Backbone.Router.extend({\n // TODO: not many client routes at this point - fill and remove from server.\n // since we're at root here, this may be the last to be routed entirely on the client.\n initialize : function( page, options ){\n this.page = page;\n this.options = options;\n },\n\n /** helper to push a new navigation state */\n push: function( url, data ) {\n data = data || {};\n data.__identifer = Math.random().toString( 36 ).substr( 2 );\n if ( !$.isEmptyObject( data ) ) {\n url += url.indexOf( '?' ) == -1 ? '?' : '&';\n url += $.param( data , true );\n }\n Galaxy.params = data;\n this.navigate( url, { 'trigger': true } );\n },\n\n /** override to parse query string into obj and send to each route */\n execute: function( callback, args, name ){\n Galaxy.debug( 'router execute:', callback, args, name );\n var queryObj = QUERY_STRING.parse( args.pop() );\n args.push( queryObj );\n if( callback ){\n if ( this.authenticate( args, name ) ) {\n callback.apply( this, args );\n } else {\n this.access_denied();\n }\n }\n },\n\n authenticate: function( args, name ) {\n return true;\n },\n\n access_denied: function() {\n this.page.display( new Ui.Message( { status: 'danger', message: 'You must be logged in with proper credentials to make this request.', persistent: true } ) );\n }\n});\n\nmodule.exports = Router"]}
{"version":3,"sources":["layout/router.js"],"names":["jQuery","require","$","Ui","Router","extend","initialize","page","options","this","push","url","data","__identifer","Math","isEmptyObject","indexOf","param","Galaxy","params","navigate","trigger","execute","callback","name","debug","args","queryObj","QUERY_STRING","parse","pop","authenticate","apply","access_denied","display","status","message","persistent","module","exports"],"mappings":"aAAA,IAAIA,OAASC,QAAQ,UACjBC,EAAIF,OADJA,aAASC,QAAQ,8BACjBC,GAAIF,QADR,kBAGIG,OAAKF,SAAQG,OAAAC,QAGbC,WAAA,SAAAC,EAAAC,GACAC,KAAAF,KAAAA,EACAD,KAAAA,QAAYE,GAMZE,KAAM,SAASC,EAAKC,IADpBA,EAAAA,OACMC,YAAAC,KAAcF,SAChBA,SAAOA,IACFC,OAAAA,GAGAX,EAAEa,cAAcH,KACjBD,IAA4B,GAArBA,EAAIK,QAAQ,KAAa,IAAM,IACtCL,GAAOT,EAAEe,MAAML,GAAM,IAEzBM,OAAOC,OAASP,EAChBH,KAAKW,SAAST,GAAOU,SAAS,KAIlCC,QAAS,SAAAC,EAASA,EAATC,GACLN,OAAOO,MAAM,kBAAmBF,EAAUG,EAAMF,GAChD,IAAIG,EAAWC,aAAaC,MAAMH,EAAKI,OACvCJ,EAAKhB,KAAKiB,GACNJ,IACId,KAAKsB,aAAaL,EAAMF,GACxBD,EAASS,MAAMvB,KAAMiB,GAErBjB,KAAKwB,kBAKjBF,aAAc,SAAAL,EAAAF,GACV,OAAO,GAGXS,cAAe,WACXxB,KAAKF,KAAK2B,QAEFC,IAAAA,GAAAA,SACAC,OACI,SACJC,QALR,sEAQHA,YAAA,QAGLC,OAAOC,QAAUnC","file":"../../scripts/layout/router.js","sourcesContent":["var jQuery = require(\"jquery\"),\n $ = jQuery,\n QUERY_STRING = require(\"utils/query-string-parsing\"),\n Ui = require(\"mvc/ui/ui-misc\");\n\nvar Router = Backbone.Router.extend({\n // TODO: not many client routes at this point - fill and remove from server.\n // since we're at root here, this may be the last to be routed entirely on the client.\n initialize: function(page, options) {\n this.page = page;\n this.options = options;\n },\n\n /** helper to push a new navigation state */\n push: function(url, data) {\n data = data || {};\n data.__identifer = Math.random()\n .toString(36)\n .substr(2);\n if (!$.isEmptyObject(data)) {\n url += url.indexOf(\"?\") == -1 ? \"?\" : \"&\";\n url += $.param(data, true);\n }\n Galaxy.params = data;\n this.navigate(url, { trigger: true });\n },\n\n /** override to parse query string into obj and send to each route */\n execute: function(callback, args, name) {\n Galaxy.debug(\"router execute:\", callback, args, name);\n var queryObj = QUERY_STRING.parse(args.pop());\n args.push(queryObj);\n if (callback) {\n if (this.authenticate(args, name)) {\n callback.apply(this, args);\n } else {\n this.access_denied();\n }\n }\n },\n\n authenticate: function(args, name) {\n return true;\n },\n\n access_denied: function() {\n this.page.display(\n new Ui.Message({\n status: \"danger\",\n message:\n \"You must be logged in with proper credentials to make this request.\",\n persistent: true\n })\n );\n }\n});\n\nmodule.exports = Router;\n"]}
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
+1 -1
View File
@@ -1 +1 @@
{"version":3,"sources":["mvc/webhooks.js"],"names":["define","WebhookModel","Backbone","Model","extend","defaults","Webhooks","Collection","model","WebhookView","View","el","options","me","this","initialize","toolId","toolVersion","$el","attr","urlRoot","fetch","success","render","webhook","toJSON","name","styles","$","type","text","appendTo","html","script","webhooks","Galaxy","url","callback"],"mappings":"aAGAA,UAAW,WAAXA,IAAOC,EAAeC,SAAAC,MAAAC,QAGdC,UADAJ,UAAAA,KAAJK,EAAAJ,SAAAK,WAAAH,QAOII,MAAOP,IADXQ,EAAAP,SAAAQ,KAAAN,QAKIO,GAAI,gBAAJA,WAAI,SAD+BC,GAI/B,IAAIC,EAAKC,KADbC,EAAYH,EAAAI,QAASJ,GACbC,EAAJD,EAAAK,aAAA,GAEAH,KAAIG,IAAAA,KAAAA,UAAcL,GAGlBE,KAAKI,IAAIC,KAAK,eAAgBF,GAA9BH,KAAKI,MAAL,IAAcjB,EAGda,KAAKN,MAAMY,QAAUR,EAAQQ,QAD7BN,KAAKN,MAALa,OACAC,QAAWF,WACNZ,EAAMa,aAKdE,OAlBkC,WAqB/B,IAAIC,EAAUV,KAAKN,MAAMiB,SAIzB,OAJAX,KAAIU,IAAAA,KAAU,YAAWC,EAAzBC,KAAA,YAGIF,EAAQG,QAAQC,EAAE,YAAaC,KAAM,aAAaC,KAAKN,EAAQG,QAAQI,SAAS,QADpFP,EAASQ,QAAKJ,EAAA,aAAsBF,KAAtB,oBAAdI,KAAAN,EAAAS,QAAAF,SAAA,QAEIP,QAcX,OAGGlB,SAAUA,EADdG,YAAOA,EACHH,IA1CJ,SAAAM,GAgCI,IAAIsB,EAAW,IAAI5B,EAAnB4B,EAAIA,IAAWC,OAAI7B,KAAnBM,EAAAwB,IAGAF,EAASb,OADTa,OAASE,EAAMD,OAAAvB,EAAcA,MAC7BsB,QAASb,EAAMgB","file":"../../scripts/mvc/webhooks.js","sourcesContent":["/**\n Webhooks\n**/\ndefine([], function() {\n\n var WebhookModel = Backbone.Model.extend({\n defaults: {\n activate: false\n }\n });\n\n var Webhooks = Backbone.Collection.extend({\n model: WebhookModel\n });\n\n var WebhookView = Backbone.View.extend({\n el: '#webhook-view',\n\n initialize: function(options) {\n var me = this;\n var toolId = options.toolId || '';\n var toolVersion = options.toolVersion || '';\n\n this.$el.attr('tool_id', toolId);\n this.$el.attr('tool_version', toolVersion);\n\n this.model = new WebhookModel();\n this.model.urlRoot = options.urlRoot;\n this.model.fetch({\n success: function() {\n me.render();\n }\n });\n },\n\n render: function() {\n var webhook = this.model.toJSON();\n\n this.$el.html('<div id=\"' + webhook.name + '\"></div>');\n if (webhook.styles) $('<style/>', {type: 'text/css'}).text(webhook.styles).appendTo('head');\n if (webhook.script) $('<script/>', {type: 'text/javascript'}).text(webhook.script).appendTo('head');\n\n return this;\n }\n });\n\n var add = function(options) {\n var webhooks = new Webhooks();\n\n webhooks.url = Galaxy.root + options.url;\n webhooks.fetch({\n async: options.async ? options.async : true,\n success: options.callback\n });\n };\n\n return {\n Webhooks: Webhooks,\n WebhookView: WebhookView,\n add: add\n };\n});\n"]}
{"version":3,"sources":["mvc/webhooks.js"],"names":["define","WebhookModel","Backbone","Model","extend","defaults","Webhooks","Collection","model","WebhookView","View","el","options","me","this","initialize","toolId","toolVersion","$el","attr","urlRoot","fetch","success","render","webhook","toJSON","add","name","styles","$","type","script","text","appendTo","webhooks","async","url","Galaxy","root","callback"],"mappings":"aAGAA,UAAW,WACP,IAAIC,EAAeC,SAASC,MAAMC,QAD/BC,UACCJ,UAAAA,KAAJK,EAAAJ,SAAAK,WAAAH,QAOII,MAAOP,IADXQ,EAAAP,SAAAQ,KAAAN,QAKIO,GAAI,gBAAJA,WAAI,SAD+BC,GAI/B,IAAIC,EAAKC,KADbC,EAAYH,EAAAI,QAASJ,GACbC,EAAJD,EAAAK,aAAA,GAEAH,KAAIG,IAAAA,KAAAA,UAAcL,GAGlBE,KAAKI,IAAIC,KAAK,eAAgBF,GAA9BH,KAAKI,MAAL,IAAcjB,EAGda,KAAKN,MAAMY,QAAUR,EAAQQ,QAD7BN,KAAKN,MAALa,OACAC,QAAWF,WACNZ,EAAMa,aAKdE,OAlBkC,WAqB/B,IAAIC,EAAUV,KAAKN,MAAMiB,SAgB7BC,OAhBIZ,KAAIU,IAAAA,KAAU,YAAWC,EAAzBE,KAAA,YAGIH,EAAQI,QADZC,EAAKX,YAASY,KAAA,aACVN,KAAQI,EACNA,QAGFJ,SAAJ,QAAIA,EAAQO,QAKZF,EAAA,aAAAC,KAAA,oBACHE,KAAAR,EAAAO,QAlCLE,SAAA,QAqCUnB,QAUH,OA1DXR,SAAAA,EA4DQG,YAAaA,EACbiB,IATAQ,SAAetB,GACXuB,IAAAA,EAAOvB,IAAQuB,EADJD,EAAfE,IAAAC,OAAAC,KAAA1B,EAAAwB,IAJJF,EAAAb,OAKQc,OAAOvB,EAAQuB,OAAQvB,EAAQuB,MAKhCb,QAAAV,EAAA2B","file":"../../scripts/mvc/webhooks.js","sourcesContent":["/**\n Webhooks\n**/\ndefine([], function() {\n var WebhookModel = Backbone.Model.extend({\n defaults: {\n activate: false\n }\n });\n\n var Webhooks = Backbone.Collection.extend({\n model: WebhookModel\n });\n\n var WebhookView = Backbone.View.extend({\n el: \"#webhook-view\",\n\n initialize: function(options) {\n var me = this;\n var toolId = options.toolId || \"\";\n var toolVersion = options.toolVersion || \"\";\n\n this.$el.attr(\"tool_id\", toolId);\n this.$el.attr(\"tool_version\", toolVersion);\n\n this.model = new WebhookModel();\n this.model.urlRoot = options.urlRoot;\n this.model.fetch({\n success: function() {\n me.render();\n }\n });\n },\n\n render: function() {\n var webhook = this.model.toJSON();\n\n this.$el.html('<div id=\"' + webhook.name + '\"></div>');\n if (webhook.styles)\n $(\"<style/>\", { type: \"text/css\" })\n .text(webhook.styles)\n .appendTo(\"head\");\n if (webhook.script)\n $(\"<script/>\", { type: \"text/javascript\" })\n .text(webhook.script)\n .appendTo(\"head\");\n\n return this;\n }\n });\n\n var add = function(options) {\n var webhooks = new Webhooks();\n\n webhooks.url = Galaxy.root + options.url;\n webhooks.fetch({\n async: options.async ? options.async : true,\n success: options.callback\n });\n };\n\n return {\n Webhooks: Webhooks,\n WebhookView: WebhookView,\n add: add\n };\n});\n"]}
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
+1 -1
View File
@@ -1 +1 @@
{"version":3,"sources":["polyfills.js"],"names":["window","console","log","debug","info","Object","assign","_","extend","vendors","x","length","requestAnimationFrame","lastTime","callback","element","getTime","currTime","Date","timeToCall","Math","id","cancelAnimationFrame","incompatibilities","features","compatible","CanvasRenderingContext2D","name","sessionStorage","err","filter","feature","map","root","document","querySelectorAll","item","location","join"],"mappings":"cAKA,WASIA,OAAAC,QAAAD,OAAAC,UACAC,IAAA,aAGIC,MAAU,aAFdH,KAAA,aACIE,KAAU,aACVC,MAAU,aACVC,OAAU,cAHqBC,OAAnCC,OAAAD,OAAAC,QAAAC,EAAAC,OAYA,IAAA,IAFAH,EAAA,EAIII,GAAW,KAAM,MAAO,SAAU,KAFtCC,EAAA,EAAAA,EAAAD,EAAAE,SAAAX,OAAAY,wBAAAF,EACIG,OAAAA,sBAAJb,OAAAS,EAAAC,GAAA,yBACID,OAAAA,4BAAUT,OAAdS,EAAAC,GACI,+BAIHV,OAAAY,wBAGGZ,OAAOY,sBAAwB,SAASE,EAAUC,GADjDf,IAAOY,GAAAA,IAAAA,MAAZI,UAEYC,EAAeC,KAAOF,IAAAA,EAAX,IAAfC,EAAAJ,IACIM,EAAAA,OAAaC,WAAY,WAAMH,EAAWJ,EAA9CM,IACIE,GACFF,OADsCL,EAASG,EAAWE,EAC1DA,IAJNnB,OAAAsB,uBAUAtB,OAAOsB,qBAAuB,SAASD,GADtCrB,aAAOsB,KAKZ,IAWIC,IAVAC,KAAAA,SACAC,WAA4BA,WAAY,OAAAzB,OAAU0B,4BAAEC,KAAA,iBAAcD,WAAAA,WADvD,IAGH,OAAA1B,OAAA4B,eAAAjB,QAAA,EACA,MAAAkB,IACH,OAAC,KAMwBC,OAAQC,SAAQN,GAAhB,OAAAM,EAAAN,eAC7BO,IAAK,SAAUD,GAAS,OAAAA,EAAAJ,OAG7B,GAAMJ,EAAkBZ,OAAQ,CADhC,IAAAsB,EAAAC,SAAAC,iBAAA,qBAAAC,KAAA,GACMb,IACEU,OAAOC,SAASC,EAAAA,KAAAA,oCAEhBnC,QAAAA,IAAOqC,0BAAuBd,EAAAe,KAAA,QAlE1C","file":"../scripts/polyfills.js","sourcesContent":["/**\n Perform feature inference and redirect if below some minimum (must have canvas, etc.)\n and polyfill for non-standard features.\n */\n\n(function() {\n /* TODO: move to modernizr or something besides us doing this...\n * These are across all of our apps (reports, tool shed), but:\n * these should be configurable via options because they all need different things.\n * So, analysis-polyfills.js, reports-polyfills.js (or analysis/polyfills)\n */\n 'use strict';\n /*globals window, clearTimeout */\n\n // ------------------------------------------------------------------ polyfills\n // console protection needed in some versions of IE (at this point (IE>=9), shouldn't be needed)\n window.console = window.console || {\n log : function(){},\n debug : function(){},\n info : function(){},\n warn : function(){},\n error : function(){},\n assert : function(){}\n };\n\n // phantomjs: does not have the native extend fn assign\n Object.assign = Object.assign || _.extend;\n\n // requestAnimationFrame polyfill\n var lastTime = 0;\n var vendors = ['ms', 'moz', 'webkit', 'o'];\n for(var x = 0; x < vendors.length && !window.requestAnimationFrame; ++x) {\n window.requestAnimationFrame = window[vendors[x]+'RequestAnimationFrame'];\n window.cancelRequestAnimationFrame = window[vendors[x]+\n 'CancelRequestAnimationFrame'];\n }\n\n if (!window.requestAnimationFrame)\n window.requestAnimationFrame = function(callback, element) {\n var currTime = new Date().getTime();\n var timeToCall = Math.max(0, 16 - (currTime - lastTime));\n var id = window.setTimeout(function() { callback(currTime + timeToCall); },\n timeToCall);\n lastTime = currTime + timeToCall;\n return id;\n };\n\n if (!window.cancelAnimationFrame)\n window.cancelAnimationFrame = function(id) {\n clearTimeout(id);\n };\n\n // ------------------------------------------------------------------ can't/won't polyfill\n var features = [\n { name: 'canvas', compatible: function(){ return window.CanvasRenderingContext2D; } },\n { name: 'sessionStorage', compatible: function(){\n try {\n return window.sessionStorage.length >= 0;\n } catch( err ){}\n return false;\n }},\n ];\n // build a list of feature names for features that were not found\n var incompatibilities = features\n .filter( function( feature ){ return !feature.compatible(); })\n .map( function( feature ){ return feature.name; });\n\n // if there are needed features missing, follow the index link to the static incompat warning\n if( !!incompatibilities.length ){\n var root = document.querySelectorAll( 'link[rel=\"index\"]' ).item( 0 );\n if( root ){\n window.location = root.href + 'static/incompatible-browser.html';\n }\n console.log( 'incompatible browser:\\n' + incompatibilities.join( '\\n' ) );\n }\n\n})();\n"]}
{"version":3,"sources":["polyfills.js"],"names":["window","console","log","debug","info","Object","assign","_","extend","vendors","x","length","requestAnimationFrame","lastTime","currTime","Date","getTime","timeToCall","Math","max","id","setTimeout","callback","cancelAnimationFrame","clearTimeout","name","incompatibilities","compatible","CanvasRenderingContext2D","sessionStorage","err","filter","feature","map","root","document","querySelectorAll","item","location","href","join"],"mappings":"cAKA,WASIA,OAAAC,QAAAD,OAAAC,UACAC,IAAA,aAGIC,MAAO,aAFXH,KAAOC,aACHC,KAAK,aACLC,MAAO,aACPC,OAAM,cAHyBC,OAAnCC,OAAAD,OAAAC,QAAAC,EAAAC,OAYA,IAAA,IAFAH,EAAA,EAIII,GAAW,KAAM,MAAO,SAAU,KAFtCC,EAAA,EAAAA,EAAAD,EAAAE,SAAAX,OAAAY,wBAAAF,EACIG,OAAAA,sBACAJ,OAAWA,EAAMC,GAAO,yBAC5BV,OAASU,4BACLV,OAAOY,EAAAA,GAAAA,+BAMNZ,OAAOY,wBAARZ,OAACA,sBACDA,SAAOY,EAAAA,GACCE,IAAAA,GAAeC,IAAJA,MAAWC,UACtBC,EAAaC,KAASC,IAAG,EAAA,IAAML,EAAWD,IAC1CO,EAAKpB,OAAOqB,WAAW,WACvBC,EAASR,EAAWG,IACrBA,GAEH,OADAJ,EAAWC,EAAWG,EACtBG,IAGHpB,OAAOuB,uBAEJC,OAAAA,qBAAA,SAAAJ,GADJI,aAAAJ,KAOIK,IAgBRC,IAdYD,KAAA,SACHE,WAAA,WAEL,OAAA3B,OAAA4B,4BAIYH,KAAA,iBACHE,WAAC,WACF,IACH,OAAA3B,OAAA6B,eAAAlB,QAAA,EAdT,MAAAmB,IAiBA,OAAA,KAEKC,OAAO,SAASC,GAOrB,OAAAA,EAAAL,eAEIM,IAAIC,SAAOC,GACPD,OAAMF,EAAAP,OAIb,GAAAC,EAAAf,OAAA,CAnFL,IAAAuB,EAAAC,SAAAC,iBAAA,qBAAAC,KAAA,GA+EYH,IACAlC,OAAOsC,SAAWJ,EAAKK,KAAO,oCAElCtC,QAAQC,IAAI,0BAA4BwB,EAAkBc,KAAK,QAlFvE","file":"../scripts/polyfills.js","sourcesContent":["/**\n Perform feature inference and redirect if below some minimum (must have canvas, etc.)\n and polyfill for non-standard features.\n */\n\n(function() {\n /* TODO: move to modernizr or something besides us doing this...\n * These are across all of our apps (reports, tool shed), but:\n * these should be configurable via options because they all need different things.\n * So, analysis-polyfills.js, reports-polyfills.js (or analysis/polyfills)\n */\n \"use strict\";\n /*globals window, clearTimeout */\n\n // ------------------------------------------------------------------ polyfills\n // console protection needed in some versions of IE (at this point (IE>=9), shouldn't be needed)\n window.console = window.console || {\n log: function() {},\n debug: function() {},\n info: function() {},\n warn: function() {},\n error: function() {},\n assert: function() {}\n };\n\n // phantomjs: does not have the native extend fn assign\n Object.assign = Object.assign || _.extend;\n\n // requestAnimationFrame polyfill\n var lastTime = 0;\n var vendors = [\"ms\", \"moz\", \"webkit\", \"o\"];\n for (var x = 0; x < vendors.length && !window.requestAnimationFrame; ++x) {\n window.requestAnimationFrame =\n window[vendors[x] + \"RequestAnimationFrame\"];\n window.cancelRequestAnimationFrame =\n window[vendors[x] + \"CancelRequestAnimationFrame\"];\n }\n\n if (!window.requestAnimationFrame)\n window.requestAnimationFrame = function(callback, element) {\n var currTime = new Date().getTime();\n var timeToCall = Math.max(0, 16 - (currTime - lastTime));\n var id = window.setTimeout(function() {\n callback(currTime + timeToCall);\n }, timeToCall);\n lastTime = currTime + timeToCall;\n return id;\n };\n\n if (!window.cancelAnimationFrame)\n window.cancelAnimationFrame = function(id) {\n clearTimeout(id);\n };\n\n // ------------------------------------------------------------------ can't/won't polyfill\n var features = [\n {\n name: \"canvas\",\n compatible: function() {\n return window.CanvasRenderingContext2D;\n }\n },\n {\n name: \"sessionStorage\",\n compatible: function() {\n try {\n return window.sessionStorage.length >= 0;\n } catch (err) {}\n return false;\n }\n }\n ];\n // build a list of feature names for features that were not found\n var incompatibilities = features\n .filter(function(feature) {\n return !feature.compatible();\n })\n .map(function(feature) {\n return feature.name;\n });\n\n // if there are needed features missing, follow the index link to the static incompat warning\n if (!!incompatibilities.length) {\n var root = document.querySelectorAll('link[rel=\"index\"]').item(0);\n if (root) {\n window.location = root.href + \"static/incompatible-browser.html\";\n }\n console.log(\"incompatible browser:\\n\" + incompatibilities.join(\"\\n\"));\n }\n})();\n"]}
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
+1 -1
View File
@@ -1 +1 @@
{"version":3,"sources":["ui/fa-icon-button.js"],"names":["root","factory","define","amd","undefined","options","faIconButton","tooltipConfig","placement","classes","concat","disabled","push","html","title","target","join","$button","tooltip","href","onclick","faIcon","$","_","isFunction","click"],"mappings":"cAAC,SAAUA,EAAMC,GACS,mBAAXC,QAAyBA,OAAOC,IAD9CD,UAAgBD,GAETC,EAAAA,aAAAD,IAFP,MAAAG,EAKI,WAyCD,OA1BmB,SAAUC,IAAzBC,EAAAA,OACAD,cAAAA,EAAAE,gBAAAC,UAAA,UAGAH,EAAQI,SAAY,YAAaC,OAAQL,EAAQI,aAAjDJ,EAAQI,UACJJ,EAAQM,QAAZC,KAAsB,YAItB,IAAIC,GAAAA,aACAR,EAAcA,QAAQI,KAAAA,KAAc,IAIhCJ,EAAAS,MAAA,WAAAT,EAAAS,MAAA,IAAA,IACAT,EAAoBA,UAAgBA,EAAAU,OAE1CC,YARFX,EAAAU,OAAA,IAAA,GASIE,WAAoBC,EAASb,UAAQE,EAAzCY,KAAAd,EAAAc,KAAA,sBAAA,KAEIF,mBAAuBG,EAAvBC,OAAA,YACH,QACDL,KAAOC,IAtBXA,EAAAK,EAAAT,GAAAK,QAAAb,EAAAE,eApBJ,OAuCYgB,EAAEC,WAAYnB,EAAQe,UAMlCH,EAAAQ,MAAApB,EAAAe,SA7CAH","file":"../../scripts/ui/fa-icon-button.js","sourcesContent":["(function (root, factory) {\n if (typeof define === 'function' && define.amd) {\n define([], factory);\n } else {\n root.faIconButton = factory();\n }\n\n}(this, function () {\n//============================================================================\n /** Returns a jQuery object containing a clickable font-awesome button.\n * options:\n * tooltipConfig : option map for bootstrap tool tip\n * classes : array of class names (will always be classed as icon-btn)\n * disabled : T/F - add the 'disabled' class?\n * title : tooltip/title string\n * target : optional href target\n * href : optional href\n * faIcon : which font awesome icon to use\n * onclick : function to call when the button is clicked\n */\n var faIconButton = function( options ){\n options = options || {};\n options.tooltipConfig = options.tooltipConfig || { placement: 'bottom' };\n\n options.classes = [ 'icon-btn' ].concat( options.classes || [] );\n if( options.disabled ){\n options.classes.push( 'disabled' );\n }\n\n var html = [\n '<a class=\"', options.classes.join( ' ' ), '\"',\n (( options.title )?( ' title=\"' + options.title + '\"' ):( '' )),\n (( !options.disabled && options.target )? ( ' target=\"' + options.target + '\"' ):( '' )),\n ' href=\"', (( !options.disabled && options.href )?( options.href ):( 'javascript:void(0);' )), '\">',\n // could go with something less specific here - like 'html'\n '<span class=\"fa ', options.faIcon, '\"></span>',\n '</a>'\n ].join( '' );\n var $button = $( html ).tooltip( options.tooltipConfig );\n if( _.isFunction( options.onclick ) ){\n $button.click( options.onclick );\n }\n return $button;\n };\n\n//============================================================================\n return faIconButton;\n}));\n"]}
{"version":3,"sources":["ui/fa-icon-button.js"],"names":["root","factory","define","amd","undefined","options","faIconButton","classes","concat","disabled","push","html","join","title","faIcon","$button","tooltip","isFunction","target","href","$","tooltipConfig","_","onclick","click"],"mappings":"cAAA,SAAUA,EAAMC,GACU,mBAAXC,QAAyBA,OAAOC,IAD9CD,UAAeD,GAERC,EAAAA,aAAAD,IAFR,MAAAG,EAIQJ,WAqDJ,OAtCmB,SAASK,IACxBA,EAAUA,OADVC,cAAAA,EAAAA,gBACAD,UAAUA,UACuCA,EAAjDE,SAAA,YAAAC,OAAAH,EAAAE,aAKIF,EAAQI,UADZJ,EAAQE,QAAWG,KAAA,YAGlB,IAAAC,GAGG,aADAA,EACAJ,QAAAK,KADO,KAaP,IACAP,EAAAQ,MACAR,WAAQS,EACRD,MACA,IAjBO,IAmBPE,EAAAA,UAAkBC,EAAQX,OACxBY,YAAWZ,EAAba,OAA+B,IAC/BH,GACH,WACDV,EAAOU,UAAPV,EAAAc,KAlCJd,EAAAc,KAsBc,sBAed,KAxDJ,mBA6CYd,EAAQS,OACR,YACA,QACFF,KAAK,IACHG,EAAUK,EAAET,GAAMK,QAAQX,EAAQgB,eAItC,OAHIC,EAAEL,WAAWZ,EAAQkB,UACrBR,EAAQS,MAAMnB,EAAQkB,SAEnBR","file":"../../scripts/ui/fa-icon-button.js","sourcesContent":["(function(root, factory) {\n if (typeof define === \"function\" && define.amd) {\n define([], factory);\n } else {\n root.faIconButton = factory();\n }\n})(this, function() {\n //============================================================================\n /** Returns a jQuery object containing a clickable font-awesome button.\n * options:\n * tooltipConfig : option map for bootstrap tool tip\n * classes : array of class names (will always be classed as icon-btn)\n * disabled : T/F - add the 'disabled' class?\n * title : tooltip/title string\n * target : optional href target\n * href : optional href\n * faIcon : which font awesome icon to use\n * onclick : function to call when the button is clicked\n */\n var faIconButton = function(options) {\n options = options || {};\n options.tooltipConfig = options.tooltipConfig || {\n placement: \"bottom\"\n };\n\n options.classes = [\"icon-btn\"].concat(options.classes || []);\n if (options.disabled) {\n options.classes.push(\"disabled\");\n }\n\n var html = [\n '<a class=\"',\n options.classes.join(\" \"),\n '\"',\n options.title ? ' title=\"' + options.title + '\"' : \"\",\n !options.disabled && options.target\n ? ' target=\"' + options.target + '\"'\n : \"\",\n ' href=\"',\n !options.disabled && options.href\n ? options.href\n : \"javascript:void(0);\",\n '\">',\n // could go with something less specific here - like 'html'\n '<span class=\"fa ',\n options.faIcon,\n '\"></span>',\n \"</a>\"\n ].join(\"\");\n var $button = $(html).tooltip(options.tooltipConfig);\n if (_.isFunction(options.onclick)) {\n $button.click(options.onclick);\n }\n return $button;\n };\n\n //============================================================================\n return faIconButton;\n});\n"]}
File diff suppressed because one or more lines are too long
+1 -1
View File
@@ -1 +1 @@
{"version":3,"sources":["ui/hoverhighlight.js"],"names":["factory","define","amd","jQuery","fn","extend","scope","color","hoverhighlight","$","this","each","$this","targetSelector","data","ev","css","background","mouseout"],"mappings":"cAAC,SAAUA,GACe,mBAAXC,QAAyBA,OAAOC,IAD9CD,UAAAD,GACGA,EAEOG,QAHV,CAMI,WAKDA,OAAOC,GAAGC,QAAVF,eAAiB,SAAAG,EAAAC,GAETD,OADJE,EAAAA,GAAiB,OACbF,KAAQA,QACYG,EAAAC,MAAAC,KAAA,WAAc,IAAAC,EAAAH,EAAAC,MAI1BG,EAAiBD,EAAME,KAAM,UAD7BF,GACAC,EAAAA,UAAiBD,SAAAG,GAIbN,EAAGI,EAAgBP,GAAQU,KAF/BH,WAAgBN,MAGRU,SAAAA,SAAYV,GADeE,EAA/BI,GAAAG,KAIHE,WAAU,SAKdR,MAlBLA","file":"../../scripts/ui/hoverhighlight.js","sourcesContent":["(function (factory) {\n if (typeof define === 'function' && define.amd) {\n define([], factory);\n } else {\n // Browser globals\n factory(jQuery);\n }\n\n}(function () {\n//=============================================================================\n\n jQuery.fn.extend({\n hoverhighlight : function $hoverhighlight( scope, color ){\n scope = scope || 'body';\n if( !this.length ){ return this; }\n\n $( this ).each( function(){\n var $this = $( this ),\n targetSelector = $this.data( 'target' );\n\n if( targetSelector ){\n $this.mouseover( function( ev ){\n $( targetSelector, scope ).css({\n background: color\n });\n })\n .mouseout( function( ev ){\n $( targetSelector ).css({\n background: ''\n });\n });\n }\n });\n return this;\n }\n });\n}));\n"]}
{"version":3,"sources":["ui/hoverhighlight.js"],"names":["factory","define","amd","jQuery","fn","extend","hoverhighlight","scope","color","$","this","each","$this","data","targetSelector","ev","mouseover","css","mouseout","background"],"mappings":"cAAA,SAAUA,GACgB,mBAAXC,QAAyBA,OAAOC,IAD9CD,UAAAD,GACGA,EAEOG,QAHX,CAKQH,WAGJG,OAAAC,GAAAC,QAGIC,eAAgB,SAAyBC,EAAOC,GAAhDF,OADGF,EAAPG,GAAiB,OACbD,KAAAA,QAIKG,EAAAC,MAAAC,KAAA,WAGG,IAAIC,EAAQH,EAAEC,MADVC,EAAgBC,EAAAC,KAAA,UAEhBC,GAGAF,EADAE,UAAJ,SAAoBC,GAEXC,EAAAA,EAAmBD,GAAIE,KAClBH,WAAgBP,MAIrBW,SAAS,SAASH,GACbD,EAAAA,GAAoBG,KAClBE,WAAY,SAKzBT,MAvBCH","file":"../../scripts/ui/hoverhighlight.js","sourcesContent":["(function(factory) {\n if (typeof define === \"function\" && define.amd) {\n define([], factory);\n } else {\n // Browser globals\n factory(jQuery);\n }\n})(function() {\n //=============================================================================\n\n jQuery.fn.extend({\n hoverhighlight: function $hoverhighlight(scope, color) {\n scope = scope || \"body\";\n if (!this.length) {\n return this;\n }\n\n $(this).each(function() {\n var $this = $(this),\n targetSelector = $this.data(\"target\");\n\n if (targetSelector) {\n $this\n .mouseover(function(ev) {\n $(targetSelector, scope).css({\n background: color\n });\n })\n .mouseout(function(ev) {\n $(targetSelector).css({\n background: \"\"\n });\n });\n }\n });\n return this;\n }\n });\n});\n"]}
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
+1 -1
View File
@@ -1 +1 @@
{"version":3,"sources":["utils/add-logging.js"],"names":["define","LOGGING_FNS","obj","namespace","addTo","addLogging","prototype","undefined","forEach","logFn","this","logger","emit","_logNamespace","arguments","apply"],"mappings":"aAAAA,UACG,WACH,IAAAC,GAAA,MAAA,QAAA,OAAA,OAAA,QAAA,UAqCA,OA/BA,SAAqBC,EAAKC,GAA1B,IAAAC,OAA0BD,IAAjBE,EAAAA,UAA4BH,EAAAI,UAAAJ,EA2BhC,YA1BaA,IAAdC,IACAC,EAAID,cAAcI,GAQlBN,EAAAO,QAAA,SAAAC,GACAR,EAAAA,GAAYO,WACRJ,GAAAA,KAAOK,OAGF,OAAAC,KAAAC,OAAAC,KACGF,KAAKC,OAAOC,KAAMH,EAAAC,KAAAG,cAAAC,WAErBJ,KAAAC,OAAAF,GAIbC,KAAAC,OAAAF,GAAAM,MAAAL,KAAAC,OAAAG,gBAJa,KAPTZ","file":"../../scripts/utils/add-logging.js","sourcesContent":["define([\n], function(){\n//==============================================================================\nvar LOGGING_FNS = [ 'log', 'debug', 'info', 'warn', 'error', 'metric' ];\n/** adds logging functions to an obj.prototype (or obj directly) adding a namespace for filtering\n * @param {Object} obj\n * @param {String} namespace\n */\nfunction addLogging( obj, namespace ){\n var addTo = ( obj.prototype !== undefined )?( obj.prototype ):( obj );\n if( namespace !== undefined ){\n addTo._logNamespace = namespace;\n }\n //yagni?: without this, may not capture Galaxy.config.debug and add Galaxy.logger properly\n // if( window.Galaxy && window.Galaxy.config && window.Galaxy.config.debug ){\n // addTo.logger = window.Galaxy.logger;\n // }\n\n // give the object each\n LOGGING_FNS.forEach( function( logFn ){\n addTo[ logFn ] = function(){\n if( !this.logger ){\n return undefined;\n }\n if( this.logger.emit ){\n return this.logger.emit( logFn, this._logNamespace, arguments );\n }\n if( this.logger[ logFn ] ){\n//TODO:! there has to be a way to get the lineno/file into this\n// http://stackoverflow.com/questions/13815640/a-proper-wrapper-for-console-log-with-correct-line-number\n// http://www.paulirish.com/2009/log-a-lightweight-wrapper-for-consolelog/\n return this.logger[ logFn ].apply( this.logger, arguments );\n }\n return undefined;\n };\n });\n return obj;\n}\n\n//==============================================================================\nreturn addLogging;\n});\n"]}
{"version":3,"sources":["utils/add-logging.js"],"names":["define","LOGGING_FNS","obj","namespace","addTo","undefined","prototype","forEach","logFn","this","logger","emit","_logNamespace","arguments","apply"],"mappings":"aAAAA,UAAW,WAAXA,IAAOC,GAAe,MAAA,QAAA,OAAA,OAAA,QAAA,UA2ClB,OApCA,SAAoBC,EAAKC,GACrB,IAAIC,OAA0BC,IAAlBH,EAAII,UAA0BJ,EAAII,UAAYJ,EA+B1D,YAhCgBA,IAApBC,IACQC,EAAAA,cAAQD,GAUZF,EAAYM,QAAQ,SAASC,GAD7BJ,EAAAI,GAAA,WACAP,GAAYM,KAAAA,OAGA,OAAAE,KAAAC,OAAOL,KACVI,KAAAC,OAAAC,KACGH,EACAC,KAAOG,cAKVC,WAGGJ,KAAAC,OAAAF,GAIGH,KAAPK,OAAAF,GAAAM,MAAAL,KAAAC,OAAAG,gBAJI,KAOLX","file":"../../scripts/utils/add-logging.js","sourcesContent":["define([], function() {\n //==============================================================================\n var LOGGING_FNS = [\"log\", \"debug\", \"info\", \"warn\", \"error\", \"metric\"];\n /** adds logging functions to an obj.prototype (or obj directly) adding a namespace for filtering\n * @param {Object} obj\n * @param {String} namespace\n */\n function addLogging(obj, namespace) {\n var addTo = obj.prototype !== undefined ? obj.prototype : obj;\n if (namespace !== undefined) {\n addTo._logNamespace = namespace;\n }\n //yagni?: without this, may not capture Galaxy.config.debug and add Galaxy.logger properly\n // if( window.Galaxy && window.Galaxy.config && window.Galaxy.config.debug ){\n // addTo.logger = window.Galaxy.logger;\n // }\n\n // give the object each\n LOGGING_FNS.forEach(function(logFn) {\n addTo[logFn] = function() {\n if (!this.logger) {\n return undefined;\n }\n if (this.logger.emit) {\n return this.logger.emit(\n logFn,\n this._logNamespace,\n arguments\n );\n }\n if (this.logger[logFn]) {\n //TODO:! there has to be a way to get the lineno/file into this\n // http://stackoverflow.com/questions/13815640/a-proper-wrapper-for-console-log-with-correct-line-number\n // http://www.paulirish.com/2009/log-a-lightweight-wrapper-for-consolelog/\n return this.logger[logFn].apply(this.logger, arguments);\n }\n return undefined;\n };\n });\n return obj;\n }\n\n //==============================================================================\n return addLogging;\n});\n"]}
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
+1 -1
View File
@@ -1 +1 @@
{"version":3,"sources":["utils/deferred.js"],"names":["define","Utils","Backbone","Model","extend","this","initialize","execute","callback","self","uid","has_deferred","length","active","id","process","$","Deferred","promise","always","Galaxy","debug","state","charAt","toUpperCase","slice","when","last","emit","reset","i","ready","isEmptyObject"],"mappings":"aAGAA,QAAQ,eAAgB,SAAUC,GAClC,OAAOC,SAASC,MAAMC,QADtBJ,WAAQ,WACRK,KAAOH,UACHI,KAAAA,KAAY,MAQZC,QAAS,SAAUC,GACf,IAAIC,EAAOJ,KADfE,EAASN,EAAAS,MACLC,EAAAH,EAAAI,OAAA,EAKAP,KAAKQ,OAAQC,IAAO,EAGpB,IAAIC,EAAUC,EAAEC,WADhBF,EAAAG,UAAAC,OAAA,kBACIJ,EAAAA,OAAYE,GAChBF,GAAAK,OAAkBD,KAAOE,MAAA,sBAAWhB,KAAAiB,QAAAC,OAAA,GAAAC,cAAAnB,KAAAiB,QAAAG,MAAA,GAAA,IAAAX,KAMpCE,EAAEU,KAAMrB,KAAKsB,MAAOR,OAAO,WAD3BV,EAAAI,OAAAC,IACEY,GAAkBP,OAAOS,KAAAP,MAAW,sBAAA,WAAAP,GAClCN,EAAUK,IACNF,GAAAA,EAAgBS,WAEhBL,EAACJ,WAGJN,KAAAsB,KAAAZ,EAAAG,WAKTW,MAAA,WAGIT,OAAOQ,KAAKP,MAAM,sBAAuB,SAD7CQ,IAAAA,IAAOC,KAAAzB,KAAAQ,OACHO,KAAAA,OAAYC,IAAM,GAMtBU,MAAA,WAGI,OAAOf,EAAEgB,cAAe3B,KAAKQ","file":"../../scripts/utils/deferred.js","sourcesContent":["/**\n * This class defines a queue to ensure that multiple deferred callbacks are executed sequentially.\n */\ndefine(['utils/utils'], function( Utils ) {\nreturn Backbone.Model.extend({\n initialize: function(){\n this.active = {};\n this.last = null;\n },\n\n /** Adds a callback to the queue. Upon execution a deferred object is parsed to the callback i.e. callback( deferred ).\n * If the callback does not take any arguments, the deferred is resolved instantly.\n */\n execute: function( callback ) {\n var self = this;\n var id = Utils.uid();\n var has_deferred = callback.length > 0;\n\n // register process\n this.active[ id ] = true;\n\n // deferred process\n var process = $.Deferred();\n process.promise().always(function() {\n delete self.active[ id ];\n has_deferred && Galaxy.emit.debug( 'deferred::execute()', this.state().charAt(0).toUpperCase() + this.state().slice(1) + ' ' + id );\n });\n\n // deferred queue\n $.when( this.last ).always(function() {\n if ( self.active[ id ] ) {\n has_deferred && Galaxy.emit.debug( 'deferred::execute()', 'Running ' + id );\n callback( process );\n !has_deferred && process.resolve();\n } else {\n process.reject();\n }\n });\n this.last = process.promise();\n },\n\n /** Resets the promise queue. All currently queued but unexecuted callbacks/promises will be rejected.\n */\n reset: function() {\n Galaxy.emit.debug('deferred::execute()', 'Reset');\n for ( var i in this.active ) {\n this.active[ i ] = false;\n }\n },\n\n /** Returns true if all processes are done.\n */\n ready: function() {\n return $.isEmptyObject( this.active );\n }\n});\n\n});"]}
{"version":3,"sources":["utils/deferred.js"],"names":["define","Utils","Backbone","Model","extend","initialize","execute","callback","self","this","uid","length","active","id","process","$","Deferred","promise","always","Galaxy","has_deferred","state","charAt","toUpperCase","slice","when","last","emit","debug","resolve","i","reset","ready","isEmptyObject"],"mappings":"aAGAA,QAAQ,eAAgB,SAASC,GAC7B,OAAOC,SAASC,MAAMC,QADlBC,WAAA,WACGH,KAAAA,UACHG,KAAAA,KAAY,MAQZC,QAAS,SAASC,GACd,IAAIC,EAAOC,KADfH,EAASL,EAAAS,MACDF,EAAJD,EAAAI,OAAA,EAKAF,KAAKG,OAAOC,IAAM,EAGlB,IAAIC,EAAUC,EAAEC,WADhBF,EAAAG,UAAAC,OAAA,kBACIJ,EAAYE,OAAAA,GAChBF,GACIK,OAAOX,KAAKI,MACZQ,sBAFJX,KAAAY,QAMiBC,OAAO,GAQxBC,cACAd,KAAkBS,QAAOM,MAAA,GACZZ,IACLQ,KASHL,EAAAU,KAAAhB,KAAAiB,MAAAR,OAAA,WAXLV,EAAAI,OAAAC,IAaKa,GA/CgBP,OAAAQ,KAAAC,MAsCL,sBAYpB,WAAAf,GAEON,EAAAO,IACIa,GAAWb,EAAAe,WAEdf,EAAYgB,WARhBrB,KAAKiB,KAAOZ,EAAQG,WAgBvBc,MAAA,WA/DLZ,OAAAQ,KAAAC,MAAA,sBAAA,SADJ,IAAA,IAAAE,KAAArB,KAAAG,OAwDgBH,KAAKG,OAAOkB,IAAK,GAMzBE,MAAO,WACH,OAAOjB,EAAEkB,cAAcxB,KAAKG","file":"../../scripts/utils/deferred.js","sourcesContent":["/**\n * This class defines a queue to ensure that multiple deferred callbacks are executed sequentially.\n */\ndefine([\"utils/utils\"], function(Utils) {\n return Backbone.Model.extend({\n initialize: function() {\n this.active = {};\n this.last = null;\n },\n\n /** Adds a callback to the queue. Upon execution a deferred object is parsed to the callback i.e. callback( deferred ).\n * If the callback does not take any arguments, the deferred is resolved instantly.\n */\n execute: function(callback) {\n var self = this;\n var id = Utils.uid();\n var has_deferred = callback.length > 0;\n\n // register process\n this.active[id] = true;\n\n // deferred process\n var process = $.Deferred();\n process.promise().always(function() {\n delete self.active[id];\n has_deferred &&\n Galaxy.emit.debug(\n \"deferred::execute()\",\n this.state()\n .charAt(0)\n .toUpperCase() +\n this.state().slice(1) +\n \" \" +\n id\n );\n });\n\n // deferred queue\n $.when(this.last).always(function() {\n if (self.active[id]) {\n has_deferred &&\n Galaxy.emit.debug(\n \"deferred::execute()\",\n \"Running \" + id\n );\n callback(process);\n !has_deferred && process.resolve();\n } else {\n process.reject();\n }\n });\n this.last = process.promise();\n },\n\n /** Resets the promise queue. All currently queued but unexecuted callbacks/promises will be rejected.\n */\n reset: function() {\n Galaxy.emit.debug(\"deferred::execute()\", \"Reset\");\n for (var i in this.active) {\n this.active[i] = false;\n }\n },\n\n /** Returns true if all processes are done.\n */\n ready: function() {\n return $.isEmptyObject(this.active);\n }\n });\n});\n"]}
File diff suppressed because one or more lines are too long
+1 -1
View File
@@ -1 +1 @@
{"version":3,"sources":["utils/levenshtein.js"],"names":["define","a","b","levenshteinDistance","length","i","matrix","j","charAt","Math","min"],"mappings":"aAAAA,UACG,WAoDD,OAhCF,SAAAC,EAAAC,GACA,GAASC,IAATF,EAAAG,OAASD,OAAAA,EAAoBF,OAC3B,GAAG,IAAHC,EAAAE,OAAA,OAAkBH,EAAAG,OAAoB,IACAC,EADAC,KAOtC,IAAID,EAAI,EAAGA,GAAKH,EAAEE,OAAQC,IAJ1BC,EAAIA,IAASD,GAIb,IAAAE,EACED,IAAAA,EAAAA,EAAOD,GAAKJ,EAAAG,OAAZG,IACDD,EAAA,GAAAC,GAAAA,EAID,IAAAF,EAAIE,EAAIF,GAAGE,EAAAA,OAAOH,IAChBE,IAAAA,EAAAA,EAAAC,GAAAN,EAAAG,OAAAG,IACDL,EAAAM,OAAAH,EAAA,KAAAJ,EAAAO,OAAAD,EAAA,GAMKD,EAAOD,GAAGE,GAAKD,EAAOD,EAAE,GAAGE,EAAE,GAHnCD,EAAAD,GAAWA,GAAOD,KAAAA,IAAQC,EAAIA,EAAA,GAAAE,EAAA,GAAA,EAC5BE,KAAAC,IAA8BJ,EAAAD,GAAAE,EAAA,GAAA,EACEA,EAA9BF,EAAmC,GAAAE,GAAA,IAMlC,OAAAD,EAAAJ,EAAAE,QAAAH,EAAAG","file":"../../scripts/utils/levenshtein.js","sourcesContent":["define([\n], function(){\n//=============================================================================\n/**\n(Imported for edit distance algorith. From: https://gist.github.com/andrei-m/982927)\nCopyright (c) 2011 Andrei Mackenzie\n\nPermission is hereby granted, free of charge, to any person obtaining a copy of this software and associated\ndocumentation files (the \"Software\"), to deal in the Software without restriction, including without limitation the\nrights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit\npersons to whom the Software is furnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all copies or substantial portions of the\nSoftware.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE\nWARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR\nCOPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR\nOTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.\n*/\n// Compute the edit distance between the two given strings\n//exports.getEditDistance = function(a, b){\nfunction levenshteinDistance(a, b){\n if(a.length === 0){ return b.length; }\n if(b.length === 0){ return a.length; }\n\n var matrix = [];\n\n // increment along the first column of each row\n var i;\n for(i = 0; i <= b.length; i++){\n matrix[i] = [i];\n }\n\n // increment each column in the first row\n var j;\n for(j = 0; j <= a.length; j++){\n matrix[0][j] = j;\n }\n\n // Fill in the rest of the matrix\n for(i = 1; i <= b.length; i++){\n for(j = 1; j <= a.length; j++){\n if(b.charAt(i-1) === a.charAt(j-1)){\n matrix[i][j] = matrix[i-1][j-1];\n } else {\n matrix[i][j] = Math.min(matrix[i-1][j-1] + 1, // substitution\n Math.min(matrix[i][j-1] + 1, // insertion\n matrix[i-1][j] + 1)); // deletion\n }\n }\n }\n\n //console.debug( '\\t\\t levenshteinDistance', a, b, matrix[b.length][a.length] );\n return matrix[b.length][a.length];\n}\n\n//=============================================================================\n return levenshteinDistance;\n});\n"]}
{"version":3,"sources":["utils/levenshtein.js"],"names":["define","a","b","length","i","matrix","j","charAt","Math","min"],"mappings":"aAAAA,UAAW,WAiEP,OA3CI,SAAoBC,EAAAC,GAChB,GAASC,IAATF,EAAAE,OACH,OAAAD,EAAAC,OAEG,GAASA,IAATD,EAAAC,OACH,OAAAF,EAAAE,OAED,IAIAC,EAJIC,KAKAA,IAAAA,EAAAA,EAAAD,GAAaA,EAAbD,OAAAC,IACHC,EAAAD,IAAAA,GAID,IAAAE,EACID,IAAAA,EAAAA,EAAAC,GAAAL,EAAeK,OAAfA,IACHD,EAAA,GAAAC,GAAAA,EAIG,IAAAF,EAAKE,EAAAA,GAAOA,EAAAA,OAAOH,IACf,IAAAG,EAAIJ,EAAEK,GAAOH,EAAAA,OAATE,IACAD,EAAAA,OAAAD,EAAA,KAAeC,EAAOD,OAAOE,EAAI,GADrCD,EAEOD,GAAAE,GAAAD,EAAAD,EAAA,GAAAE,EAAA,GAGCE,EAAKC,GACDJ,GAAAA,KAAUC,IACVD,EAAWD,EAAX,GAAAE,EAJR,GAMG,EACNE,KAAAC,IACJJ,EAAAD,GAAAE,EAAA,GAAA,EACJD,EAAAD,EAAA,GAAAE,GAAA,IA1DT,OAAAD,EAAAH,EAAAC,QAAAF,EAAAE","file":"../../scripts/utils/levenshtein.js","sourcesContent":["define([], function() {\n //=============================================================================\n /**\n(Imported for edit distance algorith. From: https://gist.github.com/andrei-m/982927)\nCopyright (c) 2011 Andrei Mackenzie\n\nPermission is hereby granted, free of charge, to any person obtaining a copy of this software and associated\ndocumentation files (the \"Software\"), to deal in the Software without restriction, including without limitation the\nrights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit\npersons to whom the Software is furnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all copies or substantial portions of the\nSoftware.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE\nWARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR\nCOPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR\nOTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.\n*/\n // Compute the edit distance between the two given strings\n //exports.getEditDistance = function(a, b){\n function levenshteinDistance(a, b) {\n if (a.length === 0) {\n return b.length;\n }\n if (b.length === 0) {\n return a.length;\n }\n\n var matrix = [];\n\n // increment along the first column of each row\n var i;\n for (i = 0; i <= b.length; i++) {\n matrix[i] = [i];\n }\n\n // increment each column in the first row\n var j;\n for (j = 0; j <= a.length; j++) {\n matrix[0][j] = j;\n }\n\n // Fill in the rest of the matrix\n for (i = 1; i <= b.length; i++) {\n for (j = 1; j <= a.length; j++) {\n if (b.charAt(i - 1) === a.charAt(j - 1)) {\n matrix[i][j] = matrix[i - 1][j - 1];\n } else {\n matrix[i][j] = Math.min(\n matrix[i - 1][j - 1] + 1, // substitution\n Math.min(\n matrix[i][j - 1] + 1, // insertion\n matrix[i - 1][j] + 1\n )\n ); // deletion\n }\n }\n }\n\n //console.debug( '\\t\\t levenshteinDistance', a, b, matrix[b.length][a.length] );\n return matrix[b.length][a.length];\n }\n\n //=============================================================================\n return levenshteinDistance;\n});\n"]}

Some files were not shown because too many files have changed in this diff Show More