191 lines
5.8 KiB
JavaScript
191 lines
5.8 KiB
JavaScript
/**
|
|
* angular-strap
|
|
* @version v2.2.1 - 2015-03-10
|
|
* @link http://mgcrea.github.io/angular-strap
|
|
* @author Olivier Louvignes (olivier@mg-crea.com)
|
|
* @license MIT License, http://www.opensource.org/licenses/MIT
|
|
*/
|
|
'use strict';
|
|
|
|
angular.module('mgcrea.ngStrap.tab', [])
|
|
|
|
.provider('$tab', function() {
|
|
|
|
var defaults = this.defaults = {
|
|
animation: 'am-fade',
|
|
template: 'tab/tab.tpl.html',
|
|
navClass: 'nav-tabs',
|
|
activeClass: 'active'
|
|
};
|
|
|
|
var controller = this.controller = function($scope, $element, $attrs) {
|
|
var self = this;
|
|
|
|
// Attributes options
|
|
self.$options = angular.copy(defaults);
|
|
angular.forEach(['animation', 'navClass', 'activeClass'], function(key) {
|
|
if(angular.isDefined($attrs[key])) self.$options[key] = $attrs[key];
|
|
});
|
|
|
|
// Publish options on scope
|
|
$scope.$navClass = self.$options.navClass;
|
|
$scope.$activeClass = self.$options.activeClass;
|
|
|
|
self.$panes = $scope.$panes = [];
|
|
|
|
// DEPRECATED: $viewChangeListeners, please use $activePaneChangeListeners
|
|
// Because we deprecated ngModel usage, we rename viewChangeListeners to
|
|
// activePaneChangeListeners to make more sense.
|
|
self.$activePaneChangeListeners = self.$viewChangeListeners = [];
|
|
|
|
self.$push = function(pane) {
|
|
self.$panes.push(pane);
|
|
};
|
|
|
|
self.$remove = function(pane) {
|
|
var index = self.$panes.indexOf(pane);
|
|
var activeIndex = self.$panes.$active;
|
|
|
|
// remove pane from $panes array
|
|
self.$panes.splice(index, 1);
|
|
|
|
if (index < activeIndex) {
|
|
// we removed a pane before the active pane, so we need to
|
|
// decrement the active pane index
|
|
activeIndex--;
|
|
}
|
|
else if (index === activeIndex && activeIndex === self.$panes.length) {
|
|
// we remove the active pane and it was the one at the end,
|
|
// so select the previous one
|
|
activeIndex--;
|
|
}
|
|
self.$setActive(activeIndex);
|
|
};
|
|
|
|
self.$panes.$active = 0;
|
|
self.$setActive = $scope.$setActive = function(value) {
|
|
self.$panes.$active = value;
|
|
self.$activePaneChangeListeners.forEach(function(fn) {
|
|
fn();
|
|
});
|
|
};
|
|
|
|
};
|
|
|
|
this.$get = function() {
|
|
var $tab = {};
|
|
$tab.defaults = defaults;
|
|
$tab.controller = controller;
|
|
return $tab;
|
|
};
|
|
|
|
})
|
|
|
|
.directive('bsTabs', ["$window", "$animate", "$tab", "$parse", function($window, $animate, $tab, $parse) {
|
|
|
|
var defaults = $tab.defaults;
|
|
|
|
return {
|
|
require: ['?ngModel', 'bsTabs'],
|
|
transclude: true,
|
|
scope: true,
|
|
controller: ['$scope', '$element', '$attrs', $tab.controller],
|
|
templateUrl: function(element, attr) {
|
|
return attr.template || defaults.template;
|
|
},
|
|
link: function postLink(scope, element, attrs, controllers) {
|
|
|
|
var ngModelCtrl = controllers[0];
|
|
var bsTabsCtrl = controllers[1];
|
|
|
|
// DEPRECATED: ngModel, please use bsActivePane
|
|
// 'ngModel' is deprecated bacause if interferes with form validation
|
|
// and status, so avoid using it here.
|
|
if(ngModelCtrl) {
|
|
console.warn('Usage of ngModel is deprecated, please use bsActivePane instead!');
|
|
|
|
// Update the modelValue following
|
|
bsTabsCtrl.$activePaneChangeListeners.push(function() {
|
|
ngModelCtrl.$setViewValue(bsTabsCtrl.$panes.$active);
|
|
});
|
|
|
|
// modelValue -> $formatters -> viewValue
|
|
ngModelCtrl.$formatters.push(function(modelValue) {
|
|
// console.warn('$formatter("%s"): modelValue=%o (%o)', element.attr('ng-model'), modelValue, typeof modelValue);
|
|
bsTabsCtrl.$setActive(modelValue * 1);
|
|
return modelValue;
|
|
});
|
|
|
|
}
|
|
|
|
if (attrs.bsActivePane) {
|
|
// adapted from angularjs ngModelController bindings
|
|
// https://github.com/angular/angular.js/blob/v1.3.1/src%2Fng%2Fdirective%2Finput.js#L1730
|
|
var parsedBsActivePane = $parse(attrs.bsActivePane);
|
|
|
|
// Update bsActivePane value with change
|
|
bsTabsCtrl.$activePaneChangeListeners.push(function() {
|
|
parsedBsActivePane.assign(scope, bsTabsCtrl.$panes.$active);
|
|
});
|
|
|
|
// watch bsActivePane for value changes
|
|
scope.$watch(attrs.bsActivePane, function(newValue, oldValue) {
|
|
bsTabsCtrl.$setActive(newValue * 1);
|
|
}, true);
|
|
}
|
|
}
|
|
};
|
|
|
|
}])
|
|
|
|
.directive('bsPane', ["$window", "$animate", "$sce", function($window, $animate, $sce) {
|
|
|
|
return {
|
|
require: ['^?ngModel', '^bsTabs'],
|
|
scope: true,
|
|
link: function postLink(scope, element, attrs, controllers) {
|
|
|
|
var ngModelCtrl = controllers[0];
|
|
var bsTabsCtrl = controllers[1];
|
|
|
|
// Add base class
|
|
element.addClass('tab-pane');
|
|
|
|
// Observe title attribute for change
|
|
attrs.$observe('title', function(newValue, oldValue) {
|
|
scope.title = $sce.trustAsHtml(newValue);
|
|
});
|
|
|
|
// Add animation class
|
|
if(bsTabsCtrl.$options.animation) {
|
|
element.addClass(bsTabsCtrl.$options.animation);
|
|
}
|
|
|
|
attrs.$observe('disabled', function(newValue, oldValue) {
|
|
scope.disabled = scope.$eval(newValue);
|
|
});
|
|
|
|
// Push pane to parent bsTabs controller
|
|
bsTabsCtrl.$push(scope);
|
|
|
|
// remove pane from tab controller when pane is destroyed
|
|
scope.$on('$destroy', function() {
|
|
bsTabsCtrl.$remove(scope);
|
|
});
|
|
|
|
function render() {
|
|
var index = bsTabsCtrl.$panes.indexOf(scope);
|
|
var active = bsTabsCtrl.$panes.$active;
|
|
$animate[index === active ? 'addClass' : 'removeClass'](element, bsTabsCtrl.$options.activeClass);
|
|
}
|
|
|
|
bsTabsCtrl.$activePaneChangeListeners.push(function() {
|
|
render();
|
|
});
|
|
render();
|
|
|
|
}
|
|
};
|
|
|
|
}]);
|