diff --git a/guacamole/src/main/webapp/app/navigation/directives/guacSectionTabs.js b/guacamole/src/main/webapp/app/navigation/directives/guacSectionTabs.js
new file mode 100644
index 000000000..97c87c2e6
--- /dev/null
+++ b/guacamole/src/main/webapp/app/navigation/directives/guacSectionTabs.js
@@ -0,0 +1,143 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+/**
+ * Directive which displays a set of tabs dividing a section of a page into
+ * logical subsections or views. The currently selected tab is communicated
+ * through assignment to the variable bound to the current
+ * attribute. No navigation occurs as a result of selecting a tab.
+ */
+angular.module('navigation').directive('guacSectionTabs', ['$injector',
+ function guacSectionTabs($injector) {
+
+ // Required services
+ var translationStringService = $injector.get('translationStringService');
+
+ var directive = {
+
+ restrict : 'E',
+ replace : true,
+ templateUrl : 'app/navigation/templates/guacSectionTabs.html',
+
+ scope : {
+
+ /**
+ * The translation namespace to use when producing translation
+ * strings for each tab. Tab translation strings will be of the
+ * form:
+ *
+ * NAMESPACE.SECTION_HEADER_NAME
+ *
+ * where NAMESPACE
is the namespace provided to this
+ * attribute and NAME
is one of the names within the
+ * array provided to the tabs
attribute and
+ * transformed via translationStringService.canonicalize().
+ */
+ namespace : '@',
+
+ /**
+ * The name of the currently selected tab. This name MUST be one of
+ * the names present in the array given via the tabs
+ * attribute. This directive will not automatically choose an
+ * initially selected tab, and a default value should be manually
+ * assigned to current
to ensure a tab is initially
+ * selected.
+ *
+ * @type String
+ */
+ current : '=',
+
+ /**
+ * The unique names of all tabs which should be made available, in
+ * display order. These names will be assigned to the variable
+ * bound to the current
attribute when the current
+ * tab changes.
+ *
+ * @type String[]
+ */
+ tabs : '='
+
+ }
+
+ };
+
+ directive.controller = ['$scope', function dataSourceTabsController($scope) {
+
+ /**
+ * Produces the translation string for the section header representing
+ * the tab having the given name. The translation string will be of the
+ * form:
+ *
+ * NAMESPACE.SECTION_HEADER_NAME
+ *
+ * where NAMESPACE
is the namespace provided to the
+ * directive and NAME
is the given name transformed
+ * via translationStringService.canonicalize().
+ *
+ * @param {String} name
+ * The name of the tab.
+ *
+ * @returns {String}
+ * The translation string which produces the translated header
+ * of the tab having the given name.
+ */
+ $scope.getSectionHeader = function getSectionHeader(name) {
+
+ // If no name, then no header
+ if (!name)
+ return '';
+
+ return translationStringService.canonicalize($scope.namespace || 'MISSING_NAMESPACE')
+ + '.SECTION_HEADER_' + translationStringService.canonicalize(name);
+
+ };
+
+ /**
+ * Selects the tab having the given name. The name of the currently
+ * selected tab will be communicated outside the directive through
+ * $scope.current.
+ *
+ * @param {String} name
+ * The name of the tab to select.
+ */
+ $scope.selectTab = function selectTab(name) {
+ $scope.current = name;
+ };
+
+ /**
+ * Returns whether the tab having the given name is currently
+ * selected. A tab is currently selected if its name is stored within
+ * $scope.current, as assigned externally or by selectTab().
+ *
+ * @param {String} name
+ * The name of the tab to test.
+ *
+ * @returns {Boolean}
+ * true if the tab having the given name is currently selected,
+ * false otherwise.
+ */
+ $scope.isSelected = function isSelected(name) {
+ return $scope.current === name;
+ };
+
+ }];
+
+ return directive;
+
+}]);
diff --git a/guacamole/src/main/webapp/app/navigation/styles/page-tabs.css b/guacamole/src/main/webapp/app/navigation/styles/tabs.css
similarity index 76%
rename from guacamole/src/main/webapp/app/navigation/styles/page-tabs.css
rename to guacamole/src/main/webapp/app/navigation/styles/tabs.css
index 5e88cd0e1..3d1c8cdec 100644
--- a/guacamole/src/main/webapp/app/navigation/styles/page-tabs.css
+++ b/guacamole/src/main/webapp/app/navigation/styles/tabs.css
@@ -17,23 +17,27 @@
* under the License.
*/
-.page-tabs .page-list ul {
+.page-tabs .page-list ul,
+.section-tabs ul {
margin: 0;
padding: 0;
background: rgba(0, 0, 0, 0.0125);
border-bottom: 1px solid rgba(0, 0, 0, 0.05);
}
-.page-tabs .page-list ul + ul {
+.page-tabs .page-list ul + ul,
+.section-tabs ul + ul {
font-size: 0.75em;
}
-.page-tabs .page-list li {
+.page-tabs .page-list li,
+.section-tabs li {
display: inline-block;
list-style: none;
}
-.page-tabs .page-list li a[href] {
+.page-tabs .page-list li a[href],
+.section-tabs li a {
display: block;
color: black;
text-decoration: none;
@@ -44,12 +48,16 @@
color: black;
}
-.page-tabs .page-list li a[href]:hover {
+.page-tabs .page-list li a[href]:hover,
+.section-tabs li a:hover {
background-color: #CDA;
+ cursor: pointer;
}
.page-tabs .page-list li a[href].current,
-.page-tabs .page-list li a[href].current:hover {
+.page-tabs .page-list li a[href].current:hover,
+.section-tabs li a.current,
+.section-tabs li a.current:hover {
background: rgba(0,0,0,0.3);
cursor: default;
}
diff --git a/guacamole/src/main/webapp/app/navigation/templates/guacSectionTabs.html b/guacamole/src/main/webapp/app/navigation/templates/guacSectionTabs.html
new file mode 100644
index 000000000..a02871534
--- /dev/null
+++ b/guacamole/src/main/webapp/app/navigation/templates/guacSectionTabs.html
@@ -0,0 +1,10 @@
+
\ No newline at end of file