angular-ui-bootstrap-rails 0.7.0.1 → 0.9.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.
@@ -82,88 +82,81 @@ angular.module('ui.bootstrap.transition', [])
82
82
  return $transition;
83
83
  }]);
84
84
 
85
- angular.module('ui.bootstrap.collapse',['ui.bootstrap.transition'])
86
-
87
- // The collapsible directive indicates a block of html that will expand and collapse
88
- .directive('collapse', ['$transition', function($transition) {
89
- // CSS transitions don't work with height: auto, so we have to manually change the height to a
90
- // specific value and then once the animation completes, we can reset the height to auto.
91
- // Unfortunately if you do this while the CSS transitions are specified (i.e. in the CSS class
92
- // "collapse") then you trigger a change to height 0 in between.
93
- // The fix is to remove the "collapse" CSS class while changing the height back to auto - phew!
94
- var fixUpHeight = function(scope, element, height) {
95
- // We remove the collapse CSS class to prevent a transition when we change to height: auto
96
- element.removeClass('collapse');
97
- element.css({ height: height });
98
- // It appears that reading offsetWidth makes the browser realise that we have changed the
99
- // height already :-/
100
- var x = element[0].offsetWidth;
101
- element.addClass('collapse');
102
- };
85
+ angular.module('ui.bootstrap.collapse', ['ui.bootstrap.transition'])
103
86
 
104
- return {
105
- link: function(scope, element, attrs) {
87
+ .directive('collapse', ['$transition', function ($transition, $timeout) {
106
88
 
107
- var isCollapsed;
108
- var initialAnimSkip = true;
89
+ return {
90
+ link: function (scope, element, attrs) {
109
91
 
110
- scope.$watch(attrs.collapse, function(value) {
111
- if (value) {
112
- collapse();
113
- } else {
114
- expand();
92
+ var initialAnimSkip = true;
93
+ var currentTransition;
94
+
95
+ function doTransition(change) {
96
+ var newTransition = $transition(element, change);
97
+ if (currentTransition) {
98
+ currentTransition.cancel();
99
+ }
100
+ currentTransition = newTransition;
101
+ newTransition.then(newTransitionDone, newTransitionDone);
102
+ return newTransition;
103
+
104
+ function newTransitionDone() {
105
+ // Make sure it's this transition, otherwise, leave it alone.
106
+ if (currentTransition === newTransition) {
107
+ currentTransition = undefined;
108
+ }
109
+ }
115
110
  }
116
- });
117
-
118
111
 
119
- var currentTransition;
120
- var doTransition = function(change) {
121
- if ( currentTransition ) {
122
- currentTransition.cancel();
112
+ function expand() {
113
+ if (initialAnimSkip) {
114
+ initialAnimSkip = false;
115
+ expandDone();
116
+ } else {
117
+ element.removeClass('collapse').addClass('collapsing');
118
+ doTransition({ height: element[0].scrollHeight + 'px' }).then(expandDone);
119
+ }
123
120
  }
124
- currentTransition = $transition(element,change);
125
- currentTransition.then(
126
- function() { currentTransition = undefined; },
127
- function() { currentTransition = undefined; }
128
- );
129
- return currentTransition;
130
- };
131
121
 
132
- var expand = function() {
133
- if (initialAnimSkip) {
134
- initialAnimSkip = false;
135
- if ( !isCollapsed ) {
136
- fixUpHeight(scope, element, 'auto');
137
- element.addClass('in');
122
+ function expandDone() {
123
+ element.removeClass('collapsing');
124
+ element.addClass('collapse in');
125
+ element.css({height: 'auto'});
126
+ }
127
+
128
+ function collapse() {
129
+ if (initialAnimSkip) {
130
+ initialAnimSkip = false;
131
+ collapseDone();
132
+ element.css({height: 0});
133
+ } else {
134
+ // CSS transitions don't work with height: auto, so we have to manually change the height to a specific value
135
+ element.css({ height: element[0].scrollHeight + 'px' });
136
+ //trigger reflow so a browser relaizes that height was updated from auto to a specific value
137
+ var x = element[0].offsetWidth;
138
+
139
+ element.removeClass('collapse in').addClass('collapsing');
140
+
141
+ doTransition({ height: 0 }).then(collapseDone);
138
142
  }
139
- } else {
140
- doTransition({ height : element[0].scrollHeight + 'px' })
141
- .then(function() {
142
- // This check ensures that we don't accidentally update the height if the user has closed
143
- // the group while the animation was still running
144
- if ( !isCollapsed ) {
145
- fixUpHeight(scope, element, 'auto');
146
- element.addClass('in');
147
- }
148
- });
149
143
  }
150
- isCollapsed = false;
151
- };
152
-
153
- var collapse = function() {
154
- isCollapsed = true;
155
- element.removeClass('in');
156
- if (initialAnimSkip) {
157
- initialAnimSkip = false;
158
- fixUpHeight(scope, element, 0);
159
- } else {
160
- fixUpHeight(scope, element, element[0].scrollHeight + 'px');
161
- doTransition({'height':'0'});
144
+
145
+ function collapseDone() {
146
+ element.removeClass('collapsing');
147
+ element.addClass('collapse');
162
148
  }
163
- };
164
- }
165
- };
166
- }]);
149
+
150
+ scope.$watch(attrs.collapse, function (shouldCollapse) {
151
+ if (shouldCollapse) {
152
+ collapse();
153
+ } else {
154
+ expand();
155
+ }
156
+ });
157
+ }
158
+ };
159
+ }]);
167
160
 
168
161
  angular.module('ui.bootstrap.accordion', ['ui.bootstrap.collapse'])
169
162
 
@@ -176,9 +169,6 @@ angular.module('ui.bootstrap.accordion', ['ui.bootstrap.collapse'])
176
169
  // This array keeps track of the accordion groups
177
170
  this.groups = [];
178
171
 
179
- // Keep reference to user's scope to properly assign `is-open`
180
- this.scope = $scope;
181
-
182
172
  // Ensure that all the groups in this accordion are closed, unless close-others explicitly says not to
183
173
  this.closeOthers = function(openGroup) {
184
174
  var closeOthers = angular.isDefined($attrs.closeOthers) ? $scope.$eval($attrs.closeOthers) : accordionConfig.closeOthers;
@@ -224,7 +214,7 @@ angular.module('ui.bootstrap.accordion', ['ui.bootstrap.collapse'])
224
214
  })
225
215
 
226
216
  // The accordion-group directive indicates a block of html that will expand and collapse in an accordion
227
- .directive('accordionGroup', ['$parse', '$transition', '$timeout', function($parse, $transition, $timeout) {
217
+ .directive('accordionGroup', ['$parse', function($parse) {
228
218
  return {
229
219
  require:'^accordion', // We need this directive to be inside an accordion
230
220
  restrict:'EA',
@@ -232,11 +222,11 @@ angular.module('ui.bootstrap.accordion', ['ui.bootstrap.collapse'])
232
222
  replace: true, // The element containing the directive will be replaced with the template
233
223
  templateUrl:'template/accordion/accordion-group.html',
234
224
  scope:{ heading:'@' }, // Create an isolated scope and interpolate the heading attribute onto this scope
235
- controller: ['$scope', function($scope) {
225
+ controller: function() {
236
226
  this.setHeading = function(element) {
237
227
  this.heading = element;
238
228
  };
239
- }],
229
+ },
240
230
  link: function(scope, element, attrs, accordionCtrl) {
241
231
  var getIsOpen, setIsOpen;
242
232
 
@@ -248,7 +238,7 @@ angular.module('ui.bootstrap.accordion', ['ui.bootstrap.collapse'])
248
238
  getIsOpen = $parse(attrs.isOpen);
249
239
  setIsOpen = getIsOpen.assign;
250
240
 
251
- accordionCtrl.scope.$watch(getIsOpen, function(value) {
241
+ scope.$parent.$watch(getIsOpen, function(value) {
252
242
  scope.isOpen = !!value;
253
243
  });
254
244
  }
@@ -258,7 +248,7 @@ angular.module('ui.bootstrap.accordion', ['ui.bootstrap.collapse'])
258
248
  accordionCtrl.closeOthers(scope);
259
249
  }
260
250
  if ( setIsOpen ) {
261
- setIsOpen(accordionCtrl.scope, value);
251
+ setIsOpen(scope.$parent, value);
262
252
  }
263
253
  });
264
254
  }
@@ -307,18 +297,22 @@ angular.module('ui.bootstrap.accordion', ['ui.bootstrap.collapse'])
307
297
  };
308
298
  });
309
299
 
310
- angular.module("ui.bootstrap.alert", []).directive('alert', function () {
300
+ angular.module("ui.bootstrap.alert", [])
301
+
302
+ .controller('AlertController', ['$scope', '$attrs', function ($scope, $attrs) {
303
+ $scope.closeable = 'close' in $attrs;
304
+ }])
305
+
306
+ .directive('alert', function () {
311
307
  return {
312
308
  restrict:'EA',
309
+ controller:'AlertController',
313
310
  templateUrl:'template/alert/alert.html',
314
311
  transclude:true,
315
312
  replace:true,
316
313
  scope: {
317
314
  type: '=',
318
315
  close: '&'
319
- },
320
- link: function(scope, iElement, iAttrs) {
321
- scope.closeable = "close" in iAttrs;
322
316
  }
323
317
  };
324
318
  });
@@ -340,22 +334,26 @@ angular.module('ui.bootstrap.buttons', [])
340
334
  toggleEvent: 'click'
341
335
  })
342
336
 
343
- .directive('btnRadio', ['buttonConfig', function (buttonConfig) {
344
- var activeClass = buttonConfig.activeClass || 'active';
345
- var toggleEvent = buttonConfig.toggleEvent || 'click';
337
+ .controller('ButtonsController', ['buttonConfig', function(buttonConfig) {
338
+ this.activeClass = buttonConfig.activeClass || 'active';
339
+ this.toggleEvent = buttonConfig.toggleEvent || 'click';
340
+ }])
346
341
 
342
+ .directive('btnRadio', function () {
347
343
  return {
348
- require: 'ngModel',
349
- link: function (scope, element, attrs, ngModelCtrl) {
344
+ require: ['btnRadio', 'ngModel'],
345
+ controller: 'ButtonsController',
346
+ link: function (scope, element, attrs, ctrls) {
347
+ var buttonsCtrl = ctrls[0], ngModelCtrl = ctrls[1];
350
348
 
351
349
  //model -> UI
352
350
  ngModelCtrl.$render = function () {
353
- element.toggleClass(activeClass, angular.equals(ngModelCtrl.$modelValue, scope.$eval(attrs.btnRadio)));
351
+ element.toggleClass(buttonsCtrl.activeClass, angular.equals(ngModelCtrl.$modelValue, scope.$eval(attrs.btnRadio)));
354
352
  };
355
353
 
356
354
  //ui->model
357
- element.bind(toggleEvent, function () {
358
- if (!element.hasClass(activeClass)) {
355
+ element.bind(buttonsCtrl.toggleEvent, function () {
356
+ if (!element.hasClass(buttonsCtrl.activeClass)) {
359
357
  scope.$apply(function () {
360
358
  ngModelCtrl.$setViewValue(scope.$eval(attrs.btnRadio));
361
359
  ngModelCtrl.$render();
@@ -364,41 +362,43 @@ angular.module('ui.bootstrap.buttons', [])
364
362
  });
365
363
  }
366
364
  };
367
- }])
368
-
369
- .directive('btnCheckbox', ['buttonConfig', function (buttonConfig) {
370
- var activeClass = buttonConfig.activeClass || 'active';
371
- var toggleEvent = buttonConfig.toggleEvent || 'click';
365
+ })
372
366
 
367
+ .directive('btnCheckbox', function () {
373
368
  return {
374
- require: 'ngModel',
375
- link: function (scope, element, attrs, ngModelCtrl) {
369
+ require: ['btnCheckbox', 'ngModel'],
370
+ controller: 'ButtonsController',
371
+ link: function (scope, element, attrs, ctrls) {
372
+ var buttonsCtrl = ctrls[0], ngModelCtrl = ctrls[1];
376
373
 
377
374
  function getTrueValue() {
378
- var trueValue = scope.$eval(attrs.btnCheckboxTrue);
379
- return angular.isDefined(trueValue) ? trueValue : true;
375
+ return getCheckboxValue(attrs.btnCheckboxTrue, true);
380
376
  }
381
377
 
382
378
  function getFalseValue() {
383
- var falseValue = scope.$eval(attrs.btnCheckboxFalse);
384
- return angular.isDefined(falseValue) ? falseValue : false;
379
+ return getCheckboxValue(attrs.btnCheckboxFalse, false);
380
+ }
381
+
382
+ function getCheckboxValue(attributeValue, defaultValue) {
383
+ var val = scope.$eval(attributeValue);
384
+ return angular.isDefined(val) ? val : defaultValue;
385
385
  }
386
386
 
387
387
  //model -> UI
388
388
  ngModelCtrl.$render = function () {
389
- element.toggleClass(activeClass, angular.equals(ngModelCtrl.$modelValue, getTrueValue()));
389
+ element.toggleClass(buttonsCtrl.activeClass, angular.equals(ngModelCtrl.$modelValue, getTrueValue()));
390
390
  };
391
391
 
392
392
  //ui->model
393
- element.bind(toggleEvent, function () {
393
+ element.bind(buttonsCtrl.toggleEvent, function () {
394
394
  scope.$apply(function () {
395
- ngModelCtrl.$setViewValue(element.hasClass(activeClass) ? getFalseValue() : getTrueValue());
395
+ ngModelCtrl.$setViewValue(element.hasClass(buttonsCtrl.activeClass) ? getFalseValue() : getTrueValue());
396
396
  ngModelCtrl.$render();
397
397
  });
398
398
  });
399
399
  }
400
400
  };
401
- }]);
401
+ });
402
402
 
403
403
  /**
404
404
  * @ngdoc overview
@@ -416,6 +416,7 @@ angular.module('ui.bootstrap.carousel', ['ui.bootstrap.transition'])
416
416
  currentTimeout, isPlaying;
417
417
  self.currentSlide = null;
418
418
 
419
+ var destroyed = false;
419
420
  /* direction: "prev" or "next" */
420
421
  self.select = function(nextSlide, direction) {
421
422
  var nextIndex = slides.indexOf(nextSlide);
@@ -433,6 +434,8 @@ angular.module('ui.bootstrap.carousel', ['ui.bootstrap.transition'])
433
434
  }
434
435
  }
435
436
  function goNext() {
437
+ // Scope has been destroyed, stop here.
438
+ if (destroyed) { return; }
436
439
  //If we have a slide to transition from and we have a transition type and we're allowed, go
437
440
  if (self.currentSlide && angular.isString(direction) && !$scope.noTransition && nextSlide.$element) {
438
441
  //We shouldn't do class manip in here, but it's the same weird thing bootstrap does. need to fix sometime
@@ -468,6 +471,9 @@ angular.module('ui.bootstrap.carousel', ['ui.bootstrap.transition'])
468
471
  $scope.$currentTransition = null;
469
472
  }
470
473
  };
474
+ $scope.$on('$destroy', function () {
475
+ destroyed = true;
476
+ });
471
477
 
472
478
  /* Allow outside people to call indexOf on slides array */
473
479
  self.indexOfSlide = function(slide) {
@@ -505,23 +511,32 @@ angular.module('ui.bootstrap.carousel', ['ui.bootstrap.transition'])
505
511
  };
506
512
 
507
513
  $scope.$watch('interval', restartTimer);
514
+ $scope.$on('$destroy', resetTimer);
515
+
508
516
  function restartTimer() {
517
+ resetTimer();
518
+ var interval = +$scope.interval;
519
+ if (!isNaN(interval) && interval>=0) {
520
+ currentTimeout = $timeout(timerFn, interval);
521
+ }
522
+ }
523
+
524
+ function resetTimer() {
509
525
  if (currentTimeout) {
510
526
  $timeout.cancel(currentTimeout);
527
+ currentTimeout = null;
511
528
  }
512
- function go() {
513
- if (isPlaying) {
514
- $scope.next();
515
- restartTimer();
516
- } else {
517
- $scope.pause();
518
- }
519
- }
520
- var interval = +$scope.interval;
521
- if (!isNaN(interval) && interval>=0) {
522
- currentTimeout = $timeout(go, interval);
529
+ }
530
+
531
+ function timerFn() {
532
+ if (isPlaying) {
533
+ $scope.next();
534
+ restartTimer();
535
+ } else {
536
+ $scope.pause();
523
537
  }
524
538
  }
539
+
525
540
  $scope.play = function() {
526
541
  if (!isPlaying) {
527
542
  isPlaying = true;
@@ -531,9 +546,7 @@ angular.module('ui.bootstrap.carousel', ['ui.bootstrap.transition'])
531
546
  $scope.pause = function() {
532
547
  if (!$scope.noPause) {
533
548
  isPlaying = false;
534
- if (currentTimeout) {
535
- $timeout.cancel(currentTimeout);
536
- }
549
+ resetTimer();
537
550
  }
538
551
  };
539
552
 
@@ -565,6 +578,7 @@ angular.module('ui.bootstrap.carousel', ['ui.bootstrap.transition'])
565
578
  currentIndex--;
566
579
  }
567
580
  };
581
+
568
582
  }])
569
583
 
570
584
  /**
@@ -1032,7 +1046,7 @@ angular.module('ui.bootstrap.datepicker', ['ui.bootstrap.position'])
1032
1046
 
1033
1047
  scope.select = function( date ) {
1034
1048
  if ( mode === 0 ) {
1035
- var dt = new Date( ngModel.$modelValue );
1049
+ var dt = ngModel.$modelValue ? new Date( ngModel.$modelValue ) : new Date(0, 0, 0, 0, 0, 0, 0);
1036
1050
  dt.setFullYear( date.getFullYear(), date.getMonth(), date.getDate() );
1037
1051
  ngModel.$setViewValue( dt );
1038
1052
  refill( true );
@@ -1073,7 +1087,8 @@ angular.module('ui.bootstrap.datepicker', ['ui.bootstrap.position'])
1073
1087
  clearText: 'Clear',
1074
1088
  closeText: 'Done',
1075
1089
  closeOnDateSelection: true,
1076
- appendToBody: false
1090
+ appendToBody: false,
1091
+ showButtonBar: true
1077
1092
  })
1078
1093
 
1079
1094
  .directive('datepickerPopup', ['$compile', '$parse', '$document', '$position', 'dateFilter', 'datepickerPopupConfig', 'datepickerConfig',
@@ -1082,19 +1097,20 @@ function ($compile, $parse, $document, $position, dateFilter, datepickerPopupCon
1082
1097
  restrict: 'EA',
1083
1098
  require: 'ngModel',
1084
1099
  link: function(originalScope, element, attrs, ngModel) {
1085
- var dateFormat;
1100
+ var scope = originalScope.$new(), // create a child scope so we are not polluting original one
1101
+ dateFormat,
1102
+ closeOnDateSelection = angular.isDefined(attrs.closeOnDateSelection) ? originalScope.$eval(attrs.closeOnDateSelection) : datepickerPopupConfig.closeOnDateSelection,
1103
+ appendToBody = angular.isDefined(attrs.datepickerAppendToBody) ? originalScope.$eval(attrs.datepickerAppendToBody) : datepickerPopupConfig.appendToBody;
1104
+
1086
1105
  attrs.$observe('datepickerPopup', function(value) {
1087
1106
  dateFormat = value || datepickerPopupConfig.dateFormat;
1088
1107
  ngModel.$render();
1089
1108
  });
1090
1109
 
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;
1093
-
1094
- // create a child scope for the datepicker directive so we are not polluting original scope
1095
- var scope = originalScope.$new();
1110
+ scope.showButtonBar = angular.isDefined(attrs.showButtonBar) ? originalScope.$eval(attrs.showButtonBar) : datepickerPopupConfig.showButtonBar;
1096
1111
 
1097
1112
  originalScope.$on('$destroy', function() {
1113
+ $popup.remove();
1098
1114
  scope.$destroy();
1099
1115
  });
1100
1116
 
@@ -1180,7 +1196,10 @@ function ($compile, $parse, $document, $position, dateFilter, datepickerPopupCon
1180
1196
  ngModel.$parsers.unshift(parseDate);
1181
1197
 
1182
1198
  // Inner change
1183
- scope.dateSelection = function() {
1199
+ scope.dateSelection = function(dt) {
1200
+ if (angular.isDefined(dt)) {
1201
+ scope.date = dt;
1202
+ }
1184
1203
  ngModel.$setViewValue(scope.date);
1185
1204
  ngModel.$render();
1186
1205
 
@@ -1191,7 +1210,7 @@ function ($compile, $parse, $document, $position, dateFilter, datepickerPopupCon
1191
1210
 
1192
1211
  element.bind('input change keyup', function() {
1193
1212
  scope.$apply(function() {
1194
- updateCalendar();
1213
+ scope.date = ngModel.$modelValue;
1195
1214
  });
1196
1215
  });
1197
1216
 
@@ -1199,14 +1218,8 @@ function ($compile, $parse, $document, $position, dateFilter, datepickerPopupCon
1199
1218
  ngModel.$render = function() {
1200
1219
  var date = ngModel.$viewValue ? dateFilter(ngModel.$viewValue, dateFormat) : '';
1201
1220
  element.val(date);
1202
-
1203
- updateCalendar();
1204
- };
1205
-
1206
- function updateCalendar() {
1207
1221
  scope.date = ngModel.$modelValue;
1208
- updatePosition();
1209
- }
1222
+ };
1210
1223
 
1211
1224
  function addWatchableAttribute(attribute, scopeProperty, datepickerAttribute) {
1212
1225
  if (attribute) {
@@ -1256,13 +1269,11 @@ function ($compile, $parse, $document, $position, dateFilter, datepickerPopupCon
1256
1269
  }
1257
1270
  });
1258
1271
 
1259
- var $setModelValue = $parse(attrs.ngModel).assign;
1260
-
1261
1272
  scope.today = function() {
1262
- $setModelValue(originalScope, new Date());
1273
+ scope.dateSelection(new Date());
1263
1274
  };
1264
1275
  scope.clear = function() {
1265
- $setModelValue(originalScope, null);
1276
+ scope.dateSelection(null);
1266
1277
  };
1267
1278
 
1268
1279
  var $popup = $compile(popupEl)(scope);
@@ -1407,7 +1418,10 @@ angular.module('ui.bootstrap.modal', [])
1407
1418
  restrict: 'EA',
1408
1419
  replace: true,
1409
1420
  templateUrl: 'template/modal/backdrop.html',
1410
- link: function (scope, element, attrs) {
1421
+ link: function (scope) {
1422
+
1423
+ scope.animate = false;
1424
+
1411
1425
  //trigger CSS transitions
1412
1426
  $timeout(function () {
1413
1427
  scope.animate = true;
@@ -1428,9 +1442,11 @@ angular.module('ui.bootstrap.modal', [])
1428
1442
  link: function (scope, element, attrs) {
1429
1443
  scope.windowClass = attrs.windowClass || '';
1430
1444
 
1431
- //trigger CSS transitions
1432
1445
  $timeout(function () {
1446
+ // trigger CSS transitions
1433
1447
  scope.animate = true;
1448
+ // focus a freshly-opened modal
1449
+ element[0].focus();
1434
1450
  });
1435
1451
 
1436
1452
  scope.close = function (evt) {
@@ -1448,9 +1464,10 @@ angular.module('ui.bootstrap.modal', [])
1448
1464
  .factory('$modalStack', ['$document', '$compile', '$rootScope', '$$stackedMap',
1449
1465
  function ($document, $compile, $rootScope, $$stackedMap) {
1450
1466
 
1467
+ var OPENED_MODAL_CLASS = 'modal-open';
1468
+
1451
1469
  var backdropjqLiteEl, backdropDomEl;
1452
1470
  var backdropScope = $rootScope.$new(true);
1453
- var body = $document.find('body').eq(0);
1454
1471
  var openedWindows = $$stackedMap.createNew();
1455
1472
  var $modalStack = {};
1456
1473
 
@@ -1465,16 +1482,13 @@ angular.module('ui.bootstrap.modal', [])
1465
1482
  return topBackdropIndex;
1466
1483
  }
1467
1484
 
1468
- $rootScope.$watch(openedWindows.length, function(noOfModals){
1469
- body.toggleClass('modal-open', openedWindows.length() > 0);
1470
- });
1471
-
1472
1485
  $rootScope.$watch(backdropIndex, function(newBackdropIndex){
1473
1486
  backdropScope.index = newBackdropIndex;
1474
1487
  });
1475
1488
 
1476
1489
  function removeModalWindow(modalInstance) {
1477
1490
 
1491
+ var body = $document.find('body').eq(0);
1478
1492
  var modalWindow = openedWindows.get(modalInstance).value;
1479
1493
 
1480
1494
  //clean up the stack
@@ -1482,6 +1496,7 @@ angular.module('ui.bootstrap.modal', [])
1482
1496
 
1483
1497
  //remove window DOM element
1484
1498
  modalWindow.modalDomEl.remove();
1499
+ body.toggleClass(OPENED_MODAL_CLASS, openedWindows.length() > 0);
1485
1500
 
1486
1501
  //remove backdrop if no longer needed
1487
1502
  if (backdropDomEl && backdropIndex() == -1) {
@@ -1515,6 +1530,14 @@ angular.module('ui.bootstrap.modal', [])
1515
1530
  keyboard: modal.keyboard
1516
1531
  });
1517
1532
 
1533
+ var body = $document.find('body').eq(0);
1534
+
1535
+ if (backdropIndex() >= 0 && !backdropDomEl) {
1536
+ backdropjqLiteEl = angular.element('<div modal-backdrop></div>');
1537
+ backdropDomEl = $compile(backdropjqLiteEl)(backdropScope);
1538
+ body.append(backdropDomEl);
1539
+ }
1540
+
1518
1541
  var angularDomEl = angular.element('<div modal-window></div>');
1519
1542
  angularDomEl.attr('window-class', modal.windowClass);
1520
1543
  angularDomEl.attr('index', openedWindows.length() - 1);
@@ -1523,12 +1546,7 @@ angular.module('ui.bootstrap.modal', [])
1523
1546
  var modalDomEl = $compile(angularDomEl)(modal.scope);
1524
1547
  openedWindows.top().value.modalDomEl = modalDomEl;
1525
1548
  body.append(modalDomEl);
1526
-
1527
- if (backdropIndex() >= 0 && !backdropDomEl) {
1528
- backdropjqLiteEl = angular.element('<div modal-backdrop></div>');
1529
- backdropDomEl = $compile(backdropjqLiteEl)(backdropScope);
1530
- body.append(backdropDomEl);
1531
- }
1549
+ body.addClass(OPENED_MODAL_CLASS);
1532
1550
  };
1533
1551
 
1534
1552
  $modalStack.close = function (modalInstance, result) {
@@ -2002,14 +2020,14 @@ angular.module( 'ui.bootstrap.tooltip', [ 'ui.bootstrap.position', 'ui.bootstrap
2002
2020
  var startSym = $interpolate.startSymbol();
2003
2021
  var endSym = $interpolate.endSymbol();
2004
2022
  var template =
2005
- '<'+ directiveName +'-popup '+
2023
+ '<div '+ directiveName +'-popup '+
2006
2024
  'title="'+startSym+'tt_title'+endSym+'" '+
2007
2025
  'content="'+startSym+'tt_content'+endSym+'" '+
2008
2026
  'placement="'+startSym+'tt_placement'+endSym+'" '+
2009
- 'animation="tt_animation()" '+
2027
+ 'animation="tt_animation" '+
2010
2028
  'is-open="tt_isOpen"'+
2011
2029
  '>'+
2012
- '</'+ directiveName +'-popup>';
2030
+ '</div>';
2013
2031
 
2014
2032
  return {
2015
2033
  restrict: 'EA',
@@ -2018,12 +2036,60 @@ angular.module( 'ui.bootstrap.tooltip', [ 'ui.bootstrap.position', 'ui.bootstrap
2018
2036
  var tooltip = $compile( template )( scope );
2019
2037
  var transitionTimeout;
2020
2038
  var popupTimeout;
2021
- var $body;
2022
2039
  var appendToBody = angular.isDefined( options.appendToBody ) ? options.appendToBody : false;
2023
2040
  var triggers = getTriggers( undefined );
2024
2041
  var hasRegisteredTriggers = false;
2025
2042
  var hasEnableExp = angular.isDefined(attrs[prefix+'Enable']);
2026
2043
 
2044
+ var positionTooltip = function (){
2045
+ var position,
2046
+ ttWidth,
2047
+ ttHeight,
2048
+ ttPosition;
2049
+ // Get the position of the directive element.
2050
+ position = appendToBody ? $position.offset( element ) : $position.position( element );
2051
+
2052
+ // Get the height and width of the tooltip so we can center it.
2053
+ ttWidth = tooltip.prop( 'offsetWidth' );
2054
+ ttHeight = tooltip.prop( 'offsetHeight' );
2055
+
2056
+ // Calculate the tooltip's top and left coordinates to center it with
2057
+ // this directive.
2058
+ switch ( scope.tt_placement ) {
2059
+ case 'right':
2060
+ ttPosition = {
2061
+ top: position.top + position.height / 2 - ttHeight / 2,
2062
+ left: position.left + position.width
2063
+ };
2064
+ break;
2065
+ case 'bottom':
2066
+ ttPosition = {
2067
+ top: position.top + position.height,
2068
+ left: position.left + position.width / 2 - ttWidth / 2
2069
+ };
2070
+ break;
2071
+ case 'left':
2072
+ ttPosition = {
2073
+ top: position.top + position.height / 2 - ttHeight / 2,
2074
+ left: position.left - ttWidth
2075
+ };
2076
+ break;
2077
+ default:
2078
+ ttPosition = {
2079
+ top: position.top - ttHeight,
2080
+ left: position.left + position.width / 2 - ttWidth / 2
2081
+ };
2082
+ break;
2083
+ }
2084
+
2085
+ ttPosition.top += 'px';
2086
+ ttPosition.left += 'px';
2087
+
2088
+ // Now set the calculated positioning.
2089
+ tooltip.css( ttPosition );
2090
+
2091
+ };
2092
+
2027
2093
  // By default, the tooltip is not open.
2028
2094
  // TODO add ability to start tooltip opened
2029
2095
  scope.tt_isOpen = false;
@@ -2043,8 +2109,9 @@ angular.module( 'ui.bootstrap.tooltip', [ 'ui.bootstrap.position', 'ui.bootstrap
2043
2109
  }
2044
2110
  if ( scope.tt_popupDelay ) {
2045
2111
  popupTimeout = $timeout( show, scope.tt_popupDelay );
2112
+ popupTimeout.then(function(reposition){reposition();});
2046
2113
  } else {
2047
- scope.$apply( show );
2114
+ scope.$apply( show )();
2048
2115
  }
2049
2116
  }
2050
2117
 
@@ -2056,14 +2123,11 @@ angular.module( 'ui.bootstrap.tooltip', [ 'ui.bootstrap.position', 'ui.bootstrap
2056
2123
 
2057
2124
  // Show the tooltip popup element.
2058
2125
  function show() {
2059
- var position,
2060
- ttWidth,
2061
- ttHeight,
2062
- ttPosition;
2126
+
2063
2127
 
2064
2128
  // Don't show empty tooltips.
2065
2129
  if ( ! scope.tt_content ) {
2066
- return;
2130
+ return angular.noop;
2067
2131
  }
2068
2132
 
2069
2133
  // If there is a pending remove transition, we must cancel it, lest the
@@ -2078,56 +2142,19 @@ angular.module( 'ui.bootstrap.tooltip', [ 'ui.bootstrap.position', 'ui.bootstrap
2078
2142
  // Now we add it to the DOM because need some info about it. But it's not
2079
2143
  // visible yet anyway.
2080
2144
  if ( appendToBody ) {
2081
- $body = $body || $document.find( 'body' );
2082
- $body.append( tooltip );
2145
+ $document.find( 'body' ).append( tooltip );
2083
2146
  } else {
2084
2147
  element.after( tooltip );
2085
2148
  }
2086
2149
 
2087
- // Get the position of the directive element.
2088
- position = appendToBody ? $position.offset( element ) : $position.position( element );
2089
-
2090
- // Get the height and width of the tooltip so we can center it.
2091
- ttWidth = tooltip.prop( 'offsetWidth' );
2092
- ttHeight = tooltip.prop( 'offsetHeight' );
2093
-
2094
- // Calculate the tooltip's top and left coordinates to center it with
2095
- // this directive.
2096
- switch ( scope.tt_placement ) {
2097
- case 'right':
2098
- ttPosition = {
2099
- top: position.top + position.height / 2 - ttHeight / 2,
2100
- left: position.left + position.width
2101
- };
2102
- break;
2103
- case 'bottom':
2104
- ttPosition = {
2105
- top: position.top + position.height,
2106
- left: position.left + position.width / 2 - ttWidth / 2
2107
- };
2108
- break;
2109
- case 'left':
2110
- ttPosition = {
2111
- top: position.top + position.height / 2 - ttHeight / 2,
2112
- left: position.left - ttWidth
2113
- };
2114
- break;
2115
- default:
2116
- ttPosition = {
2117
- top: position.top - ttHeight,
2118
- left: position.left + position.width / 2 - ttWidth / 2
2119
- };
2120
- break;
2121
- }
2122
-
2123
- ttPosition.top += 'px';
2124
- ttPosition.left += 'px';
2150
+ positionTooltip();
2125
2151
 
2126
- // Now set the calculated positioning.
2127
- tooltip.css( ttPosition );
2128
-
2129
2152
  // And show the tooltip.
2130
2153
  scope.tt_isOpen = true;
2154
+
2155
+ // Return positioning function as promise callback for correct
2156
+ // positioning after draw.
2157
+ return positionTooltip;
2131
2158
  }
2132
2159
 
2133
2160
  // Hide the tooltip popup element.
@@ -2141,8 +2168,10 @@ angular.module( 'ui.bootstrap.tooltip', [ 'ui.bootstrap.position', 'ui.bootstrap
2141
2168
  // And now we remove it from the DOM. However, if we have animation, we
2142
2169
  // need to wait for it to expire beforehand.
2143
2170
  // FIXME: this is a placeholder for a port of the transitions library.
2144
- if ( angular.isDefined( scope.tt_animation ) && scope.tt_animation() ) {
2145
- transitionTimeout = $timeout( function () { tooltip.remove(); }, 500 );
2171
+ if ( scope.tt_animation ) {
2172
+ transitionTimeout = $timeout(function () {
2173
+ tooltip.remove();
2174
+ }, 500);
2146
2175
  } else {
2147
2176
  tooltip.remove();
2148
2177
  }
@@ -2152,12 +2181,10 @@ angular.module( 'ui.bootstrap.tooltip', [ 'ui.bootstrap.position', 'ui.bootstrap
2152
2181
  * Observe the relevant attributes.
2153
2182
  */
2154
2183
  attrs.$observe( type, function ( val ) {
2155
- if (val) {
2156
- scope.tt_content = val;
2157
- } else {
2158
- if ( scope.tt_isOpen ) {
2159
- hide();
2160
- }
2184
+ scope.tt_content = val;
2185
+
2186
+ if (!val && scope.tt_isOpen ) {
2187
+ hide();
2161
2188
  }
2162
2189
  });
2163
2190
 
@@ -2169,21 +2196,20 @@ angular.module( 'ui.bootstrap.tooltip', [ 'ui.bootstrap.position', 'ui.bootstrap
2169
2196
  scope.tt_placement = angular.isDefined( val ) ? val : options.placement;
2170
2197
  });
2171
2198
 
2172
- attrs.$observe( prefix+'Animation', function ( val ) {
2173
- scope.tt_animation = angular.isDefined( val ) ? $parse( val ) : function(){ return options.animation; };
2174
- });
2175
-
2176
2199
  attrs.$observe( prefix+'PopupDelay', function ( val ) {
2177
2200
  var delay = parseInt( val, 10 );
2178
2201
  scope.tt_popupDelay = ! isNaN(delay) ? delay : options.popupDelay;
2179
2202
  });
2180
2203
 
2181
- attrs.$observe( prefix+'Trigger', function ( val ) {
2182
-
2204
+ var unregisterTriggers = function() {
2183
2205
  if (hasRegisteredTriggers) {
2184
2206
  element.unbind( triggers.show, showTooltipBind );
2185
2207
  element.unbind( triggers.hide, hideTooltipBind );
2186
2208
  }
2209
+ };
2210
+
2211
+ attrs.$observe( prefix+'Trigger', function ( val ) {
2212
+ unregisterTriggers();
2187
2213
 
2188
2214
  triggers = getTriggers( val );
2189
2215
 
@@ -2197,6 +2223,9 @@ angular.module( 'ui.bootstrap.tooltip', [ 'ui.bootstrap.position', 'ui.bootstrap
2197
2223
  hasRegisteredTriggers = true;
2198
2224
  });
2199
2225
 
2226
+ var animation = scope.$eval(attrs[prefix + 'Animation']);
2227
+ scope.tt_animation = angular.isDefined(animation) ? !!animation : options.animation;
2228
+
2200
2229
  attrs.$observe( prefix+'AppendToBody', function ( val ) {
2201
2230
  appendToBody = angular.isDefined( val ) ? $parse( val )( scope ) : appendToBody;
2202
2231
  });
@@ -2214,11 +2243,12 @@ angular.module( 'ui.bootstrap.tooltip', [ 'ui.bootstrap.position', 'ui.bootstrap
2214
2243
 
2215
2244
  // Make sure tooltip is destroyed and removed.
2216
2245
  scope.$on('$destroy', function onDestroyTooltip() {
2217
- if ( scope.tt_isOpen ) {
2218
- hide();
2219
- } else {
2220
- tooltip.remove();
2221
- }
2246
+ $timeout.cancel( transitionTimeout );
2247
+ $timeout.cancel( popupTimeout );
2248
+ unregisterTriggers();
2249
+ tooltip.remove();
2250
+ tooltip.unbind();
2251
+ tooltip = null;
2222
2252
  });
2223
2253
  }
2224
2254
  };
@@ -2228,7 +2258,7 @@ angular.module( 'ui.bootstrap.tooltip', [ 'ui.bootstrap.position', 'ui.bootstrap
2228
2258
 
2229
2259
  .directive( 'tooltipPopup', function () {
2230
2260
  return {
2231
- restrict: 'E',
2261
+ restrict: 'EA',
2232
2262
  replace: true,
2233
2263
  scope: { content: '@', placement: '@', animation: '&', isOpen: '&' },
2234
2264
  templateUrl: 'template/tooltip/tooltip-popup.html'
@@ -2241,7 +2271,7 @@ angular.module( 'ui.bootstrap.tooltip', [ 'ui.bootstrap.position', 'ui.bootstrap
2241
2271
 
2242
2272
  .directive( 'tooltipHtmlUnsafePopup', function () {
2243
2273
  return {
2244
- restrict: 'E',
2274
+ restrict: 'EA',
2245
2275
  replace: true,
2246
2276
  scope: { content: '@', placement: '@', animation: '&', isOpen: '&' },
2247
2277
  templateUrl: 'template/tooltip/tooltip-html-unsafe-popup.html'
@@ -2275,108 +2305,102 @@ angular.module('ui.bootstrap.progressbar', ['ui.bootstrap.transition'])
2275
2305
 
2276
2306
  .constant('progressConfig', {
2277
2307
  animate: true,
2278
- autoType: false,
2279
- stackedTypes: ['success', 'info', 'warning', 'danger']
2308
+ max: 100
2280
2309
  })
2281
2310
 
2282
- .controller('ProgressBarController', ['$scope', '$attrs', 'progressConfig', function($scope, $attrs, progressConfig) {
2311
+ .controller('ProgressController', ['$scope', '$attrs', 'progressConfig', '$transition', function($scope, $attrs, progressConfig, $transition) {
2312
+ var self = this,
2313
+ bars = [],
2314
+ max = angular.isDefined($attrs.max) ? $scope.$parent.$eval($attrs.max) : progressConfig.max,
2315
+ animate = angular.isDefined($attrs.animate) ? $scope.$parent.$eval($attrs.animate) : progressConfig.animate;
2283
2316
 
2284
- // Whether bar transitions should be animated
2285
- var animate = angular.isDefined($attrs.animate) ? $scope.$eval($attrs.animate) : progressConfig.animate;
2286
- var autoType = angular.isDefined($attrs.autoType) ? $scope.$eval($attrs.autoType) : progressConfig.autoType;
2287
- var stackedTypes = angular.isDefined($attrs.stackedTypes) ? $scope.$eval('[' + $attrs.stackedTypes + ']') : progressConfig.stackedTypes;
2317
+ this.addBar = function(bar, element) {
2318
+ var oldValue = 0, index = bar.$parent.$index;
2319
+ if ( angular.isDefined(index) && bars[index] ) {
2320
+ oldValue = bars[index].value;
2321
+ }
2322
+ bars.push(bar);
2288
2323
 
2289
- // Create bar object
2290
- this.makeBar = function(newBar, oldBar, index) {
2291
- var newValue = (angular.isObject(newBar)) ? newBar.value : (newBar || 0);
2292
- var oldValue = (angular.isObject(oldBar)) ? oldBar.value : (oldBar || 0);
2293
- var type = (angular.isObject(newBar) && angular.isDefined(newBar.type)) ? newBar.type : (autoType) ? getStackedType(index || 0) : null;
2324
+ this.update(element, bar.value, oldValue);
2294
2325
 
2295
- return {
2296
- from: oldValue,
2297
- to: newValue,
2298
- type: type,
2299
- animate: animate
2300
- };
2326
+ bar.$watch('value', function(value, oldValue) {
2327
+ if (value !== oldValue) {
2328
+ self.update(element, value, oldValue);
2329
+ }
2330
+ });
2331
+
2332
+ bar.$on('$destroy', function() {
2333
+ self.removeBar(bar);
2334
+ });
2301
2335
  };
2302
2336
 
2303
- function getStackedType(index) {
2304
- return stackedTypes[index];
2305
- }
2337
+ // Update bar element width
2338
+ this.update = function(element, newValue, oldValue) {
2339
+ var percent = this.getPercentage(newValue);
2306
2340
 
2307
- this.addBar = function(bar) {
2308
- $scope.bars.push(bar);
2309
- $scope.totalPercent += bar.to;
2341
+ if (animate) {
2342
+ element.css('width', this.getPercentage(oldValue) + '%');
2343
+ $transition(element, {width: percent + '%'});
2344
+ } else {
2345
+ element.css({'transition': 'none', 'width': percent + '%'});
2346
+ }
2347
+ };
2348
+
2349
+ this.removeBar = function(bar) {
2350
+ bars.splice(bars.indexOf(bar), 1);
2310
2351
  };
2311
2352
 
2312
- this.clearBars = function() {
2313
- $scope.bars = [];
2314
- $scope.totalPercent = 0;
2353
+ this.getPercentage = function(value) {
2354
+ return Math.round(100 * value / max);
2315
2355
  };
2316
- this.clearBars();
2317
2356
  }])
2318
2357
 
2319
2358
  .directive('progress', function() {
2320
2359
  return {
2321
2360
  restrict: 'EA',
2322
2361
  replace: true,
2323
- controller: 'ProgressBarController',
2362
+ transclude: true,
2363
+ controller: 'ProgressController',
2364
+ require: 'progress',
2365
+ scope: {},
2366
+ template: '<div class="progress" ng-transclude></div>'
2367
+ //templateUrl: 'template/progressbar/progress.html' // Works in AngularJS 1.2
2368
+ };
2369
+ })
2370
+
2371
+ .directive('bar', function() {
2372
+ return {
2373
+ restrict: 'EA',
2374
+ replace: true,
2375
+ transclude: true,
2376
+ require: '^progress',
2324
2377
  scope: {
2325
- value: '=percent',
2326
- onFull: '&',
2327
- onEmpty: '&'
2378
+ value: '=',
2379
+ type: '@'
2328
2380
  },
2329
- templateUrl: 'template/progressbar/progress.html',
2330
- link: function(scope, element, attrs, controller) {
2331
- scope.$watch('value', function(newValue, oldValue) {
2332
- controller.clearBars();
2333
-
2334
- if (angular.isArray(newValue)) {
2335
- // Stacked progress bar
2336
- for (var i=0, n=newValue.length; i < n; i++) {
2337
- controller.addBar(controller.makeBar(newValue[i], oldValue[i], i));
2338
- }
2339
- } else {
2340
- // Simple bar
2341
- controller.addBar(controller.makeBar(newValue, oldValue));
2342
- }
2343
- }, true);
2344
-
2345
- // Total percent listeners
2346
- scope.$watch('totalPercent', function(value) {
2347
- if (value >= 100) {
2348
- scope.onFull();
2349
- } else if (value <= 0) {
2350
- scope.onEmpty();
2351
- }
2352
- }, true);
2381
+ templateUrl: 'template/progressbar/bar.html',
2382
+ link: function(scope, element, attrs, progressCtrl) {
2383
+ progressCtrl.addBar(scope, element);
2353
2384
  }
2354
2385
  };
2355
2386
  })
2356
2387
 
2357
- .directive('progressbar', ['$transition', function($transition) {
2388
+ .directive('progressbar', function() {
2358
2389
  return {
2359
2390
  restrict: 'EA',
2360
2391
  replace: true,
2392
+ transclude: true,
2393
+ controller: 'ProgressController',
2361
2394
  scope: {
2362
- width: '=',
2363
- old: '=',
2364
- type: '=',
2365
- animate: '='
2395
+ value: '=',
2396
+ type: '@'
2366
2397
  },
2367
- templateUrl: 'template/progressbar/bar.html',
2368
- link: function(scope, element) {
2369
- scope.$watch('width', function(value) {
2370
- if (scope.animate) {
2371
- element.css('width', scope.old + '%');
2372
- $transition(element, {width: value + '%'});
2373
- } else {
2374
- element.css('width', value + '%');
2375
- }
2376
- });
2398
+ templateUrl: 'template/progressbar/progressbar.html',
2399
+ link: function(scope, element, attrs, progressCtrl) {
2400
+ progressCtrl.addBar(scope, angular.element(element.children()[0]));
2377
2401
  }
2378
2402
  };
2379
- }]);
2403
+ });
2380
2404
  angular.module('ui.bootstrap.rating', [])
2381
2405
 
2382
2406
  .constant('ratingConfig', {
@@ -2462,12 +2486,6 @@ angular.module('ui.bootstrap.rating', [])
2462
2486
 
2463
2487
  angular.module('ui.bootstrap.tabs', [])
2464
2488
 
2465
- .directive('tabs', function() {
2466
- return function() {
2467
- 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");
2468
- };
2469
- })
2470
-
2471
2489
  .controller('TabsetController', ['$scope', function TabsetCtrl($scope) {
2472
2490
  var ctrl = this,
2473
2491
  tabs = ctrl.tabs = $scope.tabs = [];
@@ -2508,8 +2526,6 @@ angular.module('ui.bootstrap.tabs', [])
2508
2526
  *
2509
2527
  * @param {boolean=} vertical Whether or not to use vertical styling for the tabs.
2510
2528
  * @param {boolean=} justified Whether or not to use justified styling for the tabs.
2511
- * @param {string=} direction What direction the tabs should be rendered. Available:
2512
- * 'right', 'left', 'below'.
2513
2529
  *
2514
2530
  * @example
2515
2531
  <example module="ui.bootstrap">
@@ -2535,20 +2551,13 @@ angular.module('ui.bootstrap.tabs', [])
2535
2551
  restrict: 'EA',
2536
2552
  transclude: true,
2537
2553
  replace: true,
2538
- require: '^tabset',
2539
2554
  scope: {},
2540
2555
  controller: 'TabsetController',
2541
2556
  templateUrl: 'template/tabs/tabset.html',
2542
- compile: function(elm, attrs, transclude) {
2543
- return function(scope, element, attrs, tabsetCtrl) {
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;
2546
- scope.type = angular.isDefined(attrs.type) ? scope.$parent.$eval(attrs.type) : 'tabs';
2547
- scope.direction = angular.isDefined(attrs.direction) ? scope.$parent.$eval(attrs.direction) : 'top';
2548
- scope.tabsAbove = (scope.direction != 'below');
2549
- tabsetCtrl.$scope = scope;
2550
- tabsetCtrl.$transcludeFn = transclude;
2551
- };
2557
+ link: function(scope, element, attrs) {
2558
+ scope.vertical = angular.isDefined(attrs.vertical) ? scope.$parent.$eval(attrs.vertical) : false;
2559
+ scope.justified = angular.isDefined(attrs.justified) ? scope.$parent.$eval(attrs.justified) : false;
2560
+ scope.type = angular.isDefined(attrs.type) ? scope.$parent.$eval(attrs.type) : 'tabs';
2552
2561
  }
2553
2562
  };
2554
2563
  })
@@ -2753,24 +2762,7 @@ angular.module('ui.bootstrap.tabs', [])
2753
2762
  }
2754
2763
  })
2755
2764
 
2756
- .directive('tabsetTitles', function() {
2757
- return {
2758
- restrict: 'A',
2759
- require: '^tabset',
2760
- templateUrl: 'template/tabs/tabset-titles.html',
2761
- replace: true,
2762
- link: function(scope, elm, attrs, tabsetCtrl) {
2763
- if (!scope.$eval(attrs.tabsetTitles)) {
2764
- elm.remove();
2765
- } else {
2766
- //now that tabs location has been decided, transclude the tab titles in
2767
- tabsetCtrl.$transcludeFn(tabsetCtrl.$scope.$parent, function(node) {
2768
- elm.append(node);
2769
- });
2770
- }
2771
- }
2772
- };
2773
- });
2765
+ ;
2774
2766
 
2775
2767
  angular.module('ui.bootstrap.timepicker', [])
2776
2768
 
@@ -2778,12 +2770,12 @@ angular.module('ui.bootstrap.timepicker', [])
2778
2770
  hourStep: 1,
2779
2771
  minuteStep: 1,
2780
2772
  showMeridian: true,
2781
- meridians: ['AM', 'PM'],
2773
+ meridians: null,
2782
2774
  readonlyInput: false,
2783
2775
  mousewheel: true
2784
2776
  })
2785
2777
 
2786
- .directive('timepicker', ['$parse', '$log', 'timepickerConfig', function ($parse, $log, timepickerConfig) {
2778
+ .directive('timepicker', ['$parse', '$log', 'timepickerConfig', '$locale', function ($parse, $log, timepickerConfig, $locale) {
2787
2779
  return {
2788
2780
  restrict: 'EA',
2789
2781
  require:'?^ngModel',
@@ -2795,7 +2787,8 @@ angular.module('ui.bootstrap.timepicker', [])
2795
2787
  return; // do nothing if no ng-model
2796
2788
  }
2797
2789
 
2798
- var selected = new Date(), meridians = timepickerConfig.meridians;
2790
+ var selected = new Date(),
2791
+ meridians = angular.isDefined(attrs.meridians) ? scope.$parent.$eval(attrs.meridians) : timepickerConfig.meridians || $locale.DATETIME_FORMATS.AMPMS;
2799
2792
 
2800
2793
  var hourStep = timepickerConfig.hourStep;
2801
2794
  if (attrs.hourStep) {
@@ -3064,6 +3057,8 @@ angular.module('ui.bootstrap.typeahead', ['ui.bootstrap.position', 'ui.bootstrap
3064
3057
 
3065
3058
  var inputFormatter = attrs.typeaheadInputFormatter ? $parse(attrs.typeaheadInputFormatter) : undefined;
3066
3059
 
3060
+ var appendToBody = attrs.typeaheadAppendToBody ? $parse(attrs.typeaheadAppendToBody) : false;
3061
+
3067
3062
  //INTERNAL VARIABLES
3068
3063
 
3069
3064
  //model setter executed upon match selection
@@ -3075,7 +3070,7 @@ angular.module('ui.bootstrap.typeahead', ['ui.bootstrap.position', 'ui.bootstrap
3075
3070
  var hasFocus;
3076
3071
 
3077
3072
  //pop-up element used to display matches
3078
- var popUpEl = angular.element('<typeahead-popup></typeahead-popup>');
3073
+ var popUpEl = angular.element('<div typeahead-popup></div>');
3079
3074
  popUpEl.attr({
3080
3075
  matches: 'matches',
3081
3076
  active: 'activeIdx',
@@ -3127,7 +3122,7 @@ angular.module('ui.bootstrap.typeahead', ['ui.bootstrap.position', 'ui.bootstrap
3127
3122
  //position pop-up with matches - we need to re-calculate its position each time we are opening a window
3128
3123
  //with matches as a pop-up might be absolute-positioned and position of an input might have changed on a page
3129
3124
  //due to other elements being rendered
3130
- scope.position = $position.position(element);
3125
+ scope.position = appendToBody ? $position.offset(element) : $position.position(element);
3131
3126
  scope.position.top = scope.position.top + element.prop('offsetHeight');
3132
3127
 
3133
3128
  } else {
@@ -3235,9 +3230,6 @@ angular.module('ui.bootstrap.typeahead', ['ui.bootstrap.position', 'ui.bootstrap
3235
3230
 
3236
3231
  //typeahead is open and an "interesting" key was pressed
3237
3232
  if (scope.matches.length === 0 || HOT_KEYS.indexOf(evt.which) === -1) {
3238
- if (evt.which === 13) {
3239
- evt.preventDefault();
3240
- }
3241
3233
  return;
3242
3234
  }
3243
3235
 
@@ -3282,7 +3274,12 @@ angular.module('ui.bootstrap.typeahead', ['ui.bootstrap.position', 'ui.bootstrap
3282
3274
  $document.unbind('click', dismissClickHandler);
3283
3275
  });
3284
3276
 
3285
- element.after($compile(popUpEl)(scope));
3277
+ var $popup = $compile(popUpEl)(scope);
3278
+ if ( appendToBody ) {
3279
+ $document.find('body').append($popup);
3280
+ } else {
3281
+ element.after($popup);
3282
+ }
3286
3283
  }
3287
3284
  };
3288
3285
 
@@ -3290,7 +3287,7 @@ angular.module('ui.bootstrap.typeahead', ['ui.bootstrap.position', 'ui.bootstrap
3290
3287
 
3291
3288
  .directive('typeaheadPopup', function () {
3292
3289
  return {
3293
- restrict:'E',
3290
+ restrict:'EA',
3294
3291
  scope:{
3295
3292
  matches:'=',
3296
3293
  query:'=',
@@ -3325,7 +3322,7 @@ angular.module('ui.bootstrap.typeahead', ['ui.bootstrap.position', 'ui.bootstrap
3325
3322
 
3326
3323
  .directive('typeaheadMatch', ['$http', '$templateCache', '$compile', '$parse', function ($http, $templateCache, $compile, $parse) {
3327
3324
  return {
3328
- restrict:'E',
3325
+ restrict:'EA',
3329
3326
  scope:{
3330
3327
  index:'=',
3331
3328
  match:'=',