From a82edf0e4a28fbfaeb70b10605864c9c3de42353 Mon Sep 17 00:00:00 2001 From: chouseknecht Date: Tue, 9 Jul 2013 00:09:37 -0400 Subject: [PATCH] AC-171 first round of changes to make select dialogs more understandable. Also changed tooltip config to include a default time for hide and a default time for show. --- awx/ui/static/css/ansible-ui.css | 12 +- awx/ui/static/js/awx-min.js | 142 +++++++++---------- awx/ui/static/js/config.js | 4 +- awx/ui/static/js/controllers/Inventories.js | 6 +- awx/ui/static/js/controllers/JobTemplates.js | 1 - awx/ui/static/js/forms/Inventories.js | 21 ++- awx/ui/static/js/helpers/Access.js | 2 +- awx/ui/static/js/helpers/Groups.js | 5 +- awx/ui/static/js/helpers/Hosts.js | 50 +++++-- awx/ui/static/js/helpers/teams.js | 2 - awx/ui/static/js/lists/Groups.js | 3 +- awx/ui/static/js/lists/Hosts.js | 14 +- awx/ui/static/js/lists/Inventories.js | 2 +- awx/ui/static/js/lists/Organizations.js | 2 +- awx/ui/static/lib/ansible/form-generator.js | 10 +- awx/ui/static/lib/ansible/list-generator.js | 2 +- awx/ui/templates/ui/index.html | 4 +- private/.license_writer.py.swo | Bin 0 -> 12288 bytes 18 files changed, 161 insertions(+), 121 deletions(-) create mode 100644 private/.license_writer.py.swo diff --git a/awx/ui/static/css/ansible-ui.css b/awx/ui/static/css/ansible-ui.css index 7ae29c2a25..4471ba3a6d 100644 --- a/awx/ui/static/css/ansible-ui.css +++ b/awx/ui/static/css/ansible-ui.css @@ -26,6 +26,7 @@ /* Attempt to make button heights consistent. For some reason success, info, etc. are taller than plain .btn */ + .btn-success, .btn-danger, .btn-info, .btn-primary { padding-top: 1px; padding-bottom: 1px; @@ -37,6 +38,11 @@ min-width: 28px; } + .btn-pad { + padding-top: 3px; + padding-bottom: 1px; + } + .modal-footer .btn-success, .modal-footer .btn-danger, .modal-footer .btn-info, @@ -318,7 +324,11 @@ display: inline-block; vertical-align: top; margin-left: 10px; - margin-top: 3px; + padding-bottom: 22px; + } + + .list-actions .btn { + margin-right: 10px; } .status-actions { diff --git a/awx/ui/static/js/awx-min.js b/awx/ui/static/js/awx-min.js index cc060c320f..b0431caef4 100644 --- a/awx/ui/static/js/awx-min.js +++ b/awx/ui/static/js/awx-min.js @@ -4,7 +4,7 @@ * * awx-min.js * - * master-4ad18dd, Mon Jul 8 14:48:03 2013 -0400 + * master-18c53bb, Mon Jul 8 15:33:44 2013 -0400 * */ var urlPrefix="/static/"; @@ -20,7 +20,7 @@ controller:CredentialsEdit}).when("/credentials",{templateUrl:urlPrefix+"partial {templateUrl:urlPrefix+"partials/users.html",controller:CredentialsList}).when("/users/:user_id/permissions/add",{templateUrl:urlPrefix+"partials/users.html",controller:PermissionsAdd}).when("/users/:user_id/permissions",{templateUrl:urlPrefix+"partials/users.html",controller:PermissionsList}).when("/users/:user_id/permissions/:permission_id",{templateUrl:urlPrefix+"partials/users.html",controller:PermissionsEdit}).when("/users/:user_id/credentials/add",{templateUrl:urlPrefix+"partials/teams.html", controller:CredentialsAdd}).when("/teams/:user_id/credentials/:credential_id",{templateUrl:urlPrefix+"partials/teams.html",controller:CredentialsEdit}).when("/login",{templateUrl:urlPrefix+"partials/login-dialog.html",controller:Authenticate}).when("/logout",{templateUrl:urlPrefix+"partials/login-dialog.html",controller:Authenticate}).otherwise({redirectTo:"/"})}]).run(["$rootScope","CheckLicense","$location","Authorization","LoadBasePaths",function(h,m,k,g,c){c();h.breadcrumbs=[];h.crumbCache=[]; h.$on("$routeChangeStart",function(c,f,e){!1==g.isTokenValid()?f.templateUrl!=urlPrefix+"partials/login.html"&&k.path("/login"):((void 0==h.current_user||null==h.current_user)&&g.restoreUserInfo(),m());c=k.path().replace(/^\//,"").split("/")[0];""==c?$('.nav-tabs a[href="#organizations"]').tab("show"):(c.replace(/\_/g," "),$('.nav-tabs a[href="#'+c+'"]').tab("show"))});g.isTokenValid()||k.path("/login");c=k.path().replace(/^\//,"").split("/")[0];""==c?(c="organizations",k.path("/organizations")): -c.replace(/\_/g," ");$('.nav-tabs a[href="#'+c+'"]').tab("show");h.viewCurrentUser=function(){k.path("/users/"+h.current_user.id)}}]);var $AnsibleConfig={session_timeout:3600,tooltip_delay:2E3,debug_mode:!0};function AdminsList(h,m,k,g,c,p,f,e,l,a,d,b,z,w,B){h=B("organizations")+c.organization_id+"/users/";var u=l.inject(e,{mode:"select"});u.selected=[];u.PostRefreshRemove&&u.PostRefreshRemove();u.PostRefreshRemove=u.$on("PostRefresh",function(){$("tr.success").each(function(a){a=$(this).attr("ng-class");u[a]=""})});b({scope:u,set:"admins",list:e,url:h});z({scope:u,list:e,url:h});u.search(e.iterator);a();u.finishSelection=function(){var a=B("organizations")+c.organization_id+"/admins/";p.setUrl(a);u.queue= +c.replace(/\_/g," ");$('.nav-tabs a[href="#'+c+'"]').tab("show");h.viewCurrentUser=function(){k.path("/users/"+h.current_user.id)}}]);var $AnsibleConfig={session_timeout:3600,tooltip_delay:{show:500,hide:100},debug_mode:!0};function AdminsList(h,m,k,g,c,p,f,e,l,a,d,b,z,w,B){h=B("organizations")+c.organization_id+"/users/";var u=l.inject(e,{mode:"select"});u.selected=[];u.PostRefreshRemove&&u.PostRefreshRemove();u.PostRefreshRemove=u.$on("PostRefresh",function(){$("tr.success").each(function(a){a=$(this).attr("ng-class");u[a]=""})});b({scope:u,set:"admins",list:e,url:h});z({scope:u,list:e,url:h});u.search(e.iterator);a();u.finishSelection=function(){var a=B("organizations")+c.organization_id+"/admins/";p.setUrl(a);u.queue= [];u.$on("callFinished",function(){if(u.queue.length==u.selected.length){$('input[type="checkbox"]').prop("checked",!1);u.selected=[];for(var a=0,b=0;b +y.$$phase||y.$digest()});y.addGroup=function(){q({inventory_id:I,group_id:y.group_id})};y.editGroup=function(){v({inventory_id:I,group_id:y.group_id})};y.deleteGroup=function(){E({scope:y,inventory_id:I,group_id:y.group_id})};y.selectHost=function(){F({scope:y,inventory_id:I,group_id:y.group_id})};y.createHost=function(){D({scope:y,inventory_id:I,group_id:y.group_id})};y.editHost=function(a,b){r({scope:y,inventory_id:I,group_id:y.group_id,host_id:a,host_name:b})};y.deleteHost=function(a,b){K({scope:y, +inventory_id:I,group_id:y.group_id,host_id:a,host_name:b})};y.showEvents=function(a,c){l.setUrl(c);l.get().success(function(d,c,f,e){b({path:"/jobs/"+d.id,title:d.name});g.url("/jobs/"+d.id+"/job_events/?host="+escape(a))}).error(function(a,b,e,l){d(y,a,b,f,{hdr:"Error!",msg:"Failed to lookup last job: "+c+". GET status: "+b})})}}InventoriesEdit.$inject="$scope $rootScope $compile $location $log $routeParams InventoryForm GenerateForm Rest Alert ProcessErrors LoadBreadCrumbs RelatedSearchInit RelatedPaginateInit ReturnToCaller ClearScope LookUpInit Prompt OrganizationList TreeInit GetBasePath GroupsList GroupsEdit LoadInventory GroupsDelete HostsList HostsAdd HostsEdit HostsDelete RefreshTree ParseTypeChange".split(" ");function JobEventsList(h,m,k,g,c,p,f,e,l,a,d,b,z,w,B,u,x,t,s,n,C){B("htmlTemplate");e.base=k.path();h=x("jobs")+c.id+"/job_events/";k.path().replace(/^\//,"").split("/");var q=l.inject(e,{mode:"edit"});m.flashMessage=null;q.selected=[];q.expand=!0;q.parentNode="parent-event";q.childNode="child-event";q.RemovePostRefresh&&q.RemovePostRefresh();q.RemovePostRefresh=q.$on("PostRefresh",function(){for(var a=q[e.name],b,d=0;d a[d].event_level)a[d].ngclick="toggleChildren("+a[d].id+', "'+a[d].related.children+'")',a[d].ngicon="icon-collapse-alt",a[d]["class"]="parentNode";else{a[d]["class"]="childNode";b=a[d];var c=a[d].event_data,f="";if(c.res){var l=!1,n=void 0,n=void 0;if("string"==typeof c.res)n=(n=c.res.match(/\n/g))?n.length:1,l=!0,f+="\n",f+='\n";else{var k=void 0;for(k in c.res)if(("msg"==k||"stdout"==k||"stderr"== k)&&null!==c.res[k]&&""!==c.res[k]){f+="\n";n=(n=c.res[k].match(/\n/g))?n.length:1;f+='\n";l=!0}}f=l?'
\n'+f+"
\n":""}f=""==f?null:f;b.event_detail=f}a[d].show=!0;a[d].spaces=24*a[d].event_level;q.jobevents[d].status=q.jobevents[d].failed?"error":q.jobevents[d].changed?"changed":"success";b= new Date(a[d].created);a[d].created=C(b)}});b({scope:q,set:"jobevents",list:e,url:h});z({scope:q,list:e,url:h});c.host&&(q[e.iterator+"SearchField"]="host",q[e.iterator+"SearchValue"]=c.host,q[e.iterator+"SearchFieldLabel"]=e.fields.host.label);q.search(e.iterator);q.toggleChildren=function(a,b){s({scope:q,list:e,id:a,children:b})};a();q.viewJobEvent=function(a){n({event_id:a})};q.refresh=function(){q.expand=!0;q.search(e.iterator)};q.jobDetails=function(){k.path("/jobs/"+c.id)};q.jobSummary=function(){k.path("/jobs/"+ @@ -66,15 +66,15 @@ list:e,url:C});c.inventory__int&&(q[e.iterator+"SearchField"]="inventory",q[e.it {hdr:"Error!",msg:"Call to "+n+" failed. POST returned status: "+b})}):p.destroy().success(function(a,b,d,c){$("#prompt-modal").modal("hide");q.search(e.iterator)}).error(function(a,b,d,c){$("#prompt-modal").modal("hide");u(q,a,b,null,{hdr:"Error!",msg:"Call to "+n+" failed. DELETE returned status: "+b})})}})}).error(function(a,b,d,c){u(q,a,b,null,{hdr:"Error!",msg:"Failed to get job details. GET returned status: "+b})})};q.submitJob=function(a,b){s({scope:q,id:a,template:b})}} JobsListCtrl.$inject="$scope $rootScope $location $log $routeParams Rest Alert JobList GenerateList LoadBreadCrumbs Prompt SearchInit PaginateInit ReturnToCaller ClearScope ProcessErrors GetBasePath LookUpInit SubmitJob FormatDate".split(" "); function JobsEdit(h,m,k,g,c,p,f,e,l,a,d,b,z,w,B,u,x,t,s,n,C,q,v,A){function E(a){if(null!==a&&""!==a&&void 0!==a){var b=q("projects")+a+"/playbooks/";l.setUrl(b);l.get().success(function(a,b,d,c){r.playbook_options=[];for(b=0;bEnter variables using either JSON or YAML syntax. Use the radio button to toggle between the two.

JSON:
\n
{
"somevar": "somevalue",
"password": "magic"
}
\nYAML:
\n
---
somevar: somevalue
password: magic
\n

View JSON examples at www.json.org

View YAML examples at ansibleworks.com

', dataTitle:"Host Variables",dataPlacement:"right",dataContainer:"#form-modal"}},buttons:{save:{label:"Save",icon:"icon-ok","class":"btn-success",ngClick:"formSave()",ngDisabled:!0},reset:{ngClick:"formReset()",label:"Reset",icon:"icon-remove",ngDisabled:!0}},related:{}});angular.module("InventoryFormDefinition",[]).value("InventoryForm",{addTitle:"Create Inventory",editTitle:"{{ inventory_name }}",name:"inventory",well:!0,collapse:!0,collapseTitle:"Edit Inventory",collapseMode:"edit",twoColumns:!0,fields:{has_active_failures:{label:"Host Status",control:'
Failed jobs
',type:"custom",ngShow:"has_active_failures",readonly:!0,column:1},inventory_name:{realName:"name",label:"Name", type:"text",addRequired:!0,editRequired:!0,capitalize:!1,column:1},inventory_description:{realName:"description",label:"Description",type:"text",addRequired:!1,editRequired:!1,column:1},organization:{label:"Organization",type:"lookup",sourceModel:"organization",sourceField:"name",addRequired:!0,editRequired:!0,ngClick:"lookUpOrganization()",column:1},variables:{label:"Variables",type:"textarea",addRequired:!1,editRequird:!1,rows:10,"class":"modal-input-xlarge","default":"---",awPopOver:'

Enter variables using either JSON or YAML syntax. Use the radio button to toggle between the two.

View JSON examples at www.json.org

View YAML examples at ansibleworks.com

', -dataTitle:"Inventory Variables",dataPlacement:"bottom",column:2}},buttons:{save:{label:"Save",icon:"icon-ok","class":"btn-success",ngClick:"formSave()",ngDisabled:!0},reset:{ngClick:"formReset()",label:"Reset",icon:"icon-remove",ngDisabled:!0}},related:{groups:{type:"tree",open:!0,actions:{}},hosts:{type:"treeview",title:"{{ groupTitle }}",iterator:"host",actions:{add:{ngClick:"addHost()",icon:"icon-plus",label:"Add Host",awToolTip:"Add a host",ngHide:"createButtonShow == false"}},fields:{name:{key:!0, -label:"Host Name",ngClick:"editHost({{ host.id }}, '{{ host.name }}')"},has_active_failures:{label:"Failed jobs?",showValue:!1,ngClick:"showEvents('{{ host.name }}', '{{ host.related.last_job }}')",ngShow:"{{ host.has_active_failures }}",icon:"icon-exclamation-sign","class":"active-failures-{{ host.has_active_failures }}",text:"View failures",searchField:"has_active_failures",searchType:"boolean",searchOptions:[{name:"No",value:0},{name:"Yes",value:1}]}},fieldActions:{edit:{ngClick:"editHost({{ host.id }}, '{{ host.name }}')", -icon:"icon-edit",label:"Edit","class":"btn-success",awToolTip:"Edit host"},"delete":{ngClick:"deleteHost({{ host.id }}, '{{ host.name }}')",icon:"icon-remove",label:"Delete","class":"btn-danger",awToolTip:"Remove host"}}}}});angular.module("JobEventFormDefinition",[]).value("JobEventForm",{editTitle:"{{ id }} - {{ event }}",name:"job_events","class":"horizontal-narrow",well:!1,fields:{event_display:{label:"Event",type:"text",readonly:!0},created:{label:"Created",type:"text",readonly:!0,"class":"span3"},status:{label:"Status",type:"text","class":"job-{{ event_status }}",readonly:!0},host:{label:"Host",type:"text",readonly:!0},status:{label:"Status",type:"text","class":"job-{{ event_status }}",readonly:!0},event_data:{label:"Event Data", +dataTitle:"Inventory Variables",dataPlacement:"bottom",column:2}},buttons:{save:{label:"Save",icon:"icon-ok","class":"btn-success",ngClick:"formSave()",ngDisabled:!0},reset:{ngClick:"formReset()",label:"Reset",icon:"icon-remove",ngDisabled:!0}},related:{groups:{type:"tree",open:!0,actions:{}},hosts:{type:"treeview",title:"{{ groupTitle }}",iterator:"host",actions:{select:{ngClick:"selectHost()",icon:"icon-check",label:"Add Existing Host",awToolTip:"Select existing host",ngHide:"createButtonShow == false", +"class":"btn btn-pad"},create:{ngClick:"createHost()",icon:"icon-plus",label:"Create New Host",awToolTip:"Create a new host",ngHide:"createButtonShow == false","class":"btn-success"}},fields:{name:{key:!0,label:"Host Name",ngClick:"editHost({{ host.id }}, '{{ host.name }}')"},has_active_failures:{label:"Failed jobs?",showValue:!1,ngClick:"showEvents('{{ host.name }}', '{{ host.related.last_job }}')",ngShow:"{{ host.has_active_failures }}",icon:"icon-exclamation-sign","class":"active-failures-{{ host.has_active_failures }}", +text:"View failures",searchField:"has_active_failures",searchType:"boolean",searchOptions:[{name:"No",value:0},{name:"Yes",value:1}]}},fieldActions:{edit:{ngClick:"editHost({{ host.id }}, '{{ host.name }}')",icon:"icon-edit",label:"Edit","class":"btn-success",awToolTip:"Edit host"},"delete":{ngClick:"deleteHost({{ host.id }}, '{{ host.name }}')",icon:"icon-remove",label:"Delete","class":"btn-danger",awToolTip:"Remove host"}}}}});angular.module("JobEventFormDefinition",[]).value("JobEventForm",{editTitle:"{{ id }} - {{ event }}",name:"job_events","class":"horizontal-narrow",well:!1,fields:{event_display:{label:"Event",type:"text",readonly:!0},created:{label:"Created",type:"text",readonly:!0,"class":"span3"},status:{label:"Status",type:"text","class":"job-{{ event_status }}",readonly:!0},host:{label:"Host",type:"text",readonly:!0},status:{label:"Status",type:"text","class":"job-{{ event_status }}",readonly:!0},event_data:{label:"Event Data", type:"textarea","class":"modal-input-xlarge",rows:10,readonly:!0}},buttons:{},related:{}});angular.module("JobFormDefinition",[]).value("JobForm",{addTitle:"Create Job",editTitle:"{{ name }}",name:"jobs",well:!0,twoColumns:!0,fields:{name:{label:"Name",type:"text",addRequired:!0,editRequired:!0,column:1},description:{label:"Description",type:"text",addRequired:!1,editRequired:!1,column:1},job_type:{label:"Job Type",type:"select",ngOptions:"type.label for type in job_type_options","default":"run",addRequired:!0,editRequired:!0,awPopOver:"

When this template is submitted as a job, setting the type to run will execute the playbook, running tasks on the selected hosts.

Setting the type to check will not execute the playbook. Instead, ansible will check playbook syntax, test environment setup and report problems.

", dataTitle:"Job Type",dataPlacement:"right",column:1},inventory:{label:"Inventory",type:"lookup",sourceModel:"inventory",sourceField:"name",addRequired:!0,editRequired:!0,ngClick:"lookUpInventory()",column:1},project:{label:"Project",type:"lookup",sourceModel:"project",sourceField:"name",addRequired:!0,editRequired:!0,ngClick:"lookUpProject()",column:1},playbook:{label:"Playbook",type:"select",ngOptions:"book for book in playbook_options",id:"playbook-select",addRequired:!0,editRequired:!0,column:1}, credential:{label:"Credential",type:"lookup",sourceModel:"credential",sourceField:"name",ngClick:"lookUpCredential()",addRequired:!1,editRequired:!1,column:2},forks:{label:"Forks",id:"forks-number",type:"number",integer:!0,min:0,max:100,slider:!0,"class":"input-mini","default":"0",addRequired:!1,editRequired:!1,column:2,awPopOver:"

The number of parallel or simultaneous processes to use while executing the playbook. Provide a value between 0 and 100. A value of zero will use the ansible default setting of 5 parallel processes.

", @@ -190,7 +189,7 @@ a})}));e||m("Access Denied","You do not have access to this function. Please con c;b++)a[b].id==l&&(d=b,c=!0);"icon-expand-alt"==a[d].ngicon?f(d):e(d)}}]);angular.module("EventsHelper",["RestServices","Utilities","JobEventFormDefinition"]).factory("EventView",["$rootScope","$location","$log","$routeParams","Rest","Alert","JobEventForm","GenerateForm","Prompt","ProcessErrors","GetBasePath","FormatDate",function(h,m,k,g,c,p,f,e,l,a,d,b){return function(l){var k=l.event_id;l=d("base")+"job_events/"+k+"/";var g=e.inject(f,{mode:"edit",modal:!0,related:!1});e.reset();g.formModalAction=function(){$("#form-modal").modal("hide")};g.formModalActionLabel="OK"; g.formModalHeader="View Event";g.formModalCancelShow=!1;$("#form-modal .btn-success").removeClass("btn-success").addClass("btn-none");c.setUrl(l);c.get().success(function(a,d,c,e){for(var l in f.fields)"status"==l?g.status=a.failed?"error":"success":"event_data"==l?g.event_data=JSON.stringify(a.event_data,void 0,"\t"):"host"==l?a.summary_fields&&a.summary_fields.host&&(g.host=a.summary_fields.host.name):"event_display"==l?g.event_display=a.event_display.replace(/^\u00a0*/g,""):"created"==l?g.created= b(new Date(a.created)):a[l]&&(g[l]=a[l])}).error(function(b,d,c,e){a(g,b,d,f,{hdr:"Error!",msg:"Failed to retrieve host: "+k+". GET status: "+d})});g.$$phase||g.$digest()}}]);angular.module("GroupsHelper","RestServices Utilities ListGenerator GroupListDefinition SearchHelper PaginateHelper ListGenerator AuthService GroupsHelper InventoryHelper".split(" ")).factory("GroupsList",["$rootScope","$location","$log","$routeParams","Rest","Alert","GroupList","GenerateList","Prompt","SearchInit","PaginateInit","ProcessErrors","GetBasePath","GroupsAdd","RefreshTree",function(h,m,k,g,c,p,f,e,l,a,d,b,z,w,B){return function(b){var l=b.inventory_id,k=void 0!==b.group_id?b.group_id: -null;b=z("inventory")+l+"/groups/";var h=e.inject(f,{id:"form-modal-body",mode:"select",breadCrumbs:!1,selectButton:!1});h.formModalActionLabel="Finished";h.formModalHeader="Add Group";h.formModalCancelShow=!0;h.formModalActionClass="btn btn-success";$("#form-modal .btn-none").removeClass("btn-none").addClass("btn-success");$("#form-modal").modal();h.selected=[];h.PostRefreshRemove&&h.PostRefreshRemove();h.PostRefreshRemove=h.$on("PostRefresh",function(){$("tr.success").each(function(a){a=$(this).attr("ng-class"); +null;b=z("inventory")+l+"/groups/";var h=e.inject(f,{id:"form-modal-body",mode:"select",breadCrumbs:!1,selectButton:!1});h.formModalActionLabel="Select";h.formModalHeader="Add Groups";h.formModalCancelShow=!0;h.formModalActionClass="btn btn-success";$("#form-modal .btn-none").removeClass("btn-none").addClass("btn-success");$("#form-modal").modal();h.selected=[];h.PostRefreshRemove&&h.PostRefreshRemove();h.PostRefreshRemove=h.$on("PostRefresh",function(){$("tr.success").each(function(a){a=$(this).attr("ng-class"); h[a]=""});if(g.group_id)for(var a=0;a button to create a new user.',editInstructions:"Create a new credential from either the Teams tab or the Users tab. Teams and Users each have an associated set of Credentials.",index:!0, +function(c,e){for(var l=[],a=0;a button to create a new user.',editInstructions:"Create a new credential from either the Teams tab or the Users tab. Teams and Users each have an associated set of Credentials.",index:!0, hover:!0,fields:{name:{key:!0,label:"Name"},description:{label:"Description"},team:{label:"Team",ngBind:"credential.summary_fields.team.name",sourceModel:"team",sourceField:"name"},user:{label:"User",ngBind:"credential.summary_fields.user.usename",sourceModel:"user",sourceField:"username"}},actions:{add:{icon:"icon-plus",label:"Add",mode:"all",ngClick:"addCredential()",basePaths:["teams","users"],"class":"btn-success btn-small",awToolTip:"Create a new credential"}},fieldActions:{edit:{ngClick:"editCredential({{ credential.id }})", -icon:"icon-edit",label:"Edit","class":"btn-small btn-success",awToolTip:"View/Edit credential"},"delete":{ngClick:"deleteCredential({{ credential.id }},'{{ credential.name }}')",icon:"icon-remove",label:"Delete","class":"btn-small btn-danger",awToolTip:"Delete credential"}}});angular.module("GroupListDefinition",[]).value("GroupList",{name:"groups",iterator:"group",selectTitle:"Add Group",editTitle:"Groups",selectInstructions:'Click on a row to select it, and Finished when done. Click the green Add to create a new row.',index:!0,well:!1,fields:{name:{key:!0,label:"Name"},description:{label:"Description"}},actions:{add:{label:"Add",icon:"icon-plus",mode:"all",ngClick:"createGroup()","class":"btn-success btn-small",awToolTip:"Create a new group"}}, -fieldActions:{edit:{label:"Edit",ngClick:"editGroup({{ group.id }})",icon:"icon-edit","class":"btn-small btn-success",awToolTip:"View/Edit group"},"delete":{label:"Delete",ngClick:"deleteGroup({{ group.id }},'{{ group.name }}')",icon:"icon-remove","class":"btn-small btn-danger",awToolTip:"Delete group"}}});angular.module("HostListDefinition",[]).value("HostList",{name:"hosts",iterator:"host",selectTitle:"Add Host",selectInstructions:'Click on a row to select it, and click Finished when done. Use the green button to create a new row.',editTitle:"Hosts",index:!0,well:!0,fields:{name:{key:!0,label:"Name",linkTo:"/inventories/{{ inventory_id }}/hosts/{{ host.id }}"},description:{label:"Description"}},actions:{add:{icon:"icon-plus",label:"Add",mode:"all",ngClick:"createHost()", -ngHide:"showAddButton == false","class":"btn-success btn-small",awToolTip:"Create a new host"}},fieldActions:{edit:{label:"Edit",ngClick:"editHost({{ host.id }})",icon:"icon-edit","class":"btn-small btn-success",awToolTip:"View/Edit host"},"delete":{label:"Delete",ngClick:"deleteHost({{ host.id }},'{{ host.name }}')",icon:"icon-remove","class":"btn-small btn-danger",awToolTip:"Delete host"}}});angular.module("InventoriesListDefinition",[]).value("InventoryList",{name:"inventories",iterator:"inventory",selectTitle:"Add Inventories",editTitle:"Inventories",selectInstructions:'Click on a row to select it, and click Finished when done. Use the green button to create a new row.',index:!0,hover:!0,fields:{name:{key:!0,label:"Name"},description:{label:"Descriptions"},organization:{label:"Organization",ngBind:"inventory.summary_fields.organization.name",sourceModel:"organization", +icon:"icon-edit",label:"Edit","class":"btn-small btn-success",awToolTip:"View/Edit credential"},"delete":{ngClick:"deleteCredential({{ credential.id }},'{{ credential.name }}')",icon:"icon-remove",label:"Delete","class":"btn-small btn-danger",awToolTip:"Delete credential"}}});angular.module("GroupListDefinition",[]).value("GroupList",{name:"groups",iterator:"group",selectTitle:"Add Group",editTitle:"Groups",index:!0,well:!1,fields:{name:{key:!0,label:"Name"},description:{label:"Description"}},actions:{add:{label:"Create New Group",icon:"icon-plus",mode:"all",ngClick:"createGroup()","class":"btn-success btn-small",awToolTip:"Create a new group"}},fieldActions:{edit:{label:"Edit",ngClick:"editGroup({{ group.id }})",icon:"icon-edit","class":"btn-small btn-success",awToolTip:"View/Edit group"}, +"delete":{label:"Delete",ngClick:"deleteGroup({{ group.id }},'{{ group.name }}')",icon:"icon-remove","class":"btn-small btn-danger",awToolTip:"Delete group"}}});angular.module("HostListDefinition",[]).value("HostList",{name:"hosts",iterator:"host",selectTitle:"Select Host",editTitle:"Hosts",index:!0,well:!0,fields:{name:{key:!0,label:"Host Name",linkTo:"/inventories/{{ inventory_id }}/hosts/{{ host.id }}"},description:{label:"Description"}},actions:{},fieldActions:{edit:{label:"Edit",ngClick:"editHost({{ host.id }})",icon:"icon-edit","class":"btn-small btn-success",awToolTip:"View/Edit host"},"delete":{label:"Delete",ngClick:"deleteHost({{ host.id }},'{{ host.name }}')", +icon:"icon-remove","class":"btn-small btn-danger",awToolTip:"Delete host"}}});angular.module("InventoriesListDefinition",[]).value("InventoryList",{name:"inventories",iterator:"inventory",selectTitle:"Add Inventories",editTitle:"Inventories",selectInstructions:'Click on a row to select it, and click Finished when done. Use the green button to create a new row.',index:!0,hover:!0,fields:{name:{key:!0,label:"Name"},description:{label:"Descriptions"},organization:{label:"Organization",ngBind:"inventory.summary_fields.organization.name",sourceModel:"organization", sourceField:"name"},has_active_failures:{label:"Failed Jobs?",showValue:!1,text:"View failures",ngShow:"{{ inventory.has_active_failures }}",icon:"icon-exclamation-sign","class":"active-failures-{{ inventory.has_active_failures }}",ngClick:"viewJobs({{ inventory.id }})",searchField:"has_active_failures",searchType:"boolean",searchOptions:[{name:"No",value:0},{name:"Yes",value:1}],excludeModal:!0}},actions:{add:{label:"Add",icon:"icon-plus",mode:"all",ngClick:"addInventory()","class":"btn-small btn-success", -awToolTip:"Create a new row"}},fieldActions:{edit:{label:"Edit",ngClick:"editInventory({{ inventory.id }})",icon:"icon-edit","class":"btn-small btn-success",awToolTip:"View/Edit inventory"},"delete":{label:"Delete",ngClick:"deleteInventory({{ inventory.id }},'{{ inventory.name }}')",icon:"icon-remove","class":"btn-small btn-danger",awToolTip:"Delete inventory"}}});angular.module("JobEventsListDefinition",[]).value("JobEventList",{name:"jobevents",iterator:"jobevent",editTitle:"Job Events",index:!1,hover:!0,hasChildren:!0,filterBy:"{ show: true }",fields:{created:{label:"Date",key:!0,nosort:!0,searchable:!1,link:!1},status:{label:"Status",icon:"icon-circle","class":"job-{{ jobevent.status }}",searchField:"failed",searchType:"boolean",searchOptions:[{name:"success",value:0},{name:"error",value:1}],nosort:!0,searchable:!1},event_display:{label:"Event",hasChildren:!0, +awToolTip:"Create a new inventory"}},fieldActions:{edit:{label:"Edit",ngClick:"editInventory({{ inventory.id }})",icon:"icon-edit","class":"btn-small btn-success",awToolTip:"View/Edit inventory"},"delete":{label:"Delete",ngClick:"deleteInventory({{ inventory.id }},'{{ inventory.name }}')",icon:"icon-remove","class":"btn-small btn-danger",awToolTip:"Delete inventory"}}});angular.module("JobEventsListDefinition",[]).value("JobEventList",{name:"jobevents",iterator:"jobevent",editTitle:"Job Events",index:!1,hover:!0,hasChildren:!0,filterBy:"{ show: true }",fields:{created:{label:"Date",key:!0,nosort:!0,searchable:!1,link:!1},status:{label:"Status",icon:"icon-circle","class":"job-{{ jobevent.status }}",searchField:"failed",searchType:"boolean",searchOptions:[{name:"success",value:0},{name:"error",value:1}],nosort:!0,searchable:!1},event_display:{label:"Event",hasChildren:!0, ngClick:"toggleChildren({{ jobevent.id }}, '{{ jobevent.related.children }}')",nosort:!0,searchable:!1,ngClass:"{{ jobevent.class }}",appendHTML:"jobevent.event_detail"},host:{label:"Host",ngBind:"jobevent.summary_fields.host.name",searchField:"hosts__name",nosort:!0,id:"job-event-host-header"}},actions:{refresh:{ngClick:"refresh()",icon:"icon-refresh",label:"Refresh",awToolTip:"Refresh the page","class":"btn-small btn-success",mode:"all"},edit:{label:"Details",ngClick:"jobDetails()",icon:"icon-zoom-in", "class":"btn btn-small",awToolTip:"Edit job details",mode:"all"},summary:{label:"Hosts",icon:"icon-th-large",ngClick:"jobSummary()","class":"btn btn-small",awToolTip:"View host summary",mode:"all"}},fieldActions:{edit:{label:"View",ngClick:"viewJobEvent({{ jobevent.id }})",icon:"icon-zoom-in","class":"btn-small",awToolTip:"View event details"}}});angular.module("JobHostDefinition",[]).value("JobHostList",{name:"jobhosts",iterator:"jobhost",editTitle:"Job Host Summary",index:!0,hover:!0,fields:{host:{label:"Host",key:!0,sourceModel:"host",sourceField:"name",ngBind:"jobhost.host_name",ngClick:"showEvents('{{ jobhost.summary_fields.host.name }}','{{ jobhost.related.job }}')"},status:{label:"Status",icon:"icon-circle","class":"job-{{ jobhost.status }}",searchField:"failed",searchType:"boolean",searchOptions:[{name:"success",value:0},{name:"error", value:1}]},ok:{label:"Success",searchable:!1},changed:{label:"Changed",searchable:!1},failures:{label:"Failure",searchType:"gtzero"},dark:{label:"Unreachable",searchable:!1},skipped:{label:"Skipped",searchable:!1}},actions:{refresh:{label:"Refresh",icon:"icon-refresh",ngClick:"refresh()","class":"btn-success btn-small",awToolTip:"Refresh the page",mode:"all"},edit:{label:"Details",icon:"icon-edit",ngClick:"jobDetails()","class":"btn btn-small",awToolTip:"Edit job details",mode:"all"},events:{label:"Events", @@ -275,8 +275,8 @@ icon:"icon-list-ul",ngClick:"jobEvents()","class":"btn btn-small",awToolTip:"Vie events:{label:"Events",icon:"icon-list-ul",mode:"all",ngClick:"viewEvents({{ job.id }}, '{{ job.name }}')","class":"btn btn-small",awToolTip:"View events",ngDisabled:"job.status == 'new'"},edit:{label:"Details",icon:"icon-zoom-in",ngClick:"editJob({{ job.id }}, '{{ job.name }}')","class":"btn btn-small",awToolTip:"View job details"},rerun:{icon:"icon-retweet",mode:"all",ngClick:"submitJob({{ job.id }}, '{{ job.summary_fields.job_template.name }}' )","class":"btn-success btn-small",awToolTip:"Re-run this job"}, cancel:{icon:"icon-minus-sign",mode:"all",ngClick:"deleteJob({{ job.id }})","class":"btn-danger btn-small",awToolTip:"Cancel job",ngDisabled:"job.status != 'new' && job.status != 'pending' && job.status != 'running'"}}});angular.module("JobTemplatesListDefinition",[]).value("JobTemplateList",{name:"job_templates",iterator:"job_template",selectTitle:"Add Job Template",editTitle:"Job Templates",selectInstructions:'Click on a row to select it, and click Finished when done. Use the green button to create a new row.',index:!0,hover:!0,fields:{name:{key:!0,label:"Name"},description:{label:"Description"}},actions:{add:{label:"Add",icon:"icon-plus",mode:"all",ngClick:"addJobTemplate()","class":"btn-success btn-small", basePaths:["job_templates"],awToolTip:"Create a new template"}},fieldActions:{edit:{label:"Edit",ngClick:"editJobTemplate({{ job_template.id }})",icon:"icon-edit",awToolTip:"Edit template","class":"btn-small btn-success"},submit:{label:"Launch",icon:"icon-rocket",mode:"all","class":"btn-small btn-success",ngClick:"submitJob({{ job_template.id }})",awToolTip:"Start a job using this template"},"delete":{label:"Delete",ngClick:"deleteJobTemplate({{ job_template.id }},'{{ job_template.name }}')",icon:"icon-remove", -"class":"btn-danger btn-small",awToolTip:"Delete template"}}});angular.module("OrganizationListDefinition",[]).value("OrganizationList",{name:"organizations",iterator:"organization",selectTitle:"Add Organizations",editTitle:"Organizations",hover:!0,index:!0,fields:{name:{key:!0,label:"Name"},description:{label:"Description"}},actions:{add:{label:"Add",icon:"icon-plus",mode:"all",ngClick:"addOrganization()","class":"btn-success btn-small",awToolTip:"Create a new row"}},fieldActions:{edit:{label:"Edit",ngClick:"editOrganization({{ organization.id }})",icon:"icon-edit", -"class":"btn-small btn-success",awToolTip:"View/Edit organization"},"delete":{label:"Delete",ngClick:"deleteOrganization({{ organization.id }},'{{ organization.name }}')",icon:"icon-remove","class":"btn-small btn-danger",awToolTip:"Delete organization"}}});angular.module("PermissionListDefinition",[]).value("PermissionList",{name:"permissions",iterator:"permission",selectTitle:"Add Permission",selectInstructions:'Click on a row to select it, and click Finished when done. Use the green button to create a new row.',editTitle:"Permissions",index:!0,well:!0,fields:{name:{key:!0,label:"Name",ngClick:"editPermission({{ permission.id }})"},inventory:{label:"Inventory",sourceModel:"inventory",sourceField:"name",ngBind:"permission.summary_fields.inventory.name"}, +"class":"btn-danger btn-small",awToolTip:"Delete template"}}});angular.module("OrganizationListDefinition",[]).value("OrganizationList",{name:"organizations",iterator:"organization",selectTitle:"Add Organizations",editTitle:"Organizations",hover:!0,index:!0,fields:{name:{key:!0,label:"Name"},description:{label:"Description"}},actions:{add:{label:"Add",icon:"icon-plus",mode:"all",ngClick:"addOrganization()","class":"btn-success btn-small",awToolTip:"Create a new organization"}},fieldActions:{edit:{label:"Edit",ngClick:"editOrganization({{ organization.id }})", +icon:"icon-edit","class":"btn-small btn-success",awToolTip:"View/Edit organization"},"delete":{label:"Delete",ngClick:"deleteOrganization({{ organization.id }},'{{ organization.name }}')",icon:"icon-remove","class":"btn-small btn-danger",awToolTip:"Delete organization"}}});angular.module("PermissionListDefinition",[]).value("PermissionList",{name:"permissions",iterator:"permission",selectTitle:"Add Permission",selectInstructions:'Click on a row to select it, and click Finished when done. Use the green button to create a new row.',editTitle:"Permissions",index:!0,well:!0,fields:{name:{key:!0,label:"Name",ngClick:"editPermission({{ permission.id }})"},inventory:{label:"Inventory",sourceModel:"inventory",sourceField:"name",ngBind:"permission.summary_fields.inventory.name"}, project:{label:"Project",sourceModel:"project",sourceField:"name",ngBind:"permission.summary_fields.project.name"},permission_type:{label:"Permission"}},actions:{add:{icon:"icon-plus",label:"Add",mode:"all",ngClick:"addPermission()","class":"btn-success btn-small",awToolTip:"Add a new permission"}},fieldActions:{edit:{label:"Edit",ngClick:"editPermission({{ permission.id }})",icon:"icon-edit","class":"btn-small btn-success",awToolTip:"View/Edit permission"},"delete":{label:"Delete",ngClick:"deletePermission({{ permission.id }},'{{ permission.name }}')", icon:"icon-remove","class":"btn-small btn-danger",awToolTip:"Delete permission"}}});angular.module("ProjectsListDefinition",[]).value("ProjectList",{name:"projects",iterator:"project",selectTitle:"Add Project",editTitle:"{{ name }}",selectInstructions:'Click on a row to select it, and click Finished when done. Use the green button to create a new row.',index:!0,hover:!0,fields:{name:{key:!0,label:"Name"},description:{label:"Descriptions"}},actions:{add:{label:"Add",icon:"icon-plus",mode:"all",ngClick:"addProject()","class":"btn-success btn-small",awToolTip:"Create a new project"}}, fieldActions:{edit:{label:"Edit",ngClick:"editProject({{ project.id }})",icon:"icon-edit","class":"btn-small btn-success",awToolTip:"View/edit project"},"delete":{label:"Delete",ngClick:"deleteProject({{ project.id }},'{{ project.name }}')",icon:"icon-remove","class":"btn-small btn-danger",awToolTip:"Delete project"}}});angular.module("TeamsListDefinition",[]).value("TeamList",{name:"teams",iterator:"team",selectTitle:"Add Team",editTitle:"Teams",selectInstructions:'Click on a row to select it, and click Finished when done. Use the green button to create a new row.',index:!0,hover:!0,fields:{name:{key:!0,label:"Name"},description:{label:"Descriptions"},organization:{label:"Organization",ngBind:"team.organization_name",sourceModel:"organization",sourceField:"name"}},actions:{add:{label:"Add", @@ -331,18 +331,18 @@ a+='
  • ← Prev
  • \n";a+='
  • → Next
  • \n";a+="\n";a+="\x3c!-- well --\x3e\n"}"inventory"==this.form.name&&"edit"==c.mode?a+=this.buildTree(c):!this.modal&&(c.related&&this.form.related)&&(a+=this.buildCollections(c)); return a},buildTree:function(f){var a=1,d=this.form;html='
    \n';html+="

    Inventory Content

    \n";html+="
    \n";for(var b in d.related){if("tree"==d.related[b].type)html+='
    ',html+='
    ',html+='',html+='', html+='',html+="
    \n",html+='
    \n',html+='
    \n',html+="
    \n";else{html+='
    \n'; -html+='
    \n';html+='

    '+d.related[b].title+"

    \n";html+=g({iterator:d.related[b].iterator,template:d.related[b],mini:!0});html+='
    \n';for(var h in d.related[b].actions)html+='\n";html+="
    \n";html+='
    \n';html+='\n';html+="\n";html+="\n";html+="\n";for(var k in d.related[b].fields)html+= -'\n";var m=1,p=d.related[b].base?d.related[b].base:b,p=p.replace(/^\//,"");for(k in d.related[b].fields)m++,html+=e({list:d.related[b],fld:k,options:f,base:p});html+='";html+="\n";html+='\n';html+='\n';html+="\n";html+='\n';html+='\n';html+="\n";html+="\n";html+="
    #"+d.related[b].fields[k].label,html+=' \n';html+="{{ $index + ("+d.related[b].iterator+ -"Page * "+d.related[b].iterator+"PageSize) + 1 }}.';for(h in d.related[b].fieldActions)html+=' ";html+="
    No records matched your search.
    Loading...
    \n";html+="
    \n";html+="
    \n";html+=c({set:b,iterator:d.related[b].iterator,mini:!0})}a++}html+="
    \n";return html+="
    \n"},buildCollections:function(f){var a=1,d=this.form;html='
    \n';for(var b in d.related)if("collection"==d.related[b].type){html+= -"

    "+d.related[b].title+"

    \n";html+="
    \n";d.related[b].instructions&&(html+='
    \n',html+='\n',html+="Hint: "+d.related[b].instructions+"\n",html+="
    \n");html+='
    \n';html+=g({iterator:d.related[b].iterator,template:d.related[b],mini:!1});html+='
    \n';for(var h in d.related[b].actions){var k=d.related[b].actions[h];html+= -'\n"}html+="
    \n";html+='
    \n';html+='\n';html+="\n";html+="\n";html+=void 0==d.related[b].index||!1!==d.related[b].index?"\n":"";for(var m in d.related[b].fields)html+= -'\n";var p=1,k=d.related[b].base?d.related[b].base:b,k=k.replace(/^\//,"");for(m in d.related[b].fields)p++,html+=e({list:d.related[b],fld:m,options:f,base:k});html+='\n';html+="\n";html+='\n';html+='\n'; -html+="\n";html+="\n";html+="
    #"+d.related[b].fields[m].label,html+=' \n';if(void 0==d.related[b].index||!1!==d.related[b].index)html+= -"{{ $index + ("+d.related[b].iterator+"Page * "+d.related[b].iterator+"PageSize) + 1 }}.';for(h in d.related[b].fieldActions)k=d.related[b].fieldActions[h],html+='
    No records matched your search.
    Loading...
    \n";html+="
    \n";html+="
    \n";html+=c({set:b,iterator:d.related[b].iterator,mini:!0});html+="
    \n";a++}html+="

    \n";return html+="

    \n"}}}]);angular.module("GeneratorHelpers",["GeneratorHelpers"]).factory("Attr",function(){return function(h,m){var k;k="string"===typeof h[m]?h[m].replace(/\'/g,"""):h[m];switch(m){case "ngClick":k='ng-click="'+k+'" ';break;case "ngOptions":k='ng-options="'+k+'" ';break;case "ngClass":k='ng-class="'+k+'" ';break;case "ngChange":k='ng-change="'+k+'" ';break;case "ngDisabled":k='ng-disabled="'+k+'" ';break;case "ngShow":k='ng-show="'+k+'" ';break;case "ngHide":k='ng-hide="'+k+'" ';break;case "ngBind":k= +html+='
    \n';html+='

    '+d.related[b].title+"

    \n";html+=g({iterator:d.related[b].iterator,template:d.related[b],mini:!0});html+='
    \n';for(var h in d.related[b].actions)html+='\n";html+="
    \n";html+='
    \n';html+='\n'; +html+="\n";html+="\n";html+="\n";for(var k in d.related[b].fields)html+='\n";var m=1,p=d.related[b].base?d.related[b].base:b,p=p.replace(/^\//,"");for(k in d.related[b].fields)m++,html+=e({list:d.related[b],fld:k,options:f,base:p});html+='";html+="\n";html+='\n';html+='\n'; +html+="\n";html+='\n';html+='\n';html+="\n";html+="\n";html+="
    #"+d.related[b].fields[k].label,html+=' \n';html+="{{ $index + ("+d.related[b].iterator+"Page * "+d.related[b].iterator+"PageSize) + 1 }}.';for(h in d.related[b].fieldActions)html+=' ";html+="
    No records matched your search.
    Loading...
    \n";html+="
    \n";html+="
    \n";html+=c({set:b,iterator:d.related[b].iterator,mini:!0})}a++}html+="\n";return html+="\n"},buildCollections:function(f){var a=1,d=this.form;html='
    \n';for(var b in d.related)if("collection"== +d.related[b].type){html+="

    "+d.related[b].title+"

    \n";html+="
    \n";d.related[b].instructions&&(html+='
    \n',html+='\n',html+="Hint: "+d.related[b].instructions+"\n",html+="
    \n");html+='
    \n';html+=g({iterator:d.related[b].iterator,template:d.related[b],mini:!0});html+='
    \n';for(var h in d.related[b].actions){var k= +d.related[b].actions[h];html+='\n"}html+="
    \n";html+='
    \n';html+='\n';html+= +"\n";html+="\n";html+=void 0==d.related[b].index||!1!==d.related[b].index?"\n":"";for(var m in d.related[b].fields)html+='\n";html+='\n';if(void 0==d.related[b].index||!1!==d.related[b].index)html+="\n";var p=1,k=d.related[b].base?d.related[b].base:b,k=k.replace(/^\//,"");for(m in d.related[b].fields)p++,html+=e({list:d.related[b],fld:m,options:f,base:k});html+='\n'; +html+="\n";html+='\n';html+='\n';html+="\n";html+="\n";html+="
    #"+d.related[b].fields[m].label,html+=' ";html+="
    {{ $index + ("+d.related[b].iterator+"Page * "+d.related[b].iterator+"PageSize) + 1 }}.';for(h in d.related[b].fieldActions)k=d.related[b].fieldActions[h], +html+='
    No records matched your search.
    Loading...
    \n";html+="
    \n";html+="
    \n";html+=c({set:b,iterator:d.related[b].iterator,mini:!0});html+="
    \n";a++}html+="

    \n";return html+="\n"}}}]);angular.module("GeneratorHelpers",["GeneratorHelpers"]).factory("Attr",function(){return function(h,m){var k;k="string"===typeof h[m]?h[m].replace(/\'/g,"""):h[m];switch(m){case "ngClick":k='ng-click="'+k+'" ';break;case "ngOptions":k='ng-options="'+k+'" ';break;case "ngClass":k='ng-class="'+k+'" ';break;case "ngChange":k='ng-change="'+k+'" ';break;case "ngDisabled":k='ng-disabled="'+k+'" ';break;case "ngShow":k='ng-show="'+k+'" ';break;case "ngHide":k='ng-hide="'+k+'" ';break;case "ngBind":k= 'ng-bind="'+k+'" ';break;case "trueValue":k='ng-true-value="'+k+'" ';break;case "falseValue":k='ng-false-value="'+k+'" ';break;case "awToolTip":k='aw-tool-tip="'+k+'" ';break;case "awPopOver":k="aw-pop-over='"+k+"' ";break;case "dataTitle":k='data-title="'+k+'" ';break;case "dataPlacement":k='data-placement="'+k+'" ';break;case "dataContainer":k='data-container="'+k+'" ';break;default:k=m+'="'+k+'" '}return k}}).factory("Icon",function(){return function(h){return' '}}).factory("Column", ["Attr","Icon",function(h,m){return function(k){var g=k.list,c=k.fld,p=k.options;k=k.base;var f=g.fields[c],e;e="\n";e+=f.ngShow?"":"";g.hasChildren&&f.hasChildren&&(e+=' "); if((f.key||f.link||f.linkTo||f.ngClick)&&"lookup"!=p.mode&&"select"!=p.mode)if(f.linkTo)e+='';else if(f.ngClick)e+='';else if(void 0==f.link||f.link)e+='';f.ngShowIcon?e+=' ':f.icon&&(e+=m(f.icon)+" ");if(void 0==f.showValue||!0==f.showValue)e=f.ngBind?e+("{{ "+f.ngBind+" }}"):e+("{{"+g.iterator+"."+c+"}}");f.text&&(e+=f.text);g.hasChildren&&f.hasChildren&& @@ -355,15 +355,15 @@ m+"','"+k+'\')" ng-disabled="'+k+"PrevUrl == null || "+k+'PrevUrl == undefined"> k+'\')" class="input-mini',c+=g?" field-mini-height":"",c+=' page-size">\n',c+='\n',c+='\n',c+='\n',c+='\n',c+='\n',c+="\n");c+='
    \n";c+="\n";return c+="
    \n"}});angular.module("ListGenerator",["GeneratorHelpers"]).factory("GenerateList",["$location","$compile","$rootScope","SearchWidget","PaginateWidget","Attr","Icon","Column",function(h,m,k,g,c,p,f,e){return{setList:function(c){this.list=c},attr:p,icon:f,has:function(c){return this.form[c]&&null!=this.form[c]&&void 0!=this.form[c]?!0:!1},hide:function(){$("#lookup-modal").modal("hide")},inject:function(c,a){var d="lookup"==a.mode?angular.element(document.getElementById("lookup-modal-body")):a.id?angular.element(document.getElementById(a.id)): angular.element(document.getElementById("htmlTemplate"));this.setList(c);d.html(this.build(a));this.scope=d.scope();m(d)(this.scope);"lookup"==a.mode&&(this.scope.lookupHeader=a.hdr,$("#lookup-modal").modal());return this.scope},build:function(f){var a="",d=this.list;if("lookup"!=f.mode&&(void 0==f.breadCrumbs||!0==f.breadCrumbs))a+='
    \n";"select"==f.mode&&d.selectInstructions?(a+='
    \n',a+='\n',a+="Hint: "+d.selectInstructions+"\n",a+="
    \n"):"edit"==f.mode&&d.editInstructions&&(a+='
    \n',a+='\n', -a+="Hint: "+d.editInstructions+"\n",a+="
    \n");if("lookup"!=f.mode&&(void 0==d.well||"true"==d.well))a+='
    \n';a="lookup"==f.mode?a+g({iterator:d.iterator,template:d,mini:!0}):a+g({iterator:d.iterator,template:d,mini:!1});if("lookup"!=f.mode){var b=h.path().replace(/^\//,"").split("/")[0],a=a+'
    \n';for(action in d.actions)if("all"==d.actions[action].mode||d.actions[action].mode==f.mode)if(void 0==d.actions[action].basePaths||d.actions[action].basePaths&& --1 Finished\n';a+="
    \n"}a+='"+d.fields[k].label;if(void 0===d.fields[k].nosort||!0!==d.fields[k].nosort)a+=' Select": -"edit"==f.mode&&(a+="\n");a+="\n";a+="\n";a+="\n";a+="\n");var m=2,b=d.base?d.base:d.name,b=b.replace(/^\//,"");for(k in d.fields)if(m++,(void 0==d.fields[k].searchOnly||!1==d.fields[k].searchOnly)&&!("lookup"==f.mode&&void 0!==d.fields[k].excludeModal&&!0==d.fields[k].excludeModal))a+=e({list:d,fld:k,options:f,base:b});if("select"==f.mode)a+='';else if("edit"==f.mode){a+='"}a+="\n";a+='\n';a+='\n';a+="\n";a+='\n';a+='\n';a+="\n";a+="\n"; -a+="
    '; -for(action in d.fieldActions)a+=' ";a+="
    No records matched your search.
    Loading...
    \n";if("lookup"!=f.mode&&(void 0==d.well||"true"==d.well))a+="
    \n";return a="lookup"==f.mode||f.id&&"form-modal-body"==f.id?a+c({set:d.name,iterator:d.iterator,mini:!0,mode:"lookup"}):a+c({set:d.name,iterator:d.iterator,mini:!0})}}}]);angular.module("PromptDialog",["Utilities"]).factory("Prompt",["Alert",function(h){return function(h){var k=angular.element(document.getElementById("prompt-modal")),g=k.scope();g.promptHeader=h.hdr;g.promptBody=h.body;var c=null==h["class"]||void 0==h["class"]?"btn-danger":h["class"];$("#prompt-action-btn").addClass(c);g.promptAction=h.action;$(k).modal({backdrop:"static",keyboard:!0,show:!0})}}]);angular.module("RestServices",["ngCookies","AuthService"]).factory("Rest",["$http","$rootScope","$cookieStore","Authorization",function(h,m,k,g){return{setUrl:function(c){this.url=c},auth:{Authorization:"Token "+g.getToken()},pReplace:function(){var c,g;for(c in this.params)g=RegExp("\\:"+c,"gm"),g.test(this.url)&&(this.url=this.url.replace(g,this.params[c]),delete this.params[c])},get:function(c){c=c?c:{};this.params=c.params?c.params:null;this.pReplace();return h({method:"GET",url:this.url,headers:this.auth, +a+="Hint: "+d.editInstructions+"\n",a+="\n");if("lookup"!=f.mode&&(void 0==d.well||"true"==d.well))a+='
    \n';a="lookup"==f.mode||void 0!=f.id?a+g({iterator:d.iterator,template:d,mini:!0}):a+g({iterator:d.iterator,template:d,mini:!1});if("lookup"!=f.mode){var b=h.path().replace(/^\//,"").split("/")[0],a=a+'
    \n';for(action in d.actions)if("all"==d.actions[action].mode||d.actions[action].mode==f.mode)if(void 0==d.actions[action].basePaths|| +d.actions[action].basePaths&&-1 Finished\n';a+="
    \n"}a+='"+d.fields[k].label;if(void 0===d.fields[k].nosort||!0!==d.fields[k].nosort)a+=' \n");a+="\n";a+="\n";a+="\n";a+="\n");var m=2,b=d.base?d.base:d.name,b=b.replace(/^\//,"");for(k in d.fields)if(m++,(void 0==d.fields[k].searchOnly||!1==d.fields[k].searchOnly)&&!("lookup"==f.mode&&void 0!==d.fields[k].excludeModal&&!0==d.fields[k].excludeModal))a+=e({list:d,fld:k,options:f,base:b});if("select"==f.mode)a+='';else if("edit"== +f.mode){a+='"}a+="\n";a+='\n';a+='\n';a+="\n";a+='\n';a+='\n'; +a+="\n";a+="\n";a+="
    ';for(action in d.fieldActions)a+=' ";a+="
    No records matched your search.
    Loading...
    \n";if("lookup"!=f.mode&&(void 0==d.well||"true"==d.well))a+="
    \n";return a="lookup"==f.mode||f.id&&"form-modal-body"==f.id?a+c({set:d.name,iterator:d.iterator,mini:!0,mode:"lookup"}):a+c({set:d.name,iterator:d.iterator,mini:!0})}}}]);angular.module("PromptDialog",["Utilities"]).factory("Prompt",["Alert",function(h){return function(h){var k=angular.element(document.getElementById("prompt-modal")),g=k.scope();g.promptHeader=h.hdr;g.promptBody=h.body;var c=null==h["class"]||void 0==h["class"]?"btn-danger":h["class"];$("#prompt-action-btn").addClass(c);g.promptAction=h.action;$(k).modal({backdrop:"static",keyboard:!0,show:!0})}}]);angular.module("RestServices",["ngCookies","AuthService"]).factory("Rest",["$http","$rootScope","$cookieStore","Authorization",function(h,m,k,g){return{setUrl:function(c){this.url=c},auth:{Authorization:"Token "+g.getToken()},pReplace:function(){var c,g;for(c in this.params)g=RegExp("\\:"+c,"gm"),g.test(this.url)&&(this.url=this.url.replace(g,this.params[c]),delete this.params[c])},get:function(c){c=c?c:{};this.params=c.params?c.params:null;this.pReplace();return h({method:"GET",url:this.url,headers:this.auth, params:this.params})},post:function(c){return h({method:"POST",url:this.url,headers:this.auth,data:c})},put:function(c){return h({method:"PUT",url:this.url,headers:this.auth,data:c})},destroy:function(c){return h({method:"DELETE",url:this.url,headers:this.auth,data:c})}}}]);angular.module("Utilities",[]).factory("ClearScope",function(){return function(h){h=document.getElementById(h);angular.element(h).scope().$destroy()}}).factory("ToggleClass",function(){return function(h,m){$(h)&&$(h).hasClass(m)?$(h).removeClass(m):$(h)&&$(h).addClass(m)}}).factory("Alert",["$rootScope","$location",function(h,m){return function(k,g,c,m,f,e){if(f){if(h.alertHeader2=k,h.alertBody2=g,h.alertClass2=c?c:"alert-error",$("#alert-modal2").modal({show:!0,keyboard:!0,backdrop:"static"}),h.disableButtons2= e?!0:!1,m)$("#alert-modal2").on("hidden",function(){m()})}else if(h.alertHeader=k,h.alertBody=g,h.alertClass=c?c:"alert-error",$("#alert-modal").modal({show:!0,keyboard:!0,backdrop:"static"}),h.disableButtons=e?!0:!1,m)$("#alert-modal").on("hidden",function(){m()})}}]).factory("ProcessErrors",["$log","Alert",function(h,m){return function(h,g,c,p,f){if(403==c)h="The API responded with a 403 Access Denied error. You do not have permission to perform the requested action. ",h=g.detail?h+("Detail: "+ g.detail):h+"Please contact your system administrator.",m("Access Denied",h);else if(g.non_field_errors)m("Error!",g.non_field_errors);else if(g.detail)m(f.hdr,f.msg+" "+g.detail);else if(g.__all__)m("Error!",g.__all__);else if(p){c=!1;for(var e in p.fields)p.fields[e].realName&&g[p.fields[e].realName]&&(h[e+"_api_error"]=g[p.fields[e]][0],c=!0),p.fields[e].sourceModel?g[e]&&(h[p.fields[e].sourceModel+"_"+p.fields[e].sourceField+"_api_error"]=g[e][0],c=!0):g[e]&&(h[e+"_api_error"]=g[e][0],c=!0);!c&& diff --git a/awx/ui/static/js/config.js b/awx/ui/static/js/config.js index a4d5de1bad..9669af7652 100644 --- a/awx/ui/static/js/config.js +++ b/awx/ui/static/js/config.js @@ -13,8 +13,8 @@ var $AnsibleConfig = session_timeout: 3600, // cookie expiration in seconds. session will expire after this many // seconds of inactivity. - tooltip_delay: 2000, // Default number of milliseconds to delay displaying/hiding tooltips + tooltip_delay: {show: 500, hide: 100}, // Default number of milliseconds to delay displaying/hiding tooltips - debug_mode: true // Enable console logging messages + debug_mode: true // Enable console logging messages } diff --git a/awx/ui/static/js/controllers/Inventories.js b/awx/ui/static/js/controllers/Inventories.js index 7e28960b19..41b06484d0 100644 --- a/awx/ui/static/js/controllers/Inventories.js +++ b/awx/ui/static/js/controllers/Inventories.js @@ -502,10 +502,14 @@ function InventoriesEdit ($scope, $rootScope, $compile, $location, $log, $routeP GroupsDelete({ scope: scope, "inventory_id": id, group_id: scope.group_id }); } - scope.addHost = function() { + scope.selectHost = function() { HostsList({ scope: scope, "inventory_id": id, group_id: scope.group_id }); } + scope.createHost = function() { + HostsAdd({ scope: scope, "inventory_id": id, group_id: scope.group_id }); + } + scope.editHost = function(host_id, host_name) { HostsEdit({ scope: scope, "inventory_id": id, group_id: scope.group_id, host_id: host_id, host_name: host_name }); } diff --git a/awx/ui/static/js/controllers/JobTemplates.js b/awx/ui/static/js/controllers/JobTemplates.js index 0eaec66cb6..49e8bbd18f 100644 --- a/awx/ui/static/js/controllers/JobTemplates.js +++ b/awx/ui/static/js/controllers/JobTemplates.js @@ -415,7 +415,6 @@ function JobTemplatesEdit ($scope, $rootScope, $compile, $location, $log, $route else { var json_obj = JSON.parse(data.extra_vars); scope.variables = jsyaml.safeDump(json_obj); - console.log(json_obj); } master.variables = scope.variables; } diff --git a/awx/ui/static/js/forms/Inventories.js b/awx/ui/static/js/forms/Inventories.js index c2935f2e6b..9440f6d70f 100644 --- a/awx/ui/static/js/forms/Inventories.js +++ b/awx/ui/static/js/forms/Inventories.js @@ -102,13 +102,22 @@ angular.module('InventoryFormDefinition', []) type: 'treeview', title: "{{ groupTitle }}", iterator: 'host', - actions: { - add: { - ngClick: "addHost()", + actions: { + select: { + ngClick: "selectHost()", + icon: 'icon-check', + label: 'Add Existing Host', + awToolTip: 'Select existing host', + ngHide: 'createButtonShow == false', + "class": 'btn btn-pad' + }, + create: { + ngClick: "createHost()", icon: 'icon-plus', - label: 'Add Host', - awToolTip: 'Add a host', - ngHide: 'createButtonShow == false' + label: 'Create New Host', + awToolTip: 'Create a new host', + ngHide: 'createButtonShow == false', + "class": 'btn-success' } }, diff --git a/awx/ui/static/js/helpers/Access.js b/awx/ui/static/js/helpers/Access.js index f33b93133c..6bb69600d8 100644 --- a/awx/ui/static/js/helpers/Access.js +++ b/awx/ui/static/js/helpers/Access.js @@ -1,7 +1,7 @@ /********************************************* * Copyright (c) 2013 AnsibleWorks, Inc. * -*/ + */ angular.module('AccessHelper', ['RestServices', 'Utilities', 'ngCookies']) .factory('CheckAccess', ['$rootScope', 'Alert', 'Rest', 'GetBasePath','ProcessErrors', diff --git a/awx/ui/static/js/helpers/Groups.js b/awx/ui/static/js/helpers/Groups.js index 0f9aaae240..39b081a4a1 100644 --- a/awx/ui/static/js/helpers/Groups.js +++ b/awx/ui/static/js/helpers/Groups.js @@ -32,8 +32,8 @@ angular.module('GroupsHelper', [ 'RestServices', 'Utilities', 'ListGenerator', ' selectButton: false }); - scope.formModalActionLabel = 'Finished'; - scope.formModalHeader = 'Add Group'; + scope.formModalActionLabel = 'Select'; + scope.formModalHeader = 'Add Groups'; scope.formModalCancelShow = true; scope.formModalActionClass = 'btn btn-success'; @@ -409,7 +409,6 @@ angular.module('GroupsHelper', [ 'RestServices', 'Utilities', 'ListGenerator', ' //if (parent.length > 0) { // parent = parent.last(); //} - console.log(parent); var url; if (parent.attr('type') == 'group') { diff --git a/awx/ui/static/js/helpers/Hosts.js b/awx/ui/static/js/helpers/Hosts.js index 96d0c6bfd5..779e6db171 100644 --- a/awx/ui/static/js/helpers/Hosts.js +++ b/awx/ui/static/js/helpers/Hosts.js @@ -33,14 +33,21 @@ angular.module('HostsHelper', [ 'RestServices', 'Utilities', 'ListGenerator', 'H selectButton: false }); - scope.formModalActionLabel = 'Finished'; - scope.formModalHeader = 'Add Host'; + scope.formModalActionLabel = 'Select'; + scope.formModalHeader = 'Select Hosts'; scope.formModalCancelShow = true; + if (scope.removeHostsReload) { + scope.removeHostsReload(); + } + scope.removeHostsReload = scope.$on('hostsReload', function() { + HostsReload(params); + }); + $('#form-modal .btn-none').removeClass('btn-none').addClass('btn-success'); $('#form-modal').modal(); $('#form-modal').unbind('hidden'); - $('#form-modal').on('hidden', function () { HostsReload(params); }); + $('#form-modal').on('hidden', function () { scope.$emit('hostsReload'); }); scope.selected = []; @@ -89,7 +96,7 @@ angular.module('HostsHelper', [ 'RestServices', 'Utilities', 'ListGenerator', 'H } else { $('#form-modal').modal('hide'); - HostsReload(params); + scope.$emit('hostsReload'); } } }); @@ -168,9 +175,16 @@ angular.module('HostsHelper', [ 'RestServices', 'Utilities', 'ListGenerator', 'H scope.parseType = 'yaml'; ParseTypeChange(scope); + if (scope.removeHostsReload) { + scope.removeHostsReload(); + } + scope.removeHostsReload = scope.$on('hostsReload', function() { + HostsReload(params); + }); + $('#form-modal .btn-none').removeClass('btn-none').addClass('btn-success'); $('#form-modal').unbind('hidden'); - $('#form-modal').on('hidden', function () { HostsReload(params); }); + $('#form-modal').on('hidden', function () { scope.$emit('hostsReload'); }); generator.reset(); var master={}; @@ -260,10 +274,17 @@ angular.module('HostsHelper', [ 'RestServices', 'Utilities', 'ListGenerator', 'H scope.formModalCancelShow = true; scope.parseType = 'yaml'; ParseTypeChange(scope); + + if (scope.removeHostsReload) { + scope.removeHostsReload(); + } + scope.removeHostsReload = scope.$on('hostsReload', function() { + HostsReload(params); + }); $('#form-modal .btn-none').removeClass('btn-none').addClass('btn-success'); $('#form-modal').unbind('hidden'); - $('#form-modal').on('hidden', function () { HostsReload(params); }); + $('#form-modal').on('hidden', function () { scope.$emit('hostsReload'); }); // After the group record is loaded, retrieve any group variables if (scope.hostLoadedRemove) { @@ -395,15 +416,21 @@ angular.module('HostsHelper', [ 'RestServices', 'Utilities', 'ListGenerator', 'H var url = (group_id !== null) ? GetBasePath('groups') + group_id + '/hosts/' : GetBasePath('inventory') + inventory_id + '/hosts/'; var action_to_take = function() { + if (scope.removeHostsReload) { + scope.removeHostsReload(); + } + scope.removeHostsReload = scope.$on('hostsReload', function() { + HostsReload(params); + }); Rest.setUrl(url); Rest.post({ id: host_id, disassociate: 1 }) .success( function(data, status, headers, config) { $('#prompt-modal').modal('hide'); - HostsReload(params); + scope.$emit('hostsReload'); }) .error( function(data, status, headers, config) { $('#prompt-modal').modal('hide'); - HostsReload(params); + scope.$emit('hostsReload'); ProcessErrors(scope, data, status, null, { hdr: 'Error!', msg: 'Call to ' + url + ' failed. DELETE returned status: ' + status }); }); @@ -427,13 +454,14 @@ angular.module('HostsHelper', [ 'RestServices', 'Utilities', 'ListGenerator', 'H function(RelatedSearchInit, RelatedPaginateInit, InventoryForm, GetBasePath) { return function(params) { // Rerfresh the Hosts view on right side of page - var url = (params.group_id !== null) ? GetBasePath('groups') + params.group_id + '/hosts/' : - GetBasePath('inventory') + params.inventory_id + '/hosts/'; + scope = params.scope; + var url = (scope.group_id !== null) ? GetBasePath('groups') + scope.group_id + '/hosts/' : + GetBasePath('inventory') + params.inventory_id + '/hosts/'; var relatedSets = { hosts: { url: url, iterator: 'host' } }; RelatedSearchInit({ scope: params.scope, form: InventoryForm, relatedSets: relatedSets }); RelatedPaginateInit({ scope: params.scope, relatedSets: relatedSets }); params.scope.search('host'); - if (!params.scope.$$phase) { + if (!params.scope.$$phase) { params.scope.$digest(); } } diff --git a/awx/ui/static/js/helpers/teams.js b/awx/ui/static/js/helpers/teams.js index d122cdf840..f2b7ff52d4 100644 --- a/awx/ui/static/js/helpers/teams.js +++ b/awx/ui/static/js/helpers/teams.js @@ -43,8 +43,6 @@ angular.module('TeamHelper', [ 'RestServices', 'Utilities', 'OrganizationListDef Rest.setUrl('/api/v1/organizations/' + results[i].organization + '/'); Rest.get() .success( function( data, status, headers, config) { - console.log('here!'); - console.log(data); lookup_results.push({ id: data.id, value: data.name }); scope.$emit('TeamResultFound', results, lookup_results); }) diff --git a/awx/ui/static/js/lists/Groups.js b/awx/ui/static/js/lists/Groups.js index e95c3b385d..a0ad24790a 100644 --- a/awx/ui/static/js/lists/Groups.js +++ b/awx/ui/static/js/lists/Groups.js @@ -14,7 +14,6 @@ angular.module('GroupListDefinition', []) iterator: 'group', selectTitle: 'Add Group', editTitle: 'Groups', - selectInstructions: 'Click on a row to select it, and Finished when done. Click the green Add to create a new row.', index: true, well: false, @@ -30,7 +29,7 @@ angular.module('GroupListDefinition', []) actions: { add: { - label: 'Add', + label: 'Create New Group', icon: 'icon-plus', mode: 'all', // One of: edit, select, all ngClick: 'createGroup()', diff --git a/awx/ui/static/js/lists/Hosts.js b/awx/ui/static/js/lists/Hosts.js index 334989c4c1..fa2fae7993 100644 --- a/awx/ui/static/js/lists/Hosts.js +++ b/awx/ui/static/js/lists/Hosts.js @@ -12,8 +12,7 @@ angular.module('HostListDefinition', []) name: 'hosts', iterator: 'host', - selectTitle: 'Add Host', - selectInstructions: 'Click on a row to select it, and click Finished when done. Use the green button to create a new row.', + selectTitle: 'Select Host', editTitle: 'Hosts', index: true, well: true, @@ -21,7 +20,7 @@ angular.module('HostListDefinition', []) fields: { name: { key: true, - label: 'Name', + label: 'Host Name', linkTo: "/inventories/\{\{ inventory_id \}\}/hosts/\{\{ host.id \}\}" }, description: { @@ -30,15 +29,6 @@ angular.module('HostListDefinition', []) }, actions: { - add: { - icon: 'icon-plus', - label: 'Add', - mode: 'all', // One of: edit, select, all - ngClick: 'createHost()', - ngHide: 'showAddButton == false', - "class": 'btn-success btn-small', - awToolTip: 'Create a new host' - } }, fieldActions: { diff --git a/awx/ui/static/js/lists/Inventories.js b/awx/ui/static/js/lists/Inventories.js index bbebe058cb..ade0967396 100644 --- a/awx/ui/static/js/lists/Inventories.js +++ b/awx/ui/static/js/lists/Inventories.js @@ -54,7 +54,7 @@ angular.module('InventoriesListDefinition', []) mode: 'all', // One of: edit, select, all ngClick: 'addInventory()', "class": 'btn-small btn-success', - awToolTip: 'Create a new row' + awToolTip: 'Create a new inventory' } }, diff --git a/awx/ui/static/js/lists/Organizations.js b/awx/ui/static/js/lists/Organizations.js index d0df278234..df500ea743 100644 --- a/awx/ui/static/js/lists/Organizations.js +++ b/awx/ui/static/js/lists/Organizations.js @@ -34,7 +34,7 @@ angular.module('OrganizationListDefinition', []) mode: 'all', // One of: edit, select, all ngClick: 'addOrganization()', "class": 'btn-success btn-small', - awToolTip: 'Create a new row' + awToolTip: 'Create a new organization' } }, diff --git a/awx/ui/static/lib/ansible/form-generator.js b/awx/ui/static/lib/ansible/form-generator.js index d63562970d..e53e9594b3 100644 --- a/awx/ui/static/lib/ansible/form-generator.js +++ b/awx/ui/static/lib/ansible/form-generator.js @@ -808,7 +808,9 @@ angular.module('FormGenerator', ['GeneratorHelpers', 'ngCookies']) // Add actions(s) html += "
    \n"; for (var action in form.related[itm].actions) { - html += "
    @@ -185,7 +185,7 @@ diff --git a/private/.license_writer.py.swo b/private/.license_writer.py.swo new file mode 100644 index 0000000000000000000000000000000000000000..a28eaa3799fe702cabfb13071319546bb9692a51 GIT binary patch literal 12288 zcmeI2PiqrF7{*5rg8xMk^di&r&=i}d)q_yzK}r$DzZDg{tkcaj8M-^W&P>|oVvmBJ zJgXl;@DqriKs@)NZdIfc$Hna$xgib&| z4l(u}`UpLT?n1|*HwPJe3_XJGK>;)e?SVcWVC)6-5L$vxL*Mo@_7-{uJ%u)*>(ChV z72|phJ%JuWG33X(3eiCXhyW2F0z`la5CJ0amlNPP(AudT?>BU|r3q+Oq6KAXS# zcSY-G3w@yYIM0*RsYKi9a$<`4Qhz9IozS5S!lVoTL3uSXxFAfl6QK)aEL2qXuOznD zYY|t+Q`GxJxjasV?gcxP4-$EK&H_WeoOiq4$(mj9hj?~lcrij*$rm?5nIWjx$_Ti! z+{pV~Wn`M-oQule8V_b` zG!8T`4<>wzbcVX#e{`p~Fl{@DnNEeVzMt8j0w2WVpOy9#qSXrWi|VyTRGZ?pMx#9G zh_s}2^OYx|Kl?jbsNAADWm_h{g!6}JC!59GrZPKQF4gF8lZC2@pNMT9S#Y>TSXc~@ zmn=%bvIVeT%hu2J48A9hg%V^dGzj?oJf8>xYz8_ACgvDhF-gqD`o^>m6`PLbDozvQ zs!KJm#e8MVijwk|q2HF@#6B0gsK-|l!!K&|vuhFYTUgoGG@^|LN3*@LJQh8!6UW<9 zOXD{`*OAKbWL+0~B41IFw0v$7uIHnOFR6CNS-vE3{hDkcT3?vt=ku=mJHE|+il3R8 VMT`IVShH(Qb6M$T+H;*mv!8Y09smFU literal 0 HcmV?d00001