From d25e712e2197115c17e3e9316d1231ed2224e1dc Mon Sep 17 00:00:00 2001 From: vagrant Date: Tue, 11 Feb 2014 04:52:15 +0000 Subject: [PATCH] Finixhed applying jsHint linting to js files. Created initial Gruntfile.js build script and package.json script for keeping track of required node modules. --- .gitignore | 1 + Gruntfile.js | 32 + awx/ui/static/js/app.js | 515 +-- awx/ui/static/js/config.js | 32 +- awx/ui/static/js/controllers/Credentials.js | 21 +- awx/ui/static/js/controllers/JobEvents.js | 15 +- awx/ui/static/js/forms/ActivityDetail.js | 15 +- awx/ui/static/js/forms/Credentials.js | 174 +- awx/ui/static/js/forms/Groups.js | 170 +- awx/ui/static/js/forms/HostGroups.js | 39 +- awx/ui/static/js/forms/Hosts.js | 66 +- awx/ui/static/js/forms/Inventories.js | 53 +- awx/ui/static/js/forms/InventoryGroups.js | 27 - awx/ui/static/js/forms/InventoryHosts.js | 147 - awx/ui/static/js/forms/InventoryStatus.js | 57 - awx/ui/static/js/forms/JobEventData.js | 7 +- awx/ui/static/js/forms/JobEvents.js | 144 + awx/ui/static/js/forms/JobSummary.js | 12 +- awx/ui/static/js/forms/JobTemplates.js | 141 +- awx/ui/static/js/forms/Jobs.js | 155 +- awx/ui/static/js/forms/LicenseForm.js | 84 + awx/ui/static/js/forms/Organizations.js | 133 +- awx/ui/static/js/forms/Permissions.js | 138 +- awx/ui/static/js/forms/ProjectStatus.js | 11 +- awx/ui/static/js/forms/Projects.js | 246 +- awx/ui/static/js/forms/Teams.js | 197 +- awx/ui/static/js/forms/Users.js | 192 +- awx/ui/static/js/help/InventoryGroups.js | 95 +- awx/ui/static/js/help/InventoryHosts.js | 24 - awx/ui/static/js/helpers/Access.js | 187 +- awx/ui/static/js/helpers/Children.js | 118 +- awx/ui/static/js/helpers/Credentials.js | 325 +- awx/ui/static/js/helpers/Events.js | 473 +-- awx/ui/static/js/helpers/Groups.js | 2075 ++++++------ awx/ui/static/js/helpers/Hosts.js | 2 + awx/ui/static/js/helpers/JobSubmission.js | 882 +++-- awx/ui/static/js/helpers/Jobs.js | 229 +- awx/ui/static/js/helpers/Lookup.js | 268 +- awx/ui/static/js/helpers/PaginationHelpers.js | 290 +- awx/ui/static/js/helpers/Parse.js | 100 +- awx/ui/static/js/helpers/Permissions.js | 75 +- awx/ui/static/js/helpers/ProjectPath.js | 130 +- awx/ui/static/js/helpers/Projects.js | 216 +- awx/ui/static/js/helpers/Selection.js | 291 +- awx/ui/static/js/helpers/Users.js | 55 +- awx/ui/static/js/helpers/api-defaults.js | 162 +- awx/ui/static/js/helpers/inventory.js | 442 +-- awx/ui/static/js/helpers/md5.js | 54 +- awx/ui/static/js/helpers/refresh-related.js | 64 +- awx/ui/static/js/helpers/refresh.js | 78 +- awx/ui/static/js/helpers/related-search.js | 412 ++- awx/ui/static/js/helpers/search.js | 898 +++--- awx/ui/static/js/helpers/teams.js | 202 +- awx/ui/static/js/lists/Admins.js | 38 +- awx/ui/static/js/lists/CloudCredentials.js | 40 +- awx/ui/static/js/lists/Credentials.js | 63 +- awx/ui/static/js/lists/Groups.js | 38 +- awx/ui/static/js/lists/HomeGroups.js | 76 +- awx/ui/static/js/lists/HomeHosts.js | 44 +- awx/ui/static/js/lists/Hosts.js | 36 +- awx/ui/static/js/lists/Inventories.js | 65 +- awx/ui/static/js/lists/InventoryHosts.js | 49 +- awx/ui/static/js/lists/JobEvents.js | 26 +- awx/ui/static/js/lists/JobHosts.js | 25 +- awx/ui/static/js/lists/JobTemplates.js | 51 +- awx/ui/static/js/lists/Jobs.js | 3 + awx/ui/static/js/lists/Organizations.js | 38 +- awx/ui/static/js/lists/Permissions.js | 49 +- awx/ui/static/js/lists/Projects.js | 70 +- awx/ui/static/js/lists/Streams.js | 88 +- awx/ui/static/js/lists/Teams.js | 44 +- awx/ui/static/js/lists/Users.js | 47 +- .../static/js/widgets/InventorySyncStatus.js | 172 +- awx/ui/static/js/widgets/JobStatus.js | 165 +- awx/ui/static/js/widgets/ObjectCount.js | 112 +- awx/ui/static/js/widgets/SCMSyncStatus.js | 171 +- awx/ui/static/js/widgets/Stream.js | 847 ++--- awx/ui/static/lib/ansible/AuthService.js | 241 +- awx/ui/static/lib/ansible/InventoryTree.js | 511 +-- awx/ui/static/lib/ansible/RestServices.js | 334 +- awx/ui/static/lib/ansible/Timer.js | 83 +- awx/ui/static/lib/ansible/TreeSelector.js | 693 ---- awx/ui/static/lib/ansible/Utilities.js | 702 ++-- awx/ui/static/lib/ansible/api-loader.js | 54 +- awx/ui/static/lib/ansible/directives.js | 1 + awx/ui/static/lib/ansible/filters.js | 31 +- awx/ui/static/lib/ansible/form-generator.js | 2825 ++++++++--------- .../static/lib/ansible/generator-helpers.js | 1218 ++++--- awx/ui/static/lib/ansible/license.js | 305 +- awx/ui/static/lib/ansible/list-generator.js | 805 ++--- awx/ui/static/lib/ansible/prompt-dialog.js | 49 +- awx/ui/static/lib/ansible/pwdmeter.js | 455 +-- awx/ui/templates/ui/index.html | 4 +- package.json | 9 + 94 files changed, 10589 insertions(+), 11059 deletions(-) create mode 100644 Gruntfile.js delete mode 100644 awx/ui/static/js/forms/InventoryGroups.js delete mode 100644 awx/ui/static/js/forms/InventoryHosts.js delete mode 100644 awx/ui/static/js/forms/InventoryStatus.js create mode 100644 awx/ui/static/js/forms/JobEvents.js create mode 100644 awx/ui/static/js/forms/LicenseForm.js delete mode 100644 awx/ui/static/js/help/InventoryHosts.js delete mode 100644 awx/ui/static/lib/ansible/TreeSelector.js create mode 100644 package.json diff --git a/.gitignore b/.gitignore index e8fd62b1cc..5538679ba9 100644 --- a/.gitignore +++ b/.gitignore @@ -7,6 +7,7 @@ awx/public/static awx/ui/static/js/awx-min.js awx/ui/static/css/awx.min.css env/* +node_modules/** build deb-build dist diff --git a/Gruntfile.js b/Gruntfile.js new file mode 100644 index 0000000000..2765d65d13 --- /dev/null +++ b/Gruntfile.js @@ -0,0 +1,32 @@ +module.exports = function(grunt) { + + grunt.initConfig({ + + pkg: grunt.file.readJSON('./package.json'), + + jshint: { + options: { + jshintrc: '.jshintrc' + }, + uses_defaults: ['awx/ui/static/js/*','awx/ui/static/lib/ansible/*', '!awx/ui/static/js/awx-min.js'] + }, + + uglify: { + options: { + banner: '/*! <%= pkg.name %> - v<%= pkg.version %> - ' + + '<%= grunt.template.today("yyyy-mm-dd") %> */' + }, + my_target: { + files: { + 'awx/ui/static/js/awx-min.js': ['awx/ui/static/js/**/*.js', 'awx/ui/static/lib/ansible/*.js', + '!awx/ui/static/js/awx-min.js'] + } + } + } + }); + + grunt.loadNpmTasks('grunt-contrib-jshint'); + grunt.loadNpmTasks('grunt-contrib-uglify'); + + grunt.registerTask('default', ['jshint', 'uglify']); +} diff --git a/awx/ui/static/js/app.js b/awx/ui/static/js/app.js index a95a888464..fa88f9140f 100644 --- a/awx/ui/static/js/app.js +++ b/awx/ui/static/js/app.js @@ -4,23 +4,22 @@ * Our main application mdoule. Declare application routes and perform initialization chores. * */ - var urlPrefix = $basePath; angular.module('ansible', [ 'RestServices', 'AuthService', 'Utilities', - 'OrganizationFormDefinition', + 'OrganizationFormDefinition', 'UserFormDefinition', 'FormGenerator', 'OrganizationListDefinition', - 'UserListDefinition', + 'UserListDefinition', 'UserHelper', - 'ListGenerator', + 'ListGenerator', 'PromptDialog', - 'ApiLoader', - 'RelatedSearchHelper', + 'ApiLoader', + 'RelatedSearchHelper', 'SearchHelper', 'PaginationHelpers', 'RefreshHelper', @@ -58,6 +57,7 @@ angular.module('ansible', [ 'JobFormDefinition', 'JobEventsListDefinition', 'JobEventDataDefinition', + 'JobEventsFormDefinition', 'JobHostDefinition', 'JobSummaryDefinition', 'ParseHelper', @@ -75,9 +75,7 @@ angular.module('ansible', [ 'ObjectCountWidget', 'StreamWidget', 'JobsHelper', - 'InventoryStatusDefinition', 'InventoryGroupsHelpDefinition', - 'InventoryHostsHelpDefinition', 'InventoryTree', 'CredentialsHelper', 'TimerService', @@ -85,253 +83,368 @@ angular.module('ansible', [ 'HomeGroupListDefinition', 'HomeHostListDefinition', 'ActivityDetailDefinition' - ]) - .config(['$routeProvider', function($routeProvider) { - $routeProvider. - when('/jobs', - { templateUrl: urlPrefix + 'partials/jobs.html', controller: JobsListCtrl }). +]) + .config(['$routeProvider', + function ($routeProvider) { + $routeProvider. + when('/jobs', { + templateUrl: urlPrefix + 'partials/jobs.html', + controller: 'JobsListCtrl' + }). - when('/jobs/:id', - { templateUrl: urlPrefix + 'partials/jobs.html', controller: JobsEdit }). + when('/jobs/:id', { + templateUrl: urlPrefix + 'partials/jobs.html', + controller: 'JobsEdit' + }). - when('/jobs/:id/job_events', - { templateUrl: urlPrefix + 'partials/jobs.html', controller: JobEventsList }). + when('/jobs/:id/job_events', { + templateUrl: urlPrefix + 'partials/jobs.html', + controller: 'JobEventsList' + }). - when('/jobs/:id/job_host_summaries', - { templateUrl: urlPrefix + 'partials/jobs.html', controller: JobHostSummaryList }). - - when('/jobs/:job_id/job_events/:event_id', - { templateUrl: urlPrefix + 'partials/jobs.html', controller: JobEventsEdit }). + when('/jobs/:id/job_host_summaries', { + templateUrl: urlPrefix + 'partials/jobs.html', + controller: 'JobHostSummaryList' + }). - when('/job_templates', - { templateUrl: urlPrefix + 'partials/job_templates.html', controller: JobTemplatesList }). - - when('/job_templates/add', - { templateUrl: urlPrefix + 'partials/job_templates.html', controller: JobTemplatesAdd }). + when('/jobs/:job_id/job_events/:event_id', { + templateUrl: urlPrefix + 'partials/jobs.html', + controller: 'JobEventsEdit' + }). - when('/job_templates/:id', - { templateUrl: urlPrefix + 'partials/job_templates.html', controller: JobTemplatesEdit }). + when('/job_templates', { + templateUrl: urlPrefix + 'partials/job_templates.html', + controller: 'JobTemplatesList' + }). - when('/projects', - { templateUrl: urlPrefix + 'partials/projects.html', controller: ProjectsList }). + when('/job_templates/add', { + templateUrl: urlPrefix + 'partials/job_templates.html', + controller: 'JobTemplatesAdd' + }). - when('/projects/add', - { templateUrl: urlPrefix + 'partials/projects.html', controller: ProjectsAdd }). + when('/job_templates/:id', { + templateUrl: urlPrefix + 'partials/job_templates.html', + controller: 'JobTemplatesEdit' + }). - when('/projects/:id', - { templateUrl: urlPrefix + 'partials/projects.html', controller: ProjectsEdit }). + when('/projects', { + templateUrl: urlPrefix + 'partials/projects.html', + controller: 'ProjectsList' + }). - when('/projects/:project_id/organizations', - { templateUrl: urlPrefix + 'partials/projects.html', controller: OrganizationsList }). + when('/projects/add', { + templateUrl: urlPrefix + 'partials/projects.html', + controller: 'ProjectsAdd' + }). - when('/projects/:project_id/organizations/add', - { templateUrl: urlPrefix + 'partials/projects.html', controller: OrganizationsAdd }). + when('/projects/:id', { + templateUrl: urlPrefix + 'partials/projects.html', + controller: 'ProjectsEdit' + }). - when('/hosts/:id/job_host_summaries', - { templateUrl: urlPrefix + 'partials/jobs.html', controller: JobHostSummaryList }). + when('/projects/:project_id/organizations', { + templateUrl: urlPrefix + 'partials/projects.html', + controller: 'OrganizationsList' + }). - when('/inventories', - { templateUrl: urlPrefix + 'partials/inventories.html', controller: InventoriesList }). + when('/projects/:project_id/organizations/add', { + templateUrl: urlPrefix + 'partials/projects.html', + controller: 'OrganizationsAdd' + }). - when('/inventories/add', - { templateUrl: urlPrefix + 'partials/inventories.html', controller: InventoriesAdd }). + when('/hosts/:id/job_host_summaries', { + templateUrl: urlPrefix + 'partials/jobs.html', + controller: 'JobHostSummaryList' + }). - when('/inventories/:inventory_id', - { templateUrl: urlPrefix + 'partials/inventory-edit.html', controller: InventoriesEdit }). + when('/inventories', { + templateUrl: urlPrefix + 'partials/inventories.html', + controller: 'InventoriesList' + }). - when('/organizations', { templateUrl: urlPrefix + 'partials/organizations.html', - controller: OrganizationsList }). + when('/inventories/add', { + templateUrl: urlPrefix + 'partials/inventories.html', + controller: 'InventoriesAdd' + }). - when('/organizations/add', { templateUrl: urlPrefix + 'partials/organizations.html', - controller: OrganizationsAdd }). + when('/inventories/:inventory_id', { + templateUrl: urlPrefix + 'partials/inventory-edit.html', + controller: 'InventoriesEdit' + }). - when('/organizations/:organization_id', { templateUrl: urlPrefix + 'partials/organizations.html', - controller: OrganizationsEdit }). + when('/organizations', { + templateUrl: urlPrefix + 'partials/organizations.html', + controller: 'OrganizationsList' + }). - when('/organizations/:organization_id/admins', { templateUrl: urlPrefix + 'partials/organizations.html', - controller: AdminsList }). + when('/organizations/add', { + templateUrl: urlPrefix + 'partials/organizations.html', + controller: 'OrganizationsAdd' + }). - when('/organizations/:organization_id/users', { templateUrl: urlPrefix + 'partials/users.html', - controller: UsersList }). + when('/organizations/:organization_id', { + templateUrl: urlPrefix + 'partials/organizations.html', + controller: 'OrganizationsEdit' + }). - when('/organizations/:organization_id/users/add', { templateUrl: urlPrefix + 'partials/users.html', - controller: UsersAdd }). + when('/organizations/:organization_id/admins', { + templateUrl: urlPrefix + 'partials/organizations.html', + controller: 'AdminsList' + }). - when('/organizations/:organization_id/users/:user_id', { templateUrl: urlPrefix + 'partials/users.html', - controller: UsersEdit }). + when('/organizations/:organization_id/users', { + templateUrl: urlPrefix + 'partials/users.html', + controller: 'UsersList' + }). - when('/teams', { templateUrl: urlPrefix + 'partials/teams.html', - controller: TeamsList }). + when('/organizations/:organization_id/users/add', { + templateUrl: urlPrefix + 'partials/users.html', + controller: 'UsersAdd' + }). - when('/teams/add', { templateUrl: urlPrefix + 'partials/teams.html', - controller: TeamsAdd }). + when('/organizations/:organization_id/users/:user_id', { + templateUrl: urlPrefix + 'partials/users.html', + controller: 'UsersEdit' + }). - when('/teams/:team_id', { templateUrl: urlPrefix + 'partials/teams.html', - controller: TeamsEdit }). - - when('/teams/:team_id/permissions/add', { templateUrl: urlPrefix + 'partials/teams.html', - controller: PermissionsAdd }). + when('/teams', { + templateUrl: urlPrefix + 'partials/teams.html', + controller: 'TeamsList' + }). - when('/teams/:team_id/permissions', { templateUrl: urlPrefix + 'partials/teams.html', - controller: PermissionsList }). + when('/teams/add', { + templateUrl: urlPrefix + 'partials/teams.html', + controller: 'TeamsAdd' + }). - when('/teams/:team_id/permissions/:permission_id', { templateUrl: urlPrefix + 'partials/teams.html', - controller: PermissionsEdit }). + when('/teams/:team_id', { + templateUrl: urlPrefix + 'partials/teams.html', + controller: 'TeamsEdit' + }). - when('/teams/:team_id/users', { templateUrl: urlPrefix + 'partials/teams.html', - controller: UsersList }). + when('/teams/:team_id/permissions/add', { + templateUrl: urlPrefix + 'partials/teams.html', + controller: 'PermissionsAdd' + }). - when('/teams/:team_id/users/:user_id', { templateUrl: urlPrefix + 'partials/teams.html', - controller: UsersEdit }). + when('/teams/:team_id/permissions', { + templateUrl: urlPrefix + 'partials/teams.html', + controller: 'PermissionsList' + }). - when('/teams/:team_id/projects', { templateUrl: urlPrefix + 'partials/teams.html', - controller: ProjectsList }). + when('/teams/:team_id/permissions/:permission_id', { + templateUrl: urlPrefix + 'partials/teams.html', + controller: 'PermissionsEdit' + }). - when('/teams/:team_id/projects/add', { templateUrl: urlPrefix + 'partials/teams.html', - controller: ProjectsAdd }). + when('/teams/:team_id/users', { + templateUrl: urlPrefix + 'partials/teams.html', + controller: 'UsersList' + }). - when('/teams/:team_id/projects/:project_id', { templateUrl: urlPrefix + 'partials/teams.html', - controller: ProjectsEdit }). - - when('/teams/:team_id/credentials', { templateUrl: urlPrefix + 'partials/teams.html', - controller: CredentialsList }). + when('/teams/:team_id/users/:user_id', { + templateUrl: urlPrefix + 'partials/teams.html', + controller: 'UsersEdit' + }). - when('/teams/:team_id/credentials/add', { templateUrl: urlPrefix + 'partials/teams.html', - controller: CredentialsAdd }). + when('/teams/:team_id/projects', { + templateUrl: urlPrefix + 'partials/teams.html', + controller: 'ProjectsList' + }). - when('/teams/:team_id/credentials/:credential_id', { templateUrl: urlPrefix + 'partials/teams.html', - controller: CredentialsEdit }). + when('/teams/:team_id/projects/add', { + templateUrl: urlPrefix + 'partials/teams.html', + controller: 'ProjectsAdd' + }). - when('/credentials', { templateUrl: urlPrefix + 'partials/credentials.html', - controller: CredentialsList }). + when('/teams/:team_id/projects/:project_id', { + templateUrl: urlPrefix + 'partials/teams.html', + controller: 'ProjectsEdit' + }). - when('/credentials/add', { templateUrl: urlPrefix + 'partials/credentials.html', - controller: CredentialsAdd }). + when('/teams/:team_id/credentials', { + templateUrl: urlPrefix + 'partials/teams.html', + controller: 'CredentialsList' + }). - when('/credentials/:credential_id', { templateUrl: urlPrefix + 'partials/credentials.html', - controller: CredentialsEdit }). - - when('/users', { templateUrl: urlPrefix + 'partials/users.html', - controller: UsersList }). + when('/teams/:team_id/credentials/add', { + templateUrl: urlPrefix + 'partials/teams.html', + controller: 'CredentialsAdd' + }). - when('/users/add', { templateUrl: urlPrefix + 'partials/users.html', - controller: UsersAdd }). + when('/teams/:team_id/credentials/:credential_id', { + templateUrl: urlPrefix + 'partials/teams.html', + controller: 'CredentialsEdit' + }). - when('/users/:user_id', { templateUrl: urlPrefix + 'partials/users.html', - controller: UsersEdit }). + when('/credentials', { + templateUrl: urlPrefix + 'partials/credentials.html', + controller: 'CredentialsList' + }). - when('/users/:user_id/credentials', { templateUrl: urlPrefix + 'partials/users.html', - controller: CredentialsList }). + when('/credentials/add', { + templateUrl: urlPrefix + 'partials/credentials.html', + controller: 'CredentialsAdd' + }). - when('/users/:user_id/permissions/add', { templateUrl: urlPrefix + 'partials/users.html', - controller: PermissionsAdd }). + when('/credentials/:credential_id', { + templateUrl: urlPrefix + 'partials/credentials.html', + controller: 'CredentialsEdit' + }). - when('/users/:user_id/permissions', { templateUrl: urlPrefix + 'partials/users.html', - controller: PermissionsList }). + when('/users', { + templateUrl: urlPrefix + 'partials/users.html', + controller: 'UsersList' + }). - when('/users/:user_id/permissions/:permission_id', { templateUrl: urlPrefix + 'partials/users.html', - controller: PermissionsEdit }). + when('/users/add', { + templateUrl: urlPrefix + 'partials/users.html', + controller: 'UsersAdd' + }). - when('/users/:user_id/credentials/add', { templateUrl: urlPrefix + 'partials/teams.html', - controller: CredentialsAdd }). + when('/users/:user_id', { + templateUrl: urlPrefix + 'partials/users.html', + controller: 'UsersEdit' + }). - when('/teams/:user_id/credentials/:credential_id', { templateUrl: urlPrefix + 'partials/teams.html', - controller: CredentialsEdit }). + when('/users/:user_id/credentials', { + templateUrl: urlPrefix + 'partials/users.html', + controller: 'CredentialsList' + }). - when('/login', { templateUrl: urlPrefix + 'partials/home.html', controller: Authenticate }). + when('/users/:user_id/permissions/add', { + templateUrl: urlPrefix + 'partials/users.html', + controller: 'PermissionsAdd' + }). - when('/logout', { templateUrl: urlPrefix + 'partials/home.html', controller: Authenticate }). + when('/users/:user_id/permissions', { + templateUrl: urlPrefix + 'partials/users.html', + controller: 'PermissionsList' + }). - when('/home', { templateUrl: urlPrefix + 'partials/home.html', controller: Home }). + when('/users/:user_id/permissions/:permission_id', { + templateUrl: urlPrefix + 'partials/users.html', + controller: 'PermissionsEdit' + }). - when('/home/groups', { templateUrl: urlPrefix + 'partials/subhome.html', controller: HomeGroups }). + when('/users/:user_id/credentials/add', { + templateUrl: urlPrefix + 'partials/teams.html', + controller: 'CredentialsAdd' + }). - when('/home/hosts', { templateUrl: urlPrefix + 'partials/subhome.html', controller: HomeHosts }). - - otherwise({redirectTo: '/home'}); - }]) - .run(['$cookieStore', '$rootScope', 'CheckLicense', '$location', 'Authorization','LoadBasePaths', 'ViewLicense', - 'Timer', 'ClearScope', 'HideStream', - function($cookieStore, $rootScope, CheckLicense, $location, Authorization, LoadBasePaths, ViewLicense, - Timer, ClearScope, HideStream) { - - LoadBasePaths(); - - $rootScope.breadcrumbs = new Array(); - $rootScope.crumbCache = new Array(); - $rootScope.sessionTimer = Timer.init(); + when('/teams/:user_id/credentials/:credential_id', { + templateUrl: urlPrefix + 'partials/teams.html', + controller: 'CredentialsEdit' + }). - $rootScope.$on("$routeChangeStart", function(event, next, current) { + when('/login', { + templateUrl: urlPrefix + 'partials/home.html', + controller: 'Authenticate' + }). - // Before navigating away from current tab, make sure the primary view is visible - if ($('#stream-container').is(':visible')) { - HideStream(); - } + when('/logout', { + templateUrl: urlPrefix + 'partials/home.html', + controller: 'Authenticate' + }). - // On each navigation request, check that the user is logged in - if ( !/^\/(login|logout)/.test($location.path()) ) { - // capture most recent URL, excluding login/logout - $rootScope.lastPath = $location.path(); - $cookieStore.put('lastPath', $location.path()); - } + when('/home', { + templateUrl: urlPrefix + 'partials/home.html', + controller: 'Home' + }). - if (Authorization.isUserLoggedIn() == false) { - if ( next.templateUrl != (urlPrefix + 'partials/login.html') ) { - $location.path('/login'); - } - } - else if ($rootScope.sessionTimer.isExpired()) { - if ( next.templateUrl != (urlPrefix + 'partials/login.html') ) { - $rootScope.sessionTimer.expireSession(); - $location.path('/login'); - } - } - else { - if ($rootScope.current_user == undefined || $rootScope.current_user == null) { - Authorization.restoreUserInfo(); //user must have hit browser refresh - } - CheckLicense(); - } - - // Make the correct tab active - var base = $location.path().replace(/^\//,'').split('/')[0]; - if (base == '') { - base = 'home'; - } - else { - base.replace(/\_/g,' '); - } - $('.nav-tabs a[href="#' + base + '"]').tab('show'); + when('/home/groups', { + templateUrl: urlPrefix + 'partials/subhome.html', + controller: 'HomeGroups' + }). + + when('/home/hosts', { + templateUrl: urlPrefix + 'partials/subhome.html', + controller: 'HomeHosts' + }). + + otherwise({ + redirectTo: '/home' + }); + } + ]) + .run(['$cookieStore', '$rootScope', 'CheckLicense', '$location', 'Authorization', 'LoadBasePaths', 'ViewLicense', + 'Timer', 'ClearScope', 'HideStream', + function ($cookieStore, $rootScope, CheckLicense, $location, Authorization, LoadBasePaths, ViewLicense, + Timer, ClearScope, HideStream) { + + LoadBasePaths(); + + $rootScope.breadcrumbs = []; + $rootScope.crumbCache = []; + $rootScope.sessionTimer = Timer.init(); + + $rootScope.$on("$routeChangeStart", function (event, next) { + + // Before navigating away from current tab, make sure the primary view is visible + if ($('#stream-container').is(':visible')) { + HideStream(); + } + + // On each navigation request, check that the user is logged in + if (!/^\/(login|logout)/.test($location.path())) { + // capture most recent URL, excluding login/logout + $rootScope.lastPath = $location.path(); + $cookieStore.put('lastPath', $location.path()); + } + + if (Authorization.isUserLoggedIn() === false) { + if (next.templateUrl !== (urlPrefix + 'partials/login.html')) { + $location.path('/login'); + } + } else if ($rootScope.sessionTimer.isExpired()) { + if (next.templateUrl !== (urlPrefix + 'partials/login.html')) { + $rootScope.sessionTimer.expireSession(); + $location.path('/login'); + } + } else { + if ($rootScope.current_user === undefined || $rootScope.current_user === null) { + Authorization.restoreUserInfo(); //user must have hit browser refresh + } + CheckLicense(); + } + + // Make the correct tab active + var base = $location.path().replace(/^\//, '').split('/')[0]; + if (base === '') { + base = 'home'; + } else { + base.replace(/\_/g, ' '); + } + $('.nav-tabs a[href="#' + base + '"]').tab('show'); }); - if (!Authorization.getToken()) { - // When the app first loads, redirect to login page - $rootScope.sessionExpired = false; - $cookieStore.put('sessionExpired', false); - $location.path('/login'); - } - else { - // If browser refresh, set the user_is_superuser value - $rootScope['user_is_superuser'] = Authorization.getUserInfo('is_superuser'); - } - - // If browser refresh, activate the correct tab - var base = ($location.path().replace(/^\//,'').split('/')[0]); - if (base == '') { - base = 'home'; - $location.path('/home'); - } - else { - base.replace(/\_/g,' '); - } - $('.nav-tabs a[href="#' + base + '"]').tab('show'); - - $rootScope.viewCurrentUser = function() { - $location.path('/users/' + $rootScope.current_user.id); + if (!Authorization.getToken()) { + // When the app first loads, redirect to login page + $rootScope.sessionExpired = false; + $cookieStore.put('sessionExpired', false); + $location.path('/login'); + } else { + // If browser refresh, set the user_is_superuser value + $rootScope.user_is_superuser = Authorization.getUserInfo('is_superuser'); } - $rootScope.viewLicense = function() { - //$location.path('/license'); - ViewLicense(); - } - }]); + // If browser refresh, activate the correct tab + var base = ($location.path().replace(/^\//, '').split('/')[0]); + if (base === '') { + base = 'home'; + $location.path('/home'); + } else { + base.replace(/\_/g, ' '); + } + $('.nav-tabs a[href="#' + base + '"]').tab('show'); + + $rootScope.viewCurrentUser = function () { + $location.path('/users/' + $rootScope.current_user.id); + }; + + $rootScope.viewLicense = function () { + //$location.path('/license'); + ViewLicense(); + }; + } + ]); \ No newline at end of file diff --git a/awx/ui/static/js/config.js b/awx/ui/static/js/config.js index c7f6b47587..5fac0d9a38 100644 --- a/awx/ui/static/js/config.js +++ b/awx/ui/static/js/config.js @@ -1,4 +1,4 @@ -/************************************ +/********************************************************************** * * Copyright (c) 2014 AnsibleWorks, Inc. * @@ -8,19 +8,21 @@ * */ -var $AnsibleConfig = -{ - tooltip_delay: {show: 500, hide: 100}, // Default number of milliseconds to delay displaying/hiding tooltips +/*jshint unused:false */ - debug_mode: true, // Enable console logging messages - - password_strength: 45, // User password strength. Integer between 0 and 100, 100 being impossibly strong. - // This value controls progress bar colors: - // 0 to password_strength - 15 = red; - // password_strength - 15 to password_strength = yellow - // > password_strength = green - // It also controls password validation. Passwords are rejected if the score is not > password_strength. +var $AnsibleConfig = { - session_timeout: 1800 // Number of seconds before an inactive session is automatically timed out and forced to log in again. - // Separate from time out value set in API. - } + tooltip_delay: {show: 500, hide: 100}, // Default number of milliseconds to delay displaying/hiding tooltips + + debug_mode: true, // Enable console logging messages + + password_strength: 45, // User password strength. Integer between 0 and 100, 100 being impossibly strong. + // This value controls progress bar colors: + // 0 to password_strength - 15 = red; + // password_strength - 15 to password_strength = yellow + // > password_strength = green + // It also controls password validation. Passwords are rejected if the score is not > password_strength. + + session_timeout: 1800 // Number of seconds before an inactive session is automatically timed out and forced to log in again. + // Separate from time out value set in API. +}; \ No newline at end of file diff --git a/awx/ui/static/js/controllers/Credentials.js b/awx/ui/static/js/controllers/Credentials.js index ef4a636035..7ba65e003c 100644 --- a/awx/ui/static/js/controllers/Credentials.js +++ b/awx/ui/static/js/controllers/Credentials.js @@ -1,12 +1,12 @@ -/************************************ +/************************************************* * Copyright (c) 2014 AnsibleWorks, Inc. * - * * Credentials.js * * Controller functions for the Credential model. * */ + 'use strict'; function CredentialsList($scope, $rootScope, $location, $log, $routeParams, Rest, Alert, CredentialList, @@ -114,10 +114,10 @@ function CredentialsList($scope, $rootScope, $location, $log, $routeParams, Rest var url = defaultUrl + id + '/'; Rest.setUrl(url); Rest.destroy() - .success(function (data, status, headers, config) { + .success(function () { scope.search(list.iterator); }) - .error(function (data, status, headers, config) { + .error(function (data, status) { Wait('stop'); ProcessErrors(scope, data, status, null, { hdr: 'Error!', @@ -143,7 +143,7 @@ CredentialsList.$inject = ['$scope', '$rootScope', '$location', '$log', '$routeP function CredentialsAdd($scope, $rootScope, $compile, $location, $log, $routeParams, CredentialForm, GenerateForm, Rest, Alert, ProcessErrors, LoadBreadCrumbs, ReturnToCaller, ClearScope, GenerateList, SearchInit, PaginateInit, LookUpInit, UserList, TeamList, GetBasePath, - GetChoices, Empty, KindChange, OwnerChange, FormSave, DebugForm) { + GetChoices, Empty, KindChange, OwnerChange, FormSave) { ClearScope('tree-form'); ClearScope('htmlTemplate'); //Garbage collection. Don't leave behind any listeners/watchers from the prior //scope. @@ -152,7 +152,6 @@ function CredentialsAdd($scope, $rootScope, $compile, $location, $log, $routePar var form = CredentialForm, generator = GenerateForm, scope = generator.inject(form, { mode: 'add', related: false }), - base = $location.path().replace(/^\//, '').split('/')[0], defaultUrl = GetBasePath('credentials'), url; @@ -193,10 +192,10 @@ function CredentialsAdd($scope, $rootScope, $compile, $location, $log, $routePar url = GetBasePath('users') + $routeParams.user_id + '/'; Rest.setUrl(url); Rest.get() - .success(function (data, status, headers, config) { + .success(function (data) { scope.user_username = data.username; }) - .error(function (data, status, headers, config) { + .error(function (data, status) { ProcessErrors(scope, data, status, null, { hdr: 'Error!', msg: 'Failed to retrieve user. GET status: ' + status }); }); @@ -210,10 +209,10 @@ function CredentialsAdd($scope, $rootScope, $compile, $location, $log, $routePar url = GetBasePath('teams') + $routeParams.team_id + '/'; Rest.setUrl(url); Rest.get() - .success(function (data, status, headers, config) { + .success(function (data) { scope.team_name = data.name; }) - .error(function (data, status, headers, config) { + .error(function (data, status) { ProcessErrors(scope, data, status, null, { hdr: 'Error!', msg: 'Failed to retrieve team. GET status: ' + status }); }); @@ -289,7 +288,7 @@ function CredentialsAdd($scope, $rootScope, $compile, $location, $log, $routePar CredentialsAdd.$inject = ['$scope', '$rootScope', '$compile', '$location', '$log', '$routeParams', 'CredentialForm', 'GenerateForm', 'Rest', 'Alert', 'ProcessErrors', 'LoadBreadCrumbs', 'ReturnToCaller', 'ClearScope', 'GenerateList', 'SearchInit', 'PaginateInit', 'LookUpInit', 'UserList', 'TeamList', 'GetBasePath', 'GetChoices', 'Empty', - 'KindChange', 'OwnerChange', 'FormSave', 'DebugForm' + 'KindChange', 'OwnerChange', 'FormSave' ]; diff --git a/awx/ui/static/js/controllers/JobEvents.js b/awx/ui/static/js/controllers/JobEvents.js index 7c9406dd0d..f62f0ff73f 100644 --- a/awx/ui/static/js/controllers/JobEvents.js +++ b/awx/ui/static/js/controllers/JobEvents.js @@ -249,13 +249,12 @@ JobEventsList.$inject = ['$filter', '$scope', '$rootScope', '$location', '$log', 'ProcessErrors', 'GetBasePath', 'LookUpInit', 'ToggleChildren', 'FormatDate', 'EventView', 'Refresh', 'Wait' ]; -function JobEventsEdit($scope, $rootScope, $compile, $location, $log, $routeParams, JobEventForm, GenerateForm, - Rest, Alert, ProcessErrors, LoadBreadCrumbs, ClearScope, GetBasePath, FormatDate, EventView, - Wait) { - ClearScope('htmlTemplate'); //Garbage collection. Don't leave behind any listeners/watchers from the prior - //scope. - // Inject dynamic view - var form = JobEventForm, +function JobEventsEdit($scope, $rootScope, $compile, $location, $log, $routeParams, JobEventsForm, GenerateForm, + Rest, Alert, ProcessErrors, LoadBreadCrumbs, ClearScope, GetBasePath, FormatDate, EventView, Wait) { + + ClearScope('htmlTemplate'); + + var form = JobEventsForm, generator = GenerateForm, scope = GenerateForm.inject(form, { mode: 'edit', related: true }), defaultUrl = GetBasePath('base') + 'job_events/' + $routeParams.event_id + '/'; @@ -348,7 +347,7 @@ function JobEventsEdit($scope, $rootScope, $compile, $location, $log, $routePara } -JobEventsEdit.$inject = ['$scope', '$rootScope', '$compile', '$location', '$log', '$routeParams', 'JobEventForm', +JobEventsEdit.$inject = ['$scope', '$rootScope', '$compile', '$location', '$log', '$routeParams', 'JobEventsForm', 'GenerateForm', 'Rest', 'Alert', 'ProcessErrors', 'LoadBreadCrumbs', 'ClearScope', 'GetBasePath', 'FormatDate', 'EventView', 'Wait' ]; \ No newline at end of file diff --git a/awx/ui/static/js/forms/ActivityDetail.js b/awx/ui/static/js/forms/ActivityDetail.js index 557e586219..e5b63ae77a 100644 --- a/awx/ui/static/js/forms/ActivityDetail.js +++ b/awx/ui/static/js/forms/ActivityDetail.js @@ -6,11 +6,10 @@ * */ angular.module('ActivityDetailDefinition', []) - .value( - 'ActivityDetailForm', { - + .value('ActivityDetailForm', { + name: 'activity', - editTitle: 'Activity Detail', + editTitle: 'Activity Detail', well: false, 'class': 'horizontal-narrow', formFieldSize: 'col-lg-10', @@ -21,18 +20,18 @@ angular.module('ActivityDetailDefinition', []) label: "Initiated by", type: 'text', readonly: true - }, + }, operation: { label: 'Action', type: 'text', readonly: true - }, + }, changes: { label: 'Changes', type: 'textarea', ngHide: "!changes || changes =='' || changes == 'null'", readonly: true - } - } + } + } }); //Form diff --git a/awx/ui/static/js/forms/Credentials.js b/awx/ui/static/js/forms/Credentials.js index eef3a5be53..31ec9dec2b 100644 --- a/awx/ui/static/js/forms/Credentials.js +++ b/awx/ui/static/js/forms/Credentials.js @@ -6,23 +6,22 @@ * */ angular.module('CredentialFormDefinition', []) - .value( - 'CredentialForm', { - - addTitle: 'Create Credential', //Legend in add mode - editTitle: '{{ name }}', //Legend in edit mode + .value('CredentialForm', { + + addTitle: 'Create Credential', //Legend in add mode + editTitle: '{{ name }}', //Legend in edit mode name: 'credential', well: true, forceListeners: true, - + actions: { stream: { ngClick: "showActivity()", awToolTip: "View Activity Stream", mode: 'edit' - } - }, - + } + }, + fields: { name: { label: 'Name', @@ -30,27 +29,31 @@ angular.module('CredentialFormDefinition', []) addRequired: true, editRequired: true, autocomplete: false - }, - description: { + }, + description: { label: 'Description', type: 'text', addRequired: false, editRequired: false - }, + }, owner: { label: "Does this credential belong to a team or user?", type: 'radio_group', ngChange: "ownerChange()", - options: [ - { label: 'User', value: 'user', selected: true }, - { label: 'Team', value: 'team' } - ], - awPopOver: "

A credential must be associated with either a user or a team. Choosing a user allows only the selected user access " + + options: [{ + label: 'User', + value: 'user', + selected: true + }, { + label: 'Team', + value: 'team' + }], + awPopOver: "

A credential must be associated with either a user or a team. Choosing a user allows only the selected user access " + "to the credential. Choosing a team shares the credential with all team members.

", dataTitle: 'Owner', dataPlacement: 'right', dataContainer: "body" - }, + }, user: { label: 'User that owns this credential', type: 'lookup', @@ -58,8 +61,11 @@ angular.module('CredentialFormDefinition', []) sourceField: 'username', ngClick: 'lookUpUser()', ngShow: "owner == 'user'", - awRequiredWhen: { variable: "user_required", init: "false" } - }, + awRequiredWhen: { + variable: "user_required", + init: "false" + } + }, team: { label: 'Team that owns this credential', type: 'lookup', @@ -67,69 +73,84 @@ angular.module('CredentialFormDefinition', []) sourceField: 'name', ngClick: 'lookUpTeam()', ngShow: "owner == 'team'", - awRequiredWhen: { variable: "team_required", init: "false" } - }, + awRequiredWhen: { + variable: "team_required", + init: "false" + } + }, kind: { label: 'Type', excludeModal: true, type: 'select', ngOptions: 'kind.label for kind in credential_kind_options', ngChange: 'kindChange()', - addRequired: true, + addRequired: true, editRequired: true, - helpCollapse: [ - { hdr: 'Select a Credential Type', - content: '
\n' + - '
AWS
\n' + - '
Access keys for Amazon Web Services used for inventory management or deployment.
\n' + - '
Machine
\n' + - '
Authentication for remote machine access. This can include SSH keys, usernames, passwords, ' + - 'and sudo information. Machine credentials are used when submitting jobs to run playbooks against ' + - 'remote hosts.
' + - '
Rackspace
\n' + - '
Access information for Rackspace Cloud used for inventory management or deployment.
\n' + - '
SCM
\n' + - '
Used to check out and synchronize playbook repositories with a remote source control ' + - 'management system such as Git, Subversion (svn), or Mercurial (hg). These credentials are ' + - 'used on the Projects tab.
\n' + - '
\n' - }] - }, + helpCollapse: [{ + hdr: 'Select a Credential Type', + content: '
\n' + + '
AWS
\n' + + '
Access keys for Amazon Web Services used for inventory management or deployment.
\n' + + '
Machine
\n' + + '
Authentication for remote machine access. This can include SSH keys, usernames, passwords, ' + + 'and sudo information. Machine credentials are used when submitting jobs to run playbooks against ' + + 'remote hosts.
' + + '
Rackspace
\n' + + '
Access information for Rackspace Cloud used for inventory management or deployment.
\n' + + '
SCM
\n' + + '
Used to check out and synchronize playbook repositories with a remote source control ' + + 'management system such as Git, Subversion (svn), or Mercurial (hg). These credentials are ' + + 'used on the Projects tab.
\n' + + '
\n' + }] + }, access_key: { label: 'Access Key', type: 'text', ngShow: "kind.value == 'aws'", - awRequiredWhen: { variable: "aws_required", init: false }, + awRequiredWhen: { + variable: "aws_required", + init: false + }, autocomplete: false, apiField: 'username' - }, + }, secret_key: { label: 'Secret Key', type: 'password', ngShow: "kind.value == 'aws'", - awRequiredWhen: { variable: "aws_required", init: false }, + awRequiredWhen: { + variable: "aws_required", + init: false + }, autocomplete: false, ask: false, clear: false, apiField: 'passwowrd' - }, + }, "username": { labelBind: 'usernameLabel', type: 'text', ngShow: "kind.value && kind.value !== 'aws'", - awRequiredWhen: {variable: 'rackspace_required', init: false }, - autocomplete: false + awRequiredWhen: { + variable: 'rackspace_required', + init: false }, + autocomplete: false + }, "api_key": { label: 'API Key', type: 'password', ngShow: "kind.value == 'rax'", - awRequiredWhen: { variable: "rackspace_required", init: false }, + awRequiredWhen: { + variable: "rackspace_required", + init: false + }, autocomplete: false, ask: false, clear: false, apiField: 'passwowrd' - }, + }, "password": { label: 'Password', type: 'password', @@ -141,7 +162,7 @@ angular.module('CredentialFormDefinition', []) clear: false, associated: 'password_confirm', autocomplete: false - }, + }, "password_confirm": { label: 'Confirm Password', type: 'password', @@ -151,7 +172,7 @@ angular.module('CredentialFormDefinition', []) awPassMatch: true, associated: 'password', autocomplete: false - }, + }, "ssh_password": { label: 'SSH Password', type: 'password', @@ -163,7 +184,7 @@ angular.module('CredentialFormDefinition', []) clear: true, associated: 'ssh_password_confirm', autocomplete: false - }, + }, "ssh_password_confirm": { label: 'Confirm SSH Password', type: 'password', @@ -173,7 +194,7 @@ angular.module('CredentialFormDefinition', []) awPassMatch: true, associated: 'ssh_password', autocomplete: false - }, + }, "ssh_key_data": { labelBind: 'sshKeyDataLabel', type: 'textarea', @@ -182,7 +203,7 @@ angular.module('CredentialFormDefinition', []) editRequired: false, 'class': 'ssh-key-field', rows: 10 - }, + }, "ssh_key_unlock": { label: 'Key Password', type: 'password', @@ -192,9 +213,9 @@ angular.module('CredentialFormDefinition', []) ngChange: "clearPWConfirm('ssh_key_unlock_confirm')", associated: 'ssh_key_unlock_confirm', ask: true, - askShow: "kind.value == 'ssh'", //Only allow ask for machine credentials + askShow: "kind.value == 'ssh'", //Only allow ask for machine credentials clear: true - }, + }, "ssh_key_unlock_confirm": { label: 'Confirm Key Password', type: 'password', @@ -203,7 +224,7 @@ angular.module('CredentialFormDefinition', []) editRequired: false, awPassMatch: true, associated: 'ssh_key_unlock' - }, + }, "sudo_username": { label: 'Sudo Username', type: 'text', @@ -211,7 +232,7 @@ angular.module('CredentialFormDefinition', []) addRequired: false, editRequired: false, autocomplete: false - }, + }, "sudo_password": { label: 'Sudo Password', type: 'password', @@ -223,7 +244,7 @@ angular.module('CredentialFormDefinition', []) clear: true, associated: 'sudo_password_confirm', autocomplete: false - }, + }, "sudo_password_confirm": { label: 'Confirm Sudo Password', type: 'password', @@ -233,24 +254,21 @@ angular.module('CredentialFormDefinition', []) awPassMatch: true, associated: 'sudo_password', autocomplete: false - } - }, - - buttons: { //for now always generates \n"; - - if (target.id == 1 || inbound.parent == 0) { - // We're moving the group to the top level, or we're moving a top level group down - html += "

Move Group

\n"; - } - else { - html += "

Copy or Move?

\n"; + + if (target.id === 1 || inbound.parent === 0) { + // We're moving the group to the top level, or we're moving a top level group down + html += "

Move Group

\n"; + } else { + html += "

Copy or Move?

\n"; } html += "\n"; html += "
\n"; - - if (target.id == 1) { + + if (target.id === 1) { html += "

Are you sure you want to move group " + inbound.name + " to the top level?

"; - } - else if (inbound.parent == 0) { + } else if (inbound.parent === 0) { html += "

Are you sure you want to move group " + inbound.name + " from the top level and make it a child of " + target.name + "?

"; - } - else { + } else { html += "
\n"; html += "

Would you like to copy or move group " + inbound.name + " to group " + target.name + "?

\n"; html += "
\n"; @@ -300,12 +321,12 @@ angular.module('InventoryTree', ['Utilities', 'RestServices', 'GroupsHelper', 'P html += "
\n"; html += "
\n"; } - + html += "
\n"; html += "
\n"; html += "Cancel\n"; - if (target.id == 1 || inbound.parent == 0) { + if (target.id === 1 || inbound.parent === 0) { // We're moving the group to the top level, or we're moving a top level group down html += "Yes\n"; } @@ -314,134 +335,135 @@ angular.module('InventoryTree', ['Utilities', 'RestServices', 'GroupsHelper', 'P html += "
\n"; html += "\n"; html += "\n"; - + // Inject our custom dialog - var e = angular.element(document.getElementById('inventory-modal-container')); + e= angular.element(document.getElementById('inventory-modal-container')); e.empty().append(html); $compile(e)(scope); - + // Display it $('#copy-prompt-modal').modal({ backdrop: 'static', keyboard: true, show: true - }); - + }); + // Respond to move - scope.moveGroup = function() { - + scope.moveGroup = function () { + var url, group, parent; $('#copy-prompt-modal').modal('hide'); Wait('start'); - + // disassociate the group from the original parent if (scope.removeGroupRemove) { - scope.removeGroupRemove(); + scope.removeGroupRemove(); } - scope.removeGroupRemove = scope.$on('removeGroup', function() { + scope.removeGroupRemove = scope.$on('removeGroup', function () { if (inbound.parent > 0) { // Only remove a group from a parent when the parent is a group and not the inventory root - var parent = Find({ list: scope.groups, key: 'id', val: inbound.parent }) - var url = GetBasePath('base') + 'groups/' + parent.group_id + '/children/'; + parent = Find({ list: scope.groups, key: 'id', val: inbound.parent }); + url = GetBasePath('base') + 'groups/' + parent.group_id + '/children/'; Rest.setUrl(url); Rest.post({ id: inbound.group_id, disassociate: 1 }) - .success( function(data, status, headers, config) { + .success(function () { //Triggers refresh of group list in inventory controller - scope.$emit('GroupDeleteCompleted'); - }) - .error( function(data, status, headers, config) { + scope.$emit('GroupDeleteCompleted'); + }) + .error(function (data, status) { Wait('stop'); - ProcessErrors(scope, data, status, null, - { hdr: 'Error!', msg: 'Failed to remove ' + inbound.name + - ' from ' + parent.name + '. POST returned status: ' + status }); + ProcessErrors(scope, data, status, null, { + hdr: 'Error!', + msg: 'Failed to remove ' + inbound.name + + ' from ' + parent.name + '. POST returned status: ' + status }); - } - else { + }); + } else { //Triggers refresh of group list in inventory controller - scope.$emit('GroupDeleteCompleted'); + scope.$emit('GroupDeleteCompleted'); } - }); + }); // add the new group to the target parent - var url = (!Empty(target.group_id)) ? - GetBasePath('base') + 'groups/' + target.group_id + '/children/' : - GetBasePath('inventory') + scope.inventory_id + '/groups/'; - var group = { + url = (!Empty(target.group_id)) ? + GetBasePath('base') + 'groups/' + target.group_id + '/children/' : + GetBasePath('inventory') + scope.inventory_id + '/groups/'; + group = { id: inbound.group_id, name: inbound.name, description: inbound.description, inventory: scope.inventory_id - } + }; Rest.setUrl(url); Rest.post(group) - .success( function(data, status, headers, config) { + .success(function () { scope.$emit('removeGroup'); - }) - .error( function(data, status, headers, config) { + }) + .error(function (data, status) { var target_name = (Empty(target.group_id)) ? 'inventory' : target.name; Wait('stop'); - ProcessErrors(scope, data, status, null, - { hdr: 'Error!', msg: 'Failed to add ' + node.attr('name') + ' to ' + - target_name + '. POST returned status: ' + status }); - }); - } - + ProcessErrors(scope, data, status, null, { hdr: 'Error!', + msg: 'Failed to add ' + inbound.name + ' to ' + target_name + '. POST returned status: ' + status }); + }); + }; - scope.copyGroup = function() { + + scope.copyGroup = function () { $('#copy-prompt-modal').modal('hide'); Wait('start'); // add the new group to the target parent - var url = (!Empty(target.group_id)) ? - GetBasePath('base') + 'groups/' + target.group_id + '/children/' : - GetBasePath('inventory') + scope.inventory_id + '/groups/'; - var group = { - id: inbound.group_id, - name: inbound.name, - description: inbound.description, - inventory: scope.inventory_id - } + var url = (!Empty(target.group_id)) ? + GetBasePath('base') + 'groups/' + target.group_id + '/children/' : + GetBasePath('inventory') + scope.inventory_id + '/groups/', + group = { + id: inbound.group_id, + name: inbound.name, + description: inbound.description, + inventory: scope.inventory_id + }; + Rest.setUrl(url); Rest.post(group) - .success( function(data, status, headers, config) { - //Triggers refresh of group list in inventory controller - scope.$emit('GroupDeleteCompleted'); - }) - .error( function(data, status, headers, config) { - var target_name = (Empty(target.group_id)) ? 'inventory' : target.name; - Wait('stop'); - ProcessErrors(scope, data, status, null, - { hdr: 'Error!', msg: 'Failed to add ' + inbound.name + ' to ' + - target_name + '. POST returned status: ' + status }); - }); - } + .success(function () { + //Triggers refresh of group list in inventory controller + scope.$emit('GroupDeleteCompleted'); + }) + .error(function (data, status) { + var target_name = (Empty(target.group_id)) ? 'inventory' : target.name; + Wait('stop'); + ProcessErrors(scope, data, status, null, { hdr: 'Error!', + msg: 'Failed to add ' + inbound.name + ' to ' + target_name + '. POST returned status: ' + status + }); + }); + }; - } - }]) - - // Copy a host after drag-n-drop - .factory('CopyMoveHost', ['$compile', 'Alert', 'ProcessErrors', 'Find', 'Wait', 'Rest', 'Empty', 'GetBasePath', - function($compile, Alert, ProcessErrors, Find, Wait, Rest, Empty, GetBasePath) { - return function(params) { + }; + } +]) + +// Copy a host after drag-n-drop +.factory('CopyMoveHost', ['$compile', 'Alert', 'ProcessErrors', 'Find', 'Wait', 'Rest', 'Empty', 'GetBasePath', + function ($compile, Alert, ProcessErrors, Find, Wait, Rest, Empty, GetBasePath) { + return function (params) { var scope = params.scope, target = Find({ list: scope.groups, key: 'id', val: params.target_tree_id }), host = Find({ list: scope.hosts, key: 'id', val: params.host_id }), - found = false, i; - + found = false, e, i, html = ''; + if (host.summary_fields.all_groups) { - for (i=0; i< host.summary_fields.all_groups.length; i++) { - if (host.summary_fields.all_groups[i].id == target.group_id) { - found = true; - break; + for (i = 0; i < host.summary_fields.all_groups.length; i++) { + if (host.summary_fields.all_groups[i].id === target.group_id) { + found = true; + break; } } } if (found) { - var html = ''; html += "
\n"; html += "
\n"; html += "
\n"; html += "
\n"; - html += "\n"; html += "

Already in Group

\n"; html += "
\n"; @@ -456,31 +478,30 @@ angular.module('InventoryTree', ['Utilities', 'RestServices', 'GroupsHelper', 'P html += "
\n"; // Inject our custom dialog - var e = angular.element(document.getElementById('inventory-modal-container')); + e = angular.element(document.getElementById('inventory-modal-container')); e.empty().append(html); $compile(e)(scope); - + // Display it $('#copy-alert-modal').modal({ backdrop: 'static', keyboard: true, show: true - }); - - } - else { + }); + + } else { // Build the html for our prompt dialog - var html = ''; + html = ''; html += "
\n"; html += "
\n"; html += "
\n"; html += "
\n"; - html += "\n"; html += "

Copy Host

\n"; html += "
\n"; html += "
\n"; - html += "

Are you sure you want to copy host " + host.name + ' to group ' + target.name + '?

'; + html += "

Are you sure you want to copy host " + host.name + ' to group ' + target.name + '?

'; html += "
\n"; html += "
\n"; html += "No\n"; @@ -489,35 +510,35 @@ angular.module('InventoryTree', ['Utilities', 'RestServices', 'GroupsHelper', 'P html += "
\n"; html += "
\n"; html += "
\n"; - + // Inject our custom dialog - var e = angular.element(document.getElementById('inventory-modal-container')); + e = angular.element(document.getElementById('inventory-modal-container')); e.empty().append(html); $compile(e)(scope); - + // Display it $('#copy-prompt-modal').modal({ backdrop: 'static', keyboard: true, show: true - }); + }); - scope.copyHost = function() { + scope.copyHost = function () { $('#copy-prompt-modal').modal('hide'); Wait('start'); Rest.setUrl(GetBasePath('groups') + target.group_id + '/hosts/'); Rest.post(host) - .success(function(data, status, headers, config) { + .success(function () { // Signal the controller to refresh the hosts view scope.$emit('GroupTreeRefreshed'); - }) - .error(function(data, status, headers, config) { + }) + .error(function (data, status) { Wait('stop'); - ProcessErrors(scope, data, status, null, - { hdr: 'Error!', msg: 'Failed to add ' + host.name + ' to ' + - target.name + '. POST returned status: ' + status }); - }); - } + ProcessErrors(scope, data, status, null, { hdr: 'Error!', msg: 'Failed to add ' + host.name + ' to ' + + target.name + '. POST returned status: ' + status }); + }); + }; } - } - }]); + }; + } +]); \ No newline at end of file diff --git a/awx/ui/static/lib/ansible/RestServices.js b/awx/ui/static/lib/ansible/RestServices.js index cf9588793f..efcd546aa4 100644 --- a/awx/ui/static/lib/ansible/RestServices.js +++ b/awx/ui/static/lib/ansible/RestServices.js @@ -4,154 +4,194 @@ * Generic accessor for Ansible Commander services * */ - - 'use strict'; +'use strict'; -angular.module('RestServices',['ngCookies','AuthService']) -.factory('Rest', ['$http','$rootScope','$cookieStore', '$q', 'Authorization', -function($http, $rootScope, $cookieStore, $q, Authorization) { - return { - - headers: {}, +angular.module('RestServices', ['ngCookies', 'AuthService']) + .factory('Rest', ['$http', '$rootScope', '$cookieStore', '$q', 'Authorization', + function ($http, $rootScope, $cookieStore, $q, Authorization) { + return { - setUrl: function (url) { - this.url = url; - }, - checkExpired: function() { - return $rootScope.sessionTimer.isExpired(); - }, - pReplace: function() { - //in our url, replace :xx params with a value, assuming - //we can find it in user supplied params. - var key,rgx; - for (key in this.params) { - rgx = new RegExp("\\:" + key,'gm'); - if (rgx.test(this.url)) { - this.url = this.url.replace(rgx,this.params[key]); - delete this.params[key]; - } - } - }, - createResponse: function(data, status) { - // Simulate an http response when a token error occurs - // http://stackoverflow.com/questions/18243286/angularjs-promises-simulate-http-promises - - var promise = $q.reject({ data: data, status: status }); - promise.success = function(fn){ - promise.then(function(response){ fn(response.data, response.status) }, null); - return promise + headers: {}, + + setUrl: function (url) { + this.url = url; + }, + checkExpired: function () { + return $rootScope.sessionTimer.isExpired(); + }, + pReplace: function () { + //in our url, replace :xx params with a value, assuming + //we can find it in user supplied params. + var key, rgx; + for (key in this.params) { + rgx = new RegExp("\\:" + key, 'gm'); + if (rgx.test(this.url)) { + this.url = this.url.replace(rgx, this.params[key]); + delete this.params[key]; + } + } + }, + createResponse: function (data, status) { + // Simulate an http response when a token error occurs + // http://stackoverflow.com/questions/18243286/angularjs-promises-simulate-http-promises + + var promise = $q.reject({ + data: data, + status: status + }); + promise.success = function (fn) { + promise.then(function (response) { + fn(response.data, response.status); + }, null); + return promise; + }; + promise.error = function (fn) { + promise.then(null, function (response) { + fn(response.data, response.status); + }); + return promise; + }; + return promise; + }, + + setHeader: function (hdr) { + // Pass in { key: value } pairs to be added to the header + for (var h in hdr) { + this.headers[h] = hdr[h]; + } + }, + get: function (args) { + args = (args) ? args : {}; + this.params = (args.params) ? args.params : null; + this.pReplace(); + var expired = this.checkExpired(), + token = Authorization.getToken(); + if (expired) { + return this.createResponse({ + detail: 'Token is expired' + }, 401); + } else if (token) { + this.setHeader({ + Authorization: 'Token ' + token + }); + this.setHeader({ + "X-Auth-Token": 'Token ' + token + }); + return $http({ + method: 'GET', + url: this.url, + headers: this.headers, + params: this.params + }); + } else { + return this.createResponse({ + detail: 'Invalid token' + }, 401); + } + }, + post: function (data) { + var token = Authorization.getToken(), + expired = this.checkExpired(); + if (expired) { + return this.createResponse({ + detail: 'Token is expired' + }, 401); + } else if (token) { + this.setHeader({ + Authorization: 'Token ' + token + }); + this.setHeader({ + "X-Auth-Token": 'Token ' + token + }); + return $http({ + method: 'POST', + url: this.url, + headers: this.headers, + data: data + }); + } else { + return this.createResponse({ + detail: 'Invalid token' + }, 401); + } + }, + put: function (data) { + var token = Authorization.getToken(), + expired = this.checkExpired(); + if (expired) { + return this.createResponse({ + detail: 'Token is expired' + }, 401); + } else if (token) { + this.setHeader({ + Authorization: 'Token ' + token + }); + this.setHeader({ + "X-Auth-Token": 'Token ' + token + }); + return $http({ + method: 'PUT', + url: this.url, + headers: this.headers, + data: data + }); + } else { + return this.createResponse({ + detail: 'Invalid token' + }, 401); + } + }, + destroy: function (data) { + var token = Authorization.getToken(), + expired = this.checkExpired(); + if (expired) { + return this.createResponse({ + detail: 'Token is expired' + }, 401); + } else if (token) { + this.setHeader({ + Authorization: 'Token ' + token + }); + this.setHeader({ + "X-Auth-Token": 'Token ' + token + }); + return $http({ + method: 'DELETE', + url: this.url, + headers: this.headers, + data: data + }); + } else { + return this.createResponse({ + detail: 'Invalid token' + }, 401); + } + }, + options: function () { + var token = Authorization.getToken(), + expired = this.checkExpired(); + if (expired) { + return this.createResponse({ + detail: 'Token is expired' + }, 401); + } else if (token) { + this.setHeader({ + Authorization: 'Token ' + token + }); + this.setHeader({ + "X-Auth-Token": 'Token ' + token + }); + return $http({ + method: 'OPTIONS', + url: this.url, + headers: this.headers + }); + } else { + return this.createResponse({ + detail: 'Invalid token' + }, 401); + } + } }; - promise.error = function(fn){ - promise.then(null, function(response){ fn(response.data, response.status) }); - return promise; - }; - return promise; - }, - - setHeader: function(hdr) { - // Pass in { key: value } pairs to be added to the header - for (var h in hdr) { - this.headers[h] = hdr[h]; } - }, - get: function(args) { - args = (args) ? args : {}; - this.params = (args.params) ? args.params : null; - this.pReplace(); - var expired = this.checkExpired(); - var token = Authorization.getToken(); - if (expired) { - return this.createResponse({ detail: 'Token is expired' }, 401); - } - else if (token) { - this.setHeader({ Authorization: 'Token ' + token }); - this.setHeader({ "X-Auth-Token": 'Token ' + token }); - return $http({method: 'GET', - url: this.url, - headers: this.headers, - params: this.params - }); - } - else { - return this.createResponse({ detail: 'Invalid token' }, 401); - } - }, - post: function(data) { - var token = Authorization.getToken(); - var expired = this.checkExpired(); - if (expired) { - return this.createResponse({ detail: 'Token is expired' }, 401); - } - else if (token) { - this.setHeader({ Authorization: 'Token ' + token }); - this.setHeader({ "X-Auth-Token": 'Token ' + token }); - return $http({ - method: 'POST', - url: this.url, - headers: this.headers, - data: data }); - } - else { - return this.createResponse({ detail: 'Invalid token' }, 401); - } - }, - put: function(data) { - var token = Authorization.getToken(); - var expired = this.checkExpired(); - if (expired) { - return this.createResponse({ detail: 'Token is expired' }, 401); - } - else if (token) { - this.setHeader({ Authorization: 'Token ' + token }); - this.setHeader({ "X-Auth-Token": 'Token ' + token }); - return $http({ - method: 'PUT', - url: this.url, - headers: this.headers, - data: data }); - } - else { - return this.createResponse({ detail: 'Invalid token' }, 401); - } - }, - destroy: function(data) { - var token = Authorization.getToken(); - var expired = this.checkExpired(); - if (expired) { - return this.createResponse({ detail: 'Token is expired' }, 401); - } - else if (token) { - this.setHeader({ Authorization: 'Token ' + token }); - this.setHeader({ "X-Auth-Token": 'Token ' + token }); - return $http({ - method: 'DELETE', - url: this.url, - headers: this.headers, - data: data }); - } - else { - return this.createResponse({ detail: 'Invalid token' }, 401); - } - }, - options: function() { - var token = Authorization.getToken(); - var expired = this.checkExpired(); - if (expired) { - return this.createResponse({ detail: 'Token is expired' }, 401); - } - else if (token) { - this.setHeader({ Authorization: 'Token ' + token }); - this.setHeader({ "X-Auth-Token": 'Token ' + token }); - return $http({ - method: 'OPTIONS', - url: this.url, - headers: this.headers - }); - } - else { - return this.createResponse({ detail: 'Invalid token' }, 401); - } - } - } -}]); - + ]); \ No newline at end of file diff --git a/awx/ui/static/lib/ansible/Timer.js b/awx/ui/static/lib/ansible/Timer.js index cfc2d4ecdd..36912da5fc 100644 --- a/awx/ui/static/lib/ansible/Timer.js +++ b/awx/ui/static/lib/ansible/Timer.js @@ -7,52 +7,51 @@ * duration set in config.js * */ - 'use strict'; angular.module('TimerService', ['ngCookies', 'Utilities']) - .factory('Timer', ['$rootScope', '$cookieStore', '$location', 'GetBasePath', 'Empty', - function($rootScope, $cookieStore, $location, GetBasePath, Empty) { - return { + .factory('Timer', ['$rootScope', '$cookieStore', '$location', 'GetBasePath', 'Empty', + function ($rootScope, $cookieStore) { + return { - sessionTime: null, - timeout: null, - - getSessionTime: function() { - return (this.sessionTime) ? this.sessionTime : $cookieStore.get('sessionTime'); - }, + sessionTime: null, + timeout: null, - isExpired: function() { - var stime = this.getSessionTime(); - var now = new Date().getTime(); - if ((stime - now) <= 0) { - //expired - return true; - } - else { - // not expired. move timer forward. - this.moveForward(); - return false; - } - }, - - expireSession: function() { - this.sessionTime = 0; - $rootScope.sessionExpired = true; - $cookieStore.put('sessionExpired', true); - }, + getSessionTime: function () { + return (this.sessionTime) ? this.sessionTime : $cookieStore.get('sessionTime'); + }, - moveForward: function() { - var t = new Date().getTime() + ($AnsibleConfig.session_timeout * 1000); - this.sessionTime = t; - $cookieStore.put('sessionTime', t); - $rootScope.sessionExpired = false; - $cookieStore.put('sessionExpired', false); - }, + isExpired: function () { + var stime = this.getSessionTime(), + now = new Date().getTime(); + if ((stime - now) <= 0) { + //expired + return true; + } else { + // not expired. move timer forward. + this.moveForward(); + return false; + } + }, - init: function() { - this.moveForward(); - return this; - } - } - }]); \ No newline at end of file + expireSession: function () { + this.sessionTime = 0; + $rootScope.sessionExpired = true; + $cookieStore.put('sessionExpired', true); + }, + + moveForward: function () { + var t = new Date().getTime() + ($AnsibleConfig.session_timeout * 1000); + this.sessionTime = t; + $cookieStore.put('sessionTime', t); + $rootScope.sessionExpired = false; + $cookieStore.put('sessionExpired', false); + }, + + init: function () { + this.moveForward(); + return this; + } + }; + } + ]); \ No newline at end of file diff --git a/awx/ui/static/lib/ansible/TreeSelector.js b/awx/ui/static/lib/ansible/TreeSelector.js deleted file mode 100644 index 2b9c723464..0000000000 --- a/awx/ui/static/lib/ansible/TreeSelector.js +++ /dev/null @@ -1,693 +0,0 @@ - -/************************************ - * - * Copyright (c) 2014 AnsibleWorks, Inc. - * - * TreeSelector.js - * - */ - -angular.module('TreeSelector', ['Utilities', 'RestServices', 'TreeSelector', 'GroupsHelper']) - - .factory('SortNodes', [ function() { - return function(data) { - //Sort nodes by name - var names = []; - var newData = []; - for (var i=0; i < data.length; i++) { - names.push(data[i].name); - } - names.sort(); - for (var j=0; j < names.length; j++) { - for (i=0; i < data.length; i++) { - if (data[i].name == names[j]) { - newData.push(data[i]); - } - } - } - return newData; - } - }]) - - // Figure out the group level tool tip - .factory('GetToolTip', [ 'FormatDate', function(FormatDate) { - return function(params) { - - var node = params.node; - - var tip = ''; - var link = ''; - var html_class = ''; - var active_failures = node.hosts_with_active_failures; - var total_hosts = node.total_hosts; - var source = node.summary_fields.inventory_source.source; - var status = node.summary_fields.inventory_source.status; - - // Return values for the status indicator - var status_date = node.summary_fields.inventory_source.last_updated - var last_update = ( status_date == "" || status_date == null ) ? null : FormatDate(new Date(status_date)); - - switch (status) { - case 'never updated': - html_class = 'na'; - tip = '

Inventory update has not been performed.

'; - link = ''; - break; - case 'failed': - tip = '

Inventory update failed! Click to view process output.

'; - link = '/#/inventories/' + node.inventory + '/groups?name=' + node.name; - html_class = true; - break; - case 'successful': - tip = '

Inventory update completed on ' + last_update + '.

'; - html_class = false; - link = ''; - break; - case 'updating': - tip = '

Inventory update process running now. Click to view status.

'; - link = '/#/inventories/' + node.inventory + '/groups?name=' + node.name; - html_class = false; - break; - } - - if (status !== 'failed' && status !== 'updating') { - // update status will not override job status - if (active_failures > 0) { - tip += "

Contains " + active_failures + - [ (active_failures == 1) ? ' host' : ' hosts' ] + ' with failed jobs. Click to view the offending ' + - [ (active_failures == 1) ? ' host' : ' hosts' ] + '.

'; - link = '/#/inventories/' + node.inventory + '/hosts?has_active_failures=true'; - html_class = 'true'; - } - else { - if (total_hosts == 0) { - // no hosts - tip += "

There are no hosts in this group. It's a sad empty shell.

"; - html_class = (html_class == '') ? 'na' : html_class; - } - else if (total_hosts == 1) { - // on host with 0 failures - tip += "

The 1 host in this group is happy! It does not have a job failure.

"; - html_class = 'false'; - } - else { - // many hosts with 0 failures - tip += "

All " + total_hosts + " hosts in this group are happy! None of them have " + - " job failures.

"; - html_class = 'false'; - } - } - } - - return { tooltip: tip, url: link, 'class': html_class }; - - } - }]) - - .factory('GetInventoryToolTip', [ 'FormatDate', function(FormatDate) { - return function(params) { - - var node = params.node; - - var tip = ''; - var link = ''; - var html_class = ''; - var active_failures = node.hosts_with_active_failures; - var total_hosts = node.total_hosts; - var group_failures = node.groups_with_active_failures; - var total_groups = node.total_groups; - var inventory_sources = node.total_inventory_sources; - - if (group_failures > 0) { - tip += "Has " + group_failures + - [ (group_failures == 1) ? ' group' : ' groups' ] + ' with failed inventory updates. ' + - 'Click to view the offending ' + - [ (group_failures == 1) ? ' group.' : ' groups.' ]; - link = '/#/inventories/' + node.id + '/groups?status=failed'; - html_class = 'true'; - } - else if (inventory_sources == 1) { - // on host with 0 failures - tip += "

1 group with an inventory source is happy! No updates have failed.

"; - link = ''; - html_class = 'false'; - } - else if (inventory_sources > 0) { - tip += "

" + inventory_sources + " groups with an inventory source are happy! No updates have failed.

"; - link = 0; - html_class = 'false'; - } - - if (html_class !== 'true') { - // Add job status - if (active_failures > 0) { - tip += "

Contains " + scope.inventories[i].hosts_with_active_failures + - [ (active_failures == 1) ? ' host' : ' hosts' ] + ' with job failures. Click to view the offending ' + - [ (active_failures == 1) ? ' host' : ' hosts' ] + '.

'; - link = '/#/inventories/' + node.id + '/hosts?has_active_failures=true'; - html_class = 'true'; - } - else if (total_hosts == 0) { - tip += "

There are no hosts in this inventory. It's a sad empty shell.

"; - link = ""; - html_class = (html_class == '') ? 'na' : html_class; - } - else if (total_hosts == 1) { - tip += "

The 1 host found in this inventory is happy! There are no job failures.

"; - link = ""; - html_class = "false"; - } - else if (total_hosts > 0) { - tip += "

All " + total_hosts + " hosts are happy! There are no job failures."; - link = ""; - html_class = "false"; - } - } - - return { tooltip: tip, url: link, 'class': html_class }; - - } - }]) - - .factory('BuildTree', ['Rest', 'GetBasePath', 'ProcessErrors', '$compile', '$rootScope', 'Wait', 'SortNodes', 'GetToolTip', - 'GetInventoryToolTip', - function(Rest, GetBasePath, ProcessErrors, $compile, $rootScope, Wait, SortNodes, GetToolTip, GetInventoryToolTip) { - return function(params) { - var scope = params.scope; - var inventory_id = params.inventory_id; - var emit_on_select = params.emit_on_select; - var target_id = params.target_id; - var refresh_tree = (params.refresh == undefined || params.refresh == false) ? false : true; - var moveable = (params.moveable == undefined || params.moveable == false) ? false : true; - var group_id = params.group_id; - var id = params.id; - - var html = ''; - var toolTip = 'Hosts have failed jobs?'; - var idx = 0; - - function refresh(parent) { - var group, title; - var id = parent.attr('id'); - if (parent.attr('data-group-id')) { - group = parent.attr('data-group-id'); - title = parent.attr('data-name'); - } - else { - group = null; - title = 'All Hosts' - } - // The following will trigger the host list to load. See Inventory.js controller. - scope.$emit(emit_on_select, id, group, title); - } - - function activate(e) { - /* Set the clicked node as active */ - var elm = angular.element(e.target); // - var parent = angular.element(e.target.parentNode.parentNode); //

  • - $('.search-tree .active').removeClass('active'); - elm.parent().addClass('active'); // add active class to
    - refresh(parent); - } - - function toggle(e) { - var id, parent, elm, icon; - - if (e.target.tagName == 'I') { - id = e.target.parentNode.parentNode.parentNode.attributes.id.value; - parent = angular.element(e.target.parentNode.parentNode.parentNode); //
  • - elm = angular.element(e.target.parentNode); // - } - else { - id = e.target.parentNode.parentNode.attributes.id.value; - parent = angular.element(e.target.parentNode.parentNode); - elm = angular.element(e.target); - } - - var sibling = angular.element(parent.children()[2]); // - var state = parent.attr('data-state'); - var icon = angular.element(elm.children()[0]); - - if (state == 'closed') { - // expand the elment - var childlists = parent.find('ul'); - if (childlists && childlists.length > 0) { - // has childen - for (var i=0; i < childlists.length; i++) { - var listChild = angular.element(childlists[i]); - var listParent = angular.element(listChild.parent()); - if (listParent.attr('id') == id) { - angular.element(childlists[i]).removeClass('hidden'); - } - } - } - parent.attr('data-state','open'); - icon.removeClass('icon-caret-right').addClass('icon-caret-down'); - } - else { - // close the element - parent.attr('data-state','closed'); - icon.removeClass('icon-caret-down').addClass('icon-caret-right'); - var childlists = parent.find('ul'); - var sublist, subicon; - if (childlists && childlists.length > 0) { - // has childen - childlists.each(function(idx) { - $(this).addClass('hidden'); - subicon = $(this).find('li').first().find('.expand-container i'); - subicon.removeClass('icon-caret-down').addClass('icon-caret-right'); - }); - } - /* When the active node's parent is closed, activate the parent */ - if ($(parent).find('.active').length > 0) { - $(parent).find('.active').removeClass('active'); - sibling.addClass('active'); - refresh(parent); - } - } - } - - if (scope.moveNodeRemove) { - scope.moveNodeRemove(); - } - scope.moveNodeRemove = scope.$on('MoveNode', function(e, node, parent, target) { - var inv_id = scope['inventory_id']; - var variables; - - function cleanUp(state) { - /* - if (state !== 'fail') { - // Visually move the element. Elment will be appended to the - // end of target element list - var elm = $('#' + node.attr('id')).detach(); - if (target.find('ul').length > 0) { - // parent has children - target.find('ul').first().append(elm); - } - else { - target.append('
      '); - target.find('ul').first().append(elm); - } - - // Remove any styling that might be left on the target - // and put the expander icon back the way it should be - target.find('div').each(function(idx) { - if (idx > 0 && idx < 3) { - $(this).css({ 'border-bottom': '2px solid #f5f5f5' }); - } - }); - - // Make sure the parent and target have the correct expander class/icon. - function setExpander(n) { - var c = n.find('.expand-container'); - var icon; - c.first().empty(); - if (n.attr('id') == 'inventory-root-node') { - c.first().html(''); - } - else if (c.length > 1) { - // not root and has children, put expander icon back - icon = (n.attr('data-state') == 'opened') ? 'icon-caret-down' : 'icon-caret-right'; - c.first().html('
      '); - c.first().find('a').first().bind('click', toggle); - } - } - setExpander(target); - setExpander(parent); - } - Wait('stop'); - */ - // Reload the tree - html = ''; - idx = 0; - loadTreeData(); - } - - // disassociate the group from the original parent - if (scope.removeGroupRemove) { - scope.removeGroupRemove(); - } - scope.removeGroupRemove = scope.$on('removeGroup', function() { - if (parent.attr('data-group-id')) { - // Only remove a group from a parent when the parent is a group and not the inventory root - var url = GetBasePath('base') + 'groups/' + parent.attr('data-group-id') + '/children/'; - Rest.setUrl(url); - Rest.post({ id: node.attr('data-group-id'), disassociate: 1 }) - .success( function(data, status, headers, config) { - cleanUp('success'); - }) - .error( function(data, status, headers, config) { - cleanUp('fail'); - ProcessErrors(scope, data, status, null, - { hdr: 'Error!', msg: 'Failed to remove ' + node.attr('name') + ' from ' + - parent.attr('name') + '. POST returned status: ' + status }); - }); - } - else { - cleanUp('success'); - } - }); - - if (scope['addToTargetRemove']) { - scope.addToTargetRemove(); - } - scope.addToTargetRemove = scope.$on('addToTarget', function() { - // add the new group to the target parent - var url = (target.attr('data-group-id')) ? GetBasePath('base') + 'groups/' + target.attr('data-group-id') + '/children/' : - GetBasePath('inventory') + inv_id + '/groups/'; - var group = { - id: node.attr('data-group-id'), - name: node.attr('data-name'), - description: node.attr('data-description'), - inventory: inv_id - } - Rest.setUrl(url); - Rest.post(group) - .success( function(data, status, headers, config) { - scope.$emit('removeGroup'); - }) - .error( function(data, status, headers, config) { - cleanUp('fail'); - ProcessErrors(scope, data, status, null, - { hdr: 'Error!', msg: 'Failed to add ' + node.attr('name') + ' to ' + - target.attr('name') + '. POST returned status: ' + status }); - }); - }); - - Wait('start'); - // Lookup the inventory. We already have what we need except for variables. - var url = GetBasePath('base') + 'groups/' + node.attr('data-group-id') + '/'; - Rest.setUrl(url); - Rest.get() - .success( function(data, status, headers, config) { - variables = (data.variables) ? JSON.parse(data.variables) : ""; - scope.$emit('addToTarget'); - }) - .error( function(data, status, headers, config) { - cleanUp('fail'); - ProcessErrors(scope, data, status, null, - { hdr: 'Error!', msg: 'Failed to lookup group ' + node.attr('name') + - '. GET returned status: ' + status }); - }); - }); - - // The HTML is ready. Insert it into the view. - if (scope.searchTreeReadyRemove) { - scope.searchTreeReadyRemove(); - } - scope.searchTreeReadyRemove = scope.$on('searchTreeReady', function(e, html) { - - var container = angular.element(document.getElementById(target_id)); - container.empty(); - var compiled = $compile(html)(scope); - container.append(compiled); - - function setTitleWidth(elm) { - // Fix for overflowing title text - var container = $('#search-tree-target'); - var container_offset = container.offset(); - var parent = elm.parent(); //
    • - var parent_offset = parent.offset(); - var expander = parent.find('.expand-container').first(); - var badge = parent.find('.badge-container').first(); - var width = container.width() - parent_offset.left + container_offset.left - - badge.width() - expander.width() - 10; - elm.css('width', width + 'px'); - } - - // Fix overflowing title text now - $('#' + target_id).find('.title-container').each(function(idx) { - setTitleWidth($(this)); - }); - - // Fix overflowing title text on screen resize - var timeout; - $(window).resize(function() { - clearTimeout(timeout); //remove prior timer so we don't resize a million times - timeout = setTimeout(function() { - $('#' + target_id).find('.title-container').each(function(idx) { - setTitleWidth($(this)); - }); - }, 500); - }); - - var links = container.find('a'); - for (var i=0; i < links.length; i++) { - var link = angular.element(links[i]); - if (link.hasClass('expand')) { - link.unbind('click', toggle); - link.bind('click', toggle); - } - if (link.hasClass('activate')) { - link.unbind('click', activate); - link.bind('click', activate); - } - } - - if (refresh_tree && group_id !== undefined) { - // pick a node by group_id - $('li[data-group-id="' + group_id + '"] .activate').first().click(); - } - else if (refresh_tree && id !== undefined) { - // pick a node by id - $('#' + id + ' .activate').first().click(); - } - else if (!refresh_tree) { - // default to the root node - $('#inventory-root-node .activate').first().click(); - } - - // Make the tree drag-n-droppable - if (moveable) { - $('#selector-tree .activate').draggable({ - cursor: "pointer", - cursorAt: { top: -16, left: -10 }, - revert: 'invalid', - helper: 'clone', - start: function (e, ui) { - var txt = '[ ' + ui.helper.text() + ' ]'; - ui.helper.css({ 'display': 'inline-block', 'font-weight': 'normal', 'color': '#171717', - 'background-color': '#f5f5f5', 'overflow': 'visible', 'white-space': 'normal', - 'z-index': 5000 }).text(txt); - } - }) - .droppable({ - //hoverClass: 'droppable-hover', - tolerance: 'pointer', - over: function (e, ui) { - var p = $(this).parent().parent(); - p.find('div').each(function(idx) { - if (idx > 0 && idx < 3) { - $(this).css({ 'border-bottom': '2px solid #171717' }); - } - }); - var c = p.find('.expand-container').first(); - c.empty().html(''); - }, - out: function (e, ui) { - var p = $(this).parent().parent(); - p.find('div').each(function(idx) { - if (idx > 0 && idx < 3) { - $(this).css({ 'border-bottom': '2px solid #f5f5f5' }); - } - }); - var c = p.find('.expand-container'); - var icon; - c.first().empty(); - if (c.length > 1) { - // has children, put expander icon back - icon = (p.attr('data-state') == 'opened') ? 'icon-caret-down' : 'icon-caret-right'; - c.first().html(''); - c.first().find('a').first().bind('click', toggle); - } - }, - drop: function (e,ui) { - var variables; - var node = ui.draggable.parent().parent(); // node being moved - var parent = node.parent().parent(); // node from - var target = $(this).parent().parent(); // node to - scope.$emit('MoveNode', node, parent, target); - - // Make sure angular picks up changes and jQuery doesn't - // leave us in limbo... - if (!scope.$$phase) { - scope.$digest(); - } - e.preventDefault(); - } - }); - } // if moveable - - Wait('stop'); - }); - - function buildHTML(tree_data) { - var sorted = SortNodes(tree_data); - var toolTip; - - html += (sorted.length > 0) ? "
        \n" : ""; - for(var i=0; i < sorted.length; i++) { - html += "
      • "; - - if (sorted[i].children.length > 0) { - html += ""; - } - else { - html += " "; - } - html += "
        "; - - toolTip = GetToolTip({ node: sorted[i] }); - - html += "
        "; - html += ""; - html += ""; - html += "
        "; - - html += ""; - - idx++; - if (sorted[i].children.length > 0) { - buildHTML(sorted[i].children); - } - else { - //html += "
          \n"; - html += "
        • \n"; - } - } - html += "
        \n"; - } - - // Build the HTML for our tree - if (scope.buildAllGroupsRemove) { - scope.buildAllGroupsRemove(); - } - scope.buildAllGroupsRemove = scope.$on('buildAllGroups', function(e, inventory_name, inventory_tree) { - Rest.setUrl(inventory_tree); - Rest.get() - .success( function(data, status, headers, config) { - buildHTML(data); - scope.$emit('searchTreeReady', html + "
      • \n\n
        \n"); - }) - .error( function(data, status, headers, config) { - ProcessErrors(scope, data, status, null, - { hdr: 'Error!', msg: 'Failed to get inventory tree for: ' + inventory_id + '. GET returned: ' + status }); - }); - }); - - // Builds scope.inventory_groups, used by the group picker on Hosts view to build the list of potential groups - // that can be added to a host. <<<< Should probably be moved to /helpers/Hosts.js - if (scope.buildGroupListRemove) { - scope.buildGroupListRemove(); - } - scope.buildGroupListRemove = scope.$on('buildAllGroups', function(e, inventory_name, inventory_tree, groups_url) { - scope.inventory_groups = []; - Rest.setUrl(groups_url); - Rest.get() - .success( function(data, status, headers, config) { - var groups = []; - for (var i=0; i < data.results.length; i++) { - groups.push({ - id: data.results[i].id, - description: data.results[i].description, - name: data.results[i].name }); - } - scope.inventory_groups = SortNodes(groups); - }) - .error( function(data, status, headers, config) { - ProcessErrors(scope, data, status, null, - { hdr: 'Error!', msg: 'Failed to get groups for inventory: ' + inventory_id + '. GET returned: ' + status }); - }); - }); - - - function loadTreeData() { - // Load the inventory root node - Wait('start'); - Rest.setUrl (GetBasePath('inventory') + inventory_id + '/'); - Rest.get() - .success( function(data, status, headers, config) { - - var tip = GetInventoryToolTip({ node: data }); - - html += "
        Group Selector:
        \n" + - "
        \n" + - "
          \n" + - "
        • " + - "
          " + - "
          \n"; - - html += ""; - html += "
          \n"; - - html += ""; - - scope.$emit('buildAllGroups', data.name, data.related.tree, data.related.groups); - - }) - .error( function(data, status, headers, config) { - ProcessErrors(scope, data, status, null, - { hdr: 'Error!', msg: 'Failed to get inventory: ' + inventory_id + '. GET returned: ' + status }); - }); - } - - loadTreeData(); - - } - }]) - - // Set node name and description after an update to Group properties. - .factory('SetNodeName', [ function() { - return function(params) { - var scope = params.scope; - var name = params.name; - var descr = params.description; - var group_id = (params.group_id !== undefined) ? params.group_id : null; - var inventory_id = (params.inventory_id != undefined) ? params.inventory_id : null; - - if (group_id !== null) { - $('#inventory-tree').find('li [data-group-id="' + group_id + '"]').each(function(idx) { - $(this).attr('data-name',name); - $(this).attr('data-description',descr); - $(this).find('.activate').first().text(name); - }); - } - - if (inventory_id !== null) { - $('#inventory-root-node').attr('data-name', name).attr('data-description', descr).find('.activate').first().text(name); - } - } - }]) - - .factory('ClickNode', [ function() { - return function(params) { - var selector = params.selector; //jquery selector string to find the correct
        • - $(selector + ' .activate').first().click(); - } - }]) - - .factory('DeleteNode', [ function() { - return function(params) { - var selector = params.selector; //jquery selector string to find the correct
        • - $(selector).first().detach(); - } - }]); diff --git a/awx/ui/static/lib/ansible/Utilities.js b/awx/ui/static/lib/ansible/Utilities.js index 046adb3b73..fd3531bb81 100644 --- a/awx/ui/static/lib/ansible/Utilities.js +++ b/awx/ui/static/lib/ansible/Utilities.js @@ -10,68 +10,72 @@ 'use strict'; -angular.module('Utilities',['RestServices', 'Utilities']) - - .factory('ClearScope', [ function() { - return function(id) { - - var element = document.getElementById(id), scope; +angular.module('Utilities', ['RestServices', 'Utilities']) + +.factory('ClearScope', [ + function () { + return function (id) { + + var element = document.getElementById(id), + scope; if (element) { scope = angular.element(element).scope(); scope.$destroy(); } - $('.tooltip').each( function() { + $('.tooltip').each(function () { // Remove any lingering tooltip and popover
          elements $(this).remove(); }); - $('.popover').each(function() { + $('.popover').each(function () { // remove lingering popover
          . Seems to be a bug in TB3 RC1 $(this).remove(); }); try { $('#help-modal').dialog('close'); - } - catch(e) { + } catch (e) { // ignore } $(window).unbind('resize'); }; - }]) + } +]) - - /* Empty() - * - * Test if a value is 'empty'. Returns true if val is null | '' | undefined. - * Only works on non-Ojbect types. - * - */ - .factory('Empty', [ function() { - return function(val) { + +/* Empty() + * + * Test if a value is 'empty'. Returns true if val is null | '' | undefined. + * Only works on non-Ojbect types. + * + */ +.factory('Empty', [ + function () { + return function (val) { return (val === null || val === undefined || val === '') ? true : false; }; - }]) - - - .factory('ToggleClass', function() { - return function(selector, cssClass) { - // Toggles the existance of a css class on a given element - if ( $(selector) && $(selector).hasClass(cssClass) ) { - $(selector).removeClass(cssClass); - } - else if ($(selector)) { - $(selector).addClass(cssClass); - } - }; - }) + } +]) - .factory('Alert', ['$rootScope', function($rootScope) { - return function(hdr, msg, cls, action, secondAlert, disableButtons) { +.factory('ToggleClass', function () { + return function (selector, cssClass) { + // Toggles the existance of a css class on a given element + if ($(selector) && $(selector).hasClass(cssClass)) { + $(selector).removeClass(cssClass); + } else if ($(selector)) { + $(selector).addClass(cssClass); + } + }; +}) + + +.factory('Alert', ['$rootScope', + function ($rootScope) { + return function (hdr, msg, cls, action, secondAlert, disableButtons) { // Pass in the header and message you want displayed on TB modal dialog found in index.html. // Assumes an #id of 'alert-modal'. Pass in an optional TB alert class (i.e. alert-danger, alert-success, // alert-info...). Pass an optional function(){}, if you want a specific action to occur when user @@ -79,15 +83,19 @@ angular.module('Utilities',['RestServices', 'Utilities']) if (secondAlert) { $rootScope.alertHeader2 = hdr; $rootScope.alertBody2 = msg; - $rootScope.alertClass2 = (cls) ? cls : 'alert-danger'; //default alert class is alert-danger - $('#alert-modal2').modal({ show: true, keyboard: true , backdrop: 'static' }); + $rootScope.alertClass2 = (cls) ? cls : 'alert-danger'; //default alert class is alert-danger + $('#alert-modal2').modal({ + show: true, + keyboard: true, + backdrop: 'static' + }); $rootScope.disableButtons2 = (disableButtons) ? true : false; if (action) { - $('#alert-modal2').on('hidden', function() { + $('#alert-modal2').on('hidden', function () { action(); }); } - $(document).bind('keydown', function(e) { + $(document).bind('keydown', function (e) { if (e.keyCode === 27) { $('#alert-modal2').modal('hide'); if (action) { @@ -95,14 +103,17 @@ angular.module('Utilities',['RestServices', 'Utilities']) } } }); - } - else { + } else { $rootScope.alertHeader = hdr; $rootScope.alertBody = msg; - $rootScope.alertClass = (cls) ? cls : 'alert-danger'; //default alert class is alert-danger - $('#alert-modal').modal({ show: true, keyboard: true , backdrop: 'static' }); - - $(document).bind('keydown', function(e) { + $rootScope.alertClass = (cls) ? cls : 'alert-danger'; //default alert class is alert-danger + $('#alert-modal').modal({ + show: true, + keyboard: true, + backdrop: 'static' + }); + + $(document).bind('keydown', function (e) { if (e.keyCode === 27) { $('#alert-modal').modal('hide'); if (action) { @@ -113,168 +124,171 @@ angular.module('Utilities',['RestServices', 'Utilities']) $rootScope.disableButtons = (disableButtons) ? true : false; if (action) { - $('#alert-modal').on('hidden', function() { + $('#alert-modal').on('hidden', function () { action(); }); } } }; - }]) + } +]) - .factory('ProcessErrors', ['$rootScope', '$cookieStore', '$log', '$location', 'Alert', 'Wait', - function($rootScope, $cookieStore, $log, $location, Alert, Wait) { - return function(scope, data, status, form, defaultMsg) { - var field, fieldErrors, msg; - Wait('stop'); - if ($AnsibleConfig.debug_mode && console) { - console.log('Debug status: ' + status); - console.log('Debug data: '); - console.log(data); +.factory('ProcessErrors', ['$rootScope', '$cookieStore', '$log', '$location', 'Alert', 'Wait', + function ($rootScope, $cookieStore, $log, $location, Alert, Wait) { + return function (scope, data, status, form, defaultMsg) { + var field, fieldErrors, msg; + Wait('stop'); + if ($AnsibleConfig.debug_mode && console) { + console.log('Debug status: ' + status); + console.log('Debug data: '); + console.log(data); + } + if (status === 403) { + msg = 'The API responded with a 403 Access Denied error. '; + if (data.detail) { + msg += 'Detail: ' + data.detail; + } else { + msg += 'Please contact your system administrator.'; } - if (status === 403) { - msg = 'The API responded with a 403 Access Denied error. '; - if (data.detail) { - msg += 'Detail: ' + data.detail; + Alert(defaultMsg.hdr, msg); + } else if ((status === 401 && data.detail && data.detail === 'Token is expired') || + (status === 401 && data.detail && data.detail === 'Invalid token')) { + $rootScope.sessionTimer.expireSession(); + $location.url('/login'); + } else if (data.non_field_errors) { + Alert('Error!', data.non_field_errors); + } else if (data.detail) { + Alert(defaultMsg.hdr, defaultMsg.msg + ' ' + data.detail); + } else if (data.__all__) { + Alert('Error!', data.__all__); + } else if (form) { + fieldErrors = false; + for (field in form.fields) { + if (data[field] && form.fields[field].tab) { + // If the form is part of a tab group, activate the tab + $('#' + form.name + "_tabs a[href=\"#" + form.fields[field].tab + '"]').tab('show'); } - else { - msg += 'Please contact your system administrator.'; - } - Alert(defaultMsg.hdr, msg); - } - else if ( (status === 401 && data.detail && data.detail === 'Token is expired') || - (status === 401 && data.detail && data.detail === 'Invalid token') ) { - $rootScope.sessionTimer.expireSession(); - $location.url('/login'); - } - else if (data.non_field_errors) { - Alert('Error!', data.non_field_errors); - } - else if (data.detail) { - Alert(defaultMsg.hdr, defaultMsg.msg + ' ' + data.detail); - } - else if (data.__all__) { - Alert('Error!', data.__all__); - } - else if (form) { - fieldErrors = false; - for (field in form.fields ) { - if (data[field] && form.fields[field].tab) { - // If the form is part of a tab group, activate the tab - $('#' + form.name + "_tabs a[href=\"#" + form.fields[field].tab + '"]').tab('show'); - } - if (form.fields[field].realName) { - if (data[form.fields[field].realName]) { - scope[field + '_api_error'] = data[form.fields[field]][0]; - //scope[form.name + '_form'][form.fields[field].realName].$setValidity('apiError', false); - $('[name="' + form.fields[field].realName + '"]').addClass('ng-invalid'); - fieldErrors = true; - } - } - if (form.fields[field].sourceModel) { - if (data[field]) { - scope[form.fields[field].sourceModel + '_' + form.fields[field].sourceField + '_api_error'] = - data[field][0]; - //scope[form.name + '_form'][form.fields[field].sourceModel + '_' + form.fields[field].sourceField].$setValidity('apiError', false); - $('[name="' + form.fields[field].sourceModel + '_' + form.fields[field].sourceField + '"]').addClass('ng-invalid'); - fieldErrors = true; - } - } - else { - if (data[field]) { - scope[field + '_api_error'] = data[field][0]; - //scope[form.name + '_form'][field].$setValidity('apiError', false); - $('[name="' + field + '"]').addClass('ng-invalid'); - fieldErrors = true; - } + if (form.fields[field].realName) { + if (data[form.fields[field].realName]) { + scope[field + '_api_error'] = data[form.fields[field]][0]; + //scope[form.name + '_form'][form.fields[field].realName].$setValidity('apiError', false); + $('[name="' + form.fields[field].realName + '"]').addClass('ng-invalid'); + fieldErrors = true; } } - if ((!fieldErrors) && defaultMsg) { - Alert(defaultMsg.hdr, defaultMsg.msg); + if (form.fields[field].sourceModel) { + if (data[field]) { + scope[form.fields[field].sourceModel + '_' + form.fields[field].sourceField + '_api_error'] = + data[field][0]; + //scope[form.name + '_form'][form.fields[field].sourceModel + '_' + form.fields[field].sourceField].$setValidity('apiError', false); + $('[name="' + form.fields[field].sourceModel + '_' + form.fields[field].sourceField + '"]').addClass('ng-invalid'); + fieldErrors = true; + } + } else { + if (data[field]) { + scope[field + '_api_error'] = data[field][0]; + //scope[form.name + '_form'][field].$setValidity('apiError', false); + $('[name="' + field + '"]').addClass('ng-invalid'); + fieldErrors = true; + } } } - else { + if ((!fieldErrors) && defaultMsg) { Alert(defaultMsg.hdr, defaultMsg.msg); } - }; - }]) + } else { + Alert(defaultMsg.hdr, defaultMsg.msg); + } + }; + } +]) - - .factory('LoadBreadCrumbs', ['$rootScope', '$routeParams', '$location', 'Empty', - function($rootScope, $routeParams, $location, Empty) { - return function(crumb) { - - var title, found, j, i, paths, ppath, parent, child; - - function toUppercase(a) { - return a.toUpperCase(); - } - function singular(a) { - return (a === 'ies') ? 'y' : ''; - } - - //Keep a list of path/title mappings. When we see /organizations/XX in the path, for example, - //we'll know the actual organization name it maps to. - if (!Empty(crumb)) { - found = false; - //crumb.title = crumb.title.charAt(0).toUpperCase() + crumb.title.slice(1); - for (i=0; i < $rootScope.crumbCache.length; i++) { - if ($rootScope.crumbCache[i].path === crumb.path) { - found = true; - $rootScope.crumbCache[i] = crumb; - break; - } - } - if (!found) { - $rootScope.crumbCache.push(crumb); +.factory('LoadBreadCrumbs', ['$rootScope', '$routeParams', '$location', 'Empty', + function ($rootScope, $routeParams, $location, Empty) { + return function (crumb) { + + var title, found, j, i, paths, ppath, parent, child; + + function toUppercase(a) { + return a.toUpperCase(); + } + + function singular(a) { + return (a === 'ies') ? 'y' : ''; + } + + //Keep a list of path/title mappings. When we see /organizations/XX in the path, for example, + //we'll know the actual organization name it maps to. + if (!Empty(crumb)) { + found = false; + //crumb.title = crumb.title.charAt(0).toUpperCase() + crumb.title.slice(1); + for (i = 0; i < $rootScope.crumbCache.length; i++) { + if ($rootScope.crumbCache[i].path === crumb.path) { + found = true; + $rootScope.crumbCache[i] = crumb; + break; } } - - paths = $location.path().replace(/^\//,'').split('/'); - ppath = ''; - $rootScope.breadcrumbs = []; - if (paths.length > 1) { - for (i=0; i < paths.length - 1; i++) { - if (i > 0 && paths[i].match(/\d+/)) { - parent = paths[i-1]; - child = parent.replace(/(ies$|s$)/, singular); - child = child.charAt(0).toUpperCase() + child.slice(1); - // find the correct title - found = false; - for (j=0; j < $rootScope.crumbCache.length; j++) { - if ($rootScope.crumbCache[j].path === '/' + parent + '/' + paths[i]) { - child = $rootScope.crumbCache[j].title; - found = true; - break; - } - } - - if (found && $rootScope.crumbCache[j].altPath !== undefined) { - // Use altPath to override default path construction - $rootScope.breadcrumbs.push({ title: child, path: $rootScope.crumbCache[j].altPath }); - } - else { - $rootScope.breadcrumbs.push({ title: child, path: ppath + '/' + paths[i] }); - } - } - else { - if (/_/.test(paths[i])) { - // replace '_' with space and uppercase each word - paths[i] = paths[i].replace(/(?:^|_)\S/g, toUppercase) - .replace(/_/g,' '); - } - title = paths[i].charAt(0).toUpperCase() + paths[i].slice(1); - $rootScope.breadcrumbs.push({ title: title, path: ppath + '/' + paths[i] }); - } - ppath += '/' + paths[i]; - } + if (!found) { + $rootScope.crumbCache.push(crumb); } - }; - }]) - - .factory('HelpDialog', ['$rootScope', '$location', 'Store', function($rootScope, $location, Store) { - return function(params) { + } + + paths = $location.path().replace(/^\//, '').split('/'); + ppath = ''; + $rootScope.breadcrumbs = []; + if (paths.length > 1) { + for (i = 0; i < paths.length - 1; i++) { + if (i > 0 && paths[i].match(/\d+/)) { + parent = paths[i - 1]; + child = parent.replace(/(ies$|s$)/, singular); + child = child.charAt(0).toUpperCase() + child.slice(1); + // find the correct title + found = false; + for (j = 0; j < $rootScope.crumbCache.length; j++) { + if ($rootScope.crumbCache[j].path === '/' + parent + '/' + paths[i]) { + child = $rootScope.crumbCache[j].title; + found = true; + break; + } + } + + if (found && $rootScope.crumbCache[j].altPath !== undefined) { + // Use altPath to override default path construction + $rootScope.breadcrumbs.push({ + title: child, + path: $rootScope.crumbCache[j].altPath + }); + } else { + $rootScope.breadcrumbs.push({ + title: child, + path: ppath + '/' + paths[i] + }); + } + } else { + if (/_/.test(paths[i])) { + // replace '_' with space and uppercase each word + paths[i] = paths[i].replace(/(?:^|_)\S/g, toUppercase) + .replace(/_/g, ' '); + } + title = paths[i].charAt(0).toUpperCase() + paths[i].slice(1); + $rootScope.breadcrumbs.push({ + title: title, + path: ppath + '/' + paths[i] + }); + } + ppath += '/' + paths[i]; + } + } + }; + } +]) + +.factory('HelpDialog', ['$rootScope', '$location', 'Store', + function ($rootScope, $location, Store) { + return function (params) { // Display a help dialog // // HelpDialog({ defn: }) @@ -285,10 +299,10 @@ angular.module('Utilities',['RestServices', 'Utilities']) autoShow = params.autoShow || false; function showHelp(step) { - - var btns, ww, width, height, isOpen=false; + + var btns, ww, width, height, isOpen = false; current_step = step; - + function buildHtml(step) { var html = ''; //html += (step.intro) ? "
          " + step.intro + "
          " : ""; @@ -301,7 +315,7 @@ angular.module('Utilities',['RestServices', 'Utilities']) "name=\"auto-off-checkbox\" id=\"auto-off-checkbox\"> Do not show this message in the future
          \n" : ""; return html; } - + width = (defn.story.width) ? defn.story.width : 510; height = (defn.story.height) ? defn.story.height : 600; @@ -311,21 +325,19 @@ angular.module('Utilities',['RestServices', 'Utilities']) try { isOpen = $('#help-modal').dialog('isOpen'); - } - catch(e) { + } catch (e) { // ignore } if (isOpen) { $('#help-modal').html(buildHtml(defn.story.steps[current_step])); - } - else { + } else { // Define buttons based on story length btns = []; if (defn.story.steps.length > 1) { btns.push({ text: "Prev", - click: function(e) { + click: function (e) { if (current_step - 1 === 0) { $(e.target).button('disable'); } @@ -338,7 +350,7 @@ angular.module('Utilities',['RestServices', 'Utilities']) }); btns.push({ text: "Next", - click: function(e) { + click: function (e) { if (current_step + 1 > 0) { $(e.target).prev().button('enable'); } @@ -349,12 +361,19 @@ angular.module('Utilities',['RestServices', 'Utilities']) } }); } - btns.push({ text: "Close", - click: function() { $('#help-modal').dialog('close'); } + btns.push({ + text: "Close", + click: function () { + $('#help-modal').dialog('close'); + } }); // Show the dialog $('#help-modal').html(buildHtml(defn.story.steps[current_step])).dialog({ - position: { my: "center top", at: "center top+150", of: 'body' }, + position: { + my: "center top", + at: "center top+150", + of: 'body' + }, title: defn.story.hdr, width: width, height: height, @@ -363,75 +382,91 @@ angular.module('Utilities',['RestServices', 'Utilities']) show: 500, hide: 500, resizable: false, - close: function() { $('#help-modal').empty(); } + close: function () { + $('#help-modal').empty(); + } }); - + // Make the buttons look like TB and add FA icons - $('.ui-dialog-buttonset button').each( function() { + $('.ui-dialog-buttonset button').each(function () { var c, h, l; l = $(this).text(); if (l === 'Close') { h = "fa-times"; c = "btn btn-default"; - $(this).attr({ 'class': c }).html(" Close"); - } - else if (l === 'Prev') { + $(this).attr({ + 'class': c + }).html(" Close"); + } else if (l === 'Prev') { h = "fa-chevron-left"; c = "btn btn-primary"; - $(this).attr({ 'class': c }).html(" Prev"); - } - else { + $(this).attr({ + 'class': c + }).html(" Prev"); + } else { h = "fa-chevron-right"; c = "btn btn-primary"; - $(this).attr({ 'class': c }).html("Next ").css({ 'margin-right': '20px'}); + $(this).attr({ + 'class': c + }).html("Next ").css({ + 'margin-right': '20px' + }); } }); $('.ui-dialog[aria-describedby="help-modal"]').find('.ui-dialog-titlebar button') - .empty().attr({ 'class': 'close' }).text('x'); - + .empty().attr({ + 'class': 'close' + }).text('x'); + // If user clicks the checkbox, update local storage - $('#auto-off-checkbox').click(function() { + $('#auto-off-checkbox').click(function () { if ($('input[name="auto-off-checkbox"]:checked').length) { - Store('inventoryAutoHelp','off'); - } - else { - Store('inventoryAutoHelp','on'); + Store('inventoryAutoHelp', 'off'); + } else { + Store('inventoryAutoHelp', 'on'); } }); } } - + showHelp(0); }; - }]) + } +]) - .factory('ReturnToCaller', ['$location', 'Empty', function($location, Empty) { - return function(idx) { +.factory('ReturnToCaller', ['$location', 'Empty', + function ($location, Empty) { + return function (idx) { // Split the current path by '/' and use the array elements from 0 up to and // including idx as the new path. If no idx value supplied, use 0 to length - 1. - var paths = $location.path().replace(/^\//,'').split('/'), - newpath = '', i; + var paths = $location.path().replace(/^\//, '').split('/'), + newpath = '', + i; idx = (Empty(idx)) ? paths.length - 1 : idx + 1; - for (i=0; i < idx; i++) { + for (i = 0; i < idx; i++) { newpath += '/' + paths[i]; } $location.path(newpath); }; - }]) + } +]) - .factory('FormatDate', ['$filter', function($filter) { - return function(dt) { +.factory('FormatDate', ['$filter', + function ($filter) { + return function (dt) { // Wrapper for data filter- an attempt to insure all dates display in // the same format. Pass in date object. return $filter('date')(dt, 'MM/dd/yy HH:mm:ss'); }; - }]) + } +]) - .factory('Wait', [ '$rootScope', function($rootScope) { - return function(directive) { +.factory('Wait', ['$rootScope', + function ($rootScope) { + return function (directive) { // Display a spinning icon in the center of the screen to freeze the // UI while waiting on async things to complete (i.e. API calls). // Wait('start' | 'stop'); @@ -448,16 +483,22 @@ angular.module('Utilities',['RestServices', 'Utilities']) width: $(document).width(), height: $(document).height() }).fadeIn(); - $('.spinny').css({ top: y, left: x }).fadeIn(400); - } - else if (directive === 'stop' && $rootScope.waiting){ - $('.spinny, .overlay').fadeOut(400, function(){ $rootScope.waiting = false; }); + $('.spinny').css({ + top: y, + left: x + }).fadeIn(400); + } else if (directive === 'stop' && $rootScope.waiting) { + $('.spinny, .overlay').fadeOut(400, function () { + $rootScope.waiting = false; + }); } }; - }]) - - .factory('HideElement', [ function() { - return function(selector, action) { + } +]) + +.factory('HideElement', [ + function () { + return function (selector, action) { // Fade-in a cloack or vail or a specific element var target = $(selector), width = target.css('width'), @@ -468,7 +509,7 @@ angular.module('Utilities',['RestServices', 'Utilities']) backgroundColor = target.css('background-color'), margin = target.css('margin'), padding = target.css('padding'); - + parent.append("
          "); $('#curtain-div').show(0, action); }; - }]) + } +]) - .factory('ShowElement', [ function() { - return function() { +.factory('ShowElement', [ + function () { + return function () { // And Fade-out the cloack revealing the element - $('#curtain-div').fadeOut(500, function() { $(this).remove(); }); + $('#curtain-div').fadeOut(500, function () { + $(this).remove(); + }); }; - }]) + } +]) - .factory('GetChoices', [ 'Rest', 'ProcessErrors', function(Rest, ProcessErrors) { - return function(params) { +.factory('GetChoices', ['Rest', 'ProcessErrors', + function (Rest, ProcessErrors) { + return function (params) { // Get dropdown options var scope = params.scope, url = params.url, field = params.field, variable = params.variable, - callback = params.callback, // Optional. Provide if you want scop.$emit on completion. - choice_name = params.choice_name; // Optional. Used when data is in something other than 'choices' + callback = params.callback, // Optional. Provide if you want scop.$emit on completion. + choice_name = params.choice_name; // Optional. Used when data is in something other than 'choices' if (scope[variable]) { scope[variable].length = 0; - } - else { + } else { scope[variable] = []; } Rest.setUrl(url); Rest.options() - .success( function(data) { + .success(function (data) { var choices, i; choices = (choice_name) ? data.actions.GET[field][choice_name] : data.actions.GET[field].choices; // including 'name' property so list can be used by search - for (i=0; i < choices.length; i++) { - scope[variable].push({ label: choices[i][1], value: choices[i][0], name: choices[i][1]}); + for (i = 0; i < choices.length; i++) { + scope[variable].push({ + label: choices[i][1], + value: choices[i][0], + name: choices[i][1] + }); } if (callback) { scope.$emit(callback); } }) - .error( function(data, status) { - ProcessErrors(scope, data, status, null, - { hdr: 'Error!', msg: 'Failed to get ' + url + '. GET status: ' + status }); + .error(function (data, status) { + ProcessErrors(scope, data, status, null, { hdr: 'Error!', + msg: 'Failed to get ' + url + '. GET status: ' + status }); }); }; - }]) - - /* - * Search an array of objects, returning the matchting object or null - * - * Find({ list: [], key: "key", val: }); - */ - .factory('Find', [ function(){ - return function(params) { + } +]) + +/* + * Search an array of objects, returning the matchting object or null + * + * Find({ list: [], key: "key", val: }); + */ +.factory('Find', [ + function () { + return function (params) { var list = params.list, key = params.key, val = params.val, - found = false, i; + found = false, + i; if (typeof list === 'object' && Array.isArray(list)) { - for (i=0; i < params.list.length; i++) { + for (i = 0; i < params.list.length; i++) { if (list[i][key] === val) { found = true; break; @@ -554,19 +607,22 @@ angular.module('Utilities',['RestServices', 'Utilities']) } return null; }; - }]) + } +]) - /* - * DeugForm({ form: , scope: }); - * - * Use to log the $pristine and $valid properties of each form element. Helpful when form - * buttons fail to enable/disable properly. - * - */ - .factory('DebugForm', [ function() { - return function(params) { +/* + * DeugForm({ form: , scope: }); + * + * Use to log the $pristine and $valid properties of each form element. Helpful when form + * buttons fail to enable/disable properly. + * + */ +.factory('DebugForm', [ + function () { + return function (params) { var form = params.form, - scope = params.scope, fld; + scope = params.scope, + fld; for (fld in form.fields) { if (scope[form.name + '_form'][fld]) { console.log(fld + ' valid: ' + scope[form.name + '_form'][fld].$valid); @@ -574,49 +630,52 @@ angular.module('Utilities',['RestServices', 'Utilities']) if (form.fields[fld].sourceModel) { if (scope[form.name + '_form'][form.fields[fld].sourceModel + '_' + form.fields[fld].sourceField]) { console.log(form.fields[fld].sourceModel + '_' + form.fields[fld].sourceField + ' valid: ' + - scope[form.name + '_form'][form.fields[fld].sourceModel + '_' + form.fields[fld].sourceField].$valid); + scope[form.name + '_form'][form.fields[fld].sourceModel + '_' + form.fields[fld].sourceField].$valid); } } } console.log('form pristine: ' + scope[form.name + '_form'].$pristine); console.log('form valid: ' + scope[form.name + '_form'].$valid); }; - }]) + } +]) - - /* Store - * - * Wrapper for local storage. All local storage requests flow through here so that we can - * stringify/unstringify objects and respond to future issues in one place. For example, - * we may at some point want to only use session storage rather than local storage. We might - * want to add a test for whether or not local/session storage exists for the browser, etc. - * - * store(key,value) will store the value using the key - * - * store(key) retrieves the value of the key - * - */ - .factory('Store', ['Empty', function(Empty) { - return function(key, value) { + +/* Store + * + * Wrapper for local storage. All local storage requests flow through here so that we can + * stringify/unstringify objects and respond to future issues in one place. For example, + * we may at some point want to only use session storage rather than local storage. We might + * want to add a test for whether or not local/session storage exists for the browser, etc. + * + * store(key,value) will store the value using the key + * + * store(key) retrieves the value of the key + * + */ +.factory('Store', ['Empty', + function (Empty) { + return function (key, value) { if (!Empty(value)) { // Store the value localStorage[key] = JSON.stringify(value); - } - else if (!Empty(key)) { + } else if (!Empty(key)) { // Return the value var val = localStorage[key]; return (!Empty(val)) ? JSON.parse(val) : null; } }; - }]) + } +]) - /* - * - * ApplyEllipsis() - * - */ - .factory('ApplyEllipsis', [ function() { - return function(selector) { +/* + * + * ApplyEllipsis() + * + */ +.factory('ApplyEllipsis', [ + function () { + return function (selector) { // Add a hidden element to the DOM. We'll use this to calc the px length of // our target text. var tmp = $('#string-test'); @@ -625,26 +684,26 @@ angular.module('Utilities',['RestServices', 'Utilities']) tmp = $('#string-test'); } // Find and process the text. - $(selector).each(function() { - var setTitle = true, txt, w, pw, cw, df; + $(selector).each(function () { + var setTitle = true, + txt, w, pw, cw, df; if ($(this).attr('title')) { txt = $(this).attr('title'); setTitle = false; - } - else { + } else { txt = $(this).text(); } tmp.text(txt); - w = tmp.width(); //text width - pw = $(this).parent().width(); //parent width + w = tmp.width(); //text width + pw = $(this).parent().width(); //parent width if (w > pw) { // text is wider than parent width if (setTitle) { // Save the original text in the title - $(this).attr('title',txt); + $(this).attr('title', txt); } cw = w / txt.length; // px width per character - df = w - pw; // difference in px + df = w - pw; // difference in px txt = txt.substr(0, txt.length - (Math.ceil(df / cw) + 3)); $(this).text(txt + '...'); } @@ -654,16 +713,7 @@ angular.module('Utilities',['RestServices', 'Utilities']) $(this).text(txt); } }); - + }; - }]); - - - - - - - - - - + } +]); \ No newline at end of file diff --git a/awx/ui/static/lib/ansible/api-loader.js b/awx/ui/static/lib/ansible/api-loader.js index bf999cd5c5..5c4c46f0bf 100644 --- a/awx/ui/static/lib/ansible/api-loader.js +++ b/awx/ui/static/lib/ansible/api-loader.js @@ -1,45 +1,53 @@ /********************************************* * Copyright (c) 2014 AnsibleWorks, Inc. * - * + * * Read /api and /api/X to discover all the base paths needed * to access the primary model objects. * */ - 'use strict'; angular.module('ApiLoader', ['Utilities']) - .factory('LoadBasePaths', ['$http', '$rootScope', 'Store', 'ProcessErrors', - function($http, $rootScope, Store, ProcessErrors) { - return function() { +.factory('LoadBasePaths', ['$http', '$rootScope', 'Store', 'ProcessErrors', + function ($http, $rootScope, Store, ProcessErrors) { + return function () { $http.get('/api/') - .success( function(data) { + .success(function (data) { var base = data.current_version; $http.get(base) - .success( function(data) { + .success(function (data) { data.base = base; $rootScope.defaultUrls = data; Store('api', data); }) - .error ( function(data, status) { - $rootScope.defaultUrls = { status: 'error' }; - ProcessErrors(null, data, status, null, - { hdr: 'Error', msg: 'Failed to read ' + base + '. GET status: ' + status }); + .error(function (data, status) { + $rootScope.defaultUrls = { + status: 'error' + }; + ProcessErrors(null, data, status, null, { + hdr: 'Error', + msg: 'Failed to read ' + base + '. GET status: ' + status + }); }); }) - .error( function(data, status) { - $rootScope.defaultUrls = { status: 'error' }; - ProcessErrors(null, data, status, null, - { hdr: 'Error', msg: 'Failed to read /api. GET status: ' + status }); + .error(function (data, status) { + $rootScope.defaultUrls = { + status: 'error' + }; + ProcessErrors(null, data, status, null, { + hdr: 'Error', + msg: 'Failed to read /api. GET status: ' + status + }); }); }; - }]) + } +]) - .factory('GetBasePath', ['$rootScope', 'Store', 'LoadBasePaths', 'Empty', - function($rootScope, Store, LoadBasePaths, Empty) { - return function(set) { +.factory('GetBasePath', ['$rootScope', 'Store', 'LoadBasePaths', 'Empty', + function ($rootScope, Store, LoadBasePaths, Empty) { + return function (set) { // use /api/v1/ results to construct API URLs. if (Empty($rootScope.defaultUrls)) { // browser refresh must have occurred. load from local storage @@ -47,11 +55,9 @@ angular.module('ApiLoader', ['Utilities']) $rootScope.defaultUrls = Store('api'); return $rootScope.defaultUrls[set]; } - return ''; //we should never get here + return ''; //we should never get here } return $rootScope.defaultUrls[set]; }; - }]); - - - + } +]); \ No newline at end of file diff --git a/awx/ui/static/lib/ansible/directives.js b/awx/ui/static/lib/ansible/directives.js index e92251dddf..6b35580257 100644 --- a/awx/ui/static/lib/ansible/directives.js +++ b/awx/ui/static/lib/ansible/directives.js @@ -10,6 +10,7 @@ /* global chkPass:false */ angular.module('AWDirectives', ['RestServices', 'Utilities', 'AuthService', 'JobsHelper']) + // awpassmatch: Add to password_confirm field. Will test if value // matches that of 'input[name="password"]' .directive('awpassmatch', function() { diff --git a/awx/ui/static/lib/ansible/filters.js b/awx/ui/static/lib/ansible/filters.js index 7c393a469a..f8eb69af6a 100644 --- a/awx/ui/static/lib/ansible/filters.js +++ b/awx/ui/static/lib/ansible/filters.js @@ -1,26 +1,29 @@ /********************************************* - * Copyright (c) 2014 AnsibleWorks, Inc. + * Copyright (c) 2014 AnsibleWorks, Inc. * - * Custom filters + * Custom filters * */ 'use strict'; angular.module('AWFilters', []) + // // capitalize -capitalize the first letter of each word // - .filter('capitalize', [ function() { - return function(input) { - var values, result, i; - if (input) { - values = input.replace(/\_/g,' ').split(" "); - result = ""; - for (i = 0; i < values.length; i++){ - result += values[i].charAt(0).toUpperCase() + values[i].substr(1) + ' '; + .filter('capitalize', [ + function () { + return function (input) { + var values, result, i; + if (input) { + values = input.replace(/\_/g, ' ').split(" "); + result = ""; + for (i = 0; i < values.length; i++) { + result += values[i].charAt(0).toUpperCase() + values[i].substr(1) + ' '; + } + return result.trim(); } - return result.trim(); - } - }; - }]); \ No newline at end of file + }; + } + ]); \ No newline at end of file diff --git a/awx/ui/static/lib/ansible/form-generator.js b/awx/ui/static/lib/ansible/form-generator.js index d7ce0277c2..dd916ab7f7 100644 --- a/awx/ui/static/lib/ansible/form-generator.js +++ b/awx/ui/static/lib/ansible/form-generator.js @@ -1,1530 +1,1483 @@ /********************************************* * Copyright (c) 2014 AnsibleWorks, Inc. * - * FormGenerator + * FormGenerator * Pass in a form definition from FormDefinitions and out pops an html template. * See js/form-definitions.js for form example. For now produces a Twitter Bootstrap - * horizontal form. - * + * horizontal form. + * */ 'use strict'; angular.module('FormGenerator', ['GeneratorHelpers', 'ngCookies', 'Utilities']) - + .factory('GenerateForm', ['$rootScope', '$location', '$cookieStore', '$compile', 'SearchWidget', 'PaginateWidget', 'Attr', 'Icon', 'Column', 'NavigationLink', 'HelpCollapse', 'Button', 'DropDown', 'Empty', 'SelectIcon', -function($rootScope, $location, $cookieStore, $compile, SearchWidget, PaginateWidget, Attr, Icon, Column, NavigationLink, - HelpCollapse, Button, DropDown, Empty, SelectIcon) { - return { - - setForm: function(form) { - this.form = form; - }, - - attr: Attr, + function ($rootScope, $location, $cookieStore, $compile, SearchWidget, PaginateWidget, Attr, Icon, Column, NavigationLink, + HelpCollapse, Button, DropDown, Empty, SelectIcon) { + return { - icon: Icon, + setForm: function (form) { + this.form = form; + }, - accordion_count: 0, + attr: Attr, - scope: null, + icon: Icon, - has: function(key) { - return (this.form[key] && this.form[key] !== null && this.form[key] !== undefined) ? true : false; - }, + accordion_count: 0, - inject: function(form, options) { - // - // Use to inject the form as html into the view. View MUST have an ng-bind for 'htmlTemplate'.t - // Returns scope of form. - // - - var element, fld, set, show; + scope: null, - if (options.modal) { - if (options.modal_body_id) { - element = angular.element(document.getElementById(options.modal_body_id)); - } - else { - // use default dialog - element = angular.element(document.getElementById('form-modal-body')); - } - } - else { - if (options.id) { - element = angular.element(document.getElementById(options.id)); - } - else { - element = angular.element(document.getElementById('htmlTemplate')); - } - } + has: function (key) { + return (this.form[key] && this.form[key] !== null && this.form[key] !== undefined) ? true : false; + }, - this.mode = options.mode; - this.modal = (options.modal) ? true : false; - this.setForm(form); + inject: function (form, options) { + // + // Use to inject the form as html into the view. View MUST have an ng-bind for 'htmlTemplate'.t + // Returns scope of form. + // - if (options.html) { - element.html(options.html); - } - else { - element.html(this.build(options)); - } - - if (options.scope) { - this.scope = options.scope; - } - else { - this.scope = element.scope(); - } + var element, fld, set, show; - $compile(element)(this.scope); - - if (!options.html) { - // Reset the scope to prevent displaying old data from our last visit to this form - for (fld in form.fields) { - this.scope[fld] = null; - } - for (set in form.related) { - this.scope[set] = null; - } - if ( ((!options.modal) && options.related) || this.form.forceListeners ) { - this.addListeners(); - } - if (options.mode === 'add') { - this.applyDefaults(); - } - } - - // Remove any lingering tooltip and popover
          elements - $('.tooltip').each( function() { - $(this).remove(); - }); - - $('.popover').each(function() { - // remove lingering popover
          . Seems to be a bug in TB3 RC1 - $(this).remove(); - }); - - $(window).unbind('resize'); - - // Prepend an asterisk to required field label - $('.form-control[required], input[type="radio"][required]').each(function() { - var label, span; - if ( Empty($(this).attr('aw-required-when')) ) { - label = $(this).parent().parent().find('label').first(); - if ($(this).attr('type') === 'radio') { - label = $(this).parent().parent().parent().find('label').first(); + if (options.modal) { + if (options.modal_body_id) { + element = angular.element(document.getElementById(options.modal_body_id)); + } else { + // use default dialog + element = angular.element(document.getElementById('form-modal-body')); } - if (label.length > 0) { - span = label.children('span'); - if (span.length > 0 && !span.first().hasClass('prepend-asterisk')) { - span.first().addClass('prepend-asterisk'); - } - else if (span.length <= 0 && !label.first().hasClass('prepend-asterisk')) { - label.first().addClass('prepend-asterisk'); - } + } else { + if (options.id) { + element = angular.element(document.getElementById(options.id)); + } else { + element = angular.element(document.getElementById('htmlTemplate')); } } - }); - try { - $('#help-modal').empty().dialog('destroy'); - } - catch(e) { - //ignore any errors should the dialog not be initialized - } + this.mode = options.mode; + this.modal = (options.modal) ? true : false; + this.setForm(form); - if (options.modal) { - this.scope.formModalActionDisabled = false; - this.scope.formModalInfo = false; //Disable info button for default modal - if (form) { - if (options.modal_title_id) { - this.scope[options.modal_title_id] = (options.mode === 'add') ? form.addTitle : form.editTitle; + if (options.html) { + element.html(options.html); + } else { + element.html(this.build(options)); + } + + if (options.scope) { + this.scope = options.scope; + } else { + this.scope = element.scope(); + } + + $compile(element)(this.scope); + + if (!options.html) { + // Reset the scope to prevent displaying old data from our last visit to this form + for (fld in form.fields) { + this.scope[fld] = null; } - else { - this.scope.formModalHeader = (options.mode === 'add') ? form.addTitle : form.editTitle; //Default title for default modal + for (set in form.related) { + this.scope[set] = null; + } + if (((!options.modal) && options.related) || this.form.forceListeners) { + this.addListeners(); + } + if (options.mode === 'add') { + this.applyDefaults(); } } - if (options.modal_selector) { - $(options.modal_selector).modal({ show: true, backdrop: 'static', keyboard: true }); - $(options.modal_selector).on('shown.bs.modal', function() { - $(options.modal_select + ' input:first').focus(); - }); - $(options.modal_selector).on('hidden.bs.modal', function() { - $('.tooltip').each( function() { - // Remove any lingering tooltip and popover
          elements - $(this).remove(); - }); - $('.popover').each(function(index) { - // remove lingering popover
          . Seems to be a bug in TB3 RC1 - $(this).remove(); - }); - }); - } - else { - show = (options.show_modal === false) ? false : true; - $('#form-modal').modal({ show: show, backdrop: 'static', keyboard: true }); - $('#form-modal').on('shown.bs.modal', function() { - $('#form-modal input:first').focus(); - }); - $('#form-modal').on('hidden.bs.modal', function() { - $('.tooltip').each( function(index) { - // Remove any lingering tooltip and popover
          elements - $(this).remove(); - }); - - $('.popover').each(function(index) { - // remove lingering popover
          . Seems to be a bug in TB3 RC1 - $(this).remove(); - }); - }); - } - $(document).bind('keydown', function(e) { - if (e.keyCode === 27) { - if (options.modal_selector) { - $(options.modal_selector).modal('hide'); - } - $('#prompt-modal').modal('hide'); - $('#form-modal').modal('hide'); - } + // Remove any lingering tooltip and popover
          elements + $('.tooltip').each(function () { + $(this).remove(); }); - } - if (!this.scope.$$phase) { - this.scope.$digest(); - } - - return this.scope; - }, - - applyDefaults: function() { - for (var fld in this.form.fields) { - if (this.form.fields[fld]['default'] || this.form.fields[fld]['default'] === 0) { - if (this.form.fields[fld].type === 'select' && this.scope[fld + '_options']) { - this.scope[fld] = this.scope[fld + '_options'][this.form.fields[fld]['default']]; - } - else { - this.scope[fld] = this.form.fields[fld]['default']; - } - } - } - }, - - reset: function() { - // The form field values cannot be reset with jQuery. Each field is tied to a model, so to clear the field - // value, you have to clear the model. - - var fld, scope = this.scope, form = this.form; - - if (scope[form.name + '_form']) { - scope[form.name + '_form'].$setPristine(); - } - - function resetField(f, fld) { - // f is the field object, fld is the key - - if (f.type === 'checkbox_group') { - for (var i=0; i < f.fields.length; i++) { - scope[f.fields[i].name] = ''; - scope[f.fields[i].name + '_api_error'] = ''; - scope[form.name + '_form'][f.fields[i].name].$setValidity('apiError', true); - } - } - else { - scope[fld] = ''; - scope[fld + '_api_error'] = ''; - } - if (f.sourceModel) { - scope[f.sourceModel + '_' + f.sourceField] = ''; - scope[f.sourceModel + '_' + f.sourceField + '_api_error'] = ''; - if (scope[form.name + '_form'][f.sourceModel + '_' + f.sourceField]) { - scope[form.name + '_form'][f.sourceModel + '_' + f.sourceField].$setValidity('apiError', true); - } - } - if (f.type === 'lookup' && scope[form.name + '_form'][f.sourceModel + '_' + f.sourceField]) { - scope[form.name + '_form'][f.sourceModel + '_' + f.sourceField].$setPristine(); - scope[form.name + '_form'][f.sourceModel + '_' + f.sourceField].$setValidity('apiError', true); - } - if (scope[form.name + '_form'][fld]) { - scope[form.name + '_form'][fld].$setPristine(); - scope[form.name + '_form'][fld].$setValidity('apiError', true); - } - if (f.chkPass && scope[form.name + '_form'][fld]) { - scope[form.name + '_form'][fld].$setValidity('complexity', true); - $('#progbar').css({ width: 0 }); - } - if (f.awPassMatch && scope[form.name + '_form'][fld]) { - scope[form.name + '_form'][fld].$setValidity('awpassmatch', true); - } - if (f.ask) { - scope[fld + '_ask'] = false; - } - } - - for (fld in form.fields) { - resetField(form.fields[fld], fld); - } - if (form.statusFields) { - for (fld in form.statusFields) { - resetField(form.statusFields[fld], fld); - } - } - if (this.mode === 'add') { - this.applyDefaults(); - } - }, - - addListeners: function() { - - if (this.modal) { - $('.jqui-accordion-modal').accordion({ - collapsible: false, - heightStyle: 'content', - active: 0 + $('.popover').each(function () { + // remove lingering popover
          . Seems to be a bug in TB3 RC1 + $(this).remove(); }); - } - else { - this.scope.accordionToggle = function(selector) { - $(selector).collapse('toggle'); - if ( $(selector + '-icon').hasClass('fa-minus') ) { - $(selector + '-icon').removeClass('fa-minus').addClass('fa-plus'); - } - else { - $(selector + '-icon').removeClass('fa-plus').addClass('fa-minus'); - } - }; - $('.jqui-accordion').each( function(index) { - - var active = false, - list = $cookieStore.get('accordions'), - found = false, - id, base, i; + $(window).unbind('resize'); - if (list) { - id = $(this).attr('id'); - base = ($location.path().replace(/^\//,'').split('/')[0]); - for (i=0; i < list.length && found === false; i++) { - if (list[i].base === base && list[i].id === id) { - found = true; - active = list[i].active; + // Prepend an asterisk to required field label + $('.form-control[required], input[type="radio"][required]').each(function () { + var label, span; + if (Empty($(this).attr('aw-required-when'))) { + label = $(this).parent().parent().find('label').first(); + if ($(this).attr('type') === 'radio') { + label = $(this).parent().parent().parent().find('label').first(); + } + if (label.length > 0) { + span = label.children('span'); + if (span.length > 0 && !span.first().hasClass('prepend-asterisk')) { + span.first().addClass('prepend-asterisk'); + } else if (span.length <= 0 && !label.first().hasClass('prepend-asterisk')) { + label.first().addClass('prepend-asterisk'); } } } + }); - if (found === false && $(this).attr('data-open') === 'true') { - active = 0; + try { + $('#help-modal').empty().dialog('destroy'); + } catch (e) { + //ignore any errors should the dialog not be initialized + } + + if (options.modal) { + this.scope.formModalActionDisabled = false; + this.scope.formModalInfo = false; //Disable info button for default modal + if (form) { + if (options.modal_title_id) { + this.scope[options.modal_title_id] = (options.mode === 'add') ? form.addTitle : form.editTitle; + } else { + this.scope.formModalHeader = (options.mode === 'add') ? form.addTitle : form.editTitle; //Default title for default modal + } } - - $(this).accordion({ - collapsible: true, - heightStyle: 'content', - active: active, - activate: function( event, ui ) { - $('.jqui-accordion').each( function(index) { - var active = $(this).accordion('option', 'active'), - id = $(this).attr('id'), - base = ($location.path().replace(/^\//,'').split('/')[0]), - list = $cookieStore.get('accordions'), - i, found; - if (list === null || list === undefined) { - list = []; - } - found = false; - for (i=0; i < list.length && found === false; i++) { - if ( list[i].base === base && list[i].id === id) { - found = true; - list[i].active = active; - } - } - if (found === false) { - list.push({ base: base, id: id, active: active }); - } - $cookieStore.put('accordions',list); + if (options.modal_selector) { + $(options.modal_selector).modal({ + show: true, + backdrop: 'static', + keyboard: true + }); + $(options.modal_selector).on('shown.bs.modal', function () { + $(options.modal_select + ' input:first').focus(); + }); + $(options.modal_selector).on('hidden.bs.modal', function () { + $('.tooltip').each(function () { + // Remove any lingering tooltip and popover
          elements + $(this).remove(); }); + + $('.popover').each(function () { + // remove lingering popover
          . Seems to be a bug in TB3 RC1 + $(this).remove(); + }); + }); + } else { + show = (options.show_modal === false) ? false : true; + $('#form-modal').modal({ + show: show, + backdrop: 'static', + keyboard: true + }); + $('#form-modal').on('shown.bs.modal', function () { + $('#form-modal input:first').focus(); + }); + $('#form-modal').on('hidden.bs.modal', function () { + $('.tooltip').each(function () { + // Remove any lingering tooltip and popover
          elements + $(this).remove(); + }); + + $('.popover').each(function () { + // remove lingering popover
          . Seems to be a bug in TB3 RC1 + $(this).remove(); + }); + }); + } + $(document).bind('keydown', function (e) { + if (e.keyCode === 27) { + if (options.modal_selector) { + $(options.modal_selector).modal('hide'); + } + $('#prompt-modal').modal('hide'); + $('#form-modal').modal('hide'); } }); - }); - } - }, - - genID: function() { - var id = new Date(); - return id.getTime(); - }, - - headerField: function(fld, field, options) { - var html = ''; - if (field.label) { - html += "\n"; - } - html += ""; - } - - html += "'; - html += (field.icon) ? Icon(field.icon) : ""; - html += "" + field.label + ""; - html += (field.awPopOver && !field.awPopOverRight) ? Attr(field, 'awPopOver', fld) : ""; - html += "\n"; + } + }, + + reset: function () { + // The form field values cannot be reset with jQuery. Each field is tied to a model, so to clear the field + // value, you have to clear the model. + + var fld, scope = this.scope, + form = this.form; + + if (scope[form.name + '_form']) { + scope[form.name + '_form'].$setPristine(); + } + + function resetField(f, fld) { + // f is the field object, fld is the key + + if (f.type === 'checkbox_group') { + for (var i = 0; i < f.fields.length; i++) { + scope[f.fields[i].name] = ''; + scope[f.fields[i].name + '_api_error'] = ''; + scope[form.name + '_form'][f.fields[i].name].$setValidity('apiError', true); + } + } else { + scope[fld] = ''; + scope[fld + '_api_error'] = ''; + } + if (f.sourceModel) { + scope[f.sourceModel + '_' + f.sourceField] = ''; + scope[f.sourceModel + '_' + f.sourceField + '_api_error'] = ''; + if (scope[form.name + '_form'][f.sourceModel + '_' + f.sourceField]) { + scope[form.name + '_form'][f.sourceModel + '_' + f.sourceField].$setValidity('apiError', true); + } + } + if (f.type === 'lookup' && scope[form.name + '_form'][f.sourceModel + '_' + f.sourceField]) { + scope[form.name + '_form'][f.sourceModel + '_' + f.sourceField].$setPristine(); + scope[form.name + '_form'][f.sourceModel + '_' + f.sourceField].$setValidity('apiError', true); + } + if (scope[form.name + '_form'][fld]) { + scope[form.name + '_form'][fld].$setPristine(); + scope[form.name + '_form'][fld].$setValidity('apiError', true); + } + if (f.chkPass && scope[form.name + '_form'][fld]) { + scope[form.name + '_form'][fld].$setValidity('complexity', true); + $('#progbar').css({ + width: 0 + }); + } + if (f.awPassMatch && scope[form.name + '_form'][fld]) { + scope[form.name + '_form'][fld].$setValidity('awpassmatch', true); + } + if (f.ask) { + scope[fld + '_ask'] = false; + } + } + + for (fld in form.fields) { + resetField(form.fields[fld], fld); + } + if (form.statusFields) { + for (fld in form.statusFields) { + resetField(form.statusFields[fld], fld); + } + } + if (this.mode === 'add') { + this.applyDefaults(); + } + }, + + addListeners: function () { + + if (this.modal) { + $('.jqui-accordion-modal').accordion({ + collapsible: false, + heightStyle: 'content', + active: 0 + }); + } else { + this.scope.accordionToggle = function (selector) { + $(selector).collapse('toggle'); + if ($(selector + '-icon').hasClass('fa-minus')) { + $(selector + '-icon').removeClass('fa-minus').addClass('fa-plus'); + } else { + $(selector + '-icon').removeClass('fa-plus').addClass('fa-minus'); + } + }; + + $('.jqui-accordion').each(function () { + + var active = false, + list = $cookieStore.get('accordions'), + found = false, + id, base, i; + + if (list) { + id = $(this).attr('id'); + base = ($location.path().replace(/^\//, '').split('/')[0]); + for (i = 0; i < list.length && found === false; i++) { + if (list[i].base === base && list[i].id === id) { + found = true; + active = list[i].active; + } + } + } + + if (found === false && $(this).attr('data-open') === 'true') { + active = 0; + } + + $(this).accordion({ + collapsible: true, + heightStyle: 'content', + active: active, + activate: function () { + $('.jqui-accordion').each(function () { + var active = $(this).accordion('option', 'active'), + id = $(this).attr('id'), + base = ($location.path().replace(/^\//, '').split('/')[0]), + list = $cookieStore.get('accordions'), + i, found; + if (list === null || list === undefined) { + list = []; + } + found = false; + for (i = 0; i < list.length && found === false; i++) { + if (list[i].base === base && list[i].id === id) { + found = true; + list[i].active = active; + } + } + if (found === false) { + list.push({ + base: base, + id: id, + active: active + }); + } + $cookieStore.put('accordions', list); + }); + } + }); + }); + } + }, + + genID: function () { + var id = new Date(); + return id.getTime(); + }, + + headerField: function (fld, field) { + var html = ''; + if (field.label) { + html += "\n"; + } + html += "\n"; - html += "
          \n"; - html += "
          ×\n" : ""; - html += field.alertTxt; - html += "
          \n"; - html += "
          \n"; - html += "
          \n"; - } + }, - if (field.type === 'hidden') { - if ( (options.mode === 'edit' && field.includeOnEdit) || - (options.mode === 'add' && field.includeOnAdd) ) { - html += ""; + + buildField: function (fld, field, options, form) { + + var i, fldWidth, offset, html = '', + horizontal = (this.form.horizontal) ? true : false; + + function getFieldWidth() { + var x; + if (form.formFieldSize) { + x = form.formFieldSize; + } else if (field.xtraWide) { + x = "col-lg-10"; + } else if (field.column) { + x = "col-lg-8"; + } else if (!form.formFieldSize && options.modal) { + x = "col-lg-10"; + } else { + x = "col-lg-6"; + } + return x; } - } - - if ( (! field.readonly) || (field.readonly && options.mode === 'edit') ) { - html += "
          "; + } + + html += "'; + html += (field.icon) ? Icon(field.icon) : ""; + html += "" + field.label + ""; + html += (field.awPopOver && !field.awPopOverRight) ? Attr(field, 'awPopOver', fld) : ""; + html += "\n"; + } + return html; + } + + + if (field.type === 'alertblock') { + html += "
          \n"; + html += "
          \n"; + html += "
          ×\n" : ""; + html += field.alertTxt; + html += "
          \n"; + html += "
          \n"; + html += "
          \n"; + } + + if (field.type === 'hidden') { + if ((options.mode === 'edit' && field.includeOnEdit) || + (options.mode === 'add' && field.includeOnAdd)) { + html += ""; + } + } + + if ((!field.readonly) || (field.readonly && options.mode === 'edit')) { + html += "
          \n" : ""; + //text fields + if (field.type === 'text' || field.type === 'password' || field.type === 'email') { + html += label(); + html += "
          \n" : ""; + + if (field.control === null || field.control === undefined || field.control) { + html += "\n"; + html += "\n
          \n"; + if (field.ask) { + html += "
          \n"; + } + + // Add error messages + if ((options.mode === 'add' && field.addRequired) || (options.mode === 'edit' && field.editRequired) || + field.awRequiredWhen) { + html += "
          A value is required!
          \n"; + } + if (field.type === "email") { + html += "
          A valid email address is required!
          \n"; + } + if (field.awPassMatch) { + html += "
          Must match Password value
          \n"; + } + if (field.awValidUrl) { + html += "
          URL must begin with ssh, http or https and may not contain '@'
          \n"; + } + + html += "
          \n"; + + if (field.chkPass) { + // complexity error + html += "
          Password must be stronger
          \n"; + + // progress bar + html += "
          \n"; + html += "
          \n"; + html += "
          \n"; + html += "
          \n"; + + // help panel + html += HelpCollapse({ + hdr: 'Password Complexity', + content: "

          A password with reasonable strength is required. As you type the password " + + "a progress bar will measure the strength. Sufficient strength is reached when the bar turns green.

          " + + "

          Password strength is judged using the following:

          " + + "
            " + + "
          • Minimum 8 characters in length
          • \n" + + "
          • Contains a sufficient combination of the following items:\n" + + "
              \n" + + "
            • UPPERCASE letters
            • \n" + + "
            • Numbers
            • \n" + + "
            • Symbols _!@#$%^&*()
            • \n" + + "
            \n" + + "
          • \n" + + "
          \n", + idx: this.accordion_count++, + show: null + }); + html += "
          \n"; + } + + // Add help panel(s) + html += (field.helpCollapse) ? this.buildHelpCollapse(field.helpCollapse) : ''; + + html += "
          \n"; + } + + //textarea fields + if (field.type === 'textarea') { + + html += label(); + + html += "
          Parse as: " + + " YAML\n"; + html += " JSON\n"; + html += "
          \n"; + } + + html += "\n"; - - // Add error messages - if ( (options.mode == 'add' && field.addRequired) || (options.mode == 'edit' && field.editRequired) ) { - html += "
          A value is required!
          \n"; - } - html += "
          \n"; - html += "
          \n"; - } - - //select field - if (field.type == 'select') { - - html += label(); - - html += "
          "; - html += (field.defaultOption) ? field.defaultOption : "Choose a " + field.label.toLowerCase(); - html += "\n"; - html += "\n"; - // Add error messages - if ( (options.mode == 'add' && field.addRequired) || (options.mode == 'edit' && field.editRequired) || - field.awRequiredWhen ) { - html += "
          A value is required!
          \n"; - } - html += "
          \n"; - - // Add help panel(s) - html += (field.helpCollapse) ? this.buildHelpCollapse(field.helpCollapse) : ''; - - html += "
          \n"; - } - - //number field - if (field.type == 'number') { - - html += label(); - - html += "
          A value is required!
          \n"; - } - if (field.integer) { - html += "
          Must be an integer value
          \n"; - } - if (field.min !== undefined || field.max !== undefined) { - html += "
          Must be an integer between " + field.min + " and "; - html += (field.max !== undefined) ? field.max : 'unlimited'; - html += "
          \n"; - } - html += "
          \n"; - html += "
          \n"; - } - - //checkbox group - if (field.type == 'checkbox_group') { - - html += label(); - - html += "
          A value is required!
          \n"; - } - if (field.integer) { - html += "
          Must be an integer value
          \n"; - } - if (field.min || field.max) { - html += "
          Must be in range " + field.min + " to " + - field.max + "
          \n"; - } - html += "
          \n"; - html += "
          \n"; - html += "
          \n"; - } - - //checkbox - if (field.type == 'checkbox') { - - if (horizontal) { - var fldWidth = getFieldWidth(); - var offset = 12 - parsetInt(fldWidth.replace(/[A-Z,a-z,-]/g,'')); - html += "
          \n"; - } - - html += "
          \n"; - html += "\n"; - html += "
          \n"; - html += "
          \n" - - if (horizontal) { - html += "
          \n"; - } - } - - //radio group - if (field.type == 'radio_group') { - - html += label(); - - html += "
          A value is required!
          \n"; - } - html += "
          \n"; - - // Add help panel(s) - html += (field.helpCollapse) ? this.buildHelpCollapse(field.helpCollapse) : ''; - - html += "
          \n"; - } - - // radio button - if (field.type == 'radio') { - - if (horizontal) { - var fldWidth = getFieldWidth(); - var offset = 12 - parsetInt(fldWidth.replace(/[A-Z,a-z,-]/g,'')); - html += "
          \n"; - } - - html += "
          \n"; - html += "
          \n"; - html += "
          \n" - - if (horizontal) { - html += "
          \n"; - } - } - - //lookup type fields - if (field.type == 'lookup' && (field.excludeMode == undefined || field.excludeMode != options.mode)) { - - html += label(); - - html += "
          \n"; - html += "\n"; - html += "\n"; - html += "\n"; - html += "A value is required!
          \n"; - } - html += "
          Value not found
          \n"; - html += "
          \n"; - html += "
          \n"; - } - - //custom fields - if (field.type == 'custom') { - /*html += "
          '; - html += (field.icon) ? this.icon(field.icon) : ""; - html += (field.label) ? field.label : ''; - html += '' + "\n"; - html += "
          \n"; - */ - - html += label(); - - html += "
          \n"; - for (var action in this.form.actions) { - if (this.form.actions[action].mode == 'all' || this.form.actions[action].mode == options.mode) { - html += this.button({ btn: this.form.actions[action], action: action, toolbar: true }); - } - } - html += "
          \n"; - return html; - }, - - - breadCrumbs: function(options, navigation) { - - var html = ''; - html += "
          \n"; - html += "
            \n"; - html += "
          • {{ crumb.title }}
          • \n"; - - if (navigation) { - - var paths = $location.path().replace(/^\//,'').split('/'); - - if (paths.length == 2) { - html += "
          • "; - if (options.mode == 'edit') { - html += this.form.editTitle; - } - else { - html += this.form.addTitle; - } - html += "
          • \n"; - } - - html += "
          • \n"; - html += "
          \n"; - html += "
          \n"; - for (var itm in navigation) { - if (navigation[itm].active) { - html += ""; - break; - } - } - - html += "\n"; - html += "
          \n"; - html += "
          \n"; - } - else { - html += "
        • "; - if (options.mode == 'edit') { - html += this.form.editTitle; - } - else { - html += this.form.addTitle; - } - html += "
        • \n
        \n
        \n"; - } - return html; - }, - - build: function(options) { - // - // Generate HTML. Do NOT call this function directly. Called by inject(). Returns an HTML - // string to be injected into the current view. - // - var html = ''; - - if (!this.modal && (options.breadCrumbs == undefined || options.breadCrumbs == true)) { - if (this.form.navigationLinks) { - html += this.breadCrumbs(options, this.form.navigationLinks); - } - else { - html += this.breadCrumbs(options); - } - } - - if ((!this.modal && this.form.statusFields)) { - // Add status fields section (used in Jobs form) - html += "
        \n"; - if (this.form.statusActions) { - html += "
        \n"; - var act; - for (var action in this.form.statusActions) { - act = this.form.statusActions[action]; - html += this.button({ btn: act, action: action, toolbar: true }); - } - html += "
        \n"; - //html += "
        \n"; - } - html += "
        \n"; - for (var fld in this.form.statusFields) { - field = this.form.statusFields[fld]; - html += this.buildField(fld, field, options, this.form); - } - html += "
        \n"; - html += "
        \n"; - } - - if (this.form.fieldsAsHeader) { - html += "
        \n"; - html += "\n"; - for (var fld in this.form.fields) { - field = this.form.fields[fld]; - html += this.headerField(fld, field, options); - } - html += "\n"; - html += "
        \n"; - } - else { - - if ( this.form.collapse && this.form.collapseMode == options.mode) { - html += "
        \n"; - html += "

        " + this.form.collapseTitle + "

        \n"; - html += "
        \n"; - } - - // Start the well - if ( !this.modal && this.has('well') ) { - html += "
        \n"; - } - - if (!this.modal && this.form.actions) { - html += this.getActions(options); - } - - // Add a title and optionally a close button (used on Inventory->Groups) - if ( (!options.modal) && this.form.showTitle ) { - html += "
        "; - html += (options.mode == 'edit') ? this.form.editTitle : this.form.addTitle; - if (this.has('titleActions')) { - html += "
        \n"; - for (var btn in this.form.titleActions) { - html += this.button({ btn: this.form.titleActions[btn], action: btn, toolbar: true }); - } - html += "
        \n"; - } - html += "
        \n"; - html += "
        \n"; - } - - html += "
        \n"; - html += "
        {{ flashMessage }}
        \n"; - - var field; - if (this.form.twoColumns) { - html += "
        \n"; - html += "
        \n"; - for (var fld in this.form.fields) { - field = this.form.fields[fld]; - if (field.column == 1) { - html += this.buildField(fld, field, options, this.form); - } - } - html += "
        \n"; - html += "
        \n"; - for (var fld in this.form.fields) { - field = this.form.fields[fld]; - if (field.column == 2) { - html += this.buildField(fld, field, options, this.form); - } - } - html += "
        \n"; - html += "
        \n"; - } - else if (this.form.tabs) { - html += "
          \n"; - for (var i=0; i < this.form.tabs.length; i++) { - var tab = this.form.tabs[i]; - html += "" + tab.label + "\n"; - } - html += "
        \n"; - html += "
        \n"; - for (var i=0; i < this.form.tabs.length; i++) { - var tab = this.form.tabs[i]; - html += "
        \n"; - for (var fld in this.form.fields) { - if (this.form.fields[fld].tab == tab.name) { - html += this.buildField(fld, this.form.fields[fld], options, this.form); - } - } - html += "
        \n" - } - html += "
        \n"; - } - else { - // original, single-column form - var section = ''; - var group = ''; - for (var fld in this.form.fields) { - var field = this.form.fields[fld]; - if (!(options.modal && field.excludeModal)) { - if (field.group && field.group != group) { - if (group !== '') { - html += "
        \n"; + if (field.integer) { + html += "
        Must be an integer value
        \n"; } - html += "
        \n"; - html += "
        " + field.group + "
        \n"; - group = field.group; - } - if (field.section && field.section != section) { - if (section !== '') { - html += "
        \n"; - } - else { + if (field.min !== undefined || field.max !== undefined) { + html += "
        Must be an integer between " + field.min + " and "; + html += (field.max !== undefined) ? field.max : 'unlimited'; html += "
        \n"; - html += "
        \n"; } - var sectionShow = (this.form[field.section + 'Show']) ? " ng-show=\"" + this.form[field.section + 'Show'] + "\"" : ""; - html += "" + field.section + "\n"; - html += "\n"; - section = field.section; - } - html += this.buildField(fld, field, options, this.form); - } - } - if (section !== '') { - html += "
        \n
        \n"; - } - if (group !== '') { - html += "
        \n"; - } - } - - //buttons - if (!this.modal) { - if (this.has('buttons')) { - - if (this.form.twoColumns) { - html += "
        \n"; - html += "
        \n"; - html += "
        \n"; - } - - html += "
        \n"; + html += "
        \n"; } - var width = 12 - offset; - html += "
        \n"; - } - //html += "\n"; - - //html += "
        \n"; - - for (var btn in this.form.buttons) { - if (typeof this.form.buttons[btn] == 'object') { - var button = this.form.buttons[btn]; + //checkbox group + if (field.type === 'checkbox_group') { - // Set default color and label for Save and Reset - if (btn == 'save') { - button.label = 'Save'; - button['class'] = 'btn-success'; - } - if (btn == 'reset') { - button.label = 'Reset'; - button['class'] = 'btn-default'; - } - - // Build button HTML - html += "
        \n"; + html += "
        \n"; } - } - html += "
        \n"; - if (this.form.horizontal) { + //checkbox + if (field.type === 'checkbox') { + + if (horizontal) { + fldWidth = getFieldWidth(); + offset = 12 - parseInt(fldWidth.replace(/[A-Z,a-z,-]/g, ''),10); + html += "
        \n"; + } + + html += "
        \n"; + html += "\n"; + html += "
        \n"; + html += "
        \n"; + + if (horizontal) { + html += "
        \n"; + } + } + + //radio group + if (field.type === 'radio_group') { + + html += label(); + + html += "
        A value is required!
        \n"; + } + html += "
        \n"; + + // Add help panel(s) + html += (field.helpCollapse) ? this.buildHelpCollapse(field.helpCollapse) : ''; + + html += "
        \n"; + } + + // radio button + if (field.type === 'radio') { + + if (horizontal) { + fldWidth = getFieldWidth(); + offset = 12 - parseInt(fldWidth.replace(/[A-Z,a-z,-]/g, ''),10); + html += "
        \n"; + } + + html += "
        \n"; + html += "
        \n"; + html += "
        \n"; + + if (horizontal) { + html += "
        \n"; + } + } + + //lookup type fields + if (field.type === 'lookup' && (field.excludeMode === undefined || field.excludeMode !== options.mode)) { + + html += label(); + + html += "
        \n"; + html += "\n"; + html += "\n"; + html += "\n"; + html += "A value is required!
        \n"; + } + html += "
        Value not found
        \n"; + html += "
        \n"; + html += "\n"; + } + + //custom fields + if (field.type === 'custom') { + html += label(); + html += "
        \n", action; + for (action in this.form.actions) { + if (this.form.actions[action].mode === 'all' || this.form.actions[action].mode === options.mode) { + html += this.button({ btn: this.form.actions[action], action: action, toolbar: true }); + } } - } - } - html += "\n"; - - if ( !this.modal && this.has('well') ) { - html += "
        \n"; - } + html += "\n"; + return html; + }, - if ( this.form.collapse && this.form.collapseMode == options.mode ) { - html += "\n"; - html += "\n"; - } - } - if ((!this.modal && this.form.items)) { - for (var itm in this.form.items) { - html += "
        \n"; - html += SearchWidget({ iterator: this.form.items[itm].iterator, template: this.form.items[itm], mini: false, label: 'Filter Events'}); - html += "
        Viewing" + " \{\{ " + this.form.items[itm].iterator + "Page + 1 \}\} of " + - "\{\{ " + this.form.items[itm].iterator + "Count \}\}
        \n"; - html += "
        \n"; - html += "\n"; - html += "
        ' + "\n"; - for (var fld in this.form.items[itm].fields) { - var field = this.form.items[itm].fields[fld]; - html += this.buildField(fld, field, options, this.form); - } - html += "
        \n"; - html += "\n"; - html += "
        \n"; - } - } - - if ((!this.modal) && options.related && this.form.related) { - html += this.buildCollections(options); - } + breadCrumbs: function (options, navigation) { - return html; - - }, + var itm, paths, html = ''; + html += "
        \n"; + html += "
          \n"; + html += "
        • {{ crumb.title }}
        • \n"; - buildCollections: function(options) { - // - // Create TB accordians with imbedded lists for related collections - // Should not be called directly. Called internally by build(). - // - var idx = 1; - var form = this.form; - var html = "
          \n"; - for (var itm in form.related) { - if (form.related[itm].type == 'collection') { - html += "

          " + form.related[itm].title + "

          \n"; - html += "
          \n"; - - if (form.related[itm].instructions) { - html += "
          \n"; - html += "\n"; - html += "Hint: " + form.related[itm].instructions + "\n"; - html += "
          \n" - } - - html += "
          \n"; - html += "
          \n"; - - html += SearchWidget({ iterator: form.related[itm].iterator, template: form.related[itm], mini: true }); - - html += "
          \n"; - html += "
          \n"; - - for (var act in form.related[itm].actions) { - var action = form.related[itm].actions[act]; - html += this.button({ btn: action, action: act, toolbar: true }); - } - - html += "
          \n"; - html += "
          \n"; - html += "
          \n" - - // Start the list - html += "
          \n"; - html += "\n"; - html += "\n"; - html += "\n"; - html += (form.related[itm].index == undefined || form.related[itm].index !== false) ? "\n" : ""; - for (var fld in form.related[itm].fields) { - html += "\n"; - } - html += "\n"; - html += "\n"; - html += ""; - html += "\n"; - html += "\n"; - if (form.related[itm].index == undefined || form.related[itm].index !== false) { - html += "\n"; - } - var cnt = 1; - var rfield; - var base = (form.related[itm].base) ? form.related[itm].base : itm; - base = base.replace(/^\//,''); - for (var fld in form.related[itm].fields) { - cnt++; - html += Column({ list: form.related[itm], fld: fld, options: options, base: base }); - } + html += "\n"; + html += "\n"; + html += "\n"; + } else { + html += "
        • "; + if (options.mode === 'edit') { + html += this.form.editTitle; + } else { + html += this.form.addTitle; + } + html += "
        • \n\n\n"; + } + return html; + }, - // Row level actions - html += "\n"; - html += "\n"; + if (!this.modal && (options.breadCrumbs === undefined || options.breadCrumbs === true)) { + if (this.form.navigationLinks) { + html += this.breadCrumbs(options, this.form.navigationLinks); + } else { + html += this.breadCrumbs(options); + } + } - // Message for loading - html += "\n"; - html += "\n"; - html += "\n"; + if ((!this.modal && this.form.statusFields)) { + // Add status fields section (used in Jobs form) + html += "
          \n"; + if (this.form.statusActions) { + html += "
          \n"; + for (action in this.form.statusActions) { + act = this.form.statusActions[action]; + html += this.button({ + btn: act, + action: action, + toolbar: true + }); + } + html += "
          \n"; + //html += "
          \n"; + } + html += "
          \n"; + for (fld in this.form.statusFields) { + field = this.form.statusFields[fld]; + html += this.buildField(fld, field, options, this.form); + } + html += "
          \n"; + html += "
          \n"; + } - // End List - html += "\n"; - html += "
          #" + - form.related[itm]['fields'][fld].label; - html += " "; + if (options.mode === 'edit') { + html += this.form.editTitle; + } else { + html += this.form.addTitle; + } + html += "\n"; } - else { - html += "icon-sort-up"; + + html += "
        • \n"; + html += "\n"; + html += "
          \n"; + for (itm in navigation) { + if (navigation[itm].active) { + html += ""; + break; + } } - } - else { - html += "icon-sort"; - } - html += "\">
          {{ $index + ((" + form.related[itm].iterator + "_page - 1) * " + - form.related[itm].iterator + "_page_size) + 1 }}."; - for (var act in form.related[itm].fieldActions) { - var fAction = form.related[itm].fieldActions[act]; - html += "\n"; - html += "
          No records matched your search.
          Loading...
          \n"; - html += "
          \n"; // close well - html += "
          \n"; // close list div + if (this.form.fieldsAsHeader) { + html += "
          \n"; + html += "
          \n"; + for (fld in this.form.fields) { + field = this.form.fields[fld]; + html += this.headerField(fld, field, options); + } + html += "
          \n"; + html += "
          \n"; + } else { - html += PaginateWidget({ set: itm, iterator: form.related[itm].iterator, mini: true }); + if (this.form.collapse && this.form.collapseMode === options.mode) { + html += "
          \n"; + html += "

          " + this.form.collapseTitle + "

          \n"; + html += "
          \n"; + } - // End Accordion - html += "
          \n"; // accordion inner + // Start the well + if (!this.modal && this.has('well')) { + html += "
          \n"; + } - idx++; + if (!this.modal && this.form.actions) { + html += this.getActions(options); + } + + // Add a title and optionally a close button (used on Inventory->Groups) + if ((!options.modal) && this.form.showTitle) { + html += "
          "; + html += (options.mode === 'edit') ? this.form.editTitle : this.form.addTitle; + if (this.has('titleActions')) { + html += "
          \n"; + for (btn in this.form.titleActions) { + html += this.button({ + btn: this.form.titleActions[btn], + action: btn, + toolbar: true + }); + } + html += "
          \n"; + } + html += "
          \n"; + html += "
          \n"; + } + + html += "
          \n"; + html += "
          {{ flashMessage }}
          \n"; + + if (this.form.twoColumns) { + html += "
          \n"; + html += "
          \n"; + for (fld in this.form.fields) { + field = this.form.fields[fld]; + if (field.column === 1) { + html += this.buildField(fld, field, options, this.form); + } + } + html += "
          \n"; + html += "
          \n"; + for (fld in this.form.fields) { + field = this.form.fields[fld]; + if (field.column === 2) { + html += this.buildField(fld, field, options, this.form); + } + } + html += "
          \n"; + html += "
          \n"; + } else if (this.form.tabs) { + html += "
            \n"; + for (i = 0; i < this.form.tabs.length; i++) { + tab = this.form.tabs[i]; + html += "" + tab.label + "\n"; + } + html += "
          \n"; + html += "
          \n"; + for (i = 0; i < this.form.tabs.length; i++) { + tab = this.form.tabs[i]; + html += "
          \n"; + for (fld in this.form.fields) { + if (this.form.fields[fld].tab === tab.name) { + html += this.buildField(fld, this.form.fields[fld], options, this.form); + } + } + html += "
          \n"; + } + html += "
          \n"; + } else { + // original, single-column form + section = ''; + group = ''; + for (fld in this.form.fields) { + field = this.form.fields[fld]; + if (!(options.modal && field.excludeModal)) { + if (field.group && field.group !== group) { + if (group !== '') { + html += "
          \n"; + } + html += "
          \n"; + html += "
          " + field.group + "
          \n"; + group = field.group; + } + if (field.section && field.section !== section) { + if (section !== '') { + html += "
          \n"; + } else { + html += "
          \n"; + html += "
          \n"; + } + sectionShow = (this.form[field.section + 'Show']) ? " ng-show=\"" + this.form[field.section + 'Show'] + "\"" : ""; + html += "" + field.section + "

          \n"; + html += "\n"; + section = field.section; + } + html += this.buildField(fld, field, options, this.form); + } + } + if (section !== '') { + html += "
          \n
        \n"; + } + if (group !== '') { + html += "\n"; + } + } + + //buttons + if (!this.modal) { + if (this.has('buttons')) { + + if (this.form.twoColumns) { + html += "
        \n"; + html += "
        \n"; + html += "
        \n"; + } + + html += "
        \n"; + } + + for (btn in this.form.buttons) { + if (typeof this.form.buttons[btn] === 'object') { + button = this.form.buttons[btn]; + + // Set default color and label for Save and Reset + if (btn === 'save') { + button.label = 'Save'; + button['class'] = 'btn-success'; + } + if (btn === 'reset') { + button.label = 'Reset'; + button['class'] = 'btn-default'; + } + + // Build button HTML + html += "
        \n"; + + if (this.form.horizontal) { + html += "
        \n"; + } + + if (this.form.twoColumns) { + html += "
        \n"; + html += "\n"; + } + } + } + html += "\n"; + + if (!this.modal && this.has('well')) { + html += "\n"; + } + + if (this.form.collapse && this.form.collapseMode === options.mode) { + html += "\n"; + html += "\n"; + } + } + + if ((!this.modal) && options.related && this.form.related) { + html += this.buildCollections(options); + } + + return html; + + }, + + buildCollections: function (options) { + // + // Create TB accordians with imbedded lists for related collections + // Should not be called directly. Called internally by build(). + // + var idx = 1, + form = this.form, + html = "
        \n", + act, fAction, fld, itm, action, cnt, base; + + for (itm in form.related) { + if (form.related[itm].type === 'collection') { + html += "

        " + form.related[itm].title + "

        \n"; + html += "
        \n"; + + if (form.related[itm].instructions) { + html += "
        \n"; + html += "\n"; + html += "Hint: " + form.related[itm].instructions + "\n"; + html += "
        \n"; + } + + html += "
        \n"; + html += "
        \n"; + + html += SearchWidget({ + iterator: form.related[itm].iterator, + template: form.related[itm], + mini: true + }); + + html += "
        \n"; + html += "
        \n"; + + for (act in form.related[itm].actions) { + action = form.related[itm].actions[act]; + html += this.button({ + btn: action, + action: act, + toolbar: true + }); + } + + html += "
        \n"; + html += "
        \n"; + html += "
        \n"; + + // Start the list + html += "
        \n"; + html += "\n"; + html += "\n"; + html += "\n"; + html += (form.related[itm].index === undefined || form.related[itm].index !== false) ? "\n" : ""; + for (fld in form.related[itm].fields) { + html += "\n"; + } + html += "\n"; + html += "\n"; + html += ""; + html += "\n"; + + html += "\n"; + if (form.related[itm].index === undefined || form.related[itm].index !== false) { + html += "\n"; + } + cnt = 1; + base = (form.related[itm].base) ? form.related[itm].base : itm; + base = base.replace(/^\//, ''); + for (fld in form.related[itm].fields) { + cnt++; + html += Column({ + list: form.related[itm], + fld: fld, + options: options, + base: base + }); + } + + // Row level actions + html += "\n"; + html += "\n"; + + // Message for loading + html += "\n"; + html += "\n"; + html += "\n"; + + // End List + html += "\n"; + html += "
        #" + + form.related[itm].fields[fld].label; + html += "
        {{ $index + ((" + form.related[itm].iterator + "_page - 1) * " + + form.related[itm].iterator + "_page_size) + 1 }}."; + for (act in form.related[itm].fieldActions) { + fAction = form.related[itm].fieldActions[act]; + html += "\n"; + html += "
        No records matched your search.
        Loading...
        \n"; + html += "
        \n"; // close well + html += "
        \n"; // close list div + + html += PaginateWidget({ + set: itm, + iterator: form.related[itm].iterator, + mini: true + }); + + // End Accordion + html += "
        \n"; // accordion inner + + idx++; + } + } + html += "

        \n"; // accordion body + html += "\n"; + + return html; } - } - html += "\n"; // accordion body - html += "\n"; - - return html; - } - -}}]); \ No newline at end of file + }; + } +]); \ No newline at end of file diff --git a/awx/ui/static/lib/ansible/generator-helpers.js b/awx/ui/static/lib/ansible/generator-helpers.js index 05398cab0c..8fb3835e99 100644 --- a/awx/ui/static/lib/ansible/generator-helpers.js +++ b/awx/ui/static/lib/ansible/generator-helpers.js @@ -3,593 +3,589 @@ * * GeneratorHelpers * - * Functions shared between FormGenerator and ListGenerator - * + * Functions shared between FormGenerator and ListGenerator + * */ 'use strict'; angular.module('GeneratorHelpers', ['GeneratorHelpers']) - - .factory('Attr', function() { - return function(obj, key, fld) { - var result; - var value = (typeof obj[key] === "string") ? obj[key].replace(/[\'\"]/g, '"') : obj[key]; + +.factory('Attr', function () { + return function (obj, key, fld) { + var i, s, result, + value = (typeof obj[key] === "string") ? obj[key].replace(/[\'\"]/g, '"') : obj[key]; if (/^ng/.test(key)) { - result = 'ng-' + key.replace(/^ng/,'').toLowerCase() + "=\"" + value + "\" "; - } - else if (/^data|^aw/.test(key) && key != 'awPopOver') { - var s = ''; - for (var i=0; i < key.length; i++) { + result = 'ng-' + key.replace(/^ng/, '').toLowerCase() + "=\"" + value + "\" "; + } else if (/^data|^aw/.test(key) && key !== 'awPopOver') { + s = ''; + for (i = 0; i < key.length; i++) { if (/[A-Z]/.test(key.charAt(i))) { s += '-' + key.charAt(i).toLowerCase(); - } - else { + } else { s += key.charAt(i); } } result = s + "=\"" + value + "\" "; - } - else { - switch(key) { - case 'trueValue': - result = "ng-true-value=\"" + value + "\" "; - break; - case 'falseValue': - result = "ng-false-value=\"" + value + "\" "; - break; - case 'awPopOver': - // construct the entire help link - result = " "; - break; - case 'columnShow': - result = "ng-show=\"" + value + "\" "; - break; - case 'icon': - // new method of constructing icon tag. Replces Icon method. - result = ""; - break; - case 'autocomplete': - result = "autocomplete=\""; - result += (value) ? 'true' : 'false'; - result += "\" "; - break; - default: - result = key + "=\"" + value + "\" "; + } else { + switch (key) { + case 'trueValue': + result = "ng-true-value=\"" + value + "\" "; + break; + case 'falseValue': + result = "ng-false-value=\"" + value + "\" "; + break; + case 'awPopOver': + // construct the entire help link + result = " "; + break; + case 'columnShow': + result = "ng-show=\"" + value + "\" "; + break; + case 'icon': + // new method of constructing icon tag. Replces Icon method. + result = ""; + break; + case 'autocomplete': + result = "autocomplete=\""; + result += (value) ? 'true' : 'false'; + result += "\" "; + break; + default: + result = key + "=\"" + value + "\" "; } } - return result; - - } - }) + return result; - .factory('Icon', function() { - return function(icon) { + }; +}) + +.factory('Icon', function () { + return function (icon) { return " "; - } - }) + }; +}) - .factory('SelectIcon', ['Icon', function(Icon) { - return function(params) { - // Common point for matching any type of action to the appropriate - // icon. The intention is to maintain consistent meaning and presentation - // for every icon used in the application. - var icon; - var action = params.action; - var size = params.size; - switch(action) { - case 'help': - icon = "fa-question-circle"; +.factory('SelectIcon', ['Icon', + function (Icon) { + return function (params) { + // Common point for matching any type of action to the appropriate + // icon. The intention is to maintain consistent meaning and presentation + // for every icon used in the application. + var icon, + action = params.action, + size = params.size; + switch (action) { + case 'help': + icon = "fa-question-circle"; break; case 'add': - case 'create': - icon= "fa-plus"; + case 'create': + icon = "fa-plus"; break; case 'edit': icon = "fa-pencil"; - break; + break; case 'delete': - icon = "fa-trash-o"; - break; + icon = "fa-trash-o"; + break; case 'group_update': icon = 'fa-exchange'; break; - case 'scm_update': + case 'scm_update': icon = 'fa-cloud-download'; break; case 'cancel': icon = 'fa-minus-circle'; break; case 'run': - case 'rerun': + case 'rerun': case 'submit': icon = 'fa-rocket'; break; - case 'stream': + case 'stream': icon = 'fa-clock-o'; break; case 'refresh': icon = 'fa-refresh'; - break; - case 'close': - icon='fa-arrow-left'; break; - case 'save': - icon='fa-check-square-o'; + case 'close': + icon = 'fa-arrow-left'; + break; + case 'save': + icon = 'fa-check-square-o'; break; case 'properties': - icon="fa-wrench"; + icon = "fa-wrench"; break; - case 'reset': - icon="fa-undo"; + case 'reset': + icon = "fa-undo"; break; case 'view': - icon="fa-search-plus"; + icon = "fa-search-plus"; break; case 'sync_status': - icon="fa-cloud"; - break; + icon = "fa-cloud"; + break; } - icon += (size) ? " " + size : ""; - return Icon(icon); - } - }]) + icon += (size) ? " " + size : ""; + return Icon(icon); + }; + } +]) - .factory('Button', ['Attr', 'SelectIcon', function(Attr, SelectIcon) { - return function(params) { - - // pass in button object, get back html +.factory('Button', ['Attr', 'SelectIcon', + function (Attr, SelectIcon) { + return function (params) { - var btn = params.btn; - var action = params.action; // label used to select the icon - var toolbar = params.toolbar; - - if (toolbar) { - //if this is a toolbar button, set some defaults - btn['class'] = 'btn-xs btn-primary'; - btn['iconSize'] = 'fa-lg'; - delete btn['label']; - } - - var html = ''; - - html += " "; - - return html; - } - }]) - - - .factory('NavigationLink', ['Attr', 'Icon', function(Attr, Icon) { - return function(link) { - var html = "\n"; - } - else { - html = ''; - } - - html += "
        \n"; - html += "\n"; - html += "\n"; - html += "
        \n"; - html += (params.td == undefined || params.td !== false) ? "\n" : ""; - - return html; - - } - }]) - - .factory('BadgeCount', [ function() { - return function(params) { - - // Adds a badge count with optional tooltip - - var list = params['list']; - var fld = params['fld']; - var field = list.fields[fld]; - var options = params['options']; - var base = params['base']; - var html = "\n"; - html += ""; - html += "\{\{ " + list.iterator + '.' + fld + " \}\}"; - html += ""; - html += (field.badgeLabel) ? " " + field.badgeLabel : ""; - html += "\n"; - html += "\n"; - return html; - } - }]) - - .factory('Badge', [ function() { - return function(field) { - - // Adds an icon(s) with optional tooltip - - var html = ''; - - if (field.badges) { - for (var i=0; i < field.badges.length; i++) { - if (field.badges[i].toolTip) { - html += "
        "; - if (field.badges[i].toolTip) { - html += ""; - } - html += "\n"; - } - } - else { - if (field.badgeToolTip) { - html += ""; - if (field.badgeToolTip) { - html += ""; - } - html += "\n"; - } - - return html; - - } - }]) - - .factory('Breadcrumbs', ['Attr', function(Attr) { - return function(params) { - - // Generate breadcrumbs using the list-generator.js method. - - var list = params.list; - var mode = params.mode; - var html = ''; - - html += "
        \n"; - html += "
          \n"; - html += "
        • {{ crumb.title }}
        • \n"; - - if (list.navigationLinks) { - var navigation = list.navigationLinks; - if (navigation['ngHide']) { - html += "
        • "; - html += list.editTitle; - html += "
        • \n"; - html += "
        • \n"; + if (toolbar) { + //if this is a toolbar button, set some defaults + btn.class = 'btn-xs btn-primary'; + btn.iconSize = 'fa-lg'; + delete btn.label; } - else { - html += "
        • \n"; - html += "
        \n"; + + html += " "; + + return html; + }; + } +]) + + +.factory('NavigationLink', ['Attr', 'Icon', + function (Attr, Icon) { + return function (link) { + var html = "\n"; + } else { + html = ''; + } + + html += "
        \n"; + html += "\n"; + html += "\n"; - html += "
        \n"; - html += "
        \n"; - } - else { - html += "
      • "; - if (mode == 'select') { - html += list.selectTitle; + html += "\n"; + html += (params.td === undefined || params.td !== false) ? "\n" : ""; + + return html; + + }; + } +]) + +.factory('BadgeCount', [ + function () { + return function (params) { + + // Adds a badge count with optional tooltip + + var list = params.list, + fld = params.fld, + field = list.fields[fld], + html; + + html = "\n"; + html += ""; + html += "{{ " + list.iterator + '.' + fld + " }}"; + html += ""; + html += (field.badgeLabel) ? " " + field.badgeLabel : ""; + html += "\n"; + html += "\n"; + return html; + }; + } +]) + +.factory('Badge', [ + function () { + return function (field) { + + // Adds an icon(s) with optional tooltip + + var i, html = ''; + + if (field.badges) { + for (i = 0; i < field.badges.length; i++) { + if (field.badges[i].toolTip) { + html += ""; + if (field.badges[i].toolTip) { + html += ""; + } + html += "\n"; + } + } else { + if (field.badgeToolTip) { + html += ""; + if (field.badgeToolTip) { + html += ""; + } + html += "\n"; } - else { - html += list.editTitle; + return html; + }; + } +]) + +.factory('Breadcrumbs', ['Attr', + function (Attr) { + return function (params) { + + // Generate breadcrumbs using the list-generator.js method. + + var list = params.list, + mode = params.mode, + html = '', itm, navigation; + + html += "
        \n"; + html += "
          \n"; + html += "
        • {{ crumb.title }}
        • \n"; + + if (list.navigationLinks) { + navigation = list.navigationLinks; + if (navigation.ngHide) { + html += "
        • "; + html += list.editTitle; + html += "
        • \n"; + html += "
        • \n"; + } else { + html += "
        • \n"; + html += "
        \n"; + } + html += "
        "; + break; + } + } + html += "\n"; + html += "
        \n"; + html += "
        \n"; + } else { + html += "
      • "; + if (mode === 'select') { + html += list.selectTitle; + } else { + html += list.editTitle; + } + html += "
      • \n\n\n"; } - html += "\n\n\n"; - } + + return html; + + }; + } +]) + +.factory('Column', ['Attr', 'Icon', 'DropDown', 'Badge', 'BadgeCount', + function (Attr, Icon, DropDown, Badge, BadgeCount) { + return function (params) { + var list = params.list, + fld = params.fld, + options = params.options, + base = params.base, + field = list.fields[fld], + html = '', cap; + + if (field.type !== undefined && field.type === 'DropDown') { + html = DropDown(params); + } else if (field.type === 'badgeCount') { + html = BadgeCount(params); + } else { + html += "" : ""; + + // Badge + if (options.mode !== 'lookup' && (field.badges || (field.badgeIcon && field.badgePlacement && field.badgePlacement === 'left'))) { + html += Badge(field); + } + + // Add collapse/expand icon --used on job_events page + if (list.hasChildren && field.hasChildren) { + html += ""; + //ng-show=\"'\{\{ " + list.iterator + ".related.children \}\}' !== ''\" + } + + if (list.name === 'groups') { + html += "
        "; + } + if (list.name === 'hosts') { + html += ""; + } + + // close ngShow + html += (field.ngShow) ? "" : ""; + + // Specific to Job Events page -showing event detail/results + html += (field.appendHTML) ? "
        \n" : ""; + + // Badge + if (options.mode !== 'lookup' && field.badgeIcon && field.badgePlacement && field.badgePlacement !== 'left') { + html += Badge(field); + } + } + return html += "\n"; + }; + } +]) + +.factory('HelpCollapse', function () { + return function (params) { - return html; - - } - }]) - - .factory('Column', ['Attr', 'Icon', 'DropDown', 'Badge', 'BadgeCount', function(Attr, Icon, DropDown, Badge, BadgeCount) { - return function(params) { - var list = params['list']; - var fld = params['fld']; - var options = params['options']; - var base = params['base']; - - var field = list['fields'][fld]; - var html = ''; + var hdr = params.hdr, + content = params.content, + show = params.show, + idx = params.idx, + bind = params.bind, + html = ''; - if (field.type !== undefined && field.type == 'DropDown') { - html = DropDown(params); - } - else if (field.type == 'badgeCount') { - html = BadgeCount(params); - } - else { - html += "" : ""; - - // Badge - if ( options.mode !== 'lookup' && (field.badges || (field.badgeIcon && field.badgePlacement && field.badgePlacement == 'left')) ) { - html += Badge(field); - } - - // Add collapse/expand icon --used on job_events page - if (list['hasChildren'] && field.hasChildren) { - html += ""; - //ng-show=\"'\{\{ " + list.iterator + ".related.children \}\}' !== ''\" - } - - if (list.name == 'groups') { - html += "
        "; - } - if (list.name == 'hosts') { - html += ""; - } - - // close ngShow - html += (field.ngShow) ? "" : ""; - - // Specific to Job Events page -showing event detail/results - html += (field.appendHTML) ? "
        \n" : ""; - - // Badge - if (options.mode !== 'lookup' && field.badgeIcon && field.badgePlacement && field.badgePlacement !== 'left') { - html += Badge(field); - } - } - - return html += "\n"; - - } - }]) - - .factory('HelpCollapse', function() { - return function(params) { - var hdr = params.hdr; - var content = params.content; - var show = params.show; - var idx = params.idx; - var bind = params.bind; //Pass in scope variable containing html - var html = ''; html += "
        \n"; - html += "
        \n"; - + html += "
        \n"; - html += "
        \n"; + html += "
        \n"; html += "\n"; html += "
          \n"; - for ( var fld in form.fields) { - if ( (form.fields[fld].searchable == undefined || form.fields[fld].searchable == true) - && (((form.fields[fld].searchWidget == undefined || form.fields[fld].searchWidget == 1) && i == 1 ) || - (form.fields[fld].searchWidget == i)) ) { - html += "
        • " + - form.fields[fld].searchLabel + "
        • \n"; - } - else { - html += form.fields[fld].label.replace(/\/g,' ') + "', " + i + ")\">" + - form.fields[fld].label.replace(/\/g,' ') + "\n"; - } + for (fld in form.fields) { + if ((form.fields[fld].searchable === undefined || form.fields[fld].searchable === true) && + (((form.fields[fld].searchWidget === undefined || form.fields[fld].searchWidget === 1) && i === 1) || + (form.fields[fld].searchWidget === i))) { + html += "
        • " + + form.fields[fld].searchLabel + "
        • \n"; + } else { + html += form.fields[fld].label.replace(/
          /g, ' ') + "', " + i + ")\">" + + form.fields[fld].label.replace(/
          /g, ' ') + "\n"; + } } } html += "
        \n"; html += "
        \n"; - - html += "\n"; - - html += "\n"; - + // Reset button for drop-down html += "
        \n"; html += "
        \n"; + html += "\n"; + + html += "
        \n"; } return html; - - } - }) - - .factory('PaginateWidget', [ function() { - return function(params) { - var iterator = params.iterator; - var set = params.set; - var html = ''; - html += "\n"; - html += "
        \n"; - html += "
        \n"; - html += "
          \n"; - html += "
        • " + - "
        • \n"; - html += "
        • " + - "
        • \n"; - html += "
        • " + - "{{ page }}
        • \n"; - html += "
        • " + iterator + "_num_pages\">
        • \n"; - html += "
        • = " + iterator + "_num_pages\">
        • \n"; - html += "
        \n"; - html += "
        \n"; - html += "
        \n"; - html += "
        \n"; - html += "Page {{ " + iterator + "_page }} of {{ " + iterator + "_num_pages }} for {{ " + iterator + "_total_rows | number:0 }} " + set + '.'; - html += "
        \n"; - html += "
        \n"; - html += "
        \n"; - return html; - } - }]); - + }; +}) +.factory('PaginateWidget', [ + function () { + return function (params) { + var iterator = params.iterator, + set = params.set, + html = ''; + html += "\n"; + html += "
        \n"; + html += "
        \n"; + html += "
          \n"; + html += "
        • " + + "
        • \n"; + html += "
        • " + + "
        • \n"; + html += "
        • " + + "{{ page }}
        • \n"; + html += "
        • " + iterator + "_num_pages\">
        • \n"; + html += "
        • = " + iterator + "_num_pages\">
        • \n"; + html += "
        \n"; + html += "
        \n"; + html += "
        \n"; + html += "
        \n"; + html += "Page {{ " + iterator + "_page }} of {{ " + iterator + "_num_pages }} for {{ " + iterator + "_total_rows | number:0 }} " + set + '.'; + html += "
        \n"; + html += "
        \n"; + html += "
        \n"; + + return html; + }; + } +]); \ No newline at end of file diff --git a/awx/ui/static/lib/ansible/license.js b/awx/ui/static/lib/ansible/license.js index ebf08eec95..42b20a3c7c 100644 --- a/awx/ui/static/lib/ansible/license.js +++ b/awx/ui/static/lib/ansible/license.js @@ -9,200 +9,129 @@ 'use strict'; -angular.module('License', ['RestServices', 'Utilities', 'FormGenerator', 'PromptDialog']) - .factory('ViewLicense', ['$location', '$rootScope', 'GenerateForm', 'Rest', 'Alert', 'GetBasePath', 'ProcessErrors', - 'FormatDate', 'Prompt', 'Empty', - function($location, $rootScope, GenerateForm, Rest, Alert, GetBasePath, ProcessErrors, FormatDate, Prompt, Empty) { - return function() { - - var defaultUrl=GetBasePath('config'); - var generator = GenerateForm; - - var form = { - name: 'license', - well: false, - forceListeners: true, - fields: { - license_status: { - type: 'custom', - control: '
        \{\{ license_status \}\}
        ', - readonly: true, - section: 'License' - }, - license_key: { - label: 'Key', - type: 'textarea', - 'class': 'modal-input-xlarge', - readonly: true, - section: 'License' - }, - license_date: { - label: 'Expires On', - type: 'text', - readonly: true, - section: 'License' - }, - time_remaining: { - label: 'Time Left', - type: 'text', - readonly: true, - section: 'License' - }, - available_instances: { - label: 'Available', - type: 'text', - readonly: true, - section: 'Managed Hosts' - }, - current_instances: { - label: 'Used', - type: 'text', - readonly: true, - section: 'Managed Hosts' - }, - free_instances: { - label: 'Remaining', - type: 'text', - readonly: true, - section: 'Managed Hosts', - controlNGClass: 'free_instances_class', - labelNGClass: 'free_instances_class' - }, - company_name: { - label: 'Company', - type: 'text', - readonly: true, - section: 'Contact Info' - }, - contact_name: { - label: 'Contact', - type: 'text', - readonly: true, - section: 'Contact Info' - }, - contact_email: { - label: 'Contact Email', - type: 'text', - readonly: true, - section: 'Contact Info' - } - } - }; +angular.module('License', ['RestServices', 'Utilities', 'FormGenerator', 'PromptDialog', 'LicenseFormDefinition']) + .factory('ViewLicense', ['$location', '$rootScope', 'GenerateForm', 'Rest', 'Alert', 'GetBasePath', 'ProcessErrors', + 'FormatDate', 'Prompt', 'Empty', 'LicenseForm', + function ($location, $rootScope, GenerateForm, Rest, Alert, GetBasePath, ProcessErrors, FormatDate, Prompt, Empty, + LicenseForm) { + return function () { - var base = $location.path().replace(/^\//,'').split('/')[0]; - - - // Retrieve detail record and prepopulate the form - Rest.setUrl(defaultUrl); - Rest.get() - .success( function(data, status, headers, config) { - - for (var fld in form.fields) { - if (fld != 'time_remaining' && fld != 'license_status') { - if (Empty(data['license_info'][fld])) { - delete form.fields[fld]; - } - } - } + var defaultUrl = GetBasePath('config'), + generator = GenerateForm, + form = angular.copy(LicenseForm), + scope; - if (data['license_info']['is_aws'] || Empty(data['license_info']['license_date'])) { - delete form.fields['license_date']; - delete form.fields['time_remaining']; - } + // Retrieve detail record and prepopulate the form + Rest.setUrl(defaultUrl); + Rest.get() + .success(function (data) { - var scope = generator.inject(form, { mode: 'edit', modal: true, related: false}); - generator.reset(); - - scope.formModalAction = function() { - $('#form-modal').modal("hide"); - } - - scope.formModalActionLabel = 'OK'; - scope.formModalCancelShow = false; - scope.formModalInfo = 'Purchase/Extend License'; - scope.formModalHeader = 'Tower License'; + var fld, dt, days, remainder, hours, minutes, seconds, license; + + for (fld in form.fields) { + if (fld !== 'time_remaining' && fld !== 'license_status') { + if (Empty(data.license_info[fld])) { + delete form.fields[fld]; + } + } + } - //$('#form-modal .btn-success').removeClass('btn-success').addClass('btn-none'); - //$('#form-modal').addClass('skinny-modal'); - - - // Respond to license button - scope.formModalInfoAction = function() { - Prompt({ - hdr: 'Tower Licensing', - body: "

        Ansible Tower licenses can be purchased or extended by visiting " + - "the Ansible online store. Would you like to purchase or extend your license now?

        ", - 'class': 'btn-primary', - action: function() { - var href = $('#license-link').attr('href'); - window.open(href, 'storeWindow'); - } - }); - } + if (data.license_info.is_aws || Empty(data.license_info.license_date)) { + delete form.fields.license_date; + delete form.fields.time_remaining; + } - for (var fld in form.fields) { - if (!Empty(data['license_info'][fld])) { - scope[fld] = data['license_info'][fld]; - } - } - - if (scope['license_date']) { - var dt = new Date(parseInt(scope['license_date'])); - if (dt.getFullYear() == '1970') { - // date was passed in seconds rather than milliseconds - dt = new Date(parseInt(scope['license_date']) * 1000); - scope['time_remaining'] = scope['time_remaining'] + '000'; - } - scope['license_date'] = FormatDate(dt); + scope = generator.inject(form, { mode: 'edit', modal: true, related: false }); + generator.reset(); - var days = parseInt(scope['time_remaining'] / 86400000); - var remainder = scope['time_remaining'] - (days * 86400000); - var hours = parseInt(remainder / 3600000); - remainder = remainder - (hours * 3600000); - var minutes = parseInt(remainder / 60000); - remainder = remainder - (minutes * 60000); - var seconds = parseInt(remainder / 1000); - scope['time_remaining'] = days + ' days ' + ('0' + hours).slice(-2) + ':' + ('0' + minutes).slice(-2) + ':' + ('0' + seconds).slice(-2); - } + scope.formModalAction = function () { + $('#form-modal').modal("hide"); + }; - if (parseInt(scope['free_instances']) <= 0) { - scope['free_instances_class'] = 'field-failure'; - } - else { - scope['free_instances_class'] = 'field-success'; - } + scope.formModalActionLabel = 'OK'; + scope.formModalCancelShow = false; + scope.formModalInfo = 'Purchase/Extend License'; + scope.formModalHeader = 'Tower License'; - var license = data['license_info']; - if (license['valid_key'] !== undefined && license['valid_key'] == false) { - scope['license_status'] = 'Invalid'; - scope['status_color'] = 'license-invalid'; - } - else if (license['demo'] !== undefined && license['demo'] == true) { - scope['license_status'] = 'Demo'; - scope['status_color'] = 'license-demo'; - } - else if (license['date_expired'] !== undefined && license['date_expired'] == true) { - scope['license_status'] = 'Expired'; - scope['status_color'] = 'license-expired'; - } - else if (license['date_warning'] !== undefined && license['date_warning'] == true) { - scope['license_status'] = 'Expiration Warning'; - scope['status_color'] = 'license-warning'; - } - else if (license['free_instances'] !== undefined && parseInt(license['free_instances']) <= 0) { - scope['license_status'] = 'No available managed hosts'; - scope['status_color'] = 'license-invalid'; - } - else { - scope['license_status'] = 'Valid'; - scope['status_color'] = 'license-valid'; - } + //$('#form-modal .btn-success').removeClass('btn-success').addClass('btn-none'); + //$('#form-modal').addClass('skinny-modal'); - }) - .error( function(data, status, headers, config) { - ProcessErrors($rootScope, data, status, form, - { hdr: 'Error!', msg: 'Failed to retrieve license. GET status: ' + status }); - }); - } - }]); + + // Respond to license button + scope.formModalInfoAction = function () { + Prompt({ + hdr: 'Tower Licensing', + body: "

        Ansible Tower licenses can be purchased or extended by visiting " + + "the Ansible online store. Would you like to purchase or extend your license now?

        ", + 'class': 'btn-primary', + action: function () { + var href = $('#license-link').attr('href'); + window.open(href, 'storeWindow'); + } + }); + }; + + for (fld in form.fields) { + if (!Empty(data.license_info[fld])) { + scope[fld] = data.license_info[fld]; + } + } + + if (scope.license_date) { + dt = new Date(parseInt(scope.license_date)); + if (dt.getFullYear() === '1970') { + // date was passed in seconds rather than milliseconds + dt = new Date(parseInt(scope.license_date,10) * 1000); + scope.time_remaining = scope.time_remaining + '000'; + } + scope.license_date = FormatDate(dt); + + days = parseInt(scope.time_remaining / 86400000, 10); + remainder = scope.time_remaining - (days * 86400000); + hours = parseInt(remainder / 3600000, 10); + remainder = remainder - (hours * 3600000); + minutes = parseInt(remainder / 60000, 10); + remainder = remainder - (minutes * 60000); + seconds = parseInt(remainder / 1000, 10); + scope.time_remaining = days + ' days ' + ('0' + hours).slice(-2) + ':' + ('0' + minutes).slice(-2) + ':' + + ('0' + seconds).slice(-2); + } + + if (parseInt(scope.free_instances) <= 0) { + scope.free_instances_class = 'field-failure'; + } else { + scope.free_instances_class = 'field-success'; + } + + license = data.license_info; + if (license.valid_key !== undefined && !license.valid_key) { + scope.license_status = 'Invalid'; + scope.status_color = 'license-invalid'; + } else if (license.demo !== undefined && license.demo) { + scope.license_status = 'Demo'; + scope.status_color = 'license-demo'; + } else if (license.date_expired !== undefined && license.date_expired) { + scope.license_status = 'Expired'; + scope.status_color = 'license-expired'; + } else if (license.date_warning !== undefined && license.date_warning) { + scope.license_status = 'Expiration Warning'; + scope.status_color = 'license-warning'; + } else if (license.free_instances !== undefined && parseInt(license.free_instances) <= 0) { + scope.license_status = 'No available managed hosts'; + scope.status_color = 'license-invalid'; + } else { + scope.license_status = 'Valid'; + scope.status_color = 'license-valid'; + } + + }) + .error(function (data, status) { + ProcessErrors($rootScope, data, status, form, { + hdr: 'Error!', + msg: 'Failed to retrieve license. GET status: ' + status + }); + }); + }; + } + ]); \ No newline at end of file diff --git a/awx/ui/static/lib/ansible/list-generator.js b/awx/ui/static/lib/ansible/list-generator.js index da7d625966..4ba4d9fa7a 100644 --- a/awx/ui/static/lib/ansible/list-generator.js +++ b/awx/ui/static/lib/ansible/list-generator.js @@ -1,7 +1,7 @@ /********************************************* * Copyright (c) 2014 AnsibleWorks, Inc. * - * ListGenerator + * ListGenerator * Pass in a list definition from ListDefinitions and out pops an html template. * Use inject method to generate the html and inject into the current view. * @@ -10,412 +10,439 @@ 'use strict'; angular.module('ListGenerator', ['GeneratorHelpers']) - .factory('GenerateList', [ '$location', '$compile', '$rootScope', 'SearchWidget', 'PaginateWidget', 'Attr', 'Icon', + .factory('GenerateList', ['$location', '$compile', '$rootScope', 'SearchWidget', 'PaginateWidget', 'Attr', 'Icon', 'Column', 'DropDown', 'NavigationLink', 'Button', 'SelectIcon', 'Breadcrumbs', -function($location, $compile, $rootScope, SearchWidget, PaginateWidget, Attr, Icon, Column, DropDown, NavigationLink, Button, SelectIcon, - Breadcrumbs) { - return { - - setList: function(list) { - this.list = list; - }, - - attr: Attr, + function ($location, $compile, $rootScope, SearchWidget, PaginateWidget, Attr, Icon, Column, DropDown, NavigationLink, Button, SelectIcon, + Breadcrumbs) { + return { - icon: Icon, + setList: function (list) { + this.list = list; + }, - has: function(key) { - return (this.form[key] && this.form[key] !== null && this.form[key] !== undefined) ? true : false; - }, + attr: Attr, - hide: function() { - $('#lookup-modal').modal('hide'); - }, + icon: Icon, - button: Button, + has: function (key) { + return (this.form[key] && this.form[key] !== null && this.form[key] !== undefined) ? true : false; + }, - inject: function(list, options) { - // options.mode = one of edit, select or lookup - // - // Modes edit and select will inject the list as html into element #htmlTemplate. - // 'lookup' mode injects the list html into #lookup-modal-body. - // - // For options.mode == 'lookup', include the following: - // - // hdr: - // - // Inject into a custom element using options.id: <'.selector'> - // Control breadcrumb creation with options.breadCrumbs: - // - var element; + hide: function () { + $('#lookup-modal').modal('hide'); + }, - if (options.mode === 'lookup') { - element = angular.element(document.getElementById('lookup-modal-body')); - } - else if (options.id) { - element = angular.element(document.getElementById(options.id)); - } - else { - element = angular.element(document.getElementById('htmlTemplate')); - } - this.setList(list); - element.html(this.build(options)); // Inject the html - if (options.prepend) { // Add any extra HTML passed in options - element.prepend(options.prepend); - } - if (options.append) { - element.append(options.prepend); - } - - if (options.scope) { - this.scope = options.scope; - } - else { - this.scope = element.scope(); // Set scope specific to the element we're compiling, avoids circular reference - } // From here use 'scope' to manipulate the form, as the form is not in '$scope' - - $compile(element)(this.scope); + button: Button, - // Reset the scope to prevent displaying old data from our last visit to this list - this.scope[list.name] = null; - this.scope[list.iterator] = null; + inject: function (list, options) { + // options.mode = one of edit, select or lookup + // + // Modes edit and select will inject the list as html into element #htmlTemplate. + // 'lookup' mode injects the list html into #lookup-modal-body. + // + // For options.mode == 'lookup', include the following: + // + // hdr: + // + // Inject into a custom element using options.id: <'.selector'> + // Control breadcrumb creation with options.breadCrumbs: + // + var element; - // Remove any lingering tooltip and popover
        elements - $('.tooltip').each( function(index) { - $(this).remove(); - }); - - $('.popover').each(function(index) { - // remove lingering popover
        . Seems to be a bug in TB3 RC1 - $(this).remove(); - }); - $(window).unbind('resize'); - - try { - $('#help-modal').empty().dialog('destroy'); - } - catch(e) { - //ignore any errors should the dialog not be initialized - } - - if (options.mode === 'lookup') { - // options should include {hdr: , action: } - this.scope.formModalActionDisabled = false; - this.scope.lookupHeader = options.hdr; - $('#lookup-modal').modal({ backdrop: 'static', keyboard: true }); - $('#lookup-modal').unbind('hidden.bs.modal'); - $(document).bind('keydown', function(e) { - if (e.keyCode === 27) { - $('#lookup-modal').modal('hide'); + if (options.mode === 'lookup') { + element = angular.element(document.getElementById('lookup-modal-body')); + } else if (options.id) { + element = angular.element(document.getElementById(options.id)); + } else { + element = angular.element(document.getElementById('htmlTemplate')); + } + this.setList(list); + element.html(this.build(options)); // Inject the html + if (options.prepend) { // Add any extra HTML passed in options + element.prepend(options.prepend); + } + if (options.append) { + element.append(options.prepend); } - }); - } - - return this.scope; - }, - build: function(options) { - // - // Generate HTML. Do NOT call this function directly. Called by inject(). Returns an HTML - // string to be injected into the current view. - // - var html = ''; - var list = this.list; + if (options.scope) { + this.scope = options.scope; + } else { + this.scope = element.scope(); // Set scope specific to the element we're compiling, avoids circular reference + } // From here use 'scope' to manipulate the form, as the form is not in '$scope' - if (options.activityStream) { - // Breadcrumbs for activity stream widget - // Make the links clickable using ng-click function so we can first remove the stream widget - // before navigation - html += "
        \n"; - html += "
          \n"; - html += "
        • {{ crumb.title }}
        • \n"; - html += "
        • "; - html += list.editTitle; - html += "
        • \n
        \n
        \n"; - } - else if (options.mode != 'lookup' && (options.breadCrumbs == undefined || options.breadCrumbs == true)) { - //Breadcrumbs - html += Breadcrumbs({ list: list, mode: options.mode }); - } - - if (options.mode == 'edit' && list.editInstructions) { - html += "
        \n"; - html += "\n"; - html += "Hint: " + list.editInstructions + "\n"; - html += "
        \n"; - } - - if (options.mode != 'lookup' && (list.well == undefined || list.well == true)) { - html += "
        \n"; - } - - if (options.activityStream) { - // Add a title row - html += "
        \n"; - html += "
        \n"; - html += "
        {{ streamTitle }}
        \n"; - html += "
        \n"; - html += "
        \n"; - } + $compile(element)(this.scope); - html += "
        \n"; - - if (list.name != 'groups') { - if (options.searchSize) { - html += SearchWidget({ iterator: list.iterator, template: list, mini: true , size: options.searchSize, - searchWidgets: list.searchWidgets }); - } - else if (options.mode == 'summary') { - html += SearchWidget({ iterator: list.iterator, template: list, mini: true , size: 'col-lg-6' }); - } - else if (options.mode == 'lookup' || options.id != undefined) { - html += SearchWidget({ iterator: list.iterator, template: list, mini: true , size: 'col-lg-8' }); - } - else { - html += SearchWidget({ iterator: list.iterator, template: list, mini: true }); - } - } + // Reset the scope to prevent displaying old data from our last visit to this list + this.scope[list.name] = null; + this.scope[list.iterator] = null; - if (options.mode != 'lookup') { - //actions - var base = $location.path().replace(/^\//,'').split('/')[0]; - html += "
        \n"; - - html += "
        \n"; - - // Add toolbar buttons or 'actions' - for (var action in list.actions) { - if (list.actions[action].mode == 'all' || list.actions[action].mode == options.mode) { - if ( (list.actions[action].basePaths == undefined) || - (list.actions[action].basePaths && list.actions[action].basePaths.indexOf(base) > -1) ) { - html += this.button({ btn: list.actions[action], action: action, toolbar: true }); - } - } - } + // Remove any lingering tooltip and popover
        elements + $('.tooltip').each(function() { + $(this).remove(); + }); - //select instructions - if (options.mode == 'select' && list.selectInstructions) { - var btn = { - awPopOver: list.selectInstructions, - dataPlacement: 'top', - dataContainer: 'body', - 'class': 'btn-xs btn-help', - awToolTip: 'Click for help', - dataTitle: 'Help', - iconSize: 'fa-lg' - }; - //html += this.button(btn, 'select'); - html += this.button({ btn: btn, action: 'help', toolbar: true }); - } + $('.popover').each(function() { + // remove lingering popover
        . Seems to be a bug in TB3 RC1 + $(this).remove(); + }); + $(window).unbind('resize'); - html += "
        \n"; - html += "
        \n"; - } - else { - //lookup - html += "
        \n"; - } + try { + $('#help-modal').empty().dialog('destroy'); + } catch (e) { + //ignore any errors should the dialog not be initialized + } - html += "
        \n"; - - // Add a title and optionally a close button (used on Inventory->Groups) - if (options.mode !== 'lookup' && list.showTitle) { - html += "
        "; - html += (options.mode == 'edit' || options.mode == 'summary') ? list.editTitle : list.addTitle; - html += "
        \n"; - } + if (options.mode === 'lookup') { + // options should include {hdr: , action: } + this.scope.formModalActionDisabled = false; + this.scope.lookupHeader = options.hdr; + $('#lookup-modal').modal({ + backdrop: 'static', + keyboard: true + }); + $('#lookup-modal').unbind('hidden.bs.modal'); + $(document).bind('keydown', function (e) { + if (e.keyCode === 27) { + $('#lookup-modal').modal('hide'); + } + }); + } - // table header row - html += "#\n"; - } - for (var fld in list.fields) { - if ( (list.fields[fld].searchOnly == undefined || list.fields[fld].searchOnly == false) && - !(options.mode == 'lookup' && list.fields[fld].excludeModal !== undefined && list.fields[fld].excludeModal == true) ) { - html += "\n"; - } - } - if (options.mode == 'select' || options.mode == 'lookup') { - html += ""; - } - else if (options.mode == 'edit' && list.fieldActions) { - html += "\n"; - } - html += "\n"; - html += "\n"; - - // table body - html += "\n"; - html += "{{ $index + ((" + list.iterator + "_page - 1) * " + list.iterator + "_page_size) + 1 }}.\n"; - } - var cnt = 2; - var base = (list.base) ? list.base : list.name; - base = base.replace(/^\//,''); - for (fld in list.fields) { - cnt++; - if ( (list.fields[fld].searchOnly == undefined || list.fields[fld].searchOnly == false) && - !(options.mode == 'lookup' && list.fields[fld].excludeModal !== undefined && list.fields[fld].excludeModal == true) ) { - html += Column({ list: list, fld: fld, options: options, base: base }); - } - } - - if (options.mode == 'select' || options.mode == 'lookup') { - html += ""; - } - else if ((options.mode == 'edit' || options.mode == 'summary') && list.fieldActions) { - - // Row level actions - - html += ""; - } - html += "\n"; - - // Message for when a collection is empty - html += "\n"; - html += "\n"; - html += "\n"; - - // Message for loading - html += "\n"; - html += "\n"; - html += "\n"; - - // End List - html += "\n"; - html += "
        \n"; + html += "
          \n"; + html += "
        • {{ crumb.title }}
        • \n"; + html += "
        • "; + html += list.editTitle; + html += "
        • \n
        \n\n"; + } else if (options.mode !== 'lookup' && (options.breadCrumbs === undefined || options.breadCrumbs)) { + //Breadcrumbs + html += Breadcrumbs({ + list: list, + mode: options.mode + }); + } + + if (options.mode === 'edit' && list.editInstructions) { + html += "
        \n"; + html += "\n"; + html += "Hint: " + list.editInstructions + "\n"; + html += "
        \n"; + } + + if (options.mode !== 'lookup' && (list.well === undefined || list.well)) { + html += "
        \n"; + } + + if (options.activityStream) { + // Add a title row + html += "
        \n"; + html += "
        \n"; + html += "
        {{ streamTitle }}
        \n"; + html += "
        \n"; + html += "
        \n"; + } + + html += "
        \n"; + + if (list.name !== 'groups') { + if (options.searchSize) { + html += SearchWidget({ + iterator: list.iterator, + template: list, + mini: true, + size: options.searchSize, + searchWidgets: list.searchWidgets + }); + } else if (options.mode === 'summary') { + html += SearchWidget({ + iterator: list.iterator, + template: list, + mini: true, + size: 'col-lg-6' + }); + } else if (options.mode === 'lookup' || options.id !== undefined) { + html += SearchWidget({ + iterator: list.iterator, + template: list, + mini: true, + size: 'col-lg-8' + }); + } else { + html += SearchWidget({ + iterator: list.iterator, + template: list, + mini: true + }); } } - else { - html += "fa-sort"; - } - html += "\">"; + + if (options.mode !== 'lookup') { + //actions + base = $location.path().replace(/^\//, '').split('/')[0]; + html += "
        \n"; + + html += "
        \n"; + + // Add toolbar buttons or 'actions' + for (action in list.actions) { + if (list.actions[action].mode === 'all' || list.actions[action].mode === options.mode) { + if ((list.actions[action].basePaths === undefined) || + (list.actions[action].basePaths && list.actions[action].basePaths.indexOf(base) > -1)) { + html += this.button({ + btn: list.actions[action], + action: action, + toolbar: true + }); + } + } + } + + //select instructions + if (options.mode === 'select' && list.selectInstructions) { + btn = { + awPopOver: list.selectInstructions, + dataPlacement: 'top', + dataContainer: 'body', + 'class': 'btn-xs btn-help', + awToolTip: 'Click for help', + dataTitle: 'Help', + iconSize: 'fa-lg' + }; + //html += this.button(btn, 'select'); + html += this.button({ + btn: btn, + action: 'help', + toolbar: true + }); + } + + html += "
        \n"; + html += "
        \n"; + } else { + //lookup + html += "
        \n"; + } + + html += "
        \n"; + + // Add a title and optionally a close button (used on Inventory->Groups) + if (options.mode !== 'lookup' && list.showTitle) { + html += "
        "; + html += (options.mode === 'edit' || options.mode === 'summary') ? list.editTitle : list.addTitle; + html += "
        \n"; + } + + // table header row + html += "#\n"; + } + for (fld in list.fields) { + if ((list.fields[fld].searchOnly === undefined || list.fields[fld].searchOnly === false) && + !(options.mode === 'lookup' && list.fields[fld].excludeModal !== undefined && list.fields[fld].excludeModal === true)) { + html += "\n"; + } + } + if (options.mode === 'select' || options.mode === 'lookup') { + html += ""; + } else if (options.mode === 'edit' && list.fieldActions) { + html += "\n"; + } + html += "\n"; + html += "\n"; + + // table body + html += "\n"; + html += "{{ $index + ((" + list.iterator + "_page - 1) * " + list.iterator + "_page_size) + 1 }}.\n"; + } + cnt = 2; + base = (list.base) ? list.base : list.name; + base = base.replace(/^\//, ''); + for (fld in list.fields) { + cnt++; + if ((list.fields[fld].searchOnly === undefined || list.fields[fld].searchOnly === false) && + !(options.mode === 'lookup' && list.fields[fld].excludeModal !== undefined && + list.fields[fld].excludeModal === true)) { + html += Column({ + list: list, + fld: fld, + options: options, + base: base + }); + } + } + + if (options.mode === 'select' || options.mode === 'lookup') { + html += ""; + } else if ((options.mode === 'edit' || options.mode === 'summary') && list.fieldActions) { + + // Row level actions + + html += ""; + } + html += "\n"; + + // Message for when a collection is empty + html += "\n"; + html += "\n"; + html += "\n"; + + // Message for loading + html += "\n"; + html += "\n"; + html += "\n"; + + // End List + html += "\n"; + html += "
        "; + } + html += "SelectActions
        "; + + for (field_action in list.fieldActions) { + if (field_action !== 'columnClass') { + if (list.fieldActions[field_action].type && list.fieldActions[field_action].type === 'DropDown') { + html += DropDown({ + list: list, + fld: field_action, + options: options, + base: base, + type: 'fieldActions', + td: false + }); + } else { + fAction = list.fieldActions[field_action]; + html += ""; + } else { + html += SelectIcon({ + action: field_action + }); + } + html += (fAction.label) ? " " + list.fieldActions[field_action].label : ""; + html += ""; + } + } + } + html += "
        No records matched your search.
        Loading...
        \n"; + + if (options.mode === 'select' && (options.selectButton === undefined || options.selectButton)) { + html += "
        \n"; + html += " \n"; + html += "
        \n"; + } + + if (options.mode !== 'lookup' && (list.well === undefined || list.well === true)) { + html += "
        \n"; //well + } + + if (list.name !== 'groups') { + if (options.mode === 'lookup' || (options.id && options.id === "form-modal-body")) { + html += PaginateWidget({ + set: list.name, + iterator: list.iterator + }); + } else { + html += PaginateWidget({ + set: list.name, + iterator: list.iterator + }); + } + } + + return html; } - html += "
        SelectActions
        "; - - var field_action, fAction, itm; - - for (field_action in list.fieldActions) { - if (field_action !== 'columnClass') { - if (list.fieldActions[field_action].type && list.fieldActions[field_action].type == 'DropDown') { - html += DropDown({ - list: list, - fld: field_action, - options: options, - base: base, - type: 'fieldActions', - td: false - }); - } - else { - fAction = list.fieldActions[field_action]; - html += ""; - } - else { - html += SelectIcon({ action: field_action }); - } - html += (fAction.label) ? " " + list.fieldActions[field_action]['label'] : ""; - html += ""; - } - } - } - html += "
        No records matched your search.
        Loading...
        \n"; - - if (options.mode == 'select' && (options.selectButton == undefined || options.selectButton == true)) { - html += "
        \n"; - html += " \n"; - html += "
        \n"; - } - - if (options.mode != 'lookup' && (list.well == undefined || list.well == true)) { - html += "
        \n"; //well - } - - if (list.name !== 'groups') { - if ( options.mode == 'lookup' || (options.id && options.id == "form-modal-body") ) { - html += PaginateWidget({ set: list.name, iterator: list.iterator }); - } - else { - html += PaginateWidget({ set: list.name, iterator: list.iterator }); - } - } - - return html; - - } - -}}]); \ No newline at end of file + }; + } + ]); \ No newline at end of file diff --git a/awx/ui/static/lib/ansible/prompt-dialog.js b/awx/ui/static/lib/ansible/prompt-dialog.js index f5891796c0..c4a11559d5 100644 --- a/awx/ui/static/lib/ansible/prompt-dialog.js +++ b/awx/ui/static/lib/ansible/prompt-dialog.js @@ -2,12 +2,12 @@ * Copyright (c) 2014 AnsibleWorks, Inc. * * PromptDialog - * Prompt the user with a Yes/No dialog to confirm an action such + * Prompt the user with a Yes/No dialog to confirm an action such * as Delete. Assumes a hidden dialog already exists in $scope. - * See example at bottom. If user responds with Yes, execute action + * See example at bottom. If user responds with Yes, execute action * parameter. * - * params: { hdr: 'header msg', + * params: { hdr: 'header msg', * body: 'body text/html', * class: 'btn-class for Yes button', --defaults to btn-danger * action: function() {} --action to take, if use clicks Yes @@ -17,25 +17,26 @@ 'use strict'; angular.module('PromptDialog', ['Utilities']) - .factory('Prompt', ['$rootScope', '$compile', 'Alert', function($rootScope, $compile, Alert) { - return function(params) { - - var dialog = angular.element(document.getElementById('prompt-modal')); - var scope = dialog.scope(); - - scope.promptHeader = params.hdr; - scope.promptBody = params.body; - scope.promptAction = params.action; - - var cls = (params['class'] == null || params['class'] == undefined) ? 'btn-danger' : params['class']; - - $('#prompt_action_btn').removeClass(cls).addClass(cls); - - $(dialog).modal({ - backdrop: 'static', - keyboard: true, - show: true - }); + .factory('Prompt', [ + function () { + return function (params) { + + var dialog = angular.element(document.getElementById('prompt-modal')), + scope = dialog.scope(), cls; + + scope.promptHeader = params.hdr; + scope.promptBody = params.body; + scope.promptAction = params.action; + + cls = (params['class'] === null || params['class'] === undefined) ? 'btn-danger' : params['class']; + + $('#prompt_action_btn').removeClass(cls).addClass(cls); + + $(dialog).modal({ + backdrop: 'static', + keyboard: true, + show: true + }); + }; } - }]); - \ No newline at end of file + ]); \ No newline at end of file diff --git a/awx/ui/static/lib/ansible/pwdmeter.js b/awx/ui/static/lib/ansible/pwdmeter.js index 535b53d587..b87818f176 100644 --- a/awx/ui/static/lib/ansible/pwdmeter.js +++ b/awx/ui/static/lib/ansible/pwdmeter.js @@ -22,205 +22,294 @@ ** 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA ** ** -** CLH 9/5/13 - Set required strength in config.js +** CLH 09/05/13 - Set required strength in config.js +** 02/10/14 - Applied jsHint */ -String.prototype.strReverse = function() { - var newstring = ""; - for (var s=0; s < this.length; s++) { - newstring = this.charAt(s) + newstring; - } - return newstring; +/*jshint eqeqeq:false, unused:false */ + +String.prototype.strReverse = function () { + var newstring = "", s; + for (s = 0; s < this.length; s++) { + newstring = this.charAt(s) + newstring; + } + return newstring; }; var nScore = 0; + function chkPass(pwd) { - // Simultaneous variable declaration and value assignment aren't supported in IE apparently - // so I'm forced to assign the same value individually per var to support a crappy browser *sigh* - var nLength=0, nAlphaUC=0, nAlphaLC=0, nNumber=0, nSymbol=0, nMidChar=0, nRequirements=0, - nAlphasOnly=0, nNumbersOnly=0, nUnqChar=0, nRepChar=0, nRepInc=0, nConsecAlphaUC=0, nConsecAlphaLC=0, - nConsecNumber=0, nConsecSymbol=0, nConsecCharType=0, nSeqAlpha=0, nSeqNumber=0, nSeqSymbol=0, - nSeqChar=0, nReqChar=0, nMultConsecCharType=0; - var nMultRepChar=1, nMultConsecSymbol=1; - var nMultMidChar=2, nMultRequirements=2, nMultConsecAlphaUC=2, nMultConsecAlphaLC=2, nMultConsecNumber=2; - var nReqCharType=3, nMultAlphaUC=3, nMultAlphaLC=3, nMultSeqAlpha=3, nMultSeqNumber=3, nMultSeqSymbol=3; - var nMultLength=4, nMultNumber=4; - var nMultSymbol=6; - var nTmpAlphaUC="", nTmpAlphaLC="", nTmpNumber="", nTmpSymbol=""; - var sAlphaUC="0", sAlphaLC="0", sNumber="0", sSymbol="0", sMidChar="0", sRequirements="0", - sAlphasOnly="0", sNumbersOnly="0", sRepChar="0", sConsecAlphaUC="0", sConsecAlphaLC="0", - sConsecNumber="0", sSeqAlpha="0", sSeqNumber="0", sSeqSymbol="0"; - var sAlphas = "abcdefghijklmnopqrstuvwxyz"; - var sNumerics = "01234567890"; - var sSymbols = ")_!@#$%^&*()"; - var sComplexity = "Too Short"; - var sStandards = "Below"; - var nMinPwdLen = 8; - if (document.all) { var nd = 0; } else { var nd = 1; } - if (pwd) { - nScore = parseInt(pwd.length * nMultLength); - nLength = pwd.length; - var arrPwd = pwd.replace(/\s+/g,"").split(/\s*/); - var arrPwdLen = arrPwd.length; - - /* Loop through password to check for Symbol, Numeric, Lowercase and Uppercase pattern matches */ - for (var a=0; a < arrPwdLen; a++) { - if (arrPwd[a].match(/[A-Z]/g)) { - if (nTmpAlphaUC !== "") { if ((nTmpAlphaUC + 1) == a) { nConsecAlphaUC++; nConsecCharType++; } } - nTmpAlphaUC = a; - nAlphaUC++; - } - else if (arrPwd[a].match(/[a-z]/g)) { - if (nTmpAlphaLC !== "") { if ((nTmpAlphaLC + 1) == a) { nConsecAlphaLC++; nConsecCharType++; } } - nTmpAlphaLC = a; - nAlphaLC++; - } - else if (arrPwd[a].match(/[0-9]/g)) { - if (a > 0 && a < (arrPwdLen - 1)) { nMidChar++; } - if (nTmpNumber !== "") { if ((nTmpNumber + 1) == a) { nConsecNumber++; nConsecCharType++; } } - nTmpNumber = a; - nNumber++; - } - else if (arrPwd[a].match(/[^a-zA-Z0-9_]/g)) { - if (a > 0 && a < (arrPwdLen - 1)) { nMidChar++; } - if (nTmpSymbol !== "") { if ((nTmpSymbol + 1) == a) { nConsecSymbol++; nConsecCharType++; } } - nTmpSymbol = a; - nSymbol++; - } - /* Internal loop through password to check for repeat characters */ - var bCharExists = false; - for (var b=0; b < arrPwdLen; b++) { - if (arrPwd[a] == arrPwd[b] && a != b) { /* repeat character exists */ - bCharExists = true; - /* + // Simultaneous variable declaration and value assignment aren't supported in IE apparently + // so I'm forced to assign the same value individually per var to support a crappy browser *sigh* + var nLength = 0, + nAlphaUC = 0, + nAlphaLC = 0, + nNumber = 0, + nSymbol = 0, + nMidChar = 0, + nRequirements = 0, + nAlphasOnly = 0, + nNumbersOnly = 0, + nUnqChar = 0, + nRepChar = 0, + nRepInc = 0, + nConsecAlphaUC = 0, + nConsecAlphaLC = 0, + nConsecNumber = 0, + nConsecSymbol = 0, + nConsecCharType = 0, + nSeqAlpha = 0, + nSeqNumber = 0, + nSeqSymbol = 0, + nSeqChar = 0, + nReqChar = 0, + nMultConsecCharType = 0, + nMultRepChar = 1, + nMultConsecSymbol = 1, + nMultMidChar = 2, + nMultRequirements = 2, + nMultConsecAlphaUC = 2, + nMultConsecAlphaLC = 2, + nMultConsecNumber = 2, + nReqCharType = 3, + nMultAlphaUC = 3, + nMultAlphaLC = 3, + nMultSeqAlpha = 3, + nMultSeqNumber = 3, + nMultSeqSymbol = 3, + nMultLength = 4, + nMultNumber = 4, + nMultSymbol = 6, + nTmpAlphaUC = "", + nTmpAlphaLC = "", + nTmpNumber = "", + nTmpSymbol = "", + sAlphaUC = "0", + sAlphaLC = "0", + sNumber = "0", + sSymbol = "0", + sMidChar = "0", + sRequirements = "0", + sAlphasOnly = "0", + sNumbersOnly = "0", + sRepChar = "0", + sConsecAlphaUC = "0", + sConsecAlphaLC = "0", + sConsecNumber = "0", + sSeqAlpha = "0", + sSeqNumber = "0", + sSeqSymbol = "0", + sAlphas = "abcdefghijklmnopqrstuvwxyz", + sNumerics = "01234567890", + sSymbols = ")_!@#$%^&*()", + sComplexity = "Too Short", + sStandards = "Below", + nMinPwdLen = 8, + a, nd, arrPwd, arrPwdLen,bCharExists,b,s,sFwd,sRev,progbar,required_strength,warning_level; + + if (document.all) { + nd = 0; + } else { + nd = 1; + } + if (pwd) { + nScore = parseInt(pwd.length * nMultLength); + nLength = pwd.length; + arrPwd = pwd.replace(/\s+/g, "").split(/\s*/); + arrPwdLen = arrPwd.length; + + /* Loop through password to check for Symbol, Numeric, Lowercase and Uppercase pattern matches */ + for (a = 0; a < arrPwdLen; a++) { + if (arrPwd[a].match(/[A-Z]/g)) { + if (nTmpAlphaUC !== "") { + if ((nTmpAlphaUC + 1) == a) { + nConsecAlphaUC++; + nConsecCharType++; + } + } + nTmpAlphaUC = a; + nAlphaUC++; + } else if (arrPwd[a].match(/[a-z]/g)) { + if (nTmpAlphaLC !== "") { + if ((nTmpAlphaLC + 1) == a) { + nConsecAlphaLC++; + nConsecCharType++; + } + } + nTmpAlphaLC = a; + nAlphaLC++; + } else if (arrPwd[a].match(/[0-9]/g)) { + if (a > 0 && a < (arrPwdLen - 1)) { + nMidChar++; + } + if (nTmpNumber !== "") { + if ((nTmpNumber + 1) == a) { + nConsecNumber++; + nConsecCharType++; + } + } + nTmpNumber = a; + nNumber++; + } else if (arrPwd[a].match(/[^a-zA-Z0-9_]/g)) { + if (a > 0 && a < (arrPwdLen - 1)) { + nMidChar++; + } + if (nTmpSymbol !== "") { + if ((nTmpSymbol + 1) == a) { + nConsecSymbol++; + nConsecCharType++; + } + } + nTmpSymbol = a; + nSymbol++; + } + + /* Internal loop through password to check for repeat characters */ + bCharExists = false; + for (b = 0; b < arrPwdLen; b++) { + if (arrPwd[a] == arrPwd[b] && a != b) { /* repeat character exists */ + bCharExists = true; + /* Calculate icrement deduction based on proximity to identical characters Deduction is incremented each time a new match is discovered Deduction amount is based on total password length divided by the difference of distance between currently selected match */ - nRepInc += Math.abs(arrPwdLen/(b-a)); + nRepInc += Math.abs(arrPwdLen / (b - a)); + } } - } - if (bCharExists) { - nRepChar++; - nUnqChar = arrPwdLen-nRepChar; - nRepInc = (nUnqChar) ? Math.ceil(nRepInc/nUnqChar) : Math.ceil(nRepInc); - } - } - - /* Check for sequential alpha string patterns (forward and reverse) */ - for (var s=0; s < 23; s++) { - var sFwd = sAlphas.substring(s,parseInt(s+3)); - var sRev = sFwd.strReverse(); - if (pwd.toLowerCase().indexOf(sFwd) != -1 || pwd.toLowerCase().indexOf(sRev) != -1) { nSeqAlpha++; nSeqChar++;} - } - - /* Check for sequential numeric string patterns (forward and reverse) */ - for (var s=0; s < 8; s++) { - var sFwd = sNumerics.substring(s,parseInt(s+3)); - var sRev = sFwd.strReverse(); - if (pwd.toLowerCase().indexOf(sFwd) != -1 || pwd.toLowerCase().indexOf(sRev) != -1) { nSeqNumber++; nSeqChar++;} - } - - /* Check for sequential symbol string patterns (forward and reverse) */ - for (var s=0; s < 8; s++) { - var sFwd = sSymbols.substring(s,parseInt(s+3)); - var sRev = sFwd.strReverse(); - if (pwd.toLowerCase().indexOf(sFwd) != -1 || pwd.toLowerCase().indexOf(sRev) != -1) { nSeqSymbol++; nSeqChar++;} - } - - /* Modify overall score value based on usage vs requirements */ + if (bCharExists) { + nRepChar++; + nUnqChar = arrPwdLen - nRepChar; + nRepInc = (nUnqChar) ? Math.ceil(nRepInc / nUnqChar) : Math.ceil(nRepInc); + } + } - /* General point assignment */ - if (nAlphaUC > 0 && nAlphaUC < nLength) { - nScore = parseInt(nScore + ((nLength - nAlphaUC) * 2)); - sAlphaUC = "+ " + parseInt((nLength - nAlphaUC) * 2); - } - if (nAlphaLC > 0 && nAlphaLC < nLength) { - nScore = parseInt(nScore + ((nLength - nAlphaLC) * 2)); - sAlphaLC = "+ " + parseInt((nLength - nAlphaLC) * 2); - } - if (nNumber > 0 && nNumber < nLength) { - nScore = parseInt(nScore + (nNumber * nMultNumber)); - sNumber = "+ " + parseInt(nNumber * nMultNumber); - } - if (nSymbol > 0) { - nScore = parseInt(nScore + (nSymbol * nMultSymbol)); - sSymbol = "+ " + parseInt(nSymbol * nMultSymbol); - } - if (nMidChar > 0) { - nScore = parseInt(nScore + (nMidChar * nMultMidChar)); - sMidChar = "+ " + parseInt(nMidChar * nMultMidChar); - } + /* Check for sequential alpha string patterns (forward and reverse) */ + for (s = 0; s < 23; s++) { + sFwd = sAlphas.substring(s, parseInt(s + 3)); + sRev = sFwd.strReverse(); + if (pwd.toLowerCase().indexOf(sFwd) != -1 || pwd.toLowerCase().indexOf(sRev) != -1) { + nSeqAlpha++; + nSeqChar++; + } + } - /* Point deductions for poor practices */ - if ((nAlphaLC > 0 || nAlphaUC > 0) && nSymbol === 0 && nNumber === 0) { // Only Letters - nScore = parseInt(nScore - nLength); - nAlphasOnly = nLength; - sAlphasOnly = "- " + nLength; - } - if (nAlphaLC === 0 && nAlphaUC === 0 && nSymbol === 0 && nNumber > 0) { // Only Numbers - nScore = parseInt(nScore - nLength); - nNumbersOnly = nLength; - sNumbersOnly = "- " + nLength; - } - if (nRepChar > 0) { // Same character exists more than once - nScore = parseInt(nScore - nRepInc); - sRepChar = "- " + nRepInc; - } - if (nConsecAlphaUC > 0) { // Consecutive Uppercase Letters exist - nScore = parseInt(nScore - (nConsecAlphaUC * nMultConsecAlphaUC)); - sConsecAlphaUC = "- " + parseInt(nConsecAlphaUC * nMultConsecAlphaUC); - } - if (nConsecAlphaLC > 0) { // Consecutive Lowercase Letters exist - nScore = parseInt(nScore - (nConsecAlphaLC * nMultConsecAlphaLC)); - sConsecAlphaLC = "- " + parseInt(nConsecAlphaLC * nMultConsecAlphaLC); - } - if (nConsecNumber > 0) { // Consecutive Numbers exist - nScore = parseInt(nScore - (nConsecNumber * nMultConsecNumber)); - sConsecNumber = "- " + parseInt(nConsecNumber * nMultConsecNumber); - } - if (nSeqAlpha > 0) { // Sequential alpha strings exist (3 characters or more) - nScore = parseInt(nScore - (nSeqAlpha * nMultSeqAlpha)); - sSeqAlpha = "- " + parseInt(nSeqAlpha * nMultSeqAlpha); - } - if (nSeqNumber > 0) { // Sequential numeric strings exist (3 characters or more) - nScore = parseInt(nScore - (nSeqNumber * nMultSeqNumber)); - sSeqNumber = "- " + parseInt(nSeqNumber * nMultSeqNumber); - } - if (nSeqSymbol > 0) { // Sequential symbol strings exist (3 characters or more) - nScore = parseInt(nScore - (nSeqSymbol * nMultSeqSymbol)); - sSeqSymbol = "- " + parseInt(nSeqSymbol * nMultSeqSymbol); - } + /* Check for sequential numeric string patterns (forward and reverse) */ + for (s = 0; s < 8; s++) { + sFwd = sNumerics.substring(s, parseInt(s + 3)); + sRev = sFwd.strReverse(); + if (pwd.toLowerCase().indexOf(sFwd) != -1 || pwd.toLowerCase().indexOf(sRev) != -1) { + nSeqNumber++; + nSeqChar++; + } + } - /* Determine complexity based on overall score */ - if (nScore > 100) { nScore = 100; } else if (nScore < 0) { nScore = 0; } + /* Check for sequential symbol string patterns (forward and reverse) */ + for (s = 0; s < 8; s++) { + sFwd = sSymbols.substring(s, parseInt(s + 3)); + sRev = sFwd.strReverse(); + if (pwd.toLowerCase().indexOf(sFwd) != -1 || pwd.toLowerCase().indexOf(sRev) != -1) { + nSeqSymbol++; + nSeqChar++; + } + } - var progbar = $("#progbar"); - var required_strength = $AnsibleConfig.password_strength; - var warning_level = ($AnsibleConfig.password_strength - 15 < 0) ? 0 : $AnsibleConfig.password_strength - 15; + /* Modify overall score value based on usage vs requirements */ - progbar.css("width", nScore + '%'); + /* General point assignment */ + if (nAlphaUC > 0 && nAlphaUC < nLength) { + nScore = parseInt(nScore + ((nLength - nAlphaUC) * 2)); + sAlphaUC = "+ " + parseInt((nLength - nAlphaUC) * 2); + } + if (nAlphaLC > 0 && nAlphaLC < nLength) { + nScore = parseInt(nScore + ((nLength - nAlphaLC) * 2)); + sAlphaLC = "+ " + parseInt((nLength - nAlphaLC) * 2); + } + if (nNumber > 0 && nNumber < nLength) { + nScore = parseInt(nScore + (nNumber * nMultNumber)); + sNumber = "+ " + parseInt(nNumber * nMultNumber); + } + if (nSymbol > 0) { + nScore = parseInt(nScore + (nSymbol * nMultSymbol)); + sSymbol = "+ " + parseInt(nSymbol * nMultSymbol); + } + if (nMidChar > 0) { + nScore = parseInt(nScore + (nMidChar * nMultMidChar)); + sMidChar = "+ " + parseInt(nMidChar * nMultMidChar); + } - if (nScore >= 0 && nScore <= warning_level) { - sComplexity = 'Weak'; - progbar.addClass('progress-bar-danger') - progbar.removeClass('progress-bar-success progress-bar-warning') - } else if (nScore > warning_level && nScore <= required_strength) { - sComplexity = 'Good'; - progbar.addClass('progress-bar-warning') - progbar.removeClass('progress-bar-success progress-bar-danger') - } else if (nScore > required_strength) { - sComplexity = "Strong"; - progbar.addClass('progress-bar-success') - progbar.removeClass('progress-bar-warning progress-bar-danger') - } - } - else { - /* no password, so reset the displays */ - var progbar = $("#progbar"); - progbar.css("width", '0%'); - progbar.removeClass('progress-bar-success progress-bar-warning') - } - return nScore; + /* Point deductions for poor practices */ + if ((nAlphaLC > 0 || nAlphaUC > 0) && nSymbol === 0 && nNumber === 0) { // Only Letters + nScore = parseInt(nScore - nLength); + nAlphasOnly = nLength; + sAlphasOnly = "- " + nLength; + } + if (nAlphaLC === 0 && nAlphaUC === 0 && nSymbol === 0 && nNumber > 0) { // Only Numbers + nScore = parseInt(nScore - nLength); + nNumbersOnly = nLength; + sNumbersOnly = "- " + nLength; + } + if (nRepChar > 0) { // Same character exists more than once + nScore = parseInt(nScore - nRepInc); + sRepChar = "- " + nRepInc; + } + if (nConsecAlphaUC > 0) { // Consecutive Uppercase Letters exist + nScore = parseInt(nScore - (nConsecAlphaUC * nMultConsecAlphaUC)); + sConsecAlphaUC = "- " + parseInt(nConsecAlphaUC * nMultConsecAlphaUC); + } + if (nConsecAlphaLC > 0) { // Consecutive Lowercase Letters exist + nScore = parseInt(nScore - (nConsecAlphaLC * nMultConsecAlphaLC)); + sConsecAlphaLC = "- " + parseInt(nConsecAlphaLC * nMultConsecAlphaLC); + } + if (nConsecNumber > 0) { // Consecutive Numbers exist + nScore = parseInt(nScore - (nConsecNumber * nMultConsecNumber)); + sConsecNumber = "- " + parseInt(nConsecNumber * nMultConsecNumber); + } + if (nSeqAlpha > 0) { // Sequential alpha strings exist (3 characters or more) + nScore = parseInt(nScore - (nSeqAlpha * nMultSeqAlpha)); + sSeqAlpha = "- " + parseInt(nSeqAlpha * nMultSeqAlpha); + } + if (nSeqNumber > 0) { // Sequential numeric strings exist (3 characters or more) + nScore = parseInt(nScore - (nSeqNumber * nMultSeqNumber)); + sSeqNumber = "- " + parseInt(nSeqNumber * nMultSeqNumber); + } + if (nSeqSymbol > 0) { // Sequential symbol strings exist (3 characters or more) + nScore = parseInt(nScore - (nSeqSymbol * nMultSeqSymbol)); + sSeqSymbol = "- " + parseInt(nSeqSymbol * nMultSeqSymbol); + } + + /* Determine complexity based on overall score */ + if (nScore > 100) { + nScore = 100; + } else if (nScore < 0) { + nScore = 0; + } + + progbar = $("#progbar"); + required_strength = $AnsibleConfig.password_strength; + warning_level = ($AnsibleConfig.password_strength - 15 < 0) ? 0 : $AnsibleConfig.password_strength - 15; + + progbar.css("width", nScore + '%'); + + if (nScore >= 0 && nScore <= warning_level) { + sComplexity = 'Weak'; + progbar.addClass('progress-bar-danger'); + progbar.removeClass('progress-bar-success progress-bar-warning'); + } else if (nScore > warning_level && nScore <= required_strength) { + sComplexity = 'Good'; + progbar.addClass('progress-bar-warning'); + progbar.removeClass('progress-bar-success progress-bar-danger'); + } else if (nScore > required_strength) { + sComplexity = "Strong"; + progbar.addClass('progress-bar-success'); + progbar.removeClass('progress-bar-warning progress-bar-danger'); + } + } else { + /* no password, so reset the displays */ + progbar = $("#progbar"); + progbar.css("width", '0%'); + progbar.removeClass('progress-bar-success progress-bar-warning'); + } + return nScore; } \ No newline at end of file diff --git a/awx/ui/templates/ui/index.html b/awx/ui/templates/ui/index.html index 92f62a7fa0..85ae82c714 100644 --- a/awx/ui/templates/ui/index.html +++ b/awx/ui/templates/ui/index.html @@ -71,10 +71,11 @@ + - + @@ -123,7 +124,6 @@ - {% endif %} diff --git a/package.json b/package.json new file mode 100644 index 0000000000..119076c330 --- /dev/null +++ b/package.json @@ -0,0 +1,9 @@ +{ + "name": "ansible-commander", + "version": "1.4.5", + "devDependencies": { + "grunt": "latest", + "grunt-contrib-jshint": "~0.8.0", + "grunt-contrib-uglify": "~0.3.1" + } +}