From 036352f10e24db04ad5465ecddce76edafbdf51c Mon Sep 17 00:00:00 2001 From: chouseknecht Date: Fri, 16 Aug 2013 01:15:02 -0400 Subject: [PATCH] AC-331 Separated 'expand/collapse' function from 'select' function on Hosts tab tree. It now functions more like the Gmail label tree. Added Wait() utiltiy that can be used to freeze the API during longer than usual API operations. Using it on the 'group move' function. --- awx/ui/static/js/controllers/Inventories.js | 26 +- awx/ui/static/js/controllers/Organizations.js | 4 +- awx/ui/static/js/helpers/Hosts.js | 6 +- awx/ui/static/js/helpers/inventory.js | 11 +- awx/ui/static/less/ansible-ui.less | 46 ++- awx/ui/static/lib/ansible/directives.js | 159 +++++--- awx/ui/static/lib/ansible/utilities.js | 383 ++++++++++-------- awx/ui/templates/ui/index.html | 3 + 8 files changed, 377 insertions(+), 261 deletions(-) diff --git a/awx/ui/static/js/controllers/Inventories.js b/awx/ui/static/js/controllers/Inventories.js index f18b0c69b2..0d64318f14 100644 --- a/awx/ui/static/js/controllers/Inventories.js +++ b/awx/ui/static/js/controllers/Inventories.js @@ -216,7 +216,6 @@ function InventoriesEdit ($scope, $rootScope, $compile, $location, $log, $routeP LoadInventory({ scope: scope, doPostSteps: true }); $('#inventory-tabs a[href="#inventory-hosts"]').on('show.bs.tab', function() { LoadSearchTree({ scope: scope, inventory_id: scope['inventory_id'] }); - HostsReload({ scope: scope, inventory_id: scope['inventory_id'], group_id: scope['group_id'] }); if (!scope.$$phase) { scope.$digest(); } @@ -382,10 +381,10 @@ function InventoriesEdit ($scope, $rootScope, $compile, $location, $log, $routeP scope.inventoryEditHide = true; scope.groupDeleteHide = false; scope.createButtonShow = true; - scope.group_id = node.attr('group_id'); - scope.groupName = n.data; - scope.groupTitle = '

' + n.data + '

'; - scope.groupTitle += (node.attr('description')) ? '

' + node.attr('description') + '

' : ''; + //scope.group_id = node.attr('group_id'); + //scope.groupName = n.data; + //scope.groupTitle = '

' + n.data + '

'; + //scope.groupTitle += (node.attr('description')) ? '

' + node.attr('description') + '

' : ''; } else if (type == 'inventory') { url = node.attr('hosts'); @@ -395,9 +394,9 @@ function InventoriesEdit ($scope, $rootScope, $compile, $location, $log, $routeP scope.inventoryEditHide=false; scope.groupDeleteHide = true; scope.createButtonShow = false; - scope.groupName = 'All Hosts'; - scope.groupTitle = '

All Hosts

'; - scope.group_id = null; + //scope.groupName = 'All Hosts'; + //scope.groupTitle = '

All Hosts

'; + //scope.group_id = null; } if (!scope.$$phase) { @@ -460,11 +459,12 @@ function InventoriesEdit ($scope, $rootScope, $compile, $location, $log, $routeP }); } - scope.showHosts = function(e) { - console.log('here'); - var elm = angular.elment(e.srcElement); - console.log('Need to show hosts: ' + elm.attr('data-hosts')); - } + // Respond to the scope.$emit from awTree directive + scope.$on('refreshHost', function(e, group, title) { + scope.groupTitle = title; + scope.group_id = group; + HostsReload({ scope: scope, inventory_id: scope['inventory_id'], group_id: group }); + }); } diff --git a/awx/ui/static/js/controllers/Organizations.js b/awx/ui/static/js/controllers/Organizations.js index 8a11ff0927..0d5ed2e48c 100644 --- a/awx/ui/static/js/controllers/Organizations.js +++ b/awx/ui/static/js/controllers/Organizations.js @@ -12,7 +12,7 @@ function OrganizationsList ($routeParams, $scope, $rootScope, $location, $log, Rest, Alert, LoadBreadCrumbs, Prompt, GenerateList, OrganizationList, SearchInit, PaginateInit, ClearScope, ProcessErrors, - GetBasePath, SelectionInit) + GetBasePath, SelectionInit, Wait) { ClearScope('htmlTemplate'); //Garbage collection. Don't leave behind any listeners/watchers from the prior //scope. @@ -70,7 +70,7 @@ function OrganizationsList ($routeParams, $scope, $rootScope, $location, $log, R OrganizationsList.$inject=[ '$routeParams', '$scope', '$rootScope', '$location', '$log', 'Rest', 'Alert', 'LoadBreadCrumbs', 'Prompt', 'GenerateList', 'OrganizationList', 'SearchInit', 'PaginateInit', 'ClearScope', 'ProcessErrors', - 'GetBasePath', 'SelectionInit' ]; + 'GetBasePath', 'SelectionInit', 'Wait' ]; function OrganizationsAdd ($scope, $rootScope, $compile, $location, $log, $routeParams, OrganizationForm, diff --git a/awx/ui/static/js/helpers/Hosts.js b/awx/ui/static/js/helpers/Hosts.js index 8d14bfd167..1c18acd691 100644 --- a/awx/ui/static/js/helpers/Hosts.js +++ b/awx/ui/static/js/helpers/Hosts.js @@ -410,10 +410,12 @@ angular.module('HostsHelper', [ 'RestServices', 'Utilities', 'ListGenerator', 'H function(RelatedSearchInit, RelatedPaginateInit, InventoryForm, GetBasePath) { return function(params) { // Rerfresh the Hosts view on right side of page - scope = params.scope; + + var group_id = params.group_id; + var scope = params.scope; scope['hosts'] = null; - var url = (scope.group_id !== null && scope.group_id !== undefined) ? GetBasePath('groups') + scope.group_id + '/all_hosts/' : + var url = (group_id !== null && group_id !== undefined) ? GetBasePath('groups') + group_id + '/all_hosts/' : GetBasePath('inventory') + params.inventory_id + '/hosts/'; var relatedSets = { hosts: { url: url, iterator: 'host' } }; diff --git a/awx/ui/static/js/helpers/inventory.js b/awx/ui/static/js/helpers/inventory.js index 2dd7864728..cb694f1909 100644 --- a/awx/ui/static/js/helpers/inventory.js +++ b/awx/ui/static/js/helpers/inventory.js @@ -100,8 +100,8 @@ angular.module('InventoryHelper', [ 'RestServices', 'Utilities', 'OrganizationLi }]) - .factory('TreeInit', ['Alert', 'Rest', 'Authorization', '$http', 'LoadTreeData', 'GetBasePath', 'ProcessErrors', - function(Alert, Rest, Authorization, $http, LoadTreeData, GetBasePath, ProcessErrors) { + .factory('TreeInit', ['Alert', 'Rest', 'Authorization', '$http', 'LoadTreeData', 'GetBasePath', 'ProcessErrors', 'Wait', + function(Alert, Rest, Authorization, $http, LoadTreeData, GetBasePath, ProcessErrors, Wait) { return function(params) { var scope = params.scope; @@ -200,6 +200,7 @@ angular.module('InventoryHelper', [ 'RestServices', 'Utilities', 'OrganizationLi $(tree_id).bind('move_node.jstree', function(e, data) { // When user drags-n-drops a node, update the API + Wait('start'); var node, target, url, parent, inv_id, variables; node = $('#tree-view li[id="' + data.rslt.o[0].id + '"]'); // node being moved parent = $('#tree-view li[id="' + data.args[0].op[0].id + '"]'); //node moving from @@ -214,9 +215,7 @@ angular.module('InventoryHelper', [ 'RestServices', 'Utilities', 'OrganizationLi function showSuccessMsg() { var parent_descr = (parent.attr('type') == 'inventory') ? 'the inventory root' : parent.attr('name'); var target_descr = (target.attr('type') == 'inventory') ? 'the inventory root' : target.attr('name'); - Alert('Group Moved', 'Group ' + node.attr('name') + ' was successfully moved from ' + parent_descr + - ' to ' + target_descr + '.', 'alert-success'); - scope['treeLoading'] = false; + Wait('stop'); if (!scope.$$phase) { scope.$digest(); } @@ -302,7 +301,7 @@ angular.module('InventoryHelper', [ 'RestServices', 'Utilities', 'OrganizationLi '. GET returned status: ' + status }); }); - scope['treeLoading'] = true; + //scope['treeLoading'] = true; if (!scope.$$phase) { scope.$digest(); diff --git a/awx/ui/static/less/ansible-ui.less b/awx/ui/static/less/ansible-ui.less index 0c98e17475..7bf4f7fcac 100644 --- a/awx/ui/static/less/ansible-ui.less +++ b/awx/ui/static/less/ansible-ui.less @@ -24,6 +24,35 @@ body { color: @black; } +.spinny { + display: none; + position: absolute; + z-index: 2000; + width: 75px; + height: 75px; + text-align:center; + color: #eee; + background-color: @black; + border: 1px solid @grey; + border-radius: 6px; + padding-top: 10px; + + p { + padding-top: 10px; + font-size: 11px; + } +} + +.overlay { + display: none; + position: absolute; + top: 0; + left: 0; + z-index: 1999; + background-color: @black; + opacity: .4; +} + .tooltip { z-index: 1050; } @@ -603,7 +632,7 @@ input[type="text"].job-successful { .search-tree { ul { list-style-type: none; - padding-left: 10px; + padding-left: 13px; } ul:first-child { padding-left: 0; @@ -611,10 +640,21 @@ input[type="text"].job-successful { } .search-tree .active { - background-color: #ddd; + /*background-color: #ddd; padding: 1px 1px 1px 0; border: 1px solid #ddd; - border-radius: 4px; + border-radius: 4px;*/ + font-weight: bold; + color: #000; + } + + .search-tree .expand { + padding: 3px; + } + + .search-tree .expand:hover { + background-color: #ddd; + border: 1px solid #ddd; } .parse-selection { diff --git a/awx/ui/static/lib/ansible/directives.js b/awx/ui/static/lib/ansible/directives.js index d3f1a5740d..15fee8919e 100644 --- a/awx/ui/static/lib/ansible/directives.js +++ b/awx/ui/static/lib/ansible/directives.js @@ -317,8 +317,8 @@ angular.module('AWDirectives', ['RestServices', 'Utilities', 'AuthService', 'Hos } }]) - .directive('awTree', ['Rest', 'ProcessErrors', 'Authorization', '$compile', '$rootScope', 'HostsReload', - function(Rest, ProcessErrors, Authorization, $compile, $rootScope, HostsReload) { + .directive('awTree', ['Rest', 'ProcessErrors', 'Authorization', '$compile', '$rootScope', + function(Rest, ProcessErrors, Authorization, $compile, $rootScope) { return { //require: 'ngModel', @@ -335,104 +335,143 @@ angular.module('AWDirectives', ['RestServices', 'Utilities', 'AuthService', 'Hos template: "
\n" + "\n" + "
\n", link: function(scope, elm , attrs) { var idx=1000; - - function toggle(e) { - var id = (e.target.tagName == 'I') ? e.target.parentNode.attributes.id.value : e.target.attributes.id.value; - var elm = angular.element(document.getElementById(id)); - function activate() { - /* Set the clicked node as active */ - $('.search-tree .active').removeClass('active'); - elm.addClass('active'); - var group = (elm.attr('data-group-id')) ? elm.attr('data-group-id') : null; - var parentScope = angular.element(document.getElementById('htmlTemplate')).scope(); - console.log('calling for group: ' + group); - HostsReload({ scope: parentScope, inventory_id: parentScope['inventory_id'], group_id: group }); + function refresh(parent) { + var group, title; + if (parent.attr('data-group-id')) { + group = parent.attr('data-group-id'); + title = '

' + parent.attr('data-name') + '

'; + title += (parent.attr('data-description') !== "") ? '

' + parent.attr('data-description') + '

' : ''; } - + else { + group = null; + title = '

All Hosts

' + } + scope.$emit('refreshHost', group, title); + } + + function activate(e) { + /* Set the clicked node as active */ + var elm = angular.element(e.target); // + var parent = angular.element(e.target.parentNode); //
  • + $('.search-tree .active').removeClass('active'); + elm.addClass('active'); + refresh(parent); + } + + function toggle(e) { + + var id, parent, elm, icon; + + if (e.target.tagName == 'I') { + id = e.target.parentNode.parentNode.attributes.id.value; + parent = angular.element(e.target.parentNode.parentNode); //
  • + elm = angular.element(e.target.parentNode); // + } + else { + id = e.target.parentNode.attributes.id.value; + parent = angular.element(e.target.parentNode); + elm = angular.element(e.target); + } + + var sibling = angular.element(parent.children()[1]); // + var state = parent.attr('data-state'); + var icon = angular.element(elm.children()[0]); + /* Open/close the node and expand */ if (scope.childrenLoadedRemove) { scope.childrenLoadedRemove(); } scope.childrenLoadedRemove = scope.$on('childrenLoaded', function() { - childlists = elm.parent().find('ul'); //look for children + childlists = parent.find('ul'); //look for children if (childlists && childlists.length > 0) { // bind toggle() to click event of each link in the group we clicked on - var parent = angular.element(elm.parent()[0]); var links = parent.find('a'); for (var i=0; i < links.length; i++) { var link = angular.element(links[i]); - link.unbind('click', toggle); - link.bind('click', toggle); + if (link.hasClass('expand')) { + link.unbind('click', toggle); + link.bind('click', toggle); + } + if (link.hasClass('activate')) { + link.unbind('click', activate); + link.bind('click', activate); + } } toggle(e); } else { - var icon = angular.element(elm.children()[0]); - icon.removeClass('icon-caret-down').removeClass('icon-caret-right').addClass('icon-ellipsis-horizontal'); + icon.removeClass('icon-caret-right').addClass('icon-caret-down'); + //activate(e); } }); - - if (elm.attr('data-state') == 'closed') { + + + if (state == 'closed') { // expand the elment - var childlists = elm.parent().find('ul'); + var childlists = parent.find('ul'); if (childlists && childlists.length > 0) { // already has childen for (var i=0; i < childlists.length; i++) { var listChild = angular.element(childlists[i]); - var listParent = angular.element(listChild.parent().find('a')[0]); - if (listParent.attr('id') == elm.attr('id')) { + var listParent = angular.element(listChild.parent()); + if (listParent.attr('id') == id) { angular.element(childlists[i]).removeClass('hidden'); } // all the children should be in a closed state - var aList = listChild.find('a'); - for (var j=0; j < aList.length; j++) { - var thisList = angular.element(aList[j]); + var liList = listChild.find('li'); + for (var j=0; j < liList.length; j++) { + var thisList = angular.element(liList[j]); + var anchor = angular.element(thisList.find('a')[0]); + var thisIcon = angular.element(anchor.children()[0]); + thisIcon.removeClass('icon-caret-down').addClass('icon-caret-right'); thisList.attr('data-state', 'closed'); - var icon = angular.element(thisList.children()[0]); - icon.removeClass('icon-caret-down').removeClass('icon-ellipsis-horizontal').addClass('icon-caret-right'); } } - elm.attr('data-state','open'); - var icon = angular.element(elm.children()[0]); - icon.removeClass('icon-caret-right').removeClass('icon-ellipsis-horizontal').addClass('icon-caret-down'); - activate(); + parent.attr('data-state','open'); + icon.removeClass('icon-caret-right').addClass('icon-caret-down'); } else { - getChildren(elm); + getChildren(elm, parent, sibling); } } else { // close the element - elm.attr('data-state','closed'); + parent.attr('data-state','closed'); var icon = angular.element(elm.children()[0]); - icon.removeClass('icon-caret-down').removeClass('icon-ellipsis-horizontal').addClass('icon-caret-right'); - var childlists = elm.parent().find('ul'); + icon.removeClass('icon-caret-down').addClass('icon-caret-right'); + var childlists = parent.find('ul'); if (childlists && childlists.length > 0) { // has childen for (var i=0; i < childlists.length; i++) { angular.element(childlists[i]).addClass('hidden'); } } - activate(); + /* 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); + } + } } - } - function getChildren(elm) { - var url = elm.attr('data-groups'); + function getChildren(elm, parent, sibling) { + var url = parent.attr('data-groups'); var html = ''; var token = Authorization.getToken(); /* For reasons unknown calling Rest fails. It just dies with no errors @@ -445,23 +484,21 @@ angular.module('AWDirectives', ['RestServices', 'Utilities', 'AuthService', 'Hos // build html and append to parent of clicked link for (var i=0; i < data.results.length; i++) { idx++; - html += "
  • \n"; - html += " " + data.results[i].name; - html += "
  • \n"; + html += "data-group-id=\"" + data.results[i].id + "\">"; + html += " "; + html += "" + data.results[i].name + "\n"; } html = (html !== '') ? "\n" : ""; - var parent = angular.element(elm.parent()[0]); var compiled = $compile(html)(scope); parent.append(compiled); //append the new list to the parent
  • - console.log('childrenLoaded'); scope.$emit('childrenLoaded'); }, error: function(data, status) { @@ -474,7 +511,10 @@ angular.module('AWDirectives', ['RestServices', 'Utilities', 'AuthService', 'Hos function initialize() { var root = angular.element(document.getElementById('search-node-1000')); - root.bind('click', toggle); + var toggleElm = angular.element(root.find('a')[0]); + var activateElm = angular.element(root.find('a')[1]) + toggleElm.bind('click', toggle); + activateElm.bind('click', activate); } if ($rootScope.hostTabInitRemove) { @@ -484,18 +524,23 @@ angular.module('AWDirectives', ['RestServices', 'Utilities', 'AuthService', 'Hos var container = angular.element(document.getElementById('search-tree-container')); container.empty(); var html = "\n"; var compiled = $compile(html)(scope); container.append(compiled); initialize(); - //setTimeout(function() { $('.search-tree .active').click(); }, 1000); //click the root node, forcing level 1 nodes to appear + // Expand the root node and show All Hosts + setTimeout(function() { + $('#search-node-1000 .expand').click(); + $('#search-node-1000 .activate').click(); + }, 500); }); } diff --git a/awx/ui/static/lib/ansible/utilities.js b/awx/ui/static/lib/ansible/utilities.js index aab37f5fa9..f9e88965bb 100644 --- a/awx/ui/static/lib/ansible/utilities.js +++ b/awx/ui/static/lib/ansible/utilities.js @@ -8,206 +8,233 @@ angular.module('Utilities',[]) .factory('ClearScope', function() { - return function(id) { - var element = document.getElementById(id); - var scope = angular.element(element).scope(); - scope.$destroy(); - } + return function(id) { + var element = document.getElementById(id); + var scope = angular.element(element).scope(); + scope.$destroy(); + } }) .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); - } - } + 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', '$location', function($rootScope, $location) { - 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 - // clicks 'OK' button. Set secondAlert to true, when a second dialog is needed. - 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.disableButtons2 = (disableButtons) ? true : false; - if (action) { - $('#alert-modal2').on('hidden', function() { - action(); - }); - } - $(document).bind('keydown', function(e) { - if (e.keyCode === 27) { - $('#alert-modal2').modal('hide'); - if (action) { - action(); - } - } - }); - } - 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' }); + 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 + // clicks 'OK' button. Set secondAlert to true, when a second dialog is needed. + 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.disableButtons2 = (disableButtons) ? true : false; + if (action) { + $('#alert-modal2').on('hidden', function() { + action(); + }); + } + $(document).bind('keydown', function(e) { + if (e.keyCode === 27) { + $('#alert-modal2').modal('hide'); + if (action) { + action(); + } + } + }); + } + 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) { - if (e.keyCode === 27) { - $('#alert-modal').modal('hide'); - if (action) { - action(); - } - } - }); + $(document).bind('keydown', function(e) { + if (e.keyCode === 27) { + $('#alert-modal').modal('hide'); + if (action) { + action(); + } + } + }); - $rootScope.disableButtons = (disableButtons) ? true : false; - if (action) { - $('#alert-modal').on('hidden', function() { - action(); - }); - } - } - } + $rootScope.disableButtons = (disableButtons) ? true : false; + if (action) { + $('#alert-modal').on('hidden', function() { + action(); + }); + } + } + } }]) .factory('ProcessErrors', ['$log', 'Alert', function($log, Alert) { - return function(scope, data, status, form, defaultMsg) { - if (status == 403) { - var msg = 'The API responded with a 403 Access Denied error. '; - if (data['detail']) { - msg += 'Detail: ' + data['detail']; - } - else { - msg += 'Please contact your system administrator.'; - } - Alert(defaultMsg.hdr, msg); - } - 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) { - var fieldErrors = false; - for (var field in form.fields ) { - if (form.fields[field].realName) { - if (data[form.fields[field].realName]) { - scope[field + '_api_error'] = data[form.fields[field]][0]; - fieldErrors = true; - } - } - if (form.fields[field].sourceModel) { - if (data[field]) { - scope[form.fields[field].sourceModel + '_' + form.fields[field].sourceField + '_api_error'] = - data[field][0]; - fieldErrors = true; - } - } - else { - if (data[field]) { - scope[field + '_api_error'] = data[field][0]; - fieldErrors = true; - } - } - } - if ( (!fieldErrors) && defaultMsg) { - Alert(defaultMsg.hdr, defaultMsg.msg); - } - } - else { - Alert(defaultMsg.hdr, defaultMsg.msg); - } - } + return function(scope, data, status, form, defaultMsg) { + if (status == 403) { + var msg = 'The API responded with a 403 Access Denied error. '; + if (data['detail']) { + msg += 'Detail: ' + data['detail']; + } + else { + msg += 'Please contact your system administrator.'; + } + Alert(defaultMsg.hdr, msg); + } + 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) { + var fieldErrors = false; + for (var field in form.fields ) { + if (form.fields[field].realName) { + if (data[form.fields[field].realName]) { + scope[field + '_api_error'] = data[form.fields[field]][0]; + fieldErrors = true; + } + } + if (form.fields[field].sourceModel) { + if (data[field]) { + scope[form.fields[field].sourceModel + '_' + form.fields[field].sourceField + '_api_error'] = + data[field][0]; + fieldErrors = true; + } + } + else { + if (data[field]) { + scope[field + '_api_error'] = data[field][0]; + fieldErrors = true; + } + } + } + if ( (!fieldErrors) && defaultMsg) { + Alert(defaultMsg.hdr, defaultMsg.msg); + } + } + else { + Alert(defaultMsg.hdr, defaultMsg.msg); + } + } }]) .factory('LoadBreadCrumbs', ['$rootScope', '$routeParams', '$location', function($rootScope, $routeParams, $location, Rest) { - return function(crumb) { - - //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 (crumb !== null && crumb !== undefined) { - var found = false; - for (var i=0; i < $rootScope.crumbCache.length; i++) { - if ($rootScope.crumbCache[i].path == crumb.path) { - found = true; - $rootScope.crumbCache[i] = crumb; - break; - } - } - if (found == false) { - $rootScope.crumbCache.push(crumb); - } - } + return function(crumb) { + //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 (crumb !== null && crumb !== undefined) { + var found = false; + for (var i=0; i < $rootScope.crumbCache.length; i++) { + if ($rootScope.crumbCache[i].path == crumb.path) { + found = true; + $rootScope.crumbCache[i] = crumb; + break; + } + } + if (found == false) { + $rootScope.crumbCache.push(crumb); + } + } - var paths = $location.path().replace(/^\//,'').split('/'); - var ppath = ''; - $rootScope.breadcrumbs = []; - if (paths.length > 1) { - var parent, child; - for (var i=0; i < paths.length - 1; i++) { - if (i > 0 && paths[i].match(/\d+/)) { - parent = paths[i-1]; - if (parent == 'inventories') { + var paths = $location.path().replace(/^\//,'').split('/'); + var ppath = ''; + $rootScope.breadcrumbs = []; + if (paths.length > 1) { + var parent, child; + for (var i=0; i < paths.length - 1; i++) { + if (i > 0 && paths[i].match(/\d+/)) { + parent = paths[i-1]; + if (parent == 'inventories') { child = 'inventory'; - } - else { - child = parent.substring(0,parent.length - 1); //assumes parent ends with 's' - } - // find the correct title - for (var j=0; j < $rootScope.crumbCache.length; j++) { - if ($rootScope.crumbCache[j].path == '/' + parent + '/' + paths[i]) { - child = $rootScope.crumbCache[j].title; - break; - } - } - $rootScope.breadcrumbs.push({ title: child, path: ppath + '/' + paths[i] }); - } - else { - $rootScope.breadcrumbs.push({ title: paths[i], path: ppath + '/' + paths[i] }); - } - ppath += '/' + paths[i]; - } - } - } + } + else { + child = parent.substring(0,parent.length - 1); //assumes parent ends with 's' + } + // find the correct title + for (var j=0; j < $rootScope.crumbCache.length; j++) { + if ($rootScope.crumbCache[j].path == '/' + parent + '/' + paths[i]) { + child = $rootScope.crumbCache[j].title; + break; + } + } + $rootScope.breadcrumbs.push({ title: child, path: ppath + '/' + paths[i] }); + } + else { + $rootScope.breadcrumbs.push({ title: paths[i], path: ppath + '/' + paths[i] }); + } + ppath += '/' + paths[i]; + } + } + } }]) .factory('ReturnToCaller', ['$location', function($location) { - 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. + 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('/'); - var newpath = ''; - idx = (idx == null || idx == undefined) ? paths.length - 1 : idx + 1; - for (var i=0; i < idx; i++) { - newpath += '/' + paths[i] - } - $location.path(newpath); - } + var paths = $location.path().replace(/^\//,'').split('/'); + var newpath = ''; + idx = (idx == null || idx == undefined) ? paths.length - 1 : idx + 1; + for (var i=0; i < idx; i++) { + newpath += '/' + paths[i] + } + $location.path(newpath); + } }]) .factory('FormatDate', [ function() { - return function(dt) { - var result = ('0' + (dt.getMonth() + 1)).slice(-2) + '/'; - result += ('0' + dt.getDate()).slice(-2) + '/'; - result += ('0' + (dt.getFullYear() - 2000)).slice(-2) + ' '; - result += ('0' + dt.getHours()).slice(-2) + ':'; - result += ('0' + dt.getMinutes()).slice(-2) + ':'; - result += ('0' + dt.getSeconds()).slice(-2); - //result += ('000' + dt.getMilliseconds()).slice(-3); - return result; - } + return function(dt) { + var result = ('0' + (dt.getMonth() + 1)).slice(-2) + '/'; + result += ('0' + dt.getDate()).slice(-2) + '/'; + result += ('0' + (dt.getFullYear() - 2000)).slice(-2) + ' '; + result += ('0' + dt.getHours()).slice(-2) + ':'; + result += ('0' + dt.getMinutes()).slice(-2) + ':'; + result += ('0' + dt.getSeconds()).slice(-2); + //result += ('000' + dt.getMilliseconds()).slice(-3); + return result; + } + }]) + + .factory('Wait', [ function() { + 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'); + if (directive == 'start') { + var docw = $(document).width(); + var doch = $(document).height(); + var spinnyw = $('.spinny').width(); + var spinnyh = $('.spinny').height(); + var x = (docw - spinnyw) / 2; + var y = (doch - spinnyh) / 2; + console.log($(document)); + $('.overlay').css({ + width: $('html').width(), + height: $(document).height() + 200 + }).fadeIn(500); + $('.spinny').css({ + top: y, + left: x, + }).fadeIn(500); + } + else { + $('.spinny, .overlay').fadeOut(2000); + } + } }]); \ No newline at end of file diff --git a/awx/ui/templates/ui/index.html b/awx/ui/templates/ui/index.html index 364ca4fee1..1658eff0f3 100644 --- a/awx/ui/templates/ui/index.html +++ b/awx/ui/templates/ui/index.html @@ -320,6 +320,9 @@ + +
    +

    working...