angular-ui-bootstrap-rails 0.7.0.1 → 0.9.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -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:'=',