angular-ui-bootstrap-rails 0.6.0.0 → 0.7.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -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) {