thalassa-aqueduct
Version:
Dynamic haproxy load balancer and configuration. Part of Thalassa
300 lines (279 loc) • 9.24 kB
JavaScript
/**
* @ngdoc overview
* @name ui.bootstrap.tabs
*
* @description
* AngularJS version of the tabs directive.
*/
angular.module('ui.bootstrap.tabs', [])
.directive('tabs', function() {
return function() {
throw new Error("The `tabs` directive is deprecated, please migrate to `tabset`. Instructions can be found at http://github.com/angular-ui/bootstrap/tree/master/CHANGELOG.md");
};
})
.controller('TabsetController', ['$scope', '$element',
function TabsetCtrl($scope, $element) {
var ctrl = this,
tabs = ctrl.tabs = $scope.tabs = [];
ctrl.select = function(tab) {
angular.forEach(tabs, function(tab) {
tab.active = false;
});
tab.active = true;
};
ctrl.addTab = function addTab(tab) {
tabs.push(tab);
if (tabs.length == 1) {
ctrl.select(tab);
}
};
ctrl.removeTab = function removeTab(tab) {
var index = tabs.indexOf(tab);
//Select a new tab if the tab to be removed is selected
if (tab.active && tabs.length > 1) {
//If this is the last tab, select the previous tab. else, the next tab.
var newActiveIndex = index == tabs.length - 1 ? index - 1 : index + 1;
ctrl.select(tabs[newActiveIndex]);
}
tabs.splice(index, 1);
};
}])
/**
* @ngdoc directive
* @name ui.bootstrap.tabs.directive:tabset
* @restrict EA
*
* @description
* Tabset is the outer container for the tabs directive
*
* @param {boolean=} vertical Whether or not to use vertical styling for the tabs.
*
* @example
<example module="ui.bootstrap">
<file name="index.html">
<tabset>
<tab heading="Vertical Tab 1"><b>First</b> Content!</tab>
<tab heading="Vertical Tab 2"><i>Second</i> Content!</tab>
</tabset>
<hr />
<tabset vertical="true">
<tab heading="Vertical Tab 1"><b>First</b> Vertical Content!</tab>
<tab heading="Vertical Tab 2"><i>Second</i> Vertical Content!</tab>
</tabset>
</file>
</example>
*/
.directive('tabset', function() {
return {
restrict: 'EA',
transclude: true,
scope: {},
controller: 'TabsetController',
templateUrl: 'template/tabs/tabset.html',
link: function(scope, element, attrs) {
scope.vertical = angular.isDefined(attrs.vertical) ? scope.$eval(attrs.vertical) : false;
scope.type = angular.isDefined(attrs.type) ? scope.$parent.$eval(attrs.type) : 'tabs';
}
};
})
/**
* @ngdoc directive
* @name ui.bootstrap.tabs.directive:tab
* @restrict EA
*
* @param {string=} heading The visible heading, or title, of the tab. Set HTML headings with {@link ui.bootstrap.tabs.directive:tabHeading tabHeading}.
* @param {string=} select An expression to evaluate when the tab is selected.
* @param {boolean=} active A binding, telling whether or not this tab is selected.
* @param {boolean=} disabled A binding, telling whether or not this tab is disabled.
*
* @description
* Creates a tab with a heading and content. Must be placed within a {@link ui.bootstrap.tabs.directive:tabset tabset}.
*
* @example
<example module="ui.bootstrap">
<file name="index.html">
<div ng-controller="TabsDemoCtrl">
<button class="btn btn-small" ng-click="items[0].active = true">
Select item 1, using active binding
</button>
<button class="btn btn-small" ng-click="items[1].disabled = !items[1].disabled">
Enable/disable item 2, using disabled binding
</button>
<br />
<tabset>
<tab heading="Tab 1">First Tab</tab>
<tab select="alertMe()">
<tab-heading><i class="icon-bell"></i> Alert me!</tab-heading>
Second Tab, with alert callback and html heading!
</tab>
<tab ng-repeat="item in items"
heading="{{item.title}}"
disabled="item.disabled"
active="item.active">
{{item.content}}
</tab>
</tabset>
</div>
</file>
<file name="script.js">
function TabsDemoCtrl($scope) {
$scope.items = [
{ title:"Dynamic Title 1", content:"Dynamic Item 0" },
{ title:"Dynamic Title 2", content:"Dynamic Item 1", disabled: true }
];
$scope.alertMe = function() {
setTimeout(function() {
alert("You've selected the alert tab!");
});
};
};
</file>
</example>
*/
/**
* @ngdoc directive
* @name ui.bootstrap.tabs.directive:tabHeading
* @restrict EA
*
* @description
* Creates an HTML heading for a {@link ui.bootstrap.tabs.directive:tab tab}. Must be placed as a child of a tab element.
*
* @example
<example module="ui.bootstrap">
<file name="index.html">
<tabset>
<tab>
<tab-heading><b>HTML</b> in my titles?!</tab-heading>
And some content, too!
</tab>
<tab>
<tab-heading><i class="icon-heart"></i> Icon heading?!?</tab-heading>
That's right.
</tab>
</tabset>
</file>
</example>
*/
.directive('tab', ['$parse', '$http', '$templateCache', '$compile',
function($parse, $http, $templateCache, $compile) {
return {
require: '^tabset',
restrict: 'EA',
replace: true,
templateUrl: 'template/tabs/tab.html',
transclude: true,
scope: {
heading: '@',
onSelect: '&select' //This callback is called in contentHeadingTransclude
//once it inserts the tab's content into the dom
},
controller: function() {
//Empty controller so other directives can require being 'under' a tab
},
compile: function(elm, attrs, transclude) {
return function postLink(scope, elm, attrs, tabsetCtrl) {
var getActive, setActive;
scope.active = false; // default value
if (attrs.active) {
getActive = $parse(attrs.active);
setActive = getActive.assign;
scope.$parent.$watch(getActive, function updateActive(value) {
if ( !!value && scope.disabled ) {
setActive(scope.$parent, false); // Prevent active assignment
} else {
scope.active = !!value;
}
});
} else {
setActive = getActive = angular.noop;
}
scope.$watch('active', function(active) {
setActive(scope.$parent, active);
if (active) {
tabsetCtrl.select(scope);
scope.onSelect();
}
});
scope.disabled = false;
if ( attrs.disabled ) {
scope.$parent.$watch($parse(attrs.disabled), function(value) {
scope.disabled = !! value;
});
}
scope.select = function() {
if ( ! scope.disabled ) {
scope.active = true;
}
};
tabsetCtrl.addTab(scope);
scope.$on('$destroy', function() {
tabsetCtrl.removeTab(scope);
});
//If the tabset sets this tab to active, set the parent scope's active
//binding too. We do this so the watch for the parent's initial active
//value won't overwrite what is initially set by the tabset
if (scope.active) {
setActive(scope.$parent, true);
}
//Transclude the collection of sibling elements. Use forEach to find
//the heading if it exists. We don't use a directive for tab-heading
//because it is problematic. Discussion @ http://git.io/MSNPwQ
transclude(scope.$parent, function(clone) {
//Look at every element in the clone collection. If it's tab-heading,
//mark it as that. If it's not tab-heading, mark it as tab contents
var contents = [], heading;
angular.forEach(clone, function(el) {
//See if it's a tab-heading attr or element directive
//First make sure it's a normal element, one that has a tagName
if (el.tagName &&
(el.hasAttribute("tab-heading") ||
el.hasAttribute("data-tab-heading") ||
el.tagName.toLowerCase() == "tab-heading" ||
el.tagName.toLowerCase() == "data-tab-heading"
)) {
heading = el;
} else {
contents.push(el);
}
});
//Share what we found on the scope, so our tabHeadingTransclude and
//tabContentTransclude directives can find out what the heading and
//contents are.
if (heading) {
scope.headingElement = angular.element(heading);
}
scope.contentElement = angular.element(contents);
});
};
}
};
}])
.directive('tabHeadingTransclude', [function() {
return {
restrict: 'A',
require: '^tab',
link: function(scope, elm, attrs, tabCtrl) {
scope.$watch('headingElement', function updateHeadingElement(heading) {
if (heading) {
elm.html('');
elm.append(heading);
}
});
}
};
}])
.directive('tabContentTransclude', ['$parse', function($parse) {
return {
restrict: 'A',
require: '^tabset',
link: function(scope, elm, attrs, tabsetCtrl) {
scope.$watch($parse(attrs.tabContentTransclude), function(tab) {
elm.html('');
if (tab) {
elm.append(tab.contentElement);
}
});
}
};
}])
;