angular-ui-bootstrap-rails 0.6.0.0 → 0.7.0.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -106,23 +106,7 @@ angular.module('ui.bootstrap.collapse',['ui.bootstrap.transition'])
106
106
 
107
107
  var isCollapsed;
108
108
  var initialAnimSkip = true;
109
- scope.$watch(function (){ return element[0].scrollHeight; }, function (value) {
110
- //The listener is called when scollHeight changes
111
- //It actually does on 2 scenarios:
112
- // 1. Parent is set to display none
113
- // 2. angular bindings inside are resolved
114
- //When we have a change of scrollHeight we are setting again the correct height if the group is opened
115
- if (element[0].scrollHeight !== 0) {
116
- if (!isCollapsed) {
117
- if (initialAnimSkip) {
118
- fixUpHeight(scope, element, element[0].scrollHeight + 'px');
119
- } else {
120
- fixUpHeight(scope, element, 'auto');
121
- }
122
- }
123
- }
124
- });
125
-
109
+
126
110
  scope.$watch(attrs.collapse, function(value) {
127
111
  if (value) {
128
112
  collapse();
@@ -150,6 +134,7 @@ angular.module('ui.bootstrap.collapse',['ui.bootstrap.transition'])
150
134
  initialAnimSkip = false;
151
135
  if ( !isCollapsed ) {
152
136
  fixUpHeight(scope, element, 'auto');
137
+ element.addClass('in');
153
138
  }
154
139
  } else {
155
140
  doTransition({ height : element[0].scrollHeight + 'px' })
@@ -158,6 +143,7 @@ angular.module('ui.bootstrap.collapse',['ui.bootstrap.transition'])
158
143
  // the group while the animation was still running
159
144
  if ( !isCollapsed ) {
160
145
  fixUpHeight(scope, element, 'auto');
146
+ element.addClass('in');
161
147
  }
162
148
  });
163
149
  }
@@ -166,6 +152,7 @@ angular.module('ui.bootstrap.collapse',['ui.bootstrap.transition'])
166
152
 
167
153
  var collapse = function() {
168
154
  isCollapsed = true;
155
+ element.removeClass('in');
169
156
  if (initialAnimSkip) {
170
157
  initialAnimSkip = false;
171
158
  fixUpHeight(scope, element, 0);
@@ -185,10 +172,13 @@ angular.module('ui.bootstrap.accordion', ['ui.bootstrap.collapse'])
185
172
  })
186
173
 
187
174
  .controller('AccordionController', ['$scope', '$attrs', 'accordionConfig', function ($scope, $attrs, accordionConfig) {
188
-
175
+
189
176
  // This array keeps track of the accordion groups
190
177
  this.groups = [];
191
178
 
179
+ // Keep reference to user's scope to properly assign `is-open`
180
+ this.scope = $scope;
181
+
192
182
  // Ensure that all the groups in this accordion are closed, unless close-others explicitly says not to
193
183
  this.closeOthers = function(openGroup) {
194
184
  var closeOthers = angular.isDefined($attrs.closeOthers) ? $scope.$eval($attrs.closeOthers) : accordionConfig.closeOthers;
@@ -258,12 +248,9 @@ angular.module('ui.bootstrap.accordion', ['ui.bootstrap.collapse'])
258
248
  getIsOpen = $parse(attrs.isOpen);
259
249
  setIsOpen = getIsOpen.assign;
260
250
 
261
- scope.$watch(
262
- function watchIsOpen() { return getIsOpen(scope.$parent); },
263
- function updateOpen(value) { scope.isOpen = value; }
264
- );
265
-
266
- scope.isOpen = getIsOpen ? getIsOpen(scope.$parent) : false;
251
+ accordionCtrl.scope.$watch(getIsOpen, function(value) {
252
+ scope.isOpen = !!value;
253
+ });
267
254
  }
268
255
 
269
256
  scope.$watch('isOpen', function(value) {
@@ -271,7 +258,7 @@ angular.module('ui.bootstrap.accordion', ['ui.bootstrap.collapse'])
271
258
  accordionCtrl.closeOthers(scope);
272
259
  }
273
260
  if ( setIsOpen ) {
274
- setIsOpen(scope.$parent, value);
261
+ setIsOpen(accordionCtrl.scope, value);
275
262
  }
276
263
  });
277
264
  }
@@ -330,7 +317,7 @@ angular.module("ui.bootstrap.alert", []).directive('alert', function () {
330
317
  type: '=',
331
318
  close: '&'
332
319
  },
333
- link: function(scope, iElement, iAttrs, controller) {
320
+ link: function(scope, iElement, iAttrs) {
334
321
  scope.closeable = "close" in iAttrs;
335
322
  }
336
323
  };
@@ -348,19 +335,18 @@ angular.module('ui.bootstrap.bindHtml', [])
348
335
  });
349
336
  angular.module('ui.bootstrap.buttons', [])
350
337
 
351
- .constant('buttonConfig', {
352
- activeClass:'active',
353
- toggleEvent:'click'
354
- })
338
+ .constant('buttonConfig', {
339
+ activeClass: 'active',
340
+ toggleEvent: 'click'
341
+ })
355
342
 
356
- .directive('btnRadio', ['buttonConfig', function (buttonConfig) {
343
+ .directive('btnRadio', ['buttonConfig', function (buttonConfig) {
357
344
  var activeClass = buttonConfig.activeClass || 'active';
358
345
  var toggleEvent = buttonConfig.toggleEvent || 'click';
359
346
 
360
347
  return {
361
-
362
- require:'ngModel',
363
- link:function (scope, element, attrs, ngModelCtrl) {
348
+ require: 'ngModel',
349
+ link: function (scope, element, attrs, ngModelCtrl) {
364
350
 
365
351
  //model -> UI
366
352
  ngModelCtrl.$render = function () {
@@ -380,14 +366,13 @@ angular.module('ui.bootstrap.buttons', [])
380
366
  };
381
367
  }])
382
368
 
383
- .directive('btnCheckbox', ['buttonConfig', function (buttonConfig) {
384
-
369
+ .directive('btnCheckbox', ['buttonConfig', function (buttonConfig) {
385
370
  var activeClass = buttonConfig.activeClass || 'active';
386
371
  var toggleEvent = buttonConfig.toggleEvent || 'click';
387
372
 
388
373
  return {
389
- require:'ngModel',
390
- link:function (scope, element, attrs, ngModelCtrl) {
374
+ require: 'ngModel',
375
+ link: function (scope, element, attrs, ngModelCtrl) {
391
376
 
392
377
  function getTrueValue() {
393
378
  var trueValue = scope.$eval(attrs.btnCheckboxTrue);
@@ -414,6 +399,7 @@ angular.module('ui.bootstrap.buttons', [])
414
399
  }
415
400
  };
416
401
  }]);
402
+
417
403
  /**
418
404
  * @ngdoc overview
419
405
  * @name ui.bootstrap.carousel
@@ -802,9 +788,10 @@ angular.module('ui.bootstrap.position', [])
802
788
  offsetParentBCR.left += offsetParentEl.clientLeft - offsetParentEl.scrollLeft;
803
789
  }
804
790
 
791
+ var boundingClientRect = element[0].getBoundingClientRect();
805
792
  return {
806
- width: element.prop('offsetWidth'),
807
- height: element.prop('offsetHeight'),
793
+ width: boundingClientRect.width || element.prop('offsetWidth'),
794
+ height: boundingClientRect.height || element.prop('offsetHeight'),
808
795
  top: elBCR.top - offsetParentBCR.top,
809
796
  left: elBCR.left - offsetParentBCR.left
810
797
  };
@@ -817,8 +804,8 @@ angular.module('ui.bootstrap.position', [])
817
804
  offset: function (element) {
818
805
  var boundingClientRect = element[0].getBoundingClientRect();
819
806
  return {
820
- width: element.prop('offsetWidth'),
821
- height: element.prop('offsetHeight'),
807
+ width: boundingClientRect.width || element.prop('offsetWidth'),
808
+ height: boundingClientRect.height || element.prop('offsetHeight'),
822
809
  top: boundingClientRect.top + ($window.pageYOffset || $document[0].body.scrollTop || $document[0].documentElement.scrollTop),
823
810
  left: boundingClientRect.left + ($window.pageXOffset || $document[0].body.scrollLeft || $document[0].documentElement.scrollLeft)
824
811
  };
@@ -1081,25 +1068,49 @@ angular.module('ui.bootstrap.datepicker', ['ui.bootstrap.position'])
1081
1068
 
1082
1069
  .constant('datepickerPopupConfig', {
1083
1070
  dateFormat: 'yyyy-MM-dd',
1084
- closeOnDateSelection: true
1071
+ currentText: 'Today',
1072
+ toggleWeeksText: 'Weeks',
1073
+ clearText: 'Clear',
1074
+ closeText: 'Done',
1075
+ closeOnDateSelection: true,
1076
+ appendToBody: false
1085
1077
  })
1086
1078
 
1087
- .directive('datepickerPopup', ['$compile', '$parse', '$document', '$position', 'dateFilter', 'datepickerPopupConfig',
1088
- function ($compile, $parse, $document, $position, dateFilter, datepickerPopupConfig) {
1079
+ .directive('datepickerPopup', ['$compile', '$parse', '$document', '$position', 'dateFilter', 'datepickerPopupConfig', 'datepickerConfig',
1080
+ function ($compile, $parse, $document, $position, dateFilter, datepickerPopupConfig, datepickerConfig) {
1089
1081
  return {
1090
1082
  restrict: 'EA',
1091
1083
  require: 'ngModel',
1092
1084
  link: function(originalScope, element, attrs, ngModel) {
1085
+ var dateFormat;
1086
+ attrs.$observe('datepickerPopup', function(value) {
1087
+ dateFormat = value || datepickerPopupConfig.dateFormat;
1088
+ ngModel.$render();
1089
+ });
1093
1090
 
1094
- var closeOnDateSelection = angular.isDefined(attrs.closeOnDateSelection) ? scope.$eval(attrs.closeOnDateSelection) : datepickerPopupConfig.closeOnDateSelection;
1095
- var dateFormat = attrs.datepickerPopup || datepickerPopupConfig.dateFormat;
1091
+ var closeOnDateSelection = angular.isDefined(attrs.closeOnDateSelection) ? originalScope.$eval(attrs.closeOnDateSelection) : datepickerPopupConfig.closeOnDateSelection;
1092
+ var appendToBody = angular.isDefined(attrs.datepickerAppendToBody) ? originalScope.$eval(attrs.datepickerAppendToBody) : datepickerPopupConfig.appendToBody;
1096
1093
 
1097
- // create a child scope for the datepicker directive so we are not polluting original scope
1094
+ // create a child scope for the datepicker directive so we are not polluting original scope
1098
1095
  var scope = originalScope.$new();
1096
+
1099
1097
  originalScope.$on('$destroy', function() {
1100
1098
  scope.$destroy();
1101
1099
  });
1102
1100
 
1101
+ attrs.$observe('currentText', function(text) {
1102
+ scope.currentText = angular.isDefined(text) ? text : datepickerPopupConfig.currentText;
1103
+ });
1104
+ attrs.$observe('toggleWeeksText', function(text) {
1105
+ scope.toggleWeeksText = angular.isDefined(text) ? text : datepickerPopupConfig.toggleWeeksText;
1106
+ });
1107
+ attrs.$observe('clearText', function(text) {
1108
+ scope.clearText = angular.isDefined(text) ? text : datepickerPopupConfig.clearText;
1109
+ });
1110
+ attrs.$observe('closeText', function(text) {
1111
+ scope.closeText = angular.isDefined(text) ? text : datepickerPopupConfig.closeText;
1112
+ });
1113
+
1103
1114
  var getIsOpen, setIsOpen;
1104
1115
  if ( attrs.isOpen ) {
1105
1116
  getIsOpen = $parse(attrs.isOpen);
@@ -1134,12 +1145,12 @@ function ($compile, $parse, $document, $position, dateFilter, datepickerPopupCon
1134
1145
  };
1135
1146
 
1136
1147
  // popup element used to display calendar
1137
- var popupEl = angular.element('<datepicker-popup-wrap><datepicker></datepicker></datepicker-popup-wrap>');
1148
+ var popupEl = angular.element('<div datepicker-popup-wrap><div datepicker></div></div>');
1138
1149
  popupEl.attr({
1139
1150
  'ng-model': 'date',
1140
1151
  'ng-change': 'dateSelection()'
1141
1152
  });
1142
- var datepickerEl = popupEl.find('datepicker');
1153
+ var datepickerEl = angular.element(popupEl.children()[0]);
1143
1154
  if (attrs.datepickerOptions) {
1144
1155
  datepickerEl.attr(angular.extend({}, originalScope.$eval(attrs.datepickerOptions)));
1145
1156
  }
@@ -1210,7 +1221,7 @@ function ($compile, $parse, $document, $position, dateFilter, datepickerPopupCon
1210
1221
  if (attrs.showWeeks) {
1211
1222
  addWatchableAttribute(attrs.showWeeks, 'showWeeks', 'show-weeks');
1212
1223
  } else {
1213
- scope.showWeeks = true;
1224
+ scope.showWeeks = datepickerConfig.showWeeks;
1214
1225
  datepickerEl.attr('show-weeks', 'showWeeks');
1215
1226
  }
1216
1227
  if (attrs.dateDisabled) {
@@ -1218,7 +1229,7 @@ function ($compile, $parse, $document, $position, dateFilter, datepickerPopupCon
1218
1229
  }
1219
1230
 
1220
1231
  function updatePosition() {
1221
- scope.position = $position.position(element);
1232
+ scope.position = appendToBody ? $position.offset(element) : $position.position(element);
1222
1233
  scope.position.top = scope.position.top + element.prop('offsetHeight');
1223
1234
  }
1224
1235
 
@@ -1254,14 +1265,19 @@ function ($compile, $parse, $document, $position, dateFilter, datepickerPopupCon
1254
1265
  $setModelValue(originalScope, null);
1255
1266
  };
1256
1267
 
1257
- element.after($compile(popupEl)(scope));
1268
+ var $popup = $compile(popupEl)(scope);
1269
+ if ( appendToBody ) {
1270
+ $document.find('body').append($popup);
1271
+ } else {
1272
+ element.after($popup);
1273
+ }
1258
1274
  }
1259
1275
  };
1260
1276
  }])
1261
1277
 
1262
- .directive('datepickerPopupWrap', [function() {
1278
+ .directive('datepickerPopupWrap', function() {
1263
1279
  return {
1264
- restrict:'E',
1280
+ restrict:'EA',
1265
1281
  replace: true,
1266
1282
  transclude: true,
1267
1283
  templateUrl: 'template/datepicker/popup.html',
@@ -1272,7 +1288,7 @@ function ($compile, $parse, $document, $position, dateFilter, datepickerPopupCon
1272
1288
  });
1273
1289
  }
1274
1290
  };
1275
- }]);
1291
+ });
1276
1292
 
1277
1293
  /*
1278
1294
  * dropdownToggle - Provides dropdown menu functionality in place of bootstrap js
@@ -1307,7 +1323,7 @@ angular.module('ui.bootstrap.dropdownToggle', []).directive('dropdownToggle', ['
1307
1323
  closeMenu();
1308
1324
  }
1309
1325
 
1310
- if (!elementWasOpen) {
1326
+ if (!elementWasOpen && !element.hasClass('disabled') && !element.prop('disabled')) {
1311
1327
  element.parent().addClass('open');
1312
1328
  openElement = element;
1313
1329
  closeMenu = function (event) {
@@ -1326,6 +1342,7 @@ angular.module('ui.bootstrap.dropdownToggle', []).directive('dropdownToggle', ['
1326
1342
  }
1327
1343
  };
1328
1344
  }]);
1345
+
1329
1346
  angular.module('ui.bootstrap.modal', [])
1330
1347
 
1331
1348
  /**
@@ -1385,31 +1402,21 @@ angular.module('ui.bootstrap.modal', [])
1385
1402
  /**
1386
1403
  * A helper directive for the $modal service. It creates a backdrop element.
1387
1404
  */
1388
- .directive('modalBackdrop', ['$modalStack', '$timeout', function ($modalStack, $timeout) {
1405
+ .directive('modalBackdrop', ['$timeout', function ($timeout) {
1389
1406
  return {
1390
1407
  restrict: 'EA',
1391
1408
  replace: true,
1392
1409
  templateUrl: 'template/modal/backdrop.html',
1393
1410
  link: function (scope, element, attrs) {
1394
-
1395
1411
  //trigger CSS transitions
1396
1412
  $timeout(function () {
1397
1413
  scope.animate = true;
1398
1414
  });
1399
-
1400
- scope.close = function (evt) {
1401
- var modal = $modalStack.getTop();
1402
- if (modal && modal.value.backdrop && modal.value.backdrop != 'static') {
1403
- evt.preventDefault();
1404
- evt.stopPropagation();
1405
- $modalStack.dismiss(modal.key, 'backdrop click');
1406
- }
1407
- };
1408
1415
  }
1409
1416
  };
1410
1417
  }])
1411
1418
 
1412
- .directive('modalWindow', ['$timeout', function ($timeout) {
1419
+ .directive('modalWindow', ['$modalStack', '$timeout', function ($modalStack, $timeout) {
1413
1420
  return {
1414
1421
  restrict: 'EA',
1415
1422
  scope: {
@@ -1425,6 +1432,15 @@ angular.module('ui.bootstrap.modal', [])
1425
1432
  $timeout(function () {
1426
1433
  scope.animate = true;
1427
1434
  });
1435
+
1436
+ scope.close = function (evt) {
1437
+ var modal = $modalStack.getTop();
1438
+ if (modal && modal.value.backdrop && modal.value.backdrop != 'static' && (evt.target === evt.currentTarget)) {
1439
+ evt.preventDefault();
1440
+ evt.stopPropagation();
1441
+ $modalStack.dismiss(modal.key, 'backdrop click');
1442
+ }
1443
+ };
1428
1444
  }
1429
1445
  };
1430
1446
  }])
@@ -1449,6 +1465,10 @@ angular.module('ui.bootstrap.modal', [])
1449
1465
  return topBackdropIndex;
1450
1466
  }
1451
1467
 
1468
+ $rootScope.$watch(openedWindows.length, function(noOfModals){
1469
+ body.toggleClass('modal-open', openedWindows.length() > 0);
1470
+ });
1471
+
1452
1472
  $rootScope.$watch(backdropIndex, function(newBackdropIndex){
1453
1473
  backdropScope.index = newBackdropIndex;
1454
1474
  });
@@ -1464,7 +1484,7 @@ angular.module('ui.bootstrap.modal', [])
1464
1484
  modalWindow.modalDomEl.remove();
1465
1485
 
1466
1486
  //remove backdrop if no longer needed
1467
- if (backdropIndex() == -1) {
1487
+ if (backdropDomEl && backdropIndex() == -1) {
1468
1488
  backdropDomEl.remove();
1469
1489
  backdropDomEl = undefined;
1470
1490
  }
@@ -1512,9 +1532,9 @@ angular.module('ui.bootstrap.modal', [])
1512
1532
  };
1513
1533
 
1514
1534
  $modalStack.close = function (modalInstance, result) {
1515
- var modal = openedWindows.get(modalInstance);
1516
- if (modal) {
1517
- modal.value.deferred.resolve(result);
1535
+ var modalWindow = openedWindows.get(modalInstance).value;
1536
+ if (modalWindow) {
1537
+ modalWindow.deferred.resolve(result);
1518
1538
  removeModalWindow(modalInstance);
1519
1539
  }
1520
1540
  };
@@ -1641,10 +1661,12 @@ angular.module('ui.bootstrap.modal', [])
1641
1661
 
1642
1662
  return $modalProvider;
1643
1663
  });
1664
+
1644
1665
  angular.module('ui.bootstrap.pagination', [])
1645
1666
 
1646
1667
  .controller('PaginationController', ['$scope', '$attrs', '$parse', '$interpolate', function ($scope, $attrs, $parse, $interpolate) {
1647
- var self = this;
1668
+ var self = this,
1669
+ setNumPages = $attrs.numPages ? $parse($attrs.numPages).assign : angular.noop;
1648
1670
 
1649
1671
  this.init = function(defaultItemsPerPage) {
1650
1672
  if ($attrs.itemsPerPage) {
@@ -1669,7 +1691,8 @@ angular.module('ui.bootstrap.pagination', [])
1669
1691
  };
1670
1692
 
1671
1693
  this.calculateTotalPages = function() {
1672
- return this.itemsPerPage < 1 ? 1 : Math.ceil($scope.totalItems / this.itemsPerPage);
1694
+ var totalPages = this.itemsPerPage < 1 ? 1 : Math.ceil($scope.totalItems / this.itemsPerPage);
1695
+ return Math.max(totalPages || 0, 1);
1673
1696
  };
1674
1697
 
1675
1698
  this.getAttributeValue = function(attribute, defaultValue, interpolate) {
@@ -1678,7 +1701,9 @@ angular.module('ui.bootstrap.pagination', [])
1678
1701
 
1679
1702
  this.render = function() {
1680
1703
  this.page = parseInt($scope.page, 10) || 1;
1681
- $scope.pages = this.getPages(this.page, $scope.totalPages);
1704
+ if (this.page > 0 && this.page <= $scope.totalPages) {
1705
+ $scope.pages = this.getPages(this.page, $scope.totalPages);
1706
+ }
1682
1707
  };
1683
1708
 
1684
1709
  $scope.selectPage = function(page) {
@@ -1688,14 +1713,16 @@ angular.module('ui.bootstrap.pagination', [])
1688
1713
  }
1689
1714
  };
1690
1715
 
1716
+ $scope.$watch('page', function() {
1717
+ self.render();
1718
+ });
1719
+
1691
1720
  $scope.$watch('totalItems', function() {
1692
1721
  $scope.totalPages = self.calculateTotalPages();
1693
1722
  });
1694
1723
 
1695
1724
  $scope.$watch('totalPages', function(value) {
1696
- if ( $attrs.numPages ) {
1697
- $scope.numPages = value; // Readonly variable
1698
- }
1725
+ setNumPages($scope.$parent, value); // Readonly variable
1699
1726
 
1700
1727
  if ( self.page > value ) {
1701
1728
  $scope.selectPage(value);
@@ -1703,10 +1730,6 @@ angular.module('ui.bootstrap.pagination', [])
1703
1730
  self.render();
1704
1731
  }
1705
1732
  });
1706
-
1707
- $scope.$watch('page', function() {
1708
- self.render();
1709
- });
1710
1733
  }])
1711
1734
 
1712
1735
  .constant('paginationConfig', {
@@ -1726,8 +1749,7 @@ angular.module('ui.bootstrap.pagination', [])
1726
1749
  scope: {
1727
1750
  page: '=',
1728
1751
  totalItems: '=',
1729
- onSelectPage:' &',
1730
- numPages: '='
1752
+ onSelectPage:' &'
1731
1753
  },
1732
1754
  controller: 'PaginationController',
1733
1755
  templateUrl: 'template/pagination/pagination.html',
@@ -1847,8 +1869,7 @@ angular.module('ui.bootstrap.pagination', [])
1847
1869
  scope: {
1848
1870
  page: '=',
1849
1871
  totalItems: '=',
1850
- onSelectPage:' &',
1851
- numPages: '='
1872
+ onSelectPage:' &'
1852
1873
  },
1853
1874
  controller: 'PaginationController',
1854
1875
  templateUrl: 'template/pagination/pager.html',
@@ -1921,9 +1942,9 @@ angular.module( 'ui.bootstrap.tooltip', [ 'ui.bootstrap.position', 'ui.bootstrap
1921
1942
  * $tooltipProvider.options( { placement: 'left' } );
1922
1943
  * });
1923
1944
  */
1924
- this.options = function( value ) {
1925
- angular.extend( globalOptions, value );
1926
- };
1945
+ this.options = function( value ) {
1946
+ angular.extend( globalOptions, value );
1947
+ };
1927
1948
 
1928
1949
  /**
1929
1950
  * This allows you to extend the set of trigger mappings available. E.g.:
@@ -2001,6 +2022,7 @@ angular.module( 'ui.bootstrap.tooltip', [ 'ui.bootstrap.position', 'ui.bootstrap
2001
2022
  var appendToBody = angular.isDefined( options.appendToBody ) ? options.appendToBody : false;
2002
2023
  var triggers = getTriggers( undefined );
2003
2024
  var hasRegisteredTriggers = false;
2025
+ var hasEnableExp = angular.isDefined(attrs[prefix+'Enable']);
2004
2026
 
2005
2027
  // By default, the tooltip is not open.
2006
2028
  // TODO add ability to start tooltip opened
@@ -2016,6 +2038,9 @@ angular.module( 'ui.bootstrap.tooltip', [ 'ui.bootstrap.position', 'ui.bootstrap
2016
2038
 
2017
2039
  // Show the tooltip with delay if specified, otherwise show it immediately
2018
2040
  function showTooltipBind() {
2041
+ if(hasEnableExp && !scope.$eval(attrs[prefix+'Enable'])) {
2042
+ return;
2043
+ }
2019
2044
  if ( scope.tt_popupDelay ) {
2020
2045
  popupTimeout = $timeout( show, scope.tt_popupDelay );
2021
2046
  } else {
@@ -2127,7 +2152,13 @@ angular.module( 'ui.bootstrap.tooltip', [ 'ui.bootstrap.position', 'ui.bootstrap
2127
2152
  * Observe the relevant attributes.
2128
2153
  */
2129
2154
  attrs.$observe( type, function ( val ) {
2130
- scope.tt_content = val;
2155
+ if (val) {
2156
+ scope.tt_content = val;
2157
+ } else {
2158
+ if ( scope.tt_isOpen ) {
2159
+ hide();
2160
+ }
2161
+ }
2131
2162
  });
2132
2163
 
2133
2164
  attrs.$observe( prefix+'Title', function ( val ) {
@@ -2360,29 +2391,20 @@ angular.module('ui.bootstrap.rating', [])
2360
2391
  this.stateOn = angular.isDefined($attrs.stateOn) ? $scope.$parent.$eval($attrs.stateOn) : ratingConfig.stateOn;
2361
2392
  this.stateOff = angular.isDefined($attrs.stateOff) ? $scope.$parent.$eval($attrs.stateOff) : ratingConfig.stateOff;
2362
2393
 
2363
- this.createDefaultRange = function(len) {
2364
- var defaultStateObject = {
2394
+ this.createRateObjects = function(states) {
2395
+ var defaultOptions = {
2365
2396
  stateOn: this.stateOn,
2366
2397
  stateOff: this.stateOff
2367
2398
  };
2368
2399
 
2369
- var states = new Array(len);
2370
- for (var i = 0; i < len; i++) {
2371
- states[i] = defaultStateObject;
2372
- }
2373
- return states;
2374
- };
2375
-
2376
- this.normalizeRange = function(states) {
2377
2400
  for (var i = 0, n = states.length; i < n; i++) {
2378
- states[i].stateOn = states[i].stateOn || this.stateOn;
2379
- states[i].stateOff = states[i].stateOff || this.stateOff;
2401
+ states[i] = angular.extend({ index: i }, defaultOptions, states[i]);
2380
2402
  }
2381
2403
  return states;
2382
2404
  };
2383
2405
 
2384
2406
  // Get objects used in template
2385
- $scope.range = angular.isDefined($attrs.ratingStates) ? this.normalizeRange(angular.copy($scope.$parent.$eval($attrs.ratingStates))): this.createDefaultRange(this.maxRange);
2407
+ $scope.range = angular.isDefined($attrs.ratingStates) ? this.createRateObjects(angular.copy($scope.$parent.$eval($attrs.ratingStates))): this.createRateObjects(new Array(this.maxRange));
2386
2408
 
2387
2409
  $scope.rate = function(value) {
2388
2410
  if ( $scope.readonly || $scope.value === value) {
@@ -2446,11 +2468,9 @@ angular.module('ui.bootstrap.tabs', [])
2446
2468
  };
2447
2469
  })
2448
2470
 
2449
- .controller('TabsetController', ['$scope', '$element',
2450
- function TabsetCtrl($scope, $element) {
2451
-
2471
+ .controller('TabsetController', ['$scope', function TabsetCtrl($scope) {
2452
2472
  var ctrl = this,
2453
- tabs = ctrl.tabs = $scope.tabs = [];
2473
+ tabs = ctrl.tabs = $scope.tabs = [];
2454
2474
 
2455
2475
  ctrl.select = function(tab) {
2456
2476
  angular.forEach(tabs, function(tab) {
@@ -2487,6 +2507,7 @@ function TabsetCtrl($scope, $element) {
2487
2507
  * Tabset is the outer container for the tabs directive
2488
2508
  *
2489
2509
  * @param {boolean=} vertical Whether or not to use vertical styling for the tabs.
2510
+ * @param {boolean=} justified Whether or not to use justified styling for the tabs.
2490
2511
  * @param {string=} direction What direction the tabs should be rendered. Available:
2491
2512
  * 'right', 'left', 'below'.
2492
2513
  *
@@ -2494,14 +2515,18 @@ function TabsetCtrl($scope, $element) {
2494
2515
  <example module="ui.bootstrap">
2495
2516
  <file name="index.html">
2496
2517
  <tabset>
2497
- <tab heading="Vertical Tab 1"><b>First</b> Content!</tab>
2498
- <tab heading="Vertical Tab 2"><i>Second</i> Content!</tab>
2518
+ <tab heading="Tab 1"><b>First</b> Content!</tab>
2519
+ <tab heading="Tab 2"><i>Second</i> Content!</tab>
2499
2520
  </tabset>
2500
2521
  <hr />
2501
2522
  <tabset vertical="true">
2502
2523
  <tab heading="Vertical Tab 1"><b>First</b> Vertical Content!</tab>
2503
2524
  <tab heading="Vertical Tab 2"><i>Second</i> Vertical Content!</tab>
2504
2525
  </tabset>
2526
+ <tabset justified="true">
2527
+ <tab heading="Justified Tab 1"><b>First</b> Justified Content!</tab>
2528
+ <tab heading="Justified Tab 2"><i>Second</i> Justified Content!</tab>
2529
+ </tabset>
2505
2530
  </file>
2506
2531
  </example>
2507
2532
  */
@@ -2517,6 +2542,7 @@ function TabsetCtrl($scope, $element) {
2517
2542
  compile: function(elm, attrs, transclude) {
2518
2543
  return function(scope, element, attrs, tabsetCtrl) {
2519
2544
  scope.vertical = angular.isDefined(attrs.vertical) ? scope.$parent.$eval(attrs.vertical) : false;
2545
+ scope.justified = angular.isDefined(attrs.justified) ? scope.$parent.$eval(attrs.justified) : false;
2520
2546
  scope.type = angular.isDefined(attrs.type) ? scope.$parent.$eval(attrs.type) : 'tabs';
2521
2547
  scope.direction = angular.isDefined(attrs.direction) ? scope.$parent.$eval(attrs.direction) : 'top';
2522
2548
  scope.tabsAbove = (scope.direction != 'below');
@@ -2607,8 +2633,7 @@ function TabsetCtrl($scope, $element) {
2607
2633
  </file>
2608
2634
  </example>
2609
2635
  */
2610
- .directive('tab', ['$parse', '$http', '$templateCache', '$compile',
2611
- function($parse, $http, $templateCache, $compile) {
2636
+ .directive('tab', ['$parse', function($parse) {
2612
2637
  return {
2613
2638
  require: '^tabset',
2614
2639
  restrict: 'EA',
@@ -2630,8 +2655,13 @@ function($parse, $http, $templateCache, $compile) {
2630
2655
  if (attrs.active) {
2631
2656
  getActive = $parse(attrs.active);
2632
2657
  setActive = getActive.assign;
2633
- scope.$parent.$watch(getActive, function updateActive(value) {
2634
- scope.active = !!value;
2658
+ scope.$parent.$watch(getActive, function updateActive(value, oldVal) {
2659
+ // Avoid re-initializing scope.active as it is already initialized
2660
+ // below. (watcher is called async during init with value ===
2661
+ // oldVal)
2662
+ if (value !== oldVal) {
2663
+ scope.active = !!value;
2664
+ }
2635
2665
  });
2636
2666
  scope.active = getActive(scope.$parent);
2637
2667
  } else {
@@ -2639,6 +2669,8 @@ function($parse, $http, $templateCache, $compile) {
2639
2669
  }
2640
2670
 
2641
2671
  scope.$watch('active', function(active) {
2672
+ // Note this watcher also initializes and assigns scope.active to the
2673
+ // attrs.active expression.
2642
2674
  setActive(scope.$parent, active);
2643
2675
  if (active) {
2644
2676
  tabsetCtrl.select(scope);
@@ -2665,9 +2697,6 @@ function($parse, $http, $templateCache, $compile) {
2665
2697
  scope.$on('$destroy', function() {
2666
2698
  tabsetCtrl.removeTab(scope);
2667
2699
  });
2668
- if (scope.active) {
2669
- setActive(scope.$parent, true);
2670
- }
2671
2700
 
2672
2701
 
2673
2702
  //We need to transclude later, once the content container is ready.
@@ -2693,7 +2722,7 @@ function($parse, $http, $templateCache, $compile) {
2693
2722
  };
2694
2723
  }])
2695
2724
 
2696
- .directive('tabContentTransclude', ['$compile', '$parse', function($compile, $parse) {
2725
+ .directive('tabContentTransclude', function() {
2697
2726
  return {
2698
2727
  restrict: 'A',
2699
2728
  require: '^tabset',
@@ -2722,9 +2751,9 @@ function($parse, $http, $templateCache, $compile) {
2722
2751
  node.tagName.toLowerCase() === 'data-tab-heading'
2723
2752
  );
2724
2753
  }
2725
- }])
2754
+ })
2726
2755
 
2727
- .directive('tabsetTitles', ['$http', function($http) {
2756
+ .directive('tabsetTitles', function() {
2728
2757
  return {
2729
2758
  restrict: 'A',
2730
2759
  require: '^tabset',
@@ -2741,10 +2770,7 @@ function($parse, $http, $templateCache, $compile) {
2741
2770
  }
2742
2771
  }
2743
2772
  };
2744
- }])
2745
-
2746
- ;
2747
-
2773
+ });
2748
2774
 
2749
2775
  angular.module('ui.bootstrap.timepicker', [])
2750
2776
 
@@ -3046,6 +3072,7 @@ angular.module('ui.bootstrap.typeahead', ['ui.bootstrap.position', 'ui.bootstrap
3046
3072
  //expressions used by typeahead
3047
3073
  var parserResult = typeaheadParser.parse(attrs.typeahead);
3048
3074
 
3075
+ var hasFocus;
3049
3076
 
3050
3077
  //pop-up element used to display matches
3051
3078
  var popUpEl = angular.element('<typeahead-popup></typeahead-popup>');
@@ -3077,11 +3104,11 @@ angular.module('ui.bootstrap.typeahead', ['ui.bootstrap.position', 'ui.bootstrap
3077
3104
 
3078
3105
  var locals = {$viewValue: inputValue};
3079
3106
  isLoadingSetter(originalScope, true);
3080
- $q.when(parserResult.source(scope, locals)).then(function(matches) {
3107
+ $q.when(parserResult.source(originalScope, locals)).then(function(matches) {
3081
3108
 
3082
3109
  //it might happen that several async queries were in progress if a user were typing fast
3083
3110
  //but we are interested only in responses that correspond to the current view value
3084
- if (inputValue === modelCtrl.$viewValue) {
3111
+ if (inputValue === modelCtrl.$viewValue && hasFocus) {
3085
3112
  if (matches.length > 0) {
3086
3113
 
3087
3114
  scope.activeIdx = 0;
@@ -3126,7 +3153,8 @@ angular.module('ui.bootstrap.typeahead', ['ui.bootstrap.position', 'ui.bootstrap
3126
3153
  //$parsers kick-in on all the changes coming from the view as well as manually triggered by $setViewValue
3127
3154
  modelCtrl.$parsers.unshift(function (inputValue) {
3128
3155
 
3129
- resetMatches();
3156
+ hasFocus = true;
3157
+
3130
3158
  if (inputValue && inputValue.length >= minSearch) {
3131
3159
  if (waitTime > 0) {
3132
3160
  if (timeoutPromise) {
@@ -3138,13 +3166,22 @@ angular.module('ui.bootstrap.typeahead', ['ui.bootstrap.position', 'ui.bootstrap
3138
3166
  } else {
3139
3167
  getMatchesAsync(inputValue);
3140
3168
  }
3169
+ } else {
3170
+ isLoadingSetter(originalScope, false);
3171
+ resetMatches();
3141
3172
  }
3142
3173
 
3143
3174
  if (isEditable) {
3144
3175
  return inputValue;
3145
3176
  } else {
3146
- modelCtrl.$setValidity('editable', false);
3147
- return undefined;
3177
+ if (!inputValue) {
3178
+ // Reset in case user had typed something previously.
3179
+ modelCtrl.$setValidity('editable', true);
3180
+ return inputValue;
3181
+ } else {
3182
+ modelCtrl.$setValidity('editable', false);
3183
+ return undefined;
3184
+ }
3148
3185
  }
3149
3186
  });
3150
3187
 
@@ -3198,6 +3235,9 @@ angular.module('ui.bootstrap.typeahead', ['ui.bootstrap.position', 'ui.bootstrap
3198
3235
 
3199
3236
  //typeahead is open and an "interesting" key was pressed
3200
3237
  if (scope.matches.length === 0 || HOT_KEYS.indexOf(evt.which) === -1) {
3238
+ if (evt.which === 13) {
3239
+ evt.preventDefault();
3240
+ }
3201
3241
  return;
3202
3242
  }
3203
3243
 
@@ -3224,6 +3264,10 @@ angular.module('ui.bootstrap.typeahead', ['ui.bootstrap.position', 'ui.bootstrap
3224
3264
  }
3225
3265
  });
3226
3266
 
3267
+ element.bind('blur', function (evt) {
3268
+ hasFocus = false;
3269
+ });
3270
+
3227
3271
  // Keep reference to click handler to unbind it.
3228
3272
  var dismissClickHandler = function (evt) {
3229
3273
  if (element[0] !== evt.target) {