angular.module("core-config-setup", ['angularFileUpload']) .directive('configSetupTool', function() { var directiveDefinitionObject = { priority: 1, templateUrl: '/static/directives/config/config-setup-tool.html', replace: true, transclude: true, restrict: 'C', scope: { 'isActive': '=isActive' }, controller: function($rootScope, $scope, $element, $timeout, ApiService) { $scope.config = null; $scope.mapped = {}; var githubSelector = function(key) { return function(value) { if (!value || !$scope.config) { return; } if (!$scope.config[key]) { $scope.config[key] = {}; } if (value == 'enterprise') { $scope.config[key]['GITHUB_ENDPOINT'] = ''; $scope.config[key]['API_ENDPOINT'] = ''; } else if (value == 'hosted') { $scope.config[key]['GITHUB_ENDPOINT'] = 'https://github.com/'; $scope.config[key]['API_ENDPOINT'] = 'https://api.github.com/'; } }; }; var getKey = function(config, path) { var parts = path.split('.'); var current = config; for (var i = 0; i < parts.length; ++i) { var part = parts[i]; if (!config[part]) { return null; } current = config[part]; } return current; }; var initializeMappedLogic = function(config) { var gle = getKey(config, 'GITHUB_LOGIN_CONFIG.GITHUB_ENDPOINT'); var gte = getKey(config, 'GITHUB_TRIGGER_CONFIG.GITHUB_ENDPOINT'); $scope.mapped['GITHUB_LOGIN_KIND'] = gle == 'https://github.com/' ? 'hosted' : 'enterprise'; $scope.mapped['GITHUB_TRIGGER_KIND'] = gte == 'https://github.com/' ? 'hosted' : 'enterprise'; $scope.mapped['redis'] = {}; $scope.mapped['redis']['host'] = getKey(config, 'BUILDLOGS_REDIS.host') || getKey(config, 'USER_EVENTS_REDIS.host'); $scope.mapped['redis']['port'] = getKey(config, 'BUILDLOGS_REDIS.port') || getKey(config, 'USER_EVENTS_REDIS.port'); $scope.mapped['redis']['password'] = getKey(config, 'BUILDLOGS_REDIS.password') || getKey(config, 'USER_EVENTS_REDIS.password'); }; var redisSetter = function(keyname) { return function(value) { if (value == null || !$scope.config) { return; } if (!$scope.config['BUILDLOGS_REDIS']) { $scope.config['BUILDLOGS_REDIS'] = {}; } if (!$scope.config['USER_EVENTS_REDIS']) { $scope.config['USER_EVENTS_REDIS'] = {}; } if (!value) { delete $scope.config['BUILDLOGS_REDIS'][keyname]; delete $scope.config['USER_EVENTS_REDIS'][keyname]; return; } $scope.config['BUILDLOGS_REDIS'][keyname] = value; $scope.config['USER_EVENTS_REDIS'][keyname] = value; }; }; // Add mapped logic. $scope.$watch('mapped.GITHUB_LOGIN_KIND', githubSelector('GITHUB_LOGIN_CONFIG')); $scope.$watch('mapped.GITHUB_TRIGGER_KIND', githubSelector('GITHUB_TRIGGER_CONFIG')); $scope.$watch('mapped.redis.host', redisSetter('host')); $scope.$watch('mapped.redis.port', redisSetter('port')); $scope.$watch('mapped.redis.password', redisSetter('password')); $scope.$watch('config', function(value) { }, true); $scope.$watch('isActive', function(value) { if (!value) { return; } ApiService.scGetConfig().then(function(resp) { $scope.config = resp['config']; initializeMappedLogic($scope.config); }); }); } }; return directiveDefinitionObject; }) .directive('configParsedField', function ($timeout) { var directiveDefinitionObject = { priority: 0, templateUrl: '/static/directives/config/config-parsed-field.html', replace: false, transclude: true, restrict: 'C', scope: { 'binding': '=binding', 'parser': '&parser', 'serializer': '&serializer' }, controller: function($scope, $element, $transclude) { $scope.childScope = null; $transclude(function(clone, scope) { $scope.childScope = scope; $scope.childScope['fields'] = {}; $element.append(clone); }); $scope.childScope.$watch('fields', function(value) { // Note: We need the timeout here because Angular starts the digest of the // parent scope AFTER the child scope, which means it can end up one action // behind. The timeout ensures that the parent scope will be fully digest-ed // and then we update the binding. Yes, this is a hack :-/. $timeout(function() { $scope.binding = $scope.serializer({'fields': value}); }); }, true); $scope.$watch('binding', function(value) { var parsed = $scope.parser({'value': value}); for (var key in parsed) { if (parsed.hasOwnProperty(key)) { $scope.childScope['fields'][key] = parsed[key]; } } }); } }; return directiveDefinitionObject; }) .directive('configVariableField', function () { var directiveDefinitionObject = { priority: 0, templateUrl: '/static/directives/config/config-variable-field.html', replace: false, transclude: true, restrict: 'C', scope: { 'binding': '=binding' }, controller: function($scope, $element) { $scope.sections = {}; $scope.currentSection = null; $scope.setSection = function(section) { $scope.binding = section.value; }; this.addSection = function(section, element) { $scope.sections[section.value] = { 'title': section.valueTitle, 'value': section.value, 'element': element }; element.hide(); if (!$scope.binding) { $scope.binding = section.value; } }; $scope.$watch('binding', function(binding) { if (!binding) { return; } if ($scope.currentSection) { $scope.currentSection.element.hide(); } if ($scope.sections[binding]) { $scope.sections[binding].element.show(); $scope.currentSection = $scope.sections[binding]; } }); } }; return directiveDefinitionObject; }) .directive('variableSection', function () { var directiveDefinitionObject = { priority: 0, templateUrl: '/static/directives/config/config-variable-field.html', priority: 1, require: '^configVariableField', replace: false, transclude: true, restrict: 'C', scope: { 'value': '@value', 'valueTitle': '@valueTitle' }, controller: function($scope, $element) { var parentCtrl = $element.parent().controller('configVariableField'); parentCtrl.addSection($scope, $element); } }; return directiveDefinitionObject; }) .directive('configListField', function () { var directiveDefinitionObject = { priority: 0, templateUrl: '/static/directives/config/config-list-field.html', replace: false, transclude: false, restrict: 'C', scope: { 'binding': '=binding', 'placeholder': '@placeholder', 'defaultValue': '@defaultValue', 'itemTitle': '@itemTitle' }, controller: function($scope, $element) { $scope.removeItem = function(item) { var index = $scope.binding.indexOf(item); if (index >= 0) { $scope.binding.splice(index, 1); } }; $scope.addItem = function() { if (!$scope.newItemName) { return; } if (!$scope.binding) { $scope.binding = []; } if ($scope.binding.indexOf($scope.newItemName) >= 0) { return; } $scope.binding.push($scope.newItemName); $scope.newItemName = null; }; $scope.$watch('binding', function(binding) { if (!binding && $scope.defaultValue) { $scope.binding = eval($scope.defaultValue); } }); } }; return directiveDefinitionObject; }) .directive('configFileField', function () { var directiveDefinitionObject = { priority: 0, templateUrl: '/static/directives/config/config-file-field.html', replace: false, transclude: false, restrict: 'C', scope: { 'filename': '@filename' }, controller: function($scope, $element, Restangular, $upload) { $scope.hasFile = false; $scope.onFileSelect = function(files) { if (files.length < 1) { return; } $scope.uploadProgress = 0; $scope.upload = $upload.upload({ url: '/api/v1/superuser/config/file', method: 'POST', data: { filename: $scope.filename }, file: files[0], }).progress(function(evt) { $scope.uploadProgress = parseInt(100.0 * evt.loaded / evt.total); if ($scope.uploadProgress == 100) { $scope.uploadProgress = null; $scope.hasFile = true; } }).success(function(data, status, headers, config) { $scope.uploadProgress = null; $scope.hasFile = true; }); }; var loadStatus = function(filename) { Restangular.one('superuser/config/file/' + filename).get().then(function(resp) { $scope.hasFile = resp['exists']; }); }; if ($scope.filename) { loadStatus($scope.filename); } } }; return directiveDefinitionObject; }) .directive('configBoolField', function () { var directiveDefinitionObject = { priority: 0, templateUrl: '/static/directives/config/config-bool-field.html', replace: false, transclude: false, restrict: 'C', scope: { 'binding': '=binding' }, controller: function($scope, $element) { } }; return directiveDefinitionObject; }) .directive('configNumericField', function () { var directiveDefinitionObject = { priority: 0, templateUrl: '/static/directives/config/config-numeric-field.html', replace: false, transclude: false, restrict: 'C', scope: { 'binding': '=binding', 'placeholder': '@placeholder', 'defaultValue': '@defaultValue' }, controller: function($scope, $element) { $scope.bindinginternal = 0; $scope.$watch('binding', function(binding) { if ($scope.binding == 0 && $scope.defaultValue) { $scope.binding = $scope.defaultValue * 1; } $scope.bindinginternal = $scope.binding; }); $scope.$watch('bindinginternal', function(binding) { var newValue = $scope.bindinginternal * 1; if (isNaN(newValue)) { newValue = 0; } $scope.binding = newValue; }); } }; return directiveDefinitionObject; }) .directive('configContactsField', function () { var directiveDefinitionObject = { priority: 0, templateUrl: '/static/directives/config/config-contacts-field.html', priority: 1, replace: false, transclude: false, restrict: 'C', scope: { 'binding': '=binding' }, controller: function($scope, $element) { $scope.$watch('items', function(items) { if (!items) { return; } // Remove the last item if both it and the second to last items are empty. if (items.length > 1 && !items[items.length - 2].value && !items[items.length - 1].value) { items.splice(items.length - 1, 1); return; } // If the last item is non-empty, add a new item. if (items[items.length - 1].value) { items.push({'value': ''}); } }, true); $scope.$watch('binding', function(binding) { $scope.items = []; $scope.items.push({'value': ''}); }); } }; return directiveDefinitionObject; }) .directive('configContactField', function () { var directiveDefinitionObject = { priority: 0, templateUrl: '/static/directives/config/config-contact-field.html', priority: 1, replace: false, transclude: true, restrict: 'C', scope: { 'binding': '=binding' }, controller: function($scope, $element) { $scope.kind = null; $scope.value = null; var updateBinding = function() { var value = $scope.value || ''; switch ($scope.kind) { case 'mailto': $scope.binding = 'mailto:' + value; return; case 'tel': $scope.binding = 'tel:' + value; return; case 'irc': $scope.binding = 'irc://' + value; return; default: $scope.binding = value; return; } }; $scope.$watch('kind', updateBinding); $scope.$watch('value', updateBinding); $scope.$watch('binding', function(value) { if (!value) { $scope.kind = null; $scope.value = null; return; } var uri = URI(value); $scope.kind = uri.scheme(); switch ($scope.kind) { case 'mailto': case 'tel': $scope.value = uri.path(); break; case 'irc': $scope.value = value.substr('irc://'.length); break; default: $scope.kind = 'http'; $scope.value = value; break; } }); $scope.getPlaceholder = function(kind) { switch (kind) { case 'mailto': return 'some@example.com'; case 'tel': return '555-555-5555'; case 'irc': return 'myserver:port/somechannel'; default: return 'http://some/url'; } }; } }; return directiveDefinitionObject; }) .directive('configStringField', function () { var directiveDefinitionObject = { priority: 0, templateUrl: '/static/directives/config/config-string-field.html', replace: false, transclude: false, restrict: 'C', scope: { 'binding': '=binding', 'placeholder': '@placeholder', 'pattern': '@pattern', 'defaultValue': '@defaultValue' }, controller: function($scope, $element) { $scope.getRegexp = function(pattern) { if (!pattern) { pattern = '.*'; } return new RegExp(pattern); }; $scope.$watch('binding', function(binding) { if (!binding && $scope.defaultValue) { $scope.binding = $scope.defaultValue; } }); } }; return directiveDefinitionObject; });