mirror of
https://github.com/ansible/awx.git
synced 2026-05-19 14:57:39 -02:30
Major rename of package from lib to ansibleworks.
This commit is contained in:
56
ansibleworks/ui/static/lib/ansible/api-loader.js
Normal file
56
ansibleworks/ui/static/lib/ansible/api-loader.js
Normal file
@@ -0,0 +1,56 @@
|
||||
/*********************************************
|
||||
* Copyright (c) 2013 AnsibleWorks, Inc.
|
||||
*
|
||||
*
|
||||
* Read /api and /api/X to discover all the base paths needed
|
||||
* to access the primary model objects.
|
||||
*
|
||||
*/
|
||||
|
||||
angular.module('ApiLoader', ['ngCookies'])
|
||||
.factory('LoadBasePaths', ['$http', '$rootScope', '$cookieStore', 'ProcessErrors',
|
||||
function($http, $rootScope, $cookieStore, ProcessErrors) {
|
||||
return function() {
|
||||
$http.get('/api/')
|
||||
.success( function(data, status, headers, config) {
|
||||
var base = data.current_version;
|
||||
$http.get(base)
|
||||
.success( function(data, status, headers, config) {
|
||||
data['base'] = base;
|
||||
$rootScope['defaultUrls'] = data;
|
||||
$cookieStore.remove('api');
|
||||
$cookieStore.put('api',data); //Preserve in cookie to prevent against
|
||||
//loss during browser refresh
|
||||
})
|
||||
.error ( function(data, status, headers, config) {
|
||||
$rootScope['defaultUrls'] = { status: 'error' };
|
||||
ProcessErrors(null, data, status, null,
|
||||
{ hdr: 'Error', msg: 'Failed to read ' + base + '. GET status: ' + status });
|
||||
});
|
||||
})
|
||||
.error( function(data, status, headers, config) {
|
||||
$rootScope['defaultUrls'] = { status: 'error' };
|
||||
ProcessErrors(null, data, status, null,
|
||||
{ hdr: 'Error', msg: 'Failed to read /api. GET status: ' + status });
|
||||
});
|
||||
}
|
||||
}])
|
||||
|
||||
.factory('GetBasePath', ['$rootScope', '$cookieStore', 'LoadBasePaths',
|
||||
function($rootScope, $cookieStore, LoadBasePaths) {
|
||||
return function(set) {
|
||||
var answer;
|
||||
if ($rootScope['defaultUrls'] == null || $rootScope['defaultUrls'] == undefined) {
|
||||
// browser refresh must have occurred. use what's in session cookie and refresh
|
||||
answer = $cookieStore.get('api')[set];
|
||||
LoadBasePaths();
|
||||
}
|
||||
else {
|
||||
answer = $rootScope['defaultUrls'][set];
|
||||
}
|
||||
return answer;
|
||||
}
|
||||
}]);
|
||||
|
||||
|
||||
|
||||
96
ansibleworks/ui/static/lib/ansible/authenticate.js
Normal file
96
ansibleworks/ui/static/lib/ansible/authenticate.js
Normal file
@@ -0,0 +1,96 @@
|
||||
/*********************************************
|
||||
* Copyright (c) 2013 AnsibleWorks, Inc.
|
||||
*
|
||||
* User authentication functions
|
||||
*
|
||||
*/
|
||||
|
||||
angular.module('AuthService', ['ngCookies'])
|
||||
.factory('Authorization', ['$http', '$rootScope', '$location', '$cookieStore', function($http, $rootScope, $location, $cookieStore) {
|
||||
return {
|
||||
setToken: function(token) {
|
||||
// set the session cookie
|
||||
var today = new Date();
|
||||
today.setTime(today.getTime() + ($AnsibleConfig.session_timeout * 1000));
|
||||
$cookieStore.remove('token');
|
||||
$cookieStore.remove('token_expire');
|
||||
$cookieStore.put('token', token);
|
||||
$cookieStore.put('token_expire', today.getTime());
|
||||
$rootScope.userLoggedIn = true;
|
||||
},
|
||||
|
||||
isTokenValid: function() {
|
||||
// check if token exists and is not expired
|
||||
var response = false;
|
||||
if ( $cookieStore.get('token') && $cookieStore.get('token_expire') ) {
|
||||
var token = $cookieStore.get('token');
|
||||
var exp = new Date($cookieStore.get('token_expire'));
|
||||
var today = new Date();
|
||||
if (today < exp) {
|
||||
this.setToken(token); //push expiration into the future while user is active
|
||||
response = true;
|
||||
}
|
||||
}
|
||||
return response;
|
||||
},
|
||||
|
||||
didSessionExpire: function() {
|
||||
// use only to test why user was sent to login page.
|
||||
var response = false;
|
||||
if ($cookieStore.get('token_expire')) {
|
||||
var exp = new Date($cookieStore.get('token_expire'));
|
||||
var today = new Date();
|
||||
if (exp < today) {
|
||||
response = true;
|
||||
}
|
||||
}
|
||||
return response;
|
||||
},
|
||||
|
||||
getToken: function() {
|
||||
if ( this.isTokenValid() ) {
|
||||
return $cookieStore.get('token');
|
||||
}
|
||||
else {
|
||||
return null;
|
||||
}
|
||||
},
|
||||
|
||||
retrieveToken: function(username, password) {
|
||||
return $http({ method: 'POST', url: '/api/v1/authtoken/',
|
||||
data: {"username": username, "password": password} });
|
||||
},
|
||||
|
||||
logout: function() {
|
||||
$rootScope.current_user = {};
|
||||
$cookieStore.remove('token');
|
||||
$cookieStore.remove('token_expire');
|
||||
$cookieStore.remove('current_user');
|
||||
$rootScope.userLoggedIn = false;
|
||||
},
|
||||
|
||||
getUser: function() {
|
||||
return $http({
|
||||
method: 'GET',
|
||||
url: '/api/v1/me/',
|
||||
headers: { 'Authorization': 'Token ' + this.getToken() },
|
||||
});
|
||||
},
|
||||
|
||||
setUserInfo: function(response) {
|
||||
// store the response values in $rootScope so we can get to them later
|
||||
$rootScope.current_user = response.results[0];
|
||||
$cookieStore.put('current_user', response.results[0]); //keep in session cookie incase user hits refresh
|
||||
},
|
||||
|
||||
restoreUserInfo: function() {
|
||||
$rootScope.current_user = $cookieStore.get('current_user');
|
||||
},
|
||||
|
||||
getUserInfo: function(key) {
|
||||
// Access values returned from the Me API call
|
||||
return ($rootScope.current_user[key]) ? $rootScope.current_user[key] : null;
|
||||
}
|
||||
}
|
||||
}]);
|
||||
|
||||
134
ansibleworks/ui/static/lib/ansible/directives.js
Normal file
134
ansibleworks/ui/static/lib/ansible/directives.js
Normal file
@@ -0,0 +1,134 @@
|
||||
/*********************************************
|
||||
* Copyright (c) 2013 AnsibleWorks, Inc.
|
||||
*
|
||||
* Custom directives for form validation
|
||||
*
|
||||
*/
|
||||
|
||||
var INTEGER_REGEXP = /^\-?\d*$/;
|
||||
|
||||
|
||||
angular.module('AWDirectives', ['RestServices'])
|
||||
// awpassmatch: Add to password_confirm field. Will test if value
|
||||
// matches that of 'input[name="password"]'
|
||||
.directive('awpassmatch', function() {
|
||||
return {
|
||||
require: 'ngModel',
|
||||
link: function(scope, elm, attrs, ctrl) {
|
||||
ctrl.$parsers.unshift( function(viewValue) {
|
||||
var associated = attrs.awpassmatch;
|
||||
var password = $('input[name="' + associated + '"]').val();
|
||||
if (viewValue == password) {
|
||||
// it is valid
|
||||
ctrl.$setValidity('awpassmatch', true);
|
||||
return viewValue;
|
||||
} else {
|
||||
// it is invalid, return undefined (no model update)
|
||||
ctrl.$setValidity('awpassmatch', false);
|
||||
return undefined;
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
// caplitalize Add to any input field where the first letter of each
|
||||
// word should be capitalized. Use in place of css test-transform.
|
||||
// For some reason "text-transform: capitalize" in breadcrumbs
|
||||
// causes a break at each blank space. And of course,
|
||||
// "autocapitalize='word'" only works in iOS. Use this as a fix.
|
||||
.directive('capitalize', function() {
|
||||
return {
|
||||
require: 'ngModel',
|
||||
link: function(scope, elm, attrs, ctrl) {
|
||||
ctrl.$parsers.unshift( function(viewValue) {
|
||||
var values = viewValue.split(" ");
|
||||
var result = "";
|
||||
for (i = 0; i < values.length; i++){
|
||||
result += values[i].charAt(0).toUpperCase() + values[i].substr(1) + ' ';
|
||||
}
|
||||
result = result.trim();
|
||||
if (result != viewValue) {
|
||||
ctrl.$setViewValue(result);
|
||||
ctrl.$render();
|
||||
}
|
||||
return result;
|
||||
});
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
// integer Validate that input is of type integer. Taken from Angular developer
|
||||
// guide, form examples. Add min and max directives, and this will check
|
||||
// entered values is within the range.
|
||||
//
|
||||
// Use input type of 'text'. Use of 'number' casuses browser validation to
|
||||
// override/interfere with this directive.
|
||||
.directive('integer', function() {
|
||||
return {
|
||||
require: 'ngModel',
|
||||
link: function(scope, elm, attrs, ctrl) {
|
||||
ctrl.$parsers.unshift(function(viewValue) {
|
||||
ctrl.$setValidity('min', true);
|
||||
ctrl.$setValidity('max', true);
|
||||
if (INTEGER_REGEXP.test(viewValue)) {
|
||||
// it is valid
|
||||
ctrl.$setValidity('integer', true);
|
||||
if ( elm.attr('min') &&
|
||||
( viewValue == '' || viewValue == null || parseInt(viewValue) < parseInt(elm.attr('min')) ) ) {
|
||||
ctrl.$setValidity('min', false);
|
||||
return undefined;
|
||||
}
|
||||
if ( elm.attr('max') && ( parseInt(viewValue) > parseInt(elm.attr('max')) ) ) {
|
||||
ctrl.$setValidity('max', false);
|
||||
return undefined;
|
||||
}
|
||||
return viewValue;
|
||||
} else {
|
||||
// it is invalid, return undefined (no model update)
|
||||
ctrl.$setValidity('integer', false);
|
||||
return undefined;
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
// lookup Validate lookup value against API
|
||||
//
|
||||
.directive('awlookup', ['Rest', function(Rest) {
|
||||
return {
|
||||
require: 'ngModel',
|
||||
link: function(scope, elm, attrs, ctrl) {
|
||||
ctrl.$parsers.unshift( function(viewValue) {
|
||||
if (viewValue !== '') {
|
||||
url = elm.attr('data-url');
|
||||
url = url.replace(/\:value/,escape(viewValue));
|
||||
scope[elm.attr('data-source')] = null;
|
||||
Rest.setUrl(url);
|
||||
Rest.get().then( function(data) {
|
||||
var results = data.data.results;
|
||||
if (results.length > 0) {
|
||||
scope[elm.attr('data-source')] = results[0].id;
|
||||
scope[elm.attr('name')] = results[0].name;
|
||||
ctrl.$setValidity('required', true);
|
||||
ctrl.$setValidity('awlookup', true);
|
||||
return viewValue;
|
||||
}
|
||||
else {
|
||||
ctrl.$setValidity('required', true);
|
||||
ctrl.$setValidity('awlookup', false);
|
||||
return undefined;
|
||||
}
|
||||
});
|
||||
|
||||
}
|
||||
else {
|
||||
ctrl.$setValidity('awlookup', true);
|
||||
scope[elm.attr('data-source')] = null;
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
}]);
|
||||
|
||||
22
ansibleworks/ui/static/lib/ansible/filters.js
Normal file
22
ansibleworks/ui/static/lib/ansible/filters.js
Normal file
@@ -0,0 +1,22 @@
|
||||
/*********************************************
|
||||
* Copyright (c) 2013 AnsibleWorks, Inc.
|
||||
*
|
||||
* Custom filters
|
||||
*
|
||||
*/
|
||||
|
||||
angular.module('AWFilters', [])
|
||||
//
|
||||
// capitalize -capitalize the first letter of each word
|
||||
//
|
||||
.filter('capitalize', function() {
|
||||
return function(input) {
|
||||
var values = input.replace(/\_/g,' ').split(" ");
|
||||
var result = "";
|
||||
for (i = 0; i < values.length; i++){
|
||||
result += values[i].charAt(0).toUpperCase() + values[i].substr(1) + ' ';
|
||||
}
|
||||
result = result.trim();
|
||||
return result;
|
||||
}
|
||||
});
|
||||
689
ansibleworks/ui/static/lib/ansible/form-generator.js
Normal file
689
ansibleworks/ui/static/lib/ansible/form-generator.js
Normal file
@@ -0,0 +1,689 @@
|
||||
/*********************************************
|
||||
* Copyright (c) 2013 AnsibleWorks, Inc.
|
||||
*
|
||||
* FormGenerator
|
||||
* Pass in a form definition from FormDefinitions and out pops an html template.
|
||||
* See js/form-definitions.js for form example. For now produces a Twitter Bootstrap
|
||||
* horizontal form.
|
||||
*
|
||||
*/
|
||||
|
||||
angular.module('FormGenerator', ['GeneratorHelpers'])
|
||||
.factory('GenerateForm', [ '$compile', 'SearchWidget', 'PaginateWidget', function($compile, SearchWidget, PaginateWidget) {
|
||||
return {
|
||||
|
||||
setForm: function(form) {
|
||||
this.form = form;
|
||||
},
|
||||
|
||||
attr: function(obj, key) {
|
||||
var result;
|
||||
switch(key) {
|
||||
case 'ngClick':
|
||||
result = "ng-click=\"" + obj[key] + "\" ";
|
||||
break;
|
||||
case 'ngOptions':
|
||||
result = "ng-options=\"" + obj[key] + "\" ";
|
||||
break;
|
||||
case 'ngChange':
|
||||
result = "ng-change=\"" + obj[key] + "\" ";
|
||||
break;
|
||||
case 'ngShow':
|
||||
result = "ng-show=\"" + obj[key] + "\" ";
|
||||
break;
|
||||
case 'trueValue':
|
||||
result = "ng-true-value=\"" + obj[key] + "\" ";
|
||||
break;
|
||||
case 'falseValue':
|
||||
result = "ng-false-value=\"" + obj[key] + "\" ";
|
||||
break;
|
||||
case 'awToolTip':
|
||||
result = "aw-tool-tip=\"" + obj[key] + "\" ";
|
||||
break;
|
||||
default:
|
||||
result = key + "=\"" + obj[key] + "\" ";
|
||||
}
|
||||
return result;
|
||||
},
|
||||
|
||||
icon: function(icon) {
|
||||
return "<i class=\"" + icon + "\"></i> ";
|
||||
},
|
||||
|
||||
has: function(key) {
|
||||
return (this.form[key] && this.form[key] != null && this.form[key] != undefined) ? true : false;
|
||||
},
|
||||
|
||||
inject: function(form, options) {
|
||||
//
|
||||
// Use to inject the form as html into the view. View MUST have an ng-bind for 'htmlTemplate'.
|
||||
// Returns scope of form.
|
||||
//
|
||||
var element;
|
||||
if (options.modal) {
|
||||
element = angular.element(document.getElementById('form-modal-body'));
|
||||
}
|
||||
else {
|
||||
var element = angular.element(document.getElementById('htmlTemplate'));
|
||||
}
|
||||
|
||||
this.setForm(form);
|
||||
element.html(this.build(options)); // Inject the html
|
||||
this.scope = element.scope(); // Set scope specific to the element we're compiling, avoids circular reference
|
||||
// From here use 'scope' to manipulate the form, as the form is not in '$scope'
|
||||
$compile(element)(this.scope);
|
||||
|
||||
if ((!options.modal) && options.related) {
|
||||
this.addListeners();
|
||||
}
|
||||
|
||||
if (options.mode == 'add') {
|
||||
this.applyDefaults();
|
||||
}
|
||||
|
||||
if (options.modal) {
|
||||
(options.mode == 'add') ? scope.formHeader = form.addTitle : form.editTitle;
|
||||
$('#form-modal').modal();
|
||||
}
|
||||
|
||||
this.mode = options.mode;
|
||||
this.modal = (options.modal) ? true : false;
|
||||
|
||||
return this.scope;
|
||||
},
|
||||
|
||||
applyDefaults: function() {
|
||||
for (fld in this.form.fields) {
|
||||
if (this.form.fields[fld].default || this.form.fields[fld].default == 0) {
|
||||
this.scope[fld] = this.form.fields[fld].default;
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
reset: function() {
|
||||
// The form field values cannot be reset with jQuery. Each field is tied to a model, so to clear the field
|
||||
// value, you have clear the model.
|
||||
this.scope[this.form.name + '_form'].$setPristine();
|
||||
for (var fld in this.form.fields) {
|
||||
this.scope[fld] = '';
|
||||
this.scope[fld + '_api_error'] = '';
|
||||
this.scope[this.form.fields[fld].sourceModel + '_' + this.form.fields[fld].sourceField] = '';
|
||||
if ( this.form.fields[fld].type == 'lookup' &&
|
||||
this.scope[this.form.name + '_form'][this.form.fields[fld].sourceModel + '_' + this.form.fields[fld].sourceField] ) {
|
||||
this.scope[this.form.name + '_form'][this.form.fields[fld].sourceModel + '_' + this.form.fields[fld].sourceField].$setPristine();
|
||||
}
|
||||
if (this.scope[this.form.name + '_form'][fld]) {
|
||||
this.scope[this.form.name + '_form'][fld].$setPristine();
|
||||
}
|
||||
if (this.form.fields[fld].awPassMatch) {
|
||||
this.scope[this.form.name + '_form'][fld].$setValidity('awpassmatch', true);
|
||||
}
|
||||
if (this.form.fields[fld].ask) {
|
||||
this.scope[fld + '_ask'] = false;
|
||||
}
|
||||
}
|
||||
if (this.mode == 'add') {
|
||||
this.applyDefaults();
|
||||
}
|
||||
},
|
||||
|
||||
addListeners: function() {
|
||||
// Listen for accordion collapse events and toggle the header icon
|
||||
$('.collapse')
|
||||
.on('show', function() {
|
||||
var element = $(this).parent().find('.accordion-heading i');
|
||||
element.removeClass('icon-angle-down');
|
||||
element.addClass('icon-angle-up');
|
||||
})
|
||||
.on('hide', function() {
|
||||
var element = $(this).parent().find('.accordion-heading i');
|
||||
element.removeClass('icon-angle-up');
|
||||
element.addClass('icon-angle-down');
|
||||
});
|
||||
},
|
||||
|
||||
headerField: function(fld, field, options) {
|
||||
var html = '';
|
||||
if (field.label) {
|
||||
html += "<label>" + field.label + "</label>\n";
|
||||
}
|
||||
html += "<input type=\"text\" name=\"" + fld + "\" ";
|
||||
html += "ng-model=\"" + fld + "\" ";
|
||||
html += (field.class) ? this.attr(field, "class") : "";
|
||||
html += " readonly />\n";
|
||||
return html;
|
||||
},
|
||||
|
||||
buildField: function(fld, field, options) {
|
||||
|
||||
var html='';
|
||||
|
||||
//Assuming horizontal form for now. This will need to be more flexible later.
|
||||
|
||||
//text fields
|
||||
if (field.type == 'text' || field.type == 'password' || field.type == 'email') {
|
||||
if ( (! field.readonly) || (field.readonly && options.mode == 'edit') ) {
|
||||
html += "<div class=\"control-group\""
|
||||
html += (field.ngShow) ? this.attr(field,'ngShow') : "";
|
||||
html += ">\n";
|
||||
html += "<label class=\"control-label";
|
||||
html += (field.labelClass) ? " " + field.labelClass : "";
|
||||
html += "\" for=\"" + fld + '">';
|
||||
html += (field.icon) ? this.icon(field.icon) : "";
|
||||
html += field.label + '</label>' + "\n";
|
||||
html += "<div class=\"controls\">\n";
|
||||
html += (field.clear) ? "<div class=\"input-append\">\n" : "";
|
||||
if (field.control === null || field.control === undefined || field.control) {
|
||||
html += "<input ";
|
||||
html += this.attr(field,'type');
|
||||
html += "ng-model=\"" + fld + '" ';
|
||||
html += 'name="' + fld + '" ';
|
||||
html += (field.ngChange) ? this.attr(field,'ngChange') : "";
|
||||
html += (field.id) ? this.attr(field,'id') : "";
|
||||
html += (field.class) ? this.attr(field, 'class') : "";
|
||||
html += (field.placeholder) ? this.attr(field,'placeholder') : "";
|
||||
html += (options.mode == 'edit' && field.editRequired) ? "required " : "";
|
||||
html += (options.mode == 'add' && field.addRequired) ? "required " : "";
|
||||
html += (field.readonly) ? "readonly " : "";
|
||||
html += (field.awPassMatch) ? "awpassmatch=\"" + field.associated + "\" " : "";
|
||||
html += (field.capitalize) ? "capitalize " : "";
|
||||
html += (field.ask) ? "ng-disabled=\"" + fld + "_ask\" " : "";
|
||||
html += (field.associated && this.form.fields[field.associated].ask) ? "ng-disabled=\"" + field.associated + "_ask\" " : "";
|
||||
html += "/>";
|
||||
if (field.clear) {
|
||||
html += " \n<button class=\"btn\" ng-click=\"clear('" + fld + "','" + field.associated + "')\" " +
|
||||
"aw-tool-tip=\"Clear " + field.label + "\" id=\"" + fld + "-clear-btn\"><i class=\"icon-undo\"></i></button>\n";
|
||||
html += "</div>\n";
|
||||
}
|
||||
if (field.ask) {
|
||||
html += " \n<label class=\"checkbox inline ask-checkbox\"><input type=\"checkbox\" ng-model=\"" +
|
||||
fld + "_ask\" ng-change=\"ask('" + fld + "','" + field.associated + "')\" /> Ask at runtime?</label>";
|
||||
}
|
||||
html += "<br />\n";
|
||||
// Add error messages
|
||||
if ( (options.mode == 'add' && field.addRequired) || (options.mode == 'edit' && field.editRequired) ) {
|
||||
html += "<span class=\"error\" ng-show=\"" + this.form.name + '_form.' + fld + ".$dirty && " +
|
||||
this.form.name + '_form.' + fld + ".$error.required\">A value is required!</span>\n";
|
||||
}
|
||||
if (field.type == "email") {
|
||||
html += "<span class=\"error\" ng-show=\"" + this.form.name + '_form.' + fld + ".$dirty && " +
|
||||
this.form.name + '_form.' + fld + ".$error.email\">A valid email address is required!</span>\n";
|
||||
}
|
||||
if (field.awPassMatch) {
|
||||
html += "<span class=\"error\" ng-show=\"" + this.form.name + '_form.' + fld +
|
||||
".$error.awpassmatch\">Must match Password value</span>\n";
|
||||
}
|
||||
html += "<span class=\"error api-error\" ng-bind=\"" + fld + "_api_error\"></span>\n";
|
||||
}
|
||||
html += "</div>\n";
|
||||
html += "</div>\n";
|
||||
}
|
||||
}
|
||||
|
||||
//textarea fields
|
||||
if (field.type == 'textarea') {
|
||||
if ( (! field.readonly) || (field.readonly && options.mode == 'edit') ) {
|
||||
html += "<div class=\"control-group\""
|
||||
html += (field.ngShow) ? this.attr(field,'ngShow') : "";
|
||||
html += ">\n";
|
||||
html += "<label class=\"control-label\" for=\"" + fld + '">' + field.label + '</label>' + "\n";
|
||||
html += "<div class=\"controls\">\n";
|
||||
html += "<textarea ";
|
||||
html += (field.rows) ? this.attr(field, 'rows') : "";
|
||||
html += "ng-model=\"" + fld + '" ';
|
||||
html += 'name="' + fld + '" ';
|
||||
html += (field.class) ? this.attr(field,'class') : "";
|
||||
html += (field.ngChange) ? this.attr(field,'ngChange') : "";
|
||||
html += (field.id) ? this.attr(field,'id') : "";
|
||||
html += (field.placeholder) ? this.attr(field,'placeholder') : "";
|
||||
html += (options.mode == 'edit' && field.editRequired) ? "required " : "";
|
||||
html += (options.mode == 'add' && field.addRequired) ? "required " : "";
|
||||
html += (field.readonly) ? "readonly " : "";
|
||||
html += "></textarea><br />\n";
|
||||
// Add error messages
|
||||
if ( (options.mode == 'add' && field.addRequired) || (options.mode == 'edit' && field.editRequired) ) {
|
||||
html += "<span class=\"error\" ng-show=\"" + this.form.name + '_form.' + fld + ".$dirty && " +
|
||||
this.form.name + '_form.' + fld + ".$error.required\">A value is required!</span>\n";
|
||||
}
|
||||
html += "<span class=\"error api-error\" ng-bind=\"" + fld + "_api_error\"></span>\n";
|
||||
html += "</div>\n";
|
||||
html += "</div>\n";
|
||||
}
|
||||
}
|
||||
|
||||
//select field
|
||||
if (field.type == 'select') {
|
||||
if ( (! field.readonly) || (field.readonly && options.mode == 'edit') ) {
|
||||
html += "<div class=\"control-group\""
|
||||
html += (field.ngShow) ? this.attr(field,'ngShow') : "";
|
||||
html += ">\n";
|
||||
html += "<label class=\"control-label\" for=\"" + fld + '">' + field.label + '</label>' + "\n";
|
||||
html += "<div class=\"controls\">\n";
|
||||
html += "<select ";
|
||||
html += "ng-model=\"" + fld + '" ';
|
||||
html += 'name="' + fld + '" ';
|
||||
//html += "ng-options=\"item.label for item in " + fld + "_options\" ";
|
||||
html += this.attr(field, 'ngOptions');
|
||||
html += (field.ngChange) ? this.attr(field,'ngChange') : "";
|
||||
html += (field.id) ? this.attr(field,'id') : "";
|
||||
html += (options.mode == 'edit' && field.editRequired) ? "required " : "";
|
||||
html += (options.mode == 'add' && field.addRequired) ? "required " : "";
|
||||
html += (field.readonly) ? "readonly " : "";
|
||||
html += ">\n";
|
||||
html += "<option value=\"\">Choose " + field.label + "</option>\n";
|
||||
html += "</select><br />\n";
|
||||
// Add error messages
|
||||
if ( (options.mode == 'add' && field.addRequired) || (options.mode == 'edit' && field.editRequired) ) {
|
||||
html += "<span class=\"error\" ng-show=\"" + this.form.name + '_form.' + fld + ".$dirty && " +
|
||||
this.form.name + '_form.' + fld + ".$error.required\">A value is required!</span>\n";
|
||||
}
|
||||
html += "<span class=\"error api-error\" ng-bind=\"" + fld + "_api_error\"></span>\n";
|
||||
html += "</div>\n";
|
||||
html += "</div>\n";
|
||||
}
|
||||
}
|
||||
|
||||
//number field
|
||||
if (field.type == 'number') {
|
||||
if ( (! field.readonly) || (field.readonly && options.mode == 'edit') ) {
|
||||
html += "<div class=\"control-group\""
|
||||
html += (field.ngShow) ? this.attr(field,'ngShow') : "";
|
||||
html += ">\n";
|
||||
html += "<label class=\"control-label\" for=\"" + fld + '">' + field.label + '</label>' + "\n";
|
||||
html += "<div class=\"controls\">\n";
|
||||
// Use 'text' rather than 'number' so that our integer directive works correctly
|
||||
html += "<input type=\"text\" value=\"" + field.default + "\" class=\"spinner\" ";
|
||||
html += "ng-model=\"" + fld + '" ';
|
||||
html += 'name="' + fld + '" ';
|
||||
html += (field.min || field.min == 0) ? this.attr(field, 'min') : "";
|
||||
html += (field.max) ? this.attr(field, 'max') : "";
|
||||
html += (field.ngChange) ? this.attr(field,'ngChange') : "";
|
||||
html += (field.id) ? this.attr(field,'id') : "";
|
||||
html += (options.mode == 'edit' && field.editRequired) ? "required " : "";
|
||||
html += (options.mode == 'add' && field.addRequired) ? "required " : "";
|
||||
html += (field.readonly) ? "readonly " : "";
|
||||
html += (field.integer) ? "integer " : "";
|
||||
html += "/><br />\n";
|
||||
// Add error messages
|
||||
if ( (options.mode == 'add' && field.addRequired) || (options.mode == 'edit' && field.editRequired) ) {
|
||||
html += "<span class=\"error\" ng-show=\"" + this.form.name + '_form.' + fld + ".$dirty && " +
|
||||
this.form.name + '_form.' + fld + ".$error.required\">A value is required!</span>\n";
|
||||
}
|
||||
if (field.integer) {
|
||||
html += "<span class=\"error\" ng-show=\"" + this.form.name + '_form.' + fld + ".$error.integer\">Must be an integer value</span>\n";
|
||||
}
|
||||
if (field.min || field.max) {
|
||||
html += "<span class=\"error\" ng-show=\"" + this.form.name + '_form.' + fld + ".$error.min || " +
|
||||
this.form.name + '_form.' + fld + ".$error.max\">Must be in range " + field.min + " to " +
|
||||
field.max + "</span>\n";
|
||||
}
|
||||
html += "<span class=\"error api-error\" ng-bind=\"" + fld + "_api_error\"></span>\n";
|
||||
html += "</div>\n";
|
||||
html += "</div>\n";
|
||||
}
|
||||
}
|
||||
|
||||
//checkbox
|
||||
if (field.type == 'checkbox') {
|
||||
if ( (! field.readonly) || (field.readonly && options.mode == 'edit') ) {
|
||||
html += "<div class=\"control-group\" "
|
||||
html += (field.ngShow) ? this.attr(field,'ngShow') : "";
|
||||
html += ">\n";
|
||||
html += "<div class=\"controls\">\n";
|
||||
html += "<label class=\"checkbox\">";
|
||||
html += "<input ";
|
||||
html += this.attr(field,'type');
|
||||
html += "ng-model=\"" + fld + '" ';
|
||||
html += "name=\"" + fld + '" ';
|
||||
html += (field.ngChange) ? this.attr(field,'ngChange') : "";
|
||||
html += (field.id) ? this.attr(field,'id') : "";
|
||||
html += this.attr(field,'trueValue');
|
||||
html += this.attr(field,'falseValue');
|
||||
html += (field.checked) ? "checked " : "";
|
||||
html += (field.readonly) ? "readonly " : "";
|
||||
html += " /> " + field.label + "\n";
|
||||
html += "</label>\n";
|
||||
html += "<span class=\"error api-error\" ng-bind=\"" + fld + "_api_error\"></span>\n";
|
||||
|
||||
html += "</div>\n";
|
||||
html += "</div>\n";
|
||||
}
|
||||
}
|
||||
|
||||
if (field.type == 'hidden') {
|
||||
if ( (options.mode == 'edit' && field.includeOnEdit) ||
|
||||
(options.mode == 'add' && field.includeOnAdd) ) {
|
||||
html += "<input type=\"hidden\" ng-model=\"" + fld + "\" name=\"" + fld + "\" />";
|
||||
}
|
||||
}
|
||||
|
||||
//lookup type fields
|
||||
if (field.type == 'lookup') {
|
||||
html += "<div class=\"control-group\""
|
||||
html += (field.ngShow) ? this.attr(field,'ngShow') : "";
|
||||
html += ">\n";
|
||||
html += "<label class=\"control-label\" for=\"" + fld + '">' + field.label + '</label>' + "\n";
|
||||
html += "<div class=\"controls\">\n";
|
||||
html += "<div class=\"input-prepend\">\n";
|
||||
html += "<button class=\"btn\" " + this.attr(field,'ngClick') + "><i class=\"icon-search\"></i></button>\n";
|
||||
html += "<input class=\"input-medium\" type=\"text\" ";
|
||||
html += "ng-model=\"" + field.sourceModel + '_' + field.sourceField + "\" ";
|
||||
html += "name=\"" + field.sourceModel + '_' + field.sourceField + "\" ";
|
||||
html += (field.ngChange) ? this.attr(field,'ngChange') : "";
|
||||
html += (field.id) ? this.attr(field,'id') : "";
|
||||
html += (field.placeholder) ? this.attr(field,'placeholder') : "";
|
||||
html += (options.mode == 'edit' && field.editRequired) ? "required " : "";
|
||||
html += (options.mode == 'add' && field.addRequired) ? "required " : "";
|
||||
html += " awlookup />\n";
|
||||
html += "</div><br />\n";
|
||||
// Add error messages
|
||||
if ( (options.mode == 'add' && field.addRequired) || (options.mode == 'edit' && field.editRequired) ) {
|
||||
html += "<span class=\"error\" ng-show=\"" + this.form.name + '_form.' +
|
||||
field.sourceModel + '_' + field.sourceField + ".$dirty && " +
|
||||
this.form.name + '_form.' + field.sourceModel + '_' + field.sourceField +
|
||||
".$error.required\">A value is required!</span>\n";
|
||||
}
|
||||
html += "<span class=\"error\" ng-show=\"" + this.form.name + '_form.' +
|
||||
field.sourceModel + '_' + field.sourceField + ".$dirty && " +
|
||||
this.form.name + '_form.' + field.sourceModel + '_' + field.sourceField +
|
||||
".$error.awlookup\">Value not found</span>\n";
|
||||
html += "<span class=\"error api-error\" ng-bind=\"" + field.sourceModel + '_' + field.sourceField +
|
||||
"_api_error\"></span>\n";
|
||||
html += "</div>\n";
|
||||
html += "</div>\n";
|
||||
}
|
||||
|
||||
return html;
|
||||
},
|
||||
|
||||
build: function(options) {
|
||||
//
|
||||
// Generate HTML. Do NOT call this function directly. Called by inject(). Returns an HTML
|
||||
// string to be injected into the current view.
|
||||
//
|
||||
var html = '';
|
||||
|
||||
if (!this.modal) {
|
||||
//Breadcrumbs
|
||||
html += "<div class=\"nav-path\">\n";
|
||||
html += "<ul class=\"breadcrumb\">\n";
|
||||
html += "<li ng-repeat=\"crumb in breadcrumbs\"><a href=\"{{ '#' + crumb.path }}\">{{ crumb.title | capitalize }}</a> " +
|
||||
"<span class=\"divider\">/</span></li>\n";
|
||||
html += "<li class=\"active\">";
|
||||
if (options.mode == 'edit') {
|
||||
html += this.form.editTitle;
|
||||
}
|
||||
else {
|
||||
html += this.form.addTitle;
|
||||
}
|
||||
html += "</li>\n</ul>\n</div>\n";
|
||||
}
|
||||
|
||||
if (this.form.fieldsAsHeader) {
|
||||
html += "<div class=\"well\">\n";
|
||||
html += "<form class=\"form-inline\" name=\"" + this.form.name + "_form\" id=\"" + this.form.name + "\" novalidate >\n";
|
||||
for (var fld in this.form.fields) {
|
||||
field = this.form.fields[fld];
|
||||
html += this.headerField(fld, field, options);
|
||||
}
|
||||
html += "</form>\n";
|
||||
html += "</div>\n";
|
||||
}
|
||||
else {
|
||||
// Start the well
|
||||
if ( this.has('well') ) {
|
||||
html += "<div class=\"well\">\n";
|
||||
}
|
||||
|
||||
html += "<form class=\"form-horizontal\" name=\"" + this.form.name + '_form" id="' + this.form.name + '" novalidate>' + "\n";
|
||||
html += "<div ng-show=\"flashMessage != null && flashMessage != undefined\" class=\"alert alert-info\">{{ flashMessage }}</div>\n";
|
||||
|
||||
var field;
|
||||
if (this.form.twoColumns) {
|
||||
html += "<div class=\"row-fluid\">\n";
|
||||
html += "<div class=\"span6\">\n";
|
||||
for (var fld in this.form.fields) {
|
||||
field = this.form.fields[fld];
|
||||
if (field.column == 1) {
|
||||
html += this.buildField(fld, field, options);
|
||||
}
|
||||
}
|
||||
html += "</div><!-- column 1 -->\n";
|
||||
html += "<div class=\"span6\">\n";
|
||||
for (var fld in this.form.fields) {
|
||||
field = this.form.fields[fld];
|
||||
if (field.column == 2) {
|
||||
html += this.buildField(fld, field, options);
|
||||
}
|
||||
}
|
||||
html += "</div><!-- column 2 -->\n";
|
||||
html += "</div><!-- inner row -->\n";
|
||||
}
|
||||
else {
|
||||
// original, single-column form
|
||||
for (var fld in this.form.fields) {
|
||||
var field = this.form.fields[fld];
|
||||
html += this.buildField(fld, field, options);
|
||||
}
|
||||
}
|
||||
|
||||
//buttons
|
||||
if (!this.modal) {
|
||||
if (this.has('buttons')) {
|
||||
html += "<div class=\"control-group\">\n";
|
||||
html += "<div class=\"controls buttons\">\n";
|
||||
}
|
||||
for (var btn in this.form.buttons) {
|
||||
var button = this.form.buttons[btn];
|
||||
//button
|
||||
html += "<button ";
|
||||
html += "class=\"btn btn-small";
|
||||
html += (button.class) ? " " + button.class : "";
|
||||
html += "\" ";
|
||||
if (button.ngClick) {
|
||||
html += this.attr(button,'ngClick');
|
||||
}
|
||||
|
||||
if (button.ngDisabled) {
|
||||
if (btn !== 'reset') {
|
||||
html += "ng-disabled=\"" + this.form.name + "_form.$pristine || " + this.form.name + "_form.$invalid";
|
||||
html += (this.form.allowReadonly) ? " || " + this.form.name + "ReadOnly == true" : "";
|
||||
html += "\" ";
|
||||
}
|
||||
else {
|
||||
html += "ng-disabled=\"" + this.form.name + "_form.$pristine";
|
||||
html += (this.form.allowReadonly) ? " || " + this.form.name + "ReadOnly == true" : "";
|
||||
html += "\" ";
|
||||
}
|
||||
}
|
||||
|
||||
html += ">";
|
||||
if (button.icon) {
|
||||
html += this.icon(button.icon);
|
||||
}
|
||||
html += button.label + "</button>\n";
|
||||
}
|
||||
if (this.has('buttons')) {
|
||||
html += "</div>\n";
|
||||
html += "</div>\n";
|
||||
}
|
||||
html += "</form>\n";
|
||||
}
|
||||
|
||||
if ( this.has('well') ) {
|
||||
html += "</div>\n";
|
||||
}
|
||||
}
|
||||
|
||||
if ((!this.modal && this.form.statusFields)) {
|
||||
// Add status fields section (used in Jobs form)
|
||||
html += "<div class=\"well\">\n";
|
||||
for (var fld in this.form.statusFields) {
|
||||
field = this.form.statusFields[fld];
|
||||
html += this.buildField(fld, field, options);
|
||||
}
|
||||
html += "</div><!-- well -->\n";
|
||||
}
|
||||
|
||||
if ((!this.modal && this.form.items)) {
|
||||
for (itm in this.form.items) {
|
||||
html += "<div class=\"well form-items\">\n";
|
||||
html += SearchWidget({ iterator: this.form.items[itm].iterator, template: this.form.items[itm], mini: false, label: 'Filter Events'});
|
||||
html += "<div class=\"item-count pull-right\">Viewing" + " \{\{ " + this.form.items[itm].iterator + "Page + 1 \}\} of " +
|
||||
"\{\{ " + this.form.items[itm].iterator + "Count \}\}</div>\n";
|
||||
html += "<hr />\n";
|
||||
html += "<ul class=\"pager\">\n";
|
||||
html += "<li ng-class=\"" + this.form.items[itm].iterator + "PrevUrlDisable\"><a href=\"\" " +
|
||||
"ng-click=\"prevSet('" + this.form.items[itm].set + "','" + this.form.items[itm].iterator + "')\">← Prev</a></li>\n";
|
||||
html += "<li ng-class=\"" + this.form.items[itm].iterator + "NextUrlDisable\"><a href=\"\" " +
|
||||
"ng-click=\"nextSet('" + this.form.items[itm].set + "','" + this.form.items[itm].iterator + "')\">→ Next</a></li>\n";
|
||||
html +="</ul>\n";
|
||||
html += "<form class=\"form-horizontal\" name=\"" + this.form.name + '_items_form" id="' + this.form.name + '_items_form" novalidate>' + "\n";
|
||||
for (var fld in this.form.items[itm].fields) {
|
||||
var field = this.form.items[itm].fields[fld];
|
||||
html += this.buildField(fld, field, options);
|
||||
}
|
||||
html += "</form>\n";
|
||||
html += "<ul class=\"pager\">\n";
|
||||
html += "<li ng-class=\"" + this.form.items[itm].iterator + "PrevUrlDisable\"><a href=\"\" " +
|
||||
"ng-click=\"prevSet('" + this.form.items[itm].set + "','" + this.form.items[itm].iterator + "')\">← Prev</a></li>\n";
|
||||
html += "<li ng-class=\"" + this.form.items[itm].iterator + "NextUrlDisable\"><a href=\"\" " +
|
||||
"ng-click=\"nextSet('" + this.form.items[itm].set + "','" + this.form.items[itm].iterator + "')\">→ Next</a></li>\n";
|
||||
html +="</ul>\n";
|
||||
html += "</div><!-- well -->\n";
|
||||
}
|
||||
}
|
||||
|
||||
if ((!this.modal) && options.related && this.form.related) {
|
||||
html += this.buildCollections();
|
||||
}
|
||||
|
||||
return html;
|
||||
|
||||
},
|
||||
|
||||
buildCollections: function() {
|
||||
//
|
||||
// Create TB accordians with imbedded lists for related collections
|
||||
// Should not be called directly. Called internally by build().
|
||||
//
|
||||
var idx = 1;
|
||||
var form = this.form;
|
||||
var html = "<div class=\"accordion\" id=\"accordion\">\n";
|
||||
for (var itm in form.related) {
|
||||
if (form.related[itm].type == 'collection' || form.related[itm].type == 'tree') {
|
||||
// Start the accordion group
|
||||
html += "<div class=\"accordion-group\">\n";
|
||||
html += "<div class=\"accordion-heading\">\n";
|
||||
html += "<a class=\"accordion-toggle\" data-toggle=\"collapse\" data-parent=\"#accordion\" href=\"#collapse" + idx + "\">";
|
||||
html += "<i class=\"icon-angle-down icon-white\"></i>" + form.related[itm].title + "</a>\n";
|
||||
html += "</div>\n";
|
||||
html += "<div id=\"collapse" + idx + "\" class=\"accordion-body collapse";
|
||||
if (form.related[itm].open) {
|
||||
html += " in"; //open accordion on load
|
||||
}
|
||||
html += "\">\n";
|
||||
html += "<div class=\"accordion-inner\">\n";
|
||||
|
||||
if (form.related[itm].instructions) {
|
||||
html += "<div class=\"alert alert-info alert-block\">\n";
|
||||
html += "<button type=\"button\" class=\"close\" data-dismiss=\"alert\">×</button>\n";
|
||||
html += "<strong>Hint: </strong>" + form.related[itm].instructions + "\n";
|
||||
html += "</div>\n"
|
||||
}
|
||||
|
||||
if (form.related[itm].type == 'tree') {
|
||||
html += "<div>\n";
|
||||
// Add actions(s)
|
||||
if (form.related[itm].actions && form.related[itm].actions.length > 0) {
|
||||
html += "<div class=\"text-right actions\">\n";
|
||||
for (var act in form.related[itm].actions) {
|
||||
var action = form.related[itm].actions[act];
|
||||
html += "<button class=\"btn btn-mini btn-success\" ";
|
||||
html += this.attr(action,'ngClick');
|
||||
html += (action.awToolTip) ? this.attr(action,'awToolTip') : "";
|
||||
html += ">" + this.icon(action.icon) + "</button>\n";
|
||||
}
|
||||
html += "</div>\n";
|
||||
}
|
||||
html += "<div id=\"tree-view\"></div>\n";
|
||||
html += "</div>\n";
|
||||
}
|
||||
else {
|
||||
html += "<div class=\"well\">\n";
|
||||
|
||||
html += SearchWidget({ iterator: form.related[itm].iterator, template: form.related[itm], mini: true });
|
||||
|
||||
// Add actions(s)
|
||||
html += "<div class=\"text-right actions\">\n";
|
||||
for (var action in form.related[itm].actions) {
|
||||
html += "<button class=\"btn btn-mini btn-success\" ";
|
||||
html += this.attr(form.related[itm]['actions'][action],'ngClick');
|
||||
html += "><i class=\"" + form.related[itm]['actions'][action].icon + "\"></i></button>\n";
|
||||
}
|
||||
html += "</div>\n";
|
||||
|
||||
// Start the list
|
||||
html += "<div class=\"list\">\n";
|
||||
html += "<table class=\"table table-condensed\">\n";
|
||||
html += "<thead>\n";
|
||||
html += "<tr>\n";
|
||||
html += "<th>#</th>\n";
|
||||
for (var fld in form.related[itm].fields) {
|
||||
html += "<th>" + form.related[itm]['fields'][fld].label + "</th>\n";
|
||||
}
|
||||
html += "<th></th>\n";
|
||||
html += "</tr>\n";
|
||||
html += "</thead>";
|
||||
html += "<tbody>\n";
|
||||
|
||||
html += "<tr ng-repeat=\"" + form.related[itm].iterator + " in " + itm + "\" >\n";
|
||||
html += "<td>{{ $index + (" + form.related[itm].iterator + "Page * " +
|
||||
form.related[itm].iterator + "PageSize) + 1 }}.</td>\n";
|
||||
var cnt = 1;
|
||||
for (var fld in form.related[itm].fields) {
|
||||
cnt++;
|
||||
html += "<td>{{ " + form.related[itm].iterator + "." + fld + " }}</td>\n";
|
||||
}
|
||||
|
||||
// Row level actions
|
||||
html += "<td class=\"actions\">";
|
||||
for (action in form.related[itm].fieldActions) {
|
||||
html += "<button class=\"btn btn-mini";
|
||||
if (form.related[itm]['fieldActions'][action].class) {
|
||||
html += " " + form.related[itm]['fieldActions'][action].class;
|
||||
}
|
||||
html += "\" " + this.attr(form.related[itm]['fieldActions'][action],'ngClick') +
|
||||
">" + this.icon(form.related[itm]['fieldActions'][action].icon) + "</button> ";
|
||||
}
|
||||
html += "</td>";
|
||||
html += "</tr>\n";
|
||||
|
||||
// Message for when a related collection is empty
|
||||
html += "<tr class=\"info\" ng-show=\"" + itm + " == null || " + itm + ".length == 0\">\n";
|
||||
html += "<td colspan=\"" + cnt + "\"><div class=\"alert alert-info\">No records matched your search.</div></td>\n";
|
||||
html += "</tr>\n";
|
||||
|
||||
// End List
|
||||
html += "</tbody>\n";
|
||||
html += "</table>\n";
|
||||
html += "</div>\n"; // close well
|
||||
html += "</div>\n"; // close list div
|
||||
|
||||
html += PaginateWidget({ set: itm, iterator: form.related[itm].iterator, mini: true });
|
||||
}
|
||||
|
||||
// End Accordion Group
|
||||
html += "</div>\n"; // accordion inner
|
||||
html += "</div>\n"; // accordion body
|
||||
html += "</div>\n"; // accordion group
|
||||
|
||||
idx++;
|
||||
}
|
||||
}
|
||||
html += "</div>\n";
|
||||
|
||||
return html;
|
||||
}
|
||||
|
||||
}}]);
|
||||
120
ansibleworks/ui/static/lib/ansible/generator-helpers.js
Normal file
120
ansibleworks/ui/static/lib/ansible/generator-helpers.js
Normal file
@@ -0,0 +1,120 @@
|
||||
/*********************************************
|
||||
* Copyright (c) 2013 AnsibleWorks, Inc.
|
||||
*
|
||||
* GeneratorHelpers
|
||||
*
|
||||
* Functions shared between FormGenerator and ListGenerator
|
||||
*
|
||||
*/
|
||||
|
||||
angular.module('GeneratorHelpers', [])
|
||||
.factory('SearchWidget', function() {
|
||||
return function(params) {
|
||||
//
|
||||
// Generate search widget
|
||||
//
|
||||
var iterator = params.iterator;
|
||||
var form = params.template;
|
||||
var useMini = params.mini;
|
||||
var label = (params.label) ? params.label : null;
|
||||
var html= '';
|
||||
|
||||
html += "<div class=\"search-widget\">\n";
|
||||
html += (label) ? "<label>" + label +"</label>" : "";
|
||||
html += "<div class=\"input-prepend input-append\">\n";
|
||||
html += "<div class=\"btn-group\">\n";
|
||||
html += "<button class=\"btn ";
|
||||
html += (useMini) ? "btn-mini " : "";
|
||||
html += "dropdown-toggle\" data-toggle=\"dropdown\">\n";
|
||||
html += "<span ng-bind=\"" + iterator + "SearchFieldLabel\"></span>\n";
|
||||
html += "<span class=\"caret\"></span>\n";
|
||||
html += "</button>\n";
|
||||
html += "<ul class=\"dropdown-menu\">\n";
|
||||
|
||||
for ( var fld in form.fields) {
|
||||
html += "<li><a href=\"\" ng-click=\"setSearchField('" + iterator + "','";
|
||||
//html += (form.fields[fld].desc) ? '-' : '';
|
||||
//html += (form.fields[fld].searchField) ? form.fields[fld].searchField : fld;
|
||||
html += fld + "','" + form.fields[fld].label + "')\">"
|
||||
+ form.fields[fld].label + "</a></li>\n";
|
||||
}
|
||||
html += "</ul>\n";
|
||||
html += "</div>\n";
|
||||
|
||||
html += "<select ng-show=\"" + iterator + "SelectShow\" ng-model=\""+ iterator + "SearchSelectValue\" ng-change=\"search('" + iterator + "')\" ";
|
||||
html += "ng-options=\"c.name for c in " + iterator + "SearchSelectOpts\" class=\"search-select\"></select>\n";
|
||||
|
||||
html += "<input ng-hide=\"" + iterator + "SelectShow\" class=\"input-medium";
|
||||
html += (useMini) ? " field-mini-height" : "";
|
||||
html += "\" ng-model=\"" + iterator + "SearchValue\" ng-change=\"search('" + iterator +
|
||||
"')\" placeholder=\"Search\" type=\"text\" >\n";
|
||||
|
||||
html += "<div class=\"btn-group\">\n";
|
||||
html += "<button ng-hide=\"" + iterator + "SelectShow || " + iterator + "HideSearchType\" class=\"btn ";
|
||||
html += (useMini) ? "btn-mini " : "";
|
||||
html += "dropdown-toggle\" data-toggle=\"dropdown\">\n";
|
||||
html += "<span ng-bind=\"" + iterator + "SearchTypeLabel\"></span>\n";
|
||||
html += "<span class=\"caret\"></span>\n";
|
||||
html += "</button>\n";
|
||||
html += "<ul class=\"dropdown-menu\">\n";
|
||||
html += "<li><a href=\"\" ng-click=\"setSearchType('" + iterator + "','iexact','Exact Match')\">Exact Match</a></li>\n";
|
||||
html += "<li><a href=\"\" ng-click=\"setSearchType('" + iterator + "','icontains','Contains')\">Contains</a></li>\n";
|
||||
html += "</ul>\n";
|
||||
html += "</div>\n";
|
||||
html += "</div>\n";
|
||||
html += "<div class=\"spin\"><i class=\"icon-spinner icon-spin\" ng-show=\"" + iterator + "SearchSpin == true\"></i></div>\n";
|
||||
html += "</div>\n";
|
||||
|
||||
return html;
|
||||
|
||||
}
|
||||
})
|
||||
|
||||
.factory('PaginateWidget', function() {
|
||||
return function(params) {
|
||||
var set = params.set;
|
||||
var iterator = params.iterator;
|
||||
var useMini = params.mini;
|
||||
var mode = (params.mode) ? params.mode : null;
|
||||
var html = '';
|
||||
|
||||
if (mode == 'lookup') {
|
||||
html += "<div class=\"lookup-navigation";
|
||||
}
|
||||
else {
|
||||
html += "<div class=\"footer-navigation";
|
||||
}
|
||||
html += (useMini) ? " related-footer" : "";
|
||||
html += "\">\n";
|
||||
html += "<form class=\"form-inline\">\n";
|
||||
html += "<button class=\"previous btn";
|
||||
html += (useMini) ? " btn-mini\" " : "\" ";
|
||||
html += "ng-click=\"prevSet('" + set + "','" + iterator + "')\" " +
|
||||
"ng-disabled=\"" + iterator + "PrevUrl == null || " + iterator + "PrevUrl == undefined\"><i class=\"icon-chevron-left\"></i> Prev</button>\n";
|
||||
html += "<button class=\"next btn btn";
|
||||
html += (useMini) ? " btn-mini\" " : "\" ";
|
||||
html += " ng-click=\"nextSet('" + set + "','" + iterator + "')\"" +
|
||||
"ng-disabled=\"" + iterator + "NextUrl == null || " + iterator + "NextUrl == undefined\">Next <i class=\"icon-chevron-right\"></i></button>\n";
|
||||
|
||||
if (mode != 'lookup') {
|
||||
html += "<label class=\"page-size-label\">Rows per page:</label>\n";
|
||||
html += "<select ng-model=\"" + iterator + "PageSize\" ng-change=\"changePageSize('" +
|
||||
set + "'," + "'" + iterator + "')\" class=\"input-mini";
|
||||
html += (useMini) ? " field-mini-height" : "";
|
||||
html += " page-size\">\n";
|
||||
html += "<option value=\"20\" selected>20</option>\n";
|
||||
html += "<option value=\"40\">40</option>\n";
|
||||
html += "<option value=\"60\">60</option>\n";
|
||||
html += "<option value=\"80\">80</option>\n";
|
||||
html += "</select>\n";
|
||||
}
|
||||
|
||||
html += "<div class=\"page-number-small\"";
|
||||
html += ">Page: {{ " + iterator + "Page + 1 }} of {{ " + iterator + "PageCount }}</div>\n";
|
||||
html += "</form>\n";
|
||||
html += "</div>\n";
|
||||
|
||||
return html;
|
||||
|
||||
}
|
||||
});
|
||||
266
ansibleworks/ui/static/lib/ansible/list-generator.js
Normal file
266
ansibleworks/ui/static/lib/ansible/list-generator.js
Normal file
@@ -0,0 +1,266 @@
|
||||
/*********************************************
|
||||
* Copyright (c) 2013 AnsibleWorks, Inc.
|
||||
*
|
||||
* ListGenerator
|
||||
* Pass in a list definition from ListDefinitions and out pops an html template.
|
||||
* Use inject method to generate the html and inject into the current view.
|
||||
*
|
||||
*/
|
||||
|
||||
angular.module('ListGenerator', ['GeneratorHelpers',])
|
||||
.factory('GenerateList', [ '$location', '$compile', '$rootScope', 'SearchWidget', 'PaginateWidget',
|
||||
function($location, $compile, $rootScope, SearchWidget, PaginateWidget) {
|
||||
return {
|
||||
|
||||
setList: function(list) {
|
||||
this.list = list;
|
||||
},
|
||||
|
||||
attr: function(obj, key) {
|
||||
var result;
|
||||
switch (key) {
|
||||
case 'ngClick':
|
||||
result = "ng-click=\"" + obj[key] + "\" ";
|
||||
break;
|
||||
case 'ngDisabled':
|
||||
result = "ng-disabled=\"" + obj[key] + "\" ";
|
||||
break;
|
||||
case 'ngBind':
|
||||
result = "ng-bind=\"" + obj[key] + "\" ";
|
||||
break;
|
||||
case 'ngShow':
|
||||
result = "ng-show=\"" + obj[key] + "\" ";
|
||||
break;
|
||||
case 'awToolTip':
|
||||
result = "aw-tool-tip=\"" + obj[key] + "\" ";
|
||||
break;
|
||||
default:
|
||||
result = key + "=\"" + obj[key] + "\" ";
|
||||
}
|
||||
return result;
|
||||
},
|
||||
|
||||
icon: function(icon) {
|
||||
return "<i class=\"" + icon + "\"></i> ";
|
||||
},
|
||||
|
||||
has: function(key) {
|
||||
return (this.form[key] && this.form[key] != null && this.form[key] != undefined) ? true : false;
|
||||
},
|
||||
|
||||
hide: function() {
|
||||
$('#lookup-modal').modal('hide');
|
||||
},
|
||||
|
||||
inject: function(list, options) {
|
||||
// options.mode = one of edit, select or lookup
|
||||
//
|
||||
// Modes edit and select will inject the list as html into element #htmlTemplate.
|
||||
// 'lookup' mode injects the list html into #lookup-modal-body.
|
||||
//
|
||||
// For options.mode == 'lookup', include the following:
|
||||
//
|
||||
// hdr: <lookup dialog header>
|
||||
//
|
||||
if (options.mode == 'lookup') {
|
||||
var element = angular.element(document.getElementById('lookup-modal-body'));
|
||||
}
|
||||
else {
|
||||
var element = angular.element(document.getElementById('htmlTemplate'));
|
||||
}
|
||||
this.setList(list);
|
||||
element.html(this.build(options)); // Inject the html
|
||||
this.scope = element.scope(); // Set scope specific to the element we're compiling, avoids circular reference
|
||||
// From here use 'scope' to manipulate the form, as the form is not in '$scope'
|
||||
$compile(element)(this.scope);
|
||||
|
||||
if (options.mode == 'lookup') {
|
||||
// options should include {hdr: <dialog header>, action: <function...> }
|
||||
this.scope.lookupHeader = options.hdr;
|
||||
$('#lookup-modal').modal();
|
||||
}
|
||||
|
||||
return this.scope;
|
||||
},
|
||||
|
||||
build: function(options) {
|
||||
//
|
||||
// Generate HTML. Do NOT call this function directly. Called by inject(). Returns an HTML
|
||||
// string to be injected into the current view.
|
||||
//
|
||||
var html = '';
|
||||
var list = this.list;
|
||||
|
||||
if (options.mode != 'lookup') {
|
||||
//Breadcrumbs
|
||||
html += "<div class=\"nav-path\">\n";
|
||||
html += "<ul class=\"breadcrumb\">\n";
|
||||
html += "<li ng-repeat=\"crumb in breadcrumbs\"><a href=\"{{ '#' + crumb.path }}\">{{ crumb.title | capitalize }}</a> " +
|
||||
"<span class=\"divider\">/</span></li>\n";
|
||||
html += "<li class=\"active\">";
|
||||
if (options.mode == 'select') {
|
||||
html += list.selectTitle;
|
||||
}
|
||||
else {
|
||||
html += list.editTitle;
|
||||
}
|
||||
html += "</li>\n</ul>\n</div>\n";
|
||||
}
|
||||
|
||||
//select instructions
|
||||
if (options.mode == 'select' && list.selectInstructions) {
|
||||
html += "<div class=\"alert alert-info alert-block\">\n";
|
||||
html += "<button type=\"button\" class=\"close\" data-dismiss=\"alert\">×</button>\n";
|
||||
html += "<strong>Hint: </strong>" + list.selectInstructions + "\n";
|
||||
html += "</div>\n"
|
||||
}
|
||||
else if (options.mode == 'edit' && list.editInstructions) {
|
||||
html += "<div class=\"alert alert-info alert-block\">\n";
|
||||
html += "<button type=\"button\" class=\"close\" data-dismiss=\"alert\">×</button>\n";
|
||||
html += "<strong>Hint: </strong>" + list.editInstructions + "\n";
|
||||
html += "</div>\n";
|
||||
}
|
||||
|
||||
if (options.mode != 'lookup') {
|
||||
html += "<div class=\"well\">\n";
|
||||
}
|
||||
|
||||
if (options.mode == 'lookup') {
|
||||
html += SearchWidget({ iterator: list.iterator, template: list, mini: true });
|
||||
}
|
||||
else {
|
||||
html += SearchWidget({ iterator: list.iterator, template: list, mini: false });
|
||||
}
|
||||
|
||||
if (options.mode != 'lookup') {
|
||||
//actions
|
||||
base = $location.path().replace(/^\//,'').split('/')[0];
|
||||
html += "<div class=\"list-actions\">\n";
|
||||
for (action in list.actions) {
|
||||
if (list.actions[action].mode == 'all' || list.actions[action].mode == options.mode) {
|
||||
if ( (list.basePaths == undefined) || (list.basePaths && list.basePaths.indexOf(base) > -1) ) {
|
||||
html += "<button " + this.attr(list.actions[action], 'ngClick') +
|
||||
"class=\"btn btn-small " + list.actions[action].class + "\" ";
|
||||
html += (list.actions[action].awToolTip) ? this.attr(list.actions[action],'awToolTip') : "";
|
||||
html += " >" + this.icon(list.actions[action].icon) + "</button> ";
|
||||
}
|
||||
}
|
||||
}
|
||||
if (options.mode == 'select') {
|
||||
html += " <button class=\"btn btn-small btn-success\" aw-tool-tip=\"Complete your selection\" " +
|
||||
"ng-click=\"finishSelection()\"><i class=\"icon-ok\"></i> Finished</button>\n";
|
||||
}
|
||||
html += "</div>\n";
|
||||
}
|
||||
|
||||
// table header row
|
||||
html += "<table class=\"table table-condensed"
|
||||
html += (list.class) ? " " + list.class : "";
|
||||
html += (options.mode == 'lookup' || list.hover) ? " table-hover" : "";
|
||||
html += "\">\n";
|
||||
html += "<thead>\n";
|
||||
html += "<tr>\n";
|
||||
if (list.index) {
|
||||
html += "<th>#</th>\n";
|
||||
}
|
||||
for (var fld in list.fields) {
|
||||
html += "<th>" + list.fields[fld].label + "</th>\n";
|
||||
}
|
||||
if (options.mode == 'select') {
|
||||
html += "<th>Select</th>";
|
||||
}
|
||||
else if (options.mode == 'edit') {
|
||||
html += "<th></th>\n";
|
||||
}
|
||||
html += "</tr>\n";
|
||||
html += "</thead>\n";
|
||||
|
||||
// table body
|
||||
html += "<tbody>\n";
|
||||
html += "<tr ";
|
||||
html += (options.mode == 'lookup' || options.mode == 'select') ? "ng-class=\"" + list.iterator + "_\{\{ " + list.iterator + ".id \}\}_class\" " : "";
|
||||
html += "class=\"" + list.iterator + "_class\" ng-repeat=\"" + list.iterator + " in " + list.name + "\"";
|
||||
html += (options.mode == 'lookup' || options.mode == 'select') ? " ng-click=\"toggle_" + list.iterator +"({{ " + list.iterator + ".id }})\"" : "";
|
||||
html += ">\n";
|
||||
if (list.index) {
|
||||
html += "<td class=\"index-column\">{{ $index + (" + list.iterator + "Page * " + list.iterator + "PageSize) + 1 }}.</td>\n";
|
||||
}
|
||||
var cnt = 2;
|
||||
var base = (list.base) ? list.base : list.name;
|
||||
for (fld in list.fields) {
|
||||
cnt++;
|
||||
if (! list.fields[fld].ngBind) {
|
||||
html += "<td class=\"" + fld + "-column";
|
||||
html += (list.fields[fld].class) ? " " + list.fields[fld].class : "";
|
||||
html += "\">";
|
||||
if ((list.fields[fld].key || list.fields[fld].link) && options.mode != 'lookup' && options.mode != 'select') {
|
||||
html += "<a href=\"#/" + base + "/{{" + list.iterator + ".id }}\">";
|
||||
}
|
||||
html += (list.fields[fld].icon) ? this.icon(list.fields[fld].icon) : "";
|
||||
html += "{{" + list.iterator + "." + fld + "}}";
|
||||
html += ((list.fields[fld].key || list.fields[fld].link) && options.mode != 'lookup' && options.mode != 'select') ? "</a>" : "";
|
||||
html += "</td>\n";
|
||||
}
|
||||
else {
|
||||
html += "<td class=\"" + fld + "-column\">";
|
||||
if ((list.fields[fld].key || list.fields[fld].link) && options.mode != 'lookup' && options.mode != 'select') {
|
||||
html += "<a href=\"#/" + base + "/{{" + list.iterator + ".id }}\">";
|
||||
}
|
||||
html += (list.fields[fld].icon) ? this.icon(list.fields[fld].icon) : "";
|
||||
html += "{{ " + list.fields[fld].ngBind + " }}";
|
||||
html += ((list.fields[fld].key || list.fields[fld].link) && options.mode != 'lookup' && options.mode != 'select') ? "</a>" : "";
|
||||
html += "</td>\n";
|
||||
}
|
||||
}
|
||||
|
||||
if (options.mode == 'select' ) {
|
||||
html += "<td><input type=\"checkbox\" name=\"check_{{" + list.iterator + ".id}}\" id=\"check_{{" + list.iterator + ".id}}\" /></td>";
|
||||
//ng-click=\"toggle_" + list.iterator +
|
||||
// "({{ " + list.iterator + ".id }}, true)\"
|
||||
}
|
||||
else if (options.mode == 'edit') {
|
||||
// Row level actions
|
||||
html += "<td class=\"actions\">";
|
||||
for (action in list.fieldActions) {
|
||||
html += "<button class=\"btn";
|
||||
if (list.fieldActions[action].class) {
|
||||
html += " " + list.fieldActions[action].class;
|
||||
}
|
||||
html += "\" " + this.attr(list.fieldActions[action],'ngClick');
|
||||
html += (list.fieldActions[action].ngShow) ? this.attr(list.fieldActions[action],'ngShow') : "";
|
||||
html += (list.fieldActions[action].awToolTip) ? this.attr(list.fieldActions[action],'awToolTip') : "";
|
||||
html += (list.fieldActions[action].ngDisabled) ? this.attr(list.fieldActions[action],'ngDisabled') : ""
|
||||
html +=">";
|
||||
html += (list.fieldActions[action].icon) ? this.icon(list.fieldActions[action].icon) : "";
|
||||
html += (list.fieldActions[action].label) ? " " + list.fieldActions[action].label : "";
|
||||
html +="</button> ";
|
||||
}
|
||||
html += "</td>";
|
||||
}
|
||||
html += "</tr>\n";
|
||||
|
||||
// Message for when a collection is empty
|
||||
html += "<tr class=\"info\" ng-show=\"" + list.name + " == null || " + list.name + ".length == 0\">\n";
|
||||
html += "<td colspan=\"" + cnt + "\"><div class=\"alert alert-info\">No " + list.iterator + " records matched your search.</div></td>\n";
|
||||
html += "</tr>\n";
|
||||
|
||||
// End List
|
||||
html += "</tbody>\n";
|
||||
html += "</table>\n";
|
||||
|
||||
if (options.mode != 'lookup') {
|
||||
html += "</div>\n"; //well
|
||||
}
|
||||
|
||||
if (options.mode == 'lookup') {
|
||||
html += PaginateWidget({ set: list.name, iterator: list.iterator, mini: true, mode: 'lookup' });
|
||||
}
|
||||
else {
|
||||
html += PaginateWidget({ set: list.name, iterator: list.iterator, mini: true });
|
||||
}
|
||||
|
||||
return html;
|
||||
|
||||
}
|
||||
|
||||
}}]);
|
||||
56
ansibleworks/ui/static/lib/ansible/prompt-dialog.js
Normal file
56
ansibleworks/ui/static/lib/ansible/prompt-dialog.js
Normal file
@@ -0,0 +1,56 @@
|
||||
/*********************************************
|
||||
* Copyright (c) 2013 AnsibleWorks, Inc.
|
||||
*
|
||||
* PromptDialog
|
||||
* Prompt the user with a Yes/No dialog to confirm an action such
|
||||
* as Delete. Assumes a hidden dialog already exists in $scope.
|
||||
* See example at bottom. If user responds with Yes, execute action
|
||||
* parameter.
|
||||
*
|
||||
* params: { hdr: 'header msg',
|
||||
* body: 'body text/html',
|
||||
* class: 'btn-class for Yes button', --defaults to btn-danger
|
||||
* action: function() {} --action to take, if use clicks Yes
|
||||
* }
|
||||
*/
|
||||
|
||||
angular.module('PromptDialog', [])
|
||||
.factory('Prompt', function(Alert) {
|
||||
return function(params) {
|
||||
//
|
||||
|
||||
var dialog = angular.element(document.getElementById('prompt-modal'));
|
||||
var scope = dialog.scope();
|
||||
scope.promptHeader = params.hdr;
|
||||
scope.promptBody = params.body;
|
||||
var cls = (params.class == null || params.class == undefined) ? 'btn-danger' : params.class;
|
||||
$('#prompt-action-btn').addClass(cls); //Use jquery because django template engine conflicts with Angular's
|
||||
// use of {{...}}
|
||||
//scope.id = params.id;
|
||||
//scope.url = params.url;
|
||||
scope.promptAction = params.action;
|
||||
$(dialog).modal({
|
||||
backdrop: 'static',
|
||||
keyboard: true,
|
||||
show: true
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
|
||||
|
||||
<!-- Generic Modal dialog. Use to confirming an action (i.e. Delete) -->
|
||||
// <div id="prompt-modal" class="modal hide">
|
||||
// <div class="modal-header">
|
||||
// <button type="button" class="close" data-target="#prompt-modal"
|
||||
// data-dismiss="modal" aria-hidden="true">×</button>
|
||||
// <h3 ng-bind="promptHeader"></h3>
|
||||
// </div>
|
||||
// <div class="modal-body" ng-bind="promptBody">
|
||||
// </div>
|
||||
// <div class="modal-footer">
|
||||
// <a href="#" data-target="#prompt-modal" data-dismiss="modal" class="btn">No</a>
|
||||
// <a href="" ng-click="promptAction()" class="btn {{ promptBtnClass }}">Yes</a>
|
||||
// </div>
|
||||
// </div>
|
||||
63
ansibleworks/ui/static/lib/ansible/rest-services.js
Normal file
63
ansibleworks/ui/static/lib/ansible/rest-services.js
Normal file
@@ -0,0 +1,63 @@
|
||||
/*********************************************
|
||||
* Copyright (c) 2013 AnsibleWorks, Inc.
|
||||
*
|
||||
* Generic accessor for Ansible Commander services
|
||||
*
|
||||
*/
|
||||
angular.module('RestServices',['ngCookies','AuthService'])
|
||||
.factory('Rest', ['$http','$rootScope','$cookieStore','Authorization', function($http, $rootScope, $cookieStore, Authorization) {
|
||||
return {
|
||||
|
||||
setUrl: function (url) {
|
||||
this.url = url;
|
||||
},
|
||||
|
||||
auth: { 'Authorization': 'Token ' + Authorization.getToken() },
|
||||
|
||||
pReplace: function() {
|
||||
//in our url, replace :xx params with a value, assuming
|
||||
// we can find it in user supplied params.
|
||||
var key,rgx;
|
||||
for (key in this.params) {
|
||||
rgx = new RegExp("\\:" + key,'gm');
|
||||
rgx.compile;
|
||||
if (rgx.test(this.url)) {
|
||||
this.url = this.url.replace(rgx,this.params[key]);
|
||||
delete this.params[key];
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
get: function(args) {
|
||||
args = (args) ? args : {};
|
||||
this.params = (args.params) ? args.params : null;
|
||||
this.pReplace();
|
||||
return $http({method: 'GET',
|
||||
url: this.url,
|
||||
headers: this.auth,
|
||||
params: this.params
|
||||
});
|
||||
},
|
||||
post: function(data) {
|
||||
return $http({method: 'POST',
|
||||
url: this.url,
|
||||
headers: this.auth,
|
||||
data: data });
|
||||
},
|
||||
put: function(data) {
|
||||
return $http({method: 'PUT',
|
||||
url: this.url,
|
||||
headers: this.auth,
|
||||
data: data });
|
||||
|
||||
},
|
||||
delete: function(data) {
|
||||
var url = this.url;
|
||||
return $http({method: 'DELETE',
|
||||
url: url,
|
||||
headers: this.auth,
|
||||
data: data});
|
||||
}
|
||||
}
|
||||
}]);
|
||||
|
||||
23
ansibleworks/ui/static/lib/ansible/tooltip.js
Normal file
23
ansibleworks/ui/static/lib/ansible/tooltip.js
Normal file
@@ -0,0 +1,23 @@
|
||||
/************************************
|
||||
* Copyright (c) 2013 AnsibleWorks, Inc.
|
||||
*
|
||||
*
|
||||
* tooltip.js
|
||||
*
|
||||
* Custom directive to enable TB tooltips. To add a tooltip to an element, include the following directive in
|
||||
* the element's attributes:
|
||||
*
|
||||
* aw-tool-tip="<< tooltip text here >>"
|
||||
*
|
||||
* Include the standard TB data-XXX attributes to controll a tooltip's appearance. We will default placement
|
||||
* to the left and delay to 2 seconds.
|
||||
*
|
||||
*/
|
||||
angular.module('AWToolTip', [])
|
||||
.directive('awToolTip', function() {
|
||||
return function(scope, element, attrs) {
|
||||
var delay = (attrs.delay != undefined && attrs.delay != null) ? attrs.delay : $AnsibleConfig.tooltip_delay;
|
||||
var placement = (attrs.placement != undefined && attrs.placement != null) ? attrs.placement : 'left';
|
||||
$(element).tooltip({ placement: placement, delay: delay, title: attrs.awToolTip });
|
||||
}
|
||||
});
|
||||
126
ansibleworks/ui/static/lib/ansible/utilities.js
Normal file
126
ansibleworks/ui/static/lib/ansible/utilities.js
Normal file
@@ -0,0 +1,126 @@
|
||||
/************************************
|
||||
*
|
||||
* Copyright (c) 2013 AnsibleWorks, Inc.
|
||||
*
|
||||
* Utility functions
|
||||
*
|
||||
*/
|
||||
angular.module('Utilities',[])
|
||||
|
||||
.factory('ClearScope', function() {
|
||||
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);
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
.factory('Alert', ['$rootScope', function($rootScope) {
|
||||
return function(hdr, msg) {
|
||||
// Pass in the header and message you want displayed on TB modal dialog found in index.html.
|
||||
// Assumes an id of 'alert-modal'
|
||||
$rootScope.alertHeader = hdr;
|
||||
$rootScope.alertBody = msg;
|
||||
$('#alert-modal').modal({ show: true, keyboard: true, backdrop: 'static' });
|
||||
}
|
||||
}])
|
||||
|
||||
.factory('ProcessErrors', ['$log', 'Alert', function($log, Alert) {
|
||||
return function(scope, data, status, form, defaultMsg) {
|
||||
if (status == 403) {
|
||||
Alert('Access Denied', 'The API responded with a 403 Access Denied error. You do not have permission to perform the ' +
|
||||
'requested action. Please contact a system administrator.');
|
||||
}
|
||||
else if (data.non_field_errors) {
|
||||
Alert('Error!', data.non_field_errors);
|
||||
}
|
||||
else if (form) {
|
||||
var fieldErrors = false;
|
||||
for (var field in form.fields ) {
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
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];
|
||||
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.
|
||||
|
||||
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);
|
||||
}
|
||||
}]);
|
||||
Reference in New Issue
Block a user