angular-ui-bootstrap-rails 0.3.0.1 → 0.4.0.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 228e8d4dcf89892df3b3f8b49e4ec1318dc9162f
4
+ data.tar.gz: 4b9fe4ac8c6b2bf0a1d871c090ff407995920285
5
+ SHA512:
6
+ metadata.gz: e9ef81d74df63d12b613cf78355298503c0655f0021fc40e7915f653fcad1b0d6912918122db9a58b31a70f32237cec4caab15f264d805c01db37c1db04a437b
7
+ data.tar.gz: 85dc8e5ea4a12e094b26f4172d88da1b9f43b81920b12274ad04722630c4e7a5eedcea286f2d36f47285b047e2b5b21dae9fc2ba0bf58288a0b32240e396849d
data/README.md CHANGED
@@ -16,4 +16,5 @@ If you would like to use the default bootstrap templates, use the following dire
16
16
 
17
17
  //= require angular-ui-bootstrap-tpls
18
18
 
19
- Gem based on Angularjs-rails(https://github.com/confuseddesi/angularjs-rails) by Hirav Gandhi
19
+ Gem based on Angularjs-rails(https://github.com/hiravgandhi/angularjs-rails) by Hirav Gandhi
20
+
@@ -1,7 +1,7 @@
1
1
  module AngularUI
2
2
  module Bootstrap
3
3
  module Rails
4
- VERSION = "0.3.0.1"
4
+ VERSION = "0.4.0.0"
5
5
  end
6
6
  end
7
7
  end
@@ -1,5 +1,5 @@
1
- angular.module("ui.bootstrap", ["ui.bootstrap.tpls", "ui.bootstrap.transition","ui.bootstrap.collapse","ui.bootstrap.accordion","ui.bootstrap.alert","ui.bootstrap.buttons","ui.bootstrap.carousel","ui.bootstrap.dialog","ui.bootstrap.dropdownToggle","ui.bootstrap.modal","ui.bootstrap.pagination","ui.bootstrap.position","ui.bootstrap.tooltip","ui.bootstrap.popover","ui.bootstrap.progressbar","ui.bootstrap.rating","ui.bootstrap.tabs","ui.bootstrap.typeahead"]);
2
- angular.module("ui.bootstrap.tpls", ["template/accordion/accordion-group.html","template/accordion/accordion.html","template/alert/alert.html","template/carousel/carousel.html","template/carousel/slide.html","template/dialog/message.html","template/pagination/pagination.html","template/tooltip/tooltip-html-unsafe-popup.html","template/tooltip/tooltip-popup.html","template/popover/popover.html","template/progressbar/bar.html","template/progressbar/progress.html","template/rating/rating.html","template/tabs/pane.html","template/tabs/tabs.html","template/typeahead/typeahead.html"]);
1
+ angular.module("ui.bootstrap", ["ui.bootstrap.tpls", "ui.bootstrap.transition","ui.bootstrap.collapse","ui.bootstrap.accordion","ui.bootstrap.alert","ui.bootstrap.buttons","ui.bootstrap.carousel","ui.bootstrap.datepicker","ui.bootstrap.dialog","ui.bootstrap.dropdownToggle","ui.bootstrap.modal","ui.bootstrap.pagination","ui.bootstrap.position","ui.bootstrap.tooltip","ui.bootstrap.popover","ui.bootstrap.progressbar","ui.bootstrap.rating","ui.bootstrap.tabs","ui.bootstrap.timepicker","ui.bootstrap.typeahead"]);
2
+ angular.module("ui.bootstrap.tpls", ["template/accordion/accordion-group.html","template/accordion/accordion.html","template/alert/alert.html","template/carousel/carousel.html","template/carousel/slide.html","template/datepicker/datepicker.html","template/dialog/message.html","template/pagination/pager.html","template/pagination/pagination.html","template/tooltip/tooltip-html-unsafe-popup.html","template/tooltip/tooltip-popup.html","template/popover/popover.html","template/progressbar/bar.html","template/progressbar/progress.html","template/rating/rating.html","template/tabs/tab.html","template/tabs/tabset.html","template/timepicker/timepicker.html","template/typeahead/typeahead.html"]);
3
3
  angular.module('ui.bootstrap.transition', [])
4
4
 
5
5
  /**
@@ -82,7 +82,7 @@ angular.module('ui.bootstrap.transition', [])
82
82
  $transition.animationEndEventName = findEndEventName(animationEndEventNames);
83
83
  return $transition;
84
84
  }]);
85
-
85
+
86
86
  angular.module('ui.bootstrap.collapse',['ui.bootstrap.transition'])
87
87
 
88
88
  // The collapsible directive indicates a block of html that will expand and collapse
@@ -178,149 +178,149 @@ angular.module('ui.bootstrap.collapse',['ui.bootstrap.transition'])
178
178
  }
179
179
  };
180
180
  }]);
181
-
182
- angular.module('ui.bootstrap.accordion', ['ui.bootstrap.collapse'])
183
-
184
- .constant('accordionConfig', {
185
- closeOthers: true
186
- })
187
-
188
- .controller('AccordionController', ['$scope', '$attrs', 'accordionConfig', function ($scope, $attrs, accordionConfig) {
189
-
190
- // This array keeps track of the accordion groups
191
- this.groups = [];
192
-
193
- // Ensure that all the groups in this accordion are closed, unless close-others explicitly says not to
194
- this.closeOthers = function(openGroup) {
195
- var closeOthers = angular.isDefined($attrs.closeOthers) ? $scope.$eval($attrs.closeOthers) : accordionConfig.closeOthers;
196
- if ( closeOthers ) {
197
- angular.forEach(this.groups, function (group) {
198
- if ( group !== openGroup ) {
199
- group.isOpen = false;
200
- }
201
- });
202
- }
203
- };
204
-
205
- // This is called from the accordion-group directive to add itself to the accordion
206
- this.addGroup = function(groupScope) {
207
- var that = this;
208
- this.groups.push(groupScope);
209
-
210
- groupScope.$on('$destroy', function (event) {
211
- that.removeGroup(groupScope);
212
- });
213
- };
214
-
215
- // This is called from the accordion-group directive when to remove itself
216
- this.removeGroup = function(group) {
217
- var index = this.groups.indexOf(group);
218
- if ( index !== -1 ) {
219
- this.groups.splice(this.groups.indexOf(group), 1);
220
- }
221
- };
222
-
223
- }])
224
-
225
- // The accordion directive simply sets up the directive controller
226
- // and adds an accordion CSS class to itself element.
227
- .directive('accordion', function () {
228
- return {
229
- restrict:'EA',
230
- controller:'AccordionController',
231
- transclude: true,
232
- replace: false,
233
- templateUrl: 'template/accordion/accordion.html'
234
- };
235
- })
236
-
237
- // The accordion-group directive indicates a block of html that will expand and collapse in an accordion
238
- .directive('accordionGroup', ['$parse', '$transition', '$timeout', function($parse, $transition, $timeout) {
239
- return {
240
- require:'^accordion', // We need this directive to be inside an accordion
241
- restrict:'EA',
242
- transclude:true, // It transcludes the contents of the directive into the template
243
- replace: true, // The element containing the directive will be replaced with the template
244
- templateUrl:'template/accordion/accordion-group.html',
245
- scope:{ heading:'@' }, // Create an isolated scope and interpolate the heading attribute onto this scope
246
- controller: ['$scope', function($scope) {
247
- this.setHeading = function(element) {
248
- this.heading = element;
249
- };
250
- }],
251
- link: function(scope, element, attrs, accordionCtrl) {
252
- var getIsOpen, setIsOpen;
253
-
254
- accordionCtrl.addGroup(scope);
255
-
256
- scope.isOpen = false;
257
-
258
- if ( attrs.isOpen ) {
259
- getIsOpen = $parse(attrs.isOpen);
260
- setIsOpen = getIsOpen.assign;
261
-
262
- scope.$watch(
263
- function watchIsOpen() { return getIsOpen(scope.$parent); },
264
- function updateOpen(value) { scope.isOpen = value; }
265
- );
266
-
267
- scope.isOpen = getIsOpen ? getIsOpen(scope.$parent) : false;
268
- }
269
-
270
- scope.$watch('isOpen', function(value) {
271
- if ( value ) {
272
- accordionCtrl.closeOthers(scope);
273
- }
274
- if ( setIsOpen ) {
275
- setIsOpen(scope.$parent, value);
276
- }
277
- });
278
- }
279
- };
280
- }])
281
-
282
- // Use accordion-heading below an accordion-group to provide a heading containing HTML
283
- // <accordion-group>
284
- // <accordion-heading>Heading containing HTML - <img src="..."></accordion-heading>
285
- // </accordion-group>
286
- .directive('accordionHeading', function() {
287
- return {
288
- restrict: 'E',
289
- transclude: true, // Grab the contents to be used as the heading
290
- template: '', // In effect remove this element!
291
- replace: true,
292
- require: '^accordionGroup',
293
- compile: function(element, attr, transclude) {
294
- return function link(scope, element, attr, accordionGroupCtrl) {
295
- // Pass the heading to the accordion-group controller
296
- // so that it can be transcluded into the right place in the template
297
- // [The second parameter to transclude causes the elements to be cloned so that they work in ng-repeat]
298
- accordionGroupCtrl.setHeading(transclude(scope, function() {}));
299
- };
300
- }
301
- };
302
- })
303
-
304
- // Use in the accordion-group template to indicate where you want the heading to be transcluded
305
- // You must provide the property on the accordion-group controller that will hold the transcluded element
306
- // <div class="accordion-group">
307
- // <div class="accordion-heading" ><a ... accordion-transclude="heading">...</a></div>
308
- // ...
309
- // </div>
310
- .directive('accordionTransclude', function() {
311
- return {
312
- require: '^accordionGroup',
313
- link: function(scope, element, attr, controller) {
314
- scope.$watch(function() { return controller[attr.accordionTransclude]; }, function(heading) {
315
- if ( heading ) {
316
- element.html('');
317
- element.append(heading);
318
- }
319
- });
320
- }
321
- };
322
- });
323
-
181
+
182
+ angular.module('ui.bootstrap.accordion', ['ui.bootstrap.collapse'])
183
+
184
+ .constant('accordionConfig', {
185
+ closeOthers: true
186
+ })
187
+
188
+ .controller('AccordionController', ['$scope', '$attrs', 'accordionConfig', function ($scope, $attrs, accordionConfig) {
189
+
190
+ // This array keeps track of the accordion groups
191
+ this.groups = [];
192
+
193
+ // Ensure that all the groups in this accordion are closed, unless close-others explicitly says not to
194
+ this.closeOthers = function(openGroup) {
195
+ var closeOthers = angular.isDefined($attrs.closeOthers) ? $scope.$eval($attrs.closeOthers) : accordionConfig.closeOthers;
196
+ if ( closeOthers ) {
197
+ angular.forEach(this.groups, function (group) {
198
+ if ( group !== openGroup ) {
199
+ group.isOpen = false;
200
+ }
201
+ });
202
+ }
203
+ };
204
+
205
+ // This is called from the accordion-group directive to add itself to the accordion
206
+ this.addGroup = function(groupScope) {
207
+ var that = this;
208
+ this.groups.push(groupScope);
209
+
210
+ groupScope.$on('$destroy', function (event) {
211
+ that.removeGroup(groupScope);
212
+ });
213
+ };
214
+
215
+ // This is called from the accordion-group directive when to remove itself
216
+ this.removeGroup = function(group) {
217
+ var index = this.groups.indexOf(group);
218
+ if ( index !== -1 ) {
219
+ this.groups.splice(this.groups.indexOf(group), 1);
220
+ }
221
+ };
222
+
223
+ }])
224
+
225
+ // The accordion directive simply sets up the directive controller
226
+ // and adds an accordion CSS class to itself element.
227
+ .directive('accordion', function () {
228
+ return {
229
+ restrict:'EA',
230
+ controller:'AccordionController',
231
+ transclude: true,
232
+ replace: false,
233
+ templateUrl: 'template/accordion/accordion.html'
234
+ };
235
+ })
236
+
237
+ // The accordion-group directive indicates a block of html that will expand and collapse in an accordion
238
+ .directive('accordionGroup', ['$parse', '$transition', '$timeout', function($parse, $transition, $timeout) {
239
+ return {
240
+ require:'^accordion', // We need this directive to be inside an accordion
241
+ restrict:'EA',
242
+ transclude:true, // It transcludes the contents of the directive into the template
243
+ replace: true, // The element containing the directive will be replaced with the template
244
+ templateUrl:'template/accordion/accordion-group.html',
245
+ scope:{ heading:'@' }, // Create an isolated scope and interpolate the heading attribute onto this scope
246
+ controller: ['$scope', function($scope) {
247
+ this.setHeading = function(element) {
248
+ this.heading = element;
249
+ };
250
+ }],
251
+ link: function(scope, element, attrs, accordionCtrl) {
252
+ var getIsOpen, setIsOpen;
253
+
254
+ accordionCtrl.addGroup(scope);
255
+
256
+ scope.isOpen = false;
257
+
258
+ if ( attrs.isOpen ) {
259
+ getIsOpen = $parse(attrs.isOpen);
260
+ setIsOpen = getIsOpen.assign;
261
+
262
+ scope.$watch(
263
+ function watchIsOpen() { return getIsOpen(scope.$parent); },
264
+ function updateOpen(value) { scope.isOpen = value; }
265
+ );
266
+
267
+ scope.isOpen = getIsOpen ? getIsOpen(scope.$parent) : false;
268
+ }
269
+
270
+ scope.$watch('isOpen', function(value) {
271
+ if ( value ) {
272
+ accordionCtrl.closeOthers(scope);
273
+ }
274
+ if ( setIsOpen ) {
275
+ setIsOpen(scope.$parent, value);
276
+ }
277
+ });
278
+ }
279
+ };
280
+ }])
281
+
282
+ // Use accordion-heading below an accordion-group to provide a heading containing HTML
283
+ // <accordion-group>
284
+ // <accordion-heading>Heading containing HTML - <img src="..."></accordion-heading>
285
+ // </accordion-group>
286
+ .directive('accordionHeading', function() {
287
+ return {
288
+ restrict: 'EA',
289
+ transclude: true, // Grab the contents to be used as the heading
290
+ template: '', // In effect remove this element!
291
+ replace: true,
292
+ require: '^accordionGroup',
293
+ compile: function(element, attr, transclude) {
294
+ return function link(scope, element, attr, accordionGroupCtrl) {
295
+ // Pass the heading to the accordion-group controller
296
+ // so that it can be transcluded into the right place in the template
297
+ // [The second parameter to transclude causes the elements to be cloned so that they work in ng-repeat]
298
+ accordionGroupCtrl.setHeading(transclude(scope, function() {}));
299
+ };
300
+ }
301
+ };
302
+ })
303
+
304
+ // Use in the accordion-group template to indicate where you want the heading to be transcluded
305
+ // You must provide the property on the accordion-group controller that will hold the transcluded element
306
+ // <div class="accordion-group">
307
+ // <div class="accordion-heading" ><a ... accordion-transclude="heading">...</a></div>
308
+ // ...
309
+ // </div>
310
+ .directive('accordionTransclude', function() {
311
+ return {
312
+ require: '^accordionGroup',
313
+ link: function(scope, element, attr, controller) {
314
+ scope.$watch(function() { return controller[attr.accordionTransclude]; }, function(heading) {
315
+ if ( heading ) {
316
+ element.html('');
317
+ element.append(heading);
318
+ }
319
+ });
320
+ }
321
+ };
322
+ });
323
+
324
324
  angular.module("ui.bootstrap.alert", []).directive('alert', function () {
325
325
  return {
326
326
  restrict:'EA',
@@ -336,93 +336,78 @@ angular.module("ui.bootstrap.alert", []).directive('alert', function () {
336
336
  }
337
337
  };
338
338
  });
339
-
340
- angular.module('ui.bootstrap.buttons', [])
341
-
342
- .constant('buttonConfig', {
343
- activeClass:'active',
344
- toggleEvent:'click'
345
- })
346
-
347
- .directive('btnRadio', ['buttonConfig', function (buttonConfig) {
348
- var activeClass = buttonConfig.activeClass || 'active';
349
- var toggleEvent = buttonConfig.toggleEvent || 'click';
350
-
351
- return {
352
-
353
- require:'ngModel',
354
- link:function (scope, element, attrs, ngModelCtrl) {
355
-
356
- var value = scope.$eval(attrs.btnRadio);
357
-
358
- //model -> UI
359
- scope.$watch(function () {
360
- return ngModelCtrl.$modelValue;
361
- }, function (modelValue) {
362
- if (angular.equals(modelValue, value)){
363
- element.addClass(activeClass);
364
- } else {
365
- element.removeClass(activeClass);
366
- }
367
- });
368
-
369
- //ui->model
370
- element.bind(toggleEvent, function () {
371
- if (!element.hasClass(activeClass)) {
372
- scope.$apply(function () {
373
- ngModelCtrl.$setViewValue(value);
374
- });
375
- }
376
- });
377
- }
378
- };
379
- }])
380
-
381
- .directive('btnCheckbox', ['buttonConfig', function (buttonConfig) {
382
-
383
- var activeClass = buttonConfig.activeClass || 'active';
384
- var toggleEvent = buttonConfig.toggleEvent || 'click';
385
-
386
- return {
387
- require:'ngModel',
388
- link:function (scope, element, attrs, ngModelCtrl) {
389
-
390
- var trueValue = scope.$eval(attrs.btnCheckboxTrue);
391
- var falseValue = scope.$eval(attrs.btnCheckboxFalse);
392
-
393
- trueValue = angular.isDefined(trueValue) ? trueValue : true;
394
- falseValue = angular.isDefined(falseValue) ? falseValue : false;
395
-
396
- //model -> UI
397
- scope.$watch(function () {
398
- return ngModelCtrl.$modelValue;
399
- }, function (modelValue) {
400
- if (angular.equals(modelValue, trueValue)) {
401
- element.addClass(activeClass);
402
- } else {
403
- element.removeClass(activeClass);
404
- }
405
- });
406
-
407
- //ui->model
408
- element.bind(toggleEvent, function () {
409
- scope.$apply(function () {
410
- ngModelCtrl.$setViewValue(element.hasClass(activeClass) ? falseValue : trueValue);
411
- });
412
- });
413
- }
414
- };
415
- }]);
416
- /*
417
- *
418
- * AngularJS Bootstrap Carousel
339
+
340
+ angular.module('ui.bootstrap.buttons', [])
341
+
342
+ .constant('buttonConfig', {
343
+ activeClass:'active',
344
+ toggleEvent:'click'
345
+ })
346
+
347
+ .directive('btnRadio', ['buttonConfig', function (buttonConfig) {
348
+ var activeClass = buttonConfig.activeClass || 'active';
349
+ var toggleEvent = buttonConfig.toggleEvent || 'click';
350
+
351
+ return {
352
+
353
+ require:'ngModel',
354
+ link:function (scope, element, attrs, ngModelCtrl) {
355
+
356
+ //model -> UI
357
+ ngModelCtrl.$render = function () {
358
+ element.toggleClass(activeClass, angular.equals(ngModelCtrl.$modelValue, scope.$eval(attrs.btnRadio)));
359
+ };
360
+
361
+ //ui->model
362
+ element.bind(toggleEvent, function () {
363
+ if (!element.hasClass(activeClass)) {
364
+ scope.$apply(function () {
365
+ ngModelCtrl.$setViewValue(scope.$eval(attrs.btnRadio));
366
+ ngModelCtrl.$render();
367
+ });
368
+ }
369
+ });
370
+ }
371
+ };
372
+ }])
373
+
374
+ .directive('btnCheckbox', ['buttonConfig', function (buttonConfig) {
375
+
376
+ var activeClass = buttonConfig.activeClass || 'active';
377
+ var toggleEvent = buttonConfig.toggleEvent || 'click';
378
+
379
+ return {
380
+ require:'ngModel',
381
+ link:function (scope, element, attrs, ngModelCtrl) {
382
+
383
+ var trueValue = scope.$eval(attrs.btnCheckboxTrue);
384
+ var falseValue = scope.$eval(attrs.btnCheckboxFalse);
385
+
386
+ trueValue = angular.isDefined(trueValue) ? trueValue : true;
387
+ falseValue = angular.isDefined(falseValue) ? falseValue : false;
388
+
389
+ //model -> UI
390
+ ngModelCtrl.$render = function () {
391
+ element.toggleClass(activeClass, angular.equals(ngModelCtrl.$modelValue, trueValue));
392
+ };
393
+
394
+ //ui->model
395
+ element.bind(toggleEvent, function () {
396
+ scope.$apply(function () {
397
+ ngModelCtrl.$setViewValue(element.hasClass(activeClass) ? falseValue : trueValue);
398
+ ngModelCtrl.$render();
399
+ });
400
+ });
401
+ }
402
+ };
403
+ }]);
404
+ /**
405
+ * @ngdoc overview
406
+ * @name ui.bootstrap.carousel
407
+ *
408
+ * @description
409
+ * AngularJS version of an image carousel.
419
410
  *
420
- * A pure AngularJS carousel.
421
- *
422
- * For no interval set the interval to non-number, or milliseconds of desired interval
423
- * Template: <carousel interval="none"><slide>{{anything}}</slide></carousel>
424
- * To change the carousel's active slide set the active attribute to true
425
- * Template: <carousel interval="none"><slide active="someModel">{{anything}}</slide></carousel>
426
411
  */
427
412
  angular.module('ui.bootstrap.carousel', ['ui.bootstrap.transition'])
428
413
  .controller('CarouselController', ['$scope', '$timeout', '$transition', '$q', function ($scope, $timeout, $transition, $q) {
@@ -492,12 +477,20 @@ angular.module('ui.bootstrap.carousel', ['ui.bootstrap.transition'])
492
477
 
493
478
  $scope.next = function() {
494
479
  var newIndex = (currentIndex + 1) % slides.length;
495
- return self.select(slides[newIndex], 'next');
480
+
481
+ //Prevent this user-triggered transition from occurring if there is already one in progress
482
+ if (!$scope.$currentTransition) {
483
+ return self.select(slides[newIndex], 'next');
484
+ }
496
485
  };
497
486
 
498
487
  $scope.prev = function() {
499
488
  var newIndex = currentIndex - 1 < 0 ? slides.length - 1 : currentIndex - 1;
500
- return self.select(slides[newIndex], 'prev');
489
+
490
+ //Prevent this user-triggered transition from occurring if there is already one in progress
491
+ if (!$scope.$currentTransition) {
492
+ return self.select(slides[newIndex], 'prev');
493
+ }
501
494
  };
502
495
 
503
496
  $scope.select = function(slide) {
@@ -537,9 +530,11 @@ angular.module('ui.bootstrap.carousel', ['ui.bootstrap.transition'])
537
530
  }
538
531
  };
539
532
  $scope.pause = function() {
540
- isPlaying = false;
541
- if (currentTimeout) {
542
- $timeout.cancel(currentTimeout);
533
+ if (!$scope.noPause) {
534
+ isPlaying = false;
535
+ if (currentTimeout) {
536
+ $timeout.cancel(currentTimeout);
537
+ }
543
538
  }
544
539
  };
545
540
 
@@ -567,9 +562,50 @@ angular.module('ui.bootstrap.carousel', ['ui.bootstrap.transition'])
567
562
  } else {
568
563
  self.select(slides[index]);
569
564
  }
565
+ } else if (currentIndex > index) {
566
+ currentIndex--;
570
567
  }
571
568
  };
572
569
  }])
570
+
571
+ /**
572
+ * @ngdoc directive
573
+ * @name ui.bootstrap.carousel.directive:carousel
574
+ * @restrict EA
575
+ *
576
+ * @description
577
+ * Carousel is the outer container for a set of image 'slides' to showcase.
578
+ *
579
+ * @param {number=} interval The time, in milliseconds, that it will take the carousel to go to the next slide.
580
+ * @param {boolean=} noTransition Whether to disable transitions on the carousel.
581
+ * @param {boolean=} noPause Whether to disable pausing on the carousel (by default, the carousel interval pauses on hover).
582
+ *
583
+ * @example
584
+ <example module="ui.bootstrap">
585
+ <file name="index.html">
586
+ <carousel>
587
+ <slide>
588
+ <img src="http://placekitten.com/150/150" style="margin:auto;">
589
+ <div class="carousel-caption">
590
+ <p>Beautiful!</p>
591
+ </div>
592
+ </slide>
593
+ <slide>
594
+ <img src="http://placekitten.com/100/150" style="margin:auto;">
595
+ <div class="carousel-caption">
596
+ <p>D'aww!</p>
597
+ </div>
598
+ </slide>
599
+ </carousel>
600
+ </file>
601
+ <file name="demo.css">
602
+ .carousel-indicators {
603
+ top: auto;
604
+ bottom: 15px;
605
+ }
606
+ </file>
607
+ </example>
608
+ */
573
609
  .directive('carousel', [function() {
574
610
  return {
575
611
  restrict: 'EA',
@@ -580,11 +616,77 @@ angular.module('ui.bootstrap.carousel', ['ui.bootstrap.transition'])
580
616
  templateUrl: 'template/carousel/carousel.html',
581
617
  scope: {
582
618
  interval: '=',
583
- noTransition: '='
619
+ noTransition: '=',
620
+ noPause: '='
584
621
  }
585
622
  };
586
623
  }])
587
- .directive('slide', [function() {
624
+
625
+ /**
626
+ * @ngdoc directive
627
+ * @name ui.bootstrap.carousel.directive:slide
628
+ * @restrict EA
629
+ *
630
+ * @description
631
+ * Creates a slide inside a {@link ui.bootstrap.carousel.directive:carousel carousel}. Must be placed as a child of a carousel element.
632
+ *
633
+ * @param {boolean=} active Model binding, whether or not this slide is currently active.
634
+ *
635
+ * @example
636
+ <example module="ui.bootstrap">
637
+ <file name="index.html">
638
+ <div ng-controller="CarouselDemoCtrl">
639
+ <carousel>
640
+ <slide ng-repeat="slide in slides" active="slide.active">
641
+ <img ng-src="{{slide.image}}" style="margin:auto;">
642
+ <div class="carousel-caption">
643
+ <h4>Slide {{$index}}</h4>
644
+ <p>{{slide.text}}</p>
645
+ </div>
646
+ </slide>
647
+ </carousel>
648
+ <div class="row-fluid">
649
+ <div class="span6">
650
+ <ul>
651
+ <li ng-repeat="slide in slides">
652
+ <button class="btn btn-mini" ng-class="{'btn-info': !slide.active, 'btn-success': slide.active}" ng-disabled="slide.active" ng-click="slide.active = true">select</button>
653
+ {{$index}}: {{slide.text}}
654
+ </li>
655
+ </ul>
656
+ <a class="btn" ng-click="addSlide()">Add Slide</a>
657
+ </div>
658
+ <div class="span6">
659
+ Interval, in milliseconds: <input type="number" ng-model="myInterval">
660
+ <br />Enter a negative number to stop the interval.
661
+ </div>
662
+ </div>
663
+ </div>
664
+ </file>
665
+ <file name="script.js">
666
+ function CarouselDemoCtrl($scope) {
667
+ $scope.myInterval = 5000;
668
+ var slides = $scope.slides = [];
669
+ $scope.addSlide = function() {
670
+ var newWidth = 200 + ((slides.length + (25 * slides.length)) % 150);
671
+ slides.push({
672
+ image: 'http://placekitten.com/' + newWidth + '/200',
673
+ text: ['More','Extra','Lots of','Surplus'][slides.length % 4] + ' '
674
+ ['Cats', 'Kittys', 'Felines', 'Cutes'][slides.length % 4]
675
+ });
676
+ };
677
+ for (var i=0; i<4; i++) $scope.addSlide();
678
+ }
679
+ </file>
680
+ <file name="demo.css">
681
+ .carousel-indicators {
682
+ top: auto;
683
+ bottom: 15px;
684
+ }
685
+ </file>
686
+ </example>
687
+ */
688
+
689
+ .directive('slide', ['$parse', function($parse) {
588
690
  return {
589
691
  require: '^carousel',
590
692
  restrict: 'EA',
@@ -592,9 +694,30 @@ angular.module('ui.bootstrap.carousel', ['ui.bootstrap.transition'])
592
694
  replace: true,
593
695
  templateUrl: 'template/carousel/slide.html',
594
696
  scope: {
595
- active: '='
596
697
  },
597
698
  link: function (scope, element, attrs, carouselCtrl) {
699
+ //Set up optional 'active' = binding
700
+ if (attrs.active) {
701
+ var getActive = $parse(attrs.active);
702
+ var setActive = getActive.assign;
703
+ var lastValue = scope.active = getActive(scope.$parent);
704
+ scope.$watch(function parentActiveWatch() {
705
+ var parentActive = getActive(scope.$parent);
706
+
707
+ if (parentActive !== scope.active) {
708
+ // we are out of sync and need to copy
709
+ if (parentActive !== lastValue) {
710
+ // parent changed and it has precedence
711
+ lastValue = scope.active = parentActive;
712
+ } else {
713
+ // if the parent can be assigned then do so
714
+ setActive(scope.$parent, parentActive = lastValue = scope.active);
715
+ }
716
+ }
717
+ return parentActive;
718
+ });
719
+ }
720
+
598
721
  carouselCtrl.addSlide(scope, element);
599
722
  //when the scope is destroyed then remove the slide from the current slides array
600
723
  scope.$on('$destroy', function() {
@@ -609,7 +732,221 @@ angular.module('ui.bootstrap.carousel', ['ui.bootstrap.transition'])
609
732
  }
610
733
  };
611
734
  }]);
612
-
735
+
736
+ angular.module('ui.bootstrap.datepicker', [])
737
+
738
+ .constant('datepickerConfig', {
739
+ dayFormat: 'dd',
740
+ monthFormat: 'MMMM',
741
+ yearFormat: 'yyyy',
742
+ dayHeaderFormat: 'EEE',
743
+ dayTitleFormat: 'MMMM yyyy',
744
+ monthTitleFormat: 'yyyy',
745
+ showWeeks: true,
746
+ startingDay: 0,
747
+ yearRange: 20
748
+ })
749
+
750
+ .directive( 'datepicker', ['dateFilter', '$parse', 'datepickerConfig', function (dateFilter, $parse, datepickerConfig) {
751
+ return {
752
+ restrict: 'EA',
753
+ replace: true,
754
+ scope: {
755
+ model: '=ngModel',
756
+ dateDisabled: '&'
757
+ },
758
+ templateUrl: 'template/datepicker/datepicker.html',
759
+ link: function(scope, element, attrs) {
760
+ scope.mode = 'day'; // Initial mode
761
+
762
+ // Configuration parameters
763
+ var selected = new Date(), showWeeks, minDate, maxDate, format = {};
764
+ format.day = angular.isDefined(attrs.dayFormat) ? scope.$eval(attrs.dayFormat) : datepickerConfig.dayFormat;
765
+ format.month = angular.isDefined(attrs.monthFormat) ? scope.$eval(attrs.monthFormat) : datepickerConfig.monthFormat;
766
+ format.year = angular.isDefined(attrs.yearFormat) ? scope.$eval(attrs.yearFormat) : datepickerConfig.yearFormat;
767
+ format.dayHeader = angular.isDefined(attrs.dayHeaderFormat) ? scope.$eval(attrs.dayHeaderFormat) : datepickerConfig.dayHeaderFormat;
768
+ format.dayTitle = angular.isDefined(attrs.dayTitleFormat) ? scope.$eval(attrs.dayTitleFormat) : datepickerConfig.dayTitleFormat;
769
+ format.monthTitle = angular.isDefined(attrs.monthTitleFormat) ? scope.$eval(attrs.monthTitleFormat) : datepickerConfig.monthTitleFormat;
770
+ var startingDay = angular.isDefined(attrs.startingDay) ? scope.$eval(attrs.startingDay) : datepickerConfig.startingDay;
771
+ var yearRange = angular.isDefined(attrs.yearRange) ? scope.$eval(attrs.yearRange) : datepickerConfig.yearRange;
772
+
773
+ if (attrs.showWeeks) {
774
+ scope.$parent.$watch($parse(attrs.showWeeks), function(value) {
775
+ showWeeks = !! value;
776
+ updateShowWeekNumbers();
777
+ });
778
+ } else {
779
+ showWeeks = datepickerConfig.showWeeks;
780
+ updateShowWeekNumbers();
781
+ }
782
+
783
+ if (attrs.min) {
784
+ scope.$parent.$watch($parse(attrs.min), function(value) {
785
+ minDate = new Date(value);
786
+ refill();
787
+ });
788
+ }
789
+ if (attrs.max) {
790
+ scope.$parent.$watch($parse(attrs.max), function(value) {
791
+ maxDate = new Date(value);
792
+ refill();
793
+ });
794
+ }
795
+
796
+ function updateCalendar (rows, labels, title) {
797
+ scope.rows = rows;
798
+ scope.labels = labels;
799
+ scope.title = title;
800
+ }
801
+
802
+ // Define whether the week number are visible
803
+ function updateShowWeekNumbers() {
804
+ scope.showWeekNumbers = ( scope.mode === 'day' && showWeeks );
805
+ }
806
+
807
+ function compare( date1, date2 ) {
808
+ if ( scope.mode === 'year') {
809
+ return date2.getFullYear() - date1.getFullYear();
810
+ } else if ( scope.mode === 'month' ) {
811
+ return new Date( date2.getFullYear(), date2.getMonth() ) - new Date( date1.getFullYear(), date1.getMonth() );
812
+ } else if ( scope.mode === 'day' ) {
813
+ return (new Date( date2.getFullYear(), date2.getMonth(), date2.getDate() ) - new Date( date1.getFullYear(), date1.getMonth(), date1.getDate() ) );
814
+ }
815
+ }
816
+
817
+ function isDisabled(date) {
818
+ return ((minDate && compare(date, minDate) > 0) || (maxDate && compare(date, maxDate) < 0) || (scope.dateDisabled && scope.dateDisabled({ date: date, mode: scope.mode })));
819
+ }
820
+
821
+ // Split array into smaller arrays
822
+ var split = function(a, size) {
823
+ var arrays = [];
824
+ while (a.length > 0) {
825
+ arrays.push(a.splice(0, size));
826
+ }
827
+ return arrays;
828
+ };
829
+ var getDaysInMonth = function( year, month ) {
830
+ return new Date(year, month + 1, 0).getDate();
831
+ };
832
+
833
+ var fill = {
834
+ day: function() {
835
+ var days = [], labels = [], lastDate = null;
836
+
837
+ function addDays( dt, n, isCurrentMonth ) {
838
+ for (var i =0; i < n; i ++) {
839
+ days.push( {date: new Date(dt), isCurrent: isCurrentMonth, isSelected: isSelected(dt), label: dateFilter(dt, format.day), disabled: isDisabled(dt) } );
840
+ dt.setDate( dt.getDate() + 1 );
841
+ }
842
+ lastDate = dt;
843
+ }
844
+
845
+ var d = new Date(selected);
846
+ d.setDate(1);
847
+
848
+ var difference = startingDay - d.getDay();
849
+ var numDisplayedFromPreviousMonth = (difference > 0) ? 7 - difference : - difference;
850
+
851
+ if ( numDisplayedFromPreviousMonth > 0 ) {
852
+ d.setDate( - numDisplayedFromPreviousMonth + 1 );
853
+ addDays(d, numDisplayedFromPreviousMonth, false);
854
+ }
855
+ addDays(lastDate || d, getDaysInMonth(selected.getFullYear(), selected.getMonth()), true);
856
+ addDays(lastDate, (7 - days.length % 7) % 7, false);
857
+
858
+ // Day labels
859
+ for (i = 0; i < 7; i++) {
860
+ labels.push( dateFilter(days[i].date, format.dayHeader) );
861
+ }
862
+ updateCalendar( split( days, 7 ), labels, dateFilter(selected, format.dayTitle) );
863
+ },
864
+ month: function() {
865
+ var months = [], i = 0, year = selected.getFullYear();
866
+ while ( i < 12 ) {
867
+ var dt = new Date(year, i++, 1);
868
+ months.push( {date: dt, isCurrent: true, isSelected: isSelected(dt), label: dateFilter(dt, format.month), disabled: isDisabled(dt)} );
869
+ }
870
+ updateCalendar( split( months, 3 ), [], dateFilter(selected, format.monthTitle) );
871
+ },
872
+ year: function() {
873
+ var years = [], year = parseInt((selected.getFullYear() - 1) / yearRange, 10) * yearRange + 1;
874
+ for ( var i = 0; i < yearRange; i++ ) {
875
+ var dt = new Date(year + i, 0, 1);
876
+ years.push( {date: dt, isCurrent: true, isSelected: isSelected(dt), label: dateFilter(dt, format.year), disabled: isDisabled(dt)} );
877
+ }
878
+ var title = years[0].label + ' - ' + years[years.length - 1].label;
879
+ updateCalendar( split( years, 5 ), [], title );
880
+ }
881
+ };
882
+ var refill = function() {
883
+ fill[scope.mode]();
884
+ };
885
+ var isSelected = function( dt ) {
886
+ if ( scope.model && scope.model.getFullYear() === dt.getFullYear() ) {
887
+ if ( scope.mode === 'year' ) {
888
+ return true;
889
+ }
890
+ if ( scope.model.getMonth() === dt.getMonth() ) {
891
+ return ( scope.mode === 'month' || (scope.mode === 'day' && scope.model.getDate() === dt.getDate()) );
892
+ }
893
+ }
894
+ return false;
895
+ };
896
+
897
+ scope.$watch('model', function ( dt, olddt ) {
898
+ if ( angular.isDate(dt) ) {
899
+ selected = angular.copy(dt);
900
+ }
901
+
902
+ if ( ! angular.equals(dt, olddt) ) {
903
+ refill();
904
+ }
905
+ });
906
+ scope.$watch('mode', function() {
907
+ updateShowWeekNumbers();
908
+ refill();
909
+ });
910
+
911
+ scope.select = function( dt ) {
912
+ selected = new Date(dt);
913
+
914
+ if ( scope.mode === 'year' ) {
915
+ scope.mode = 'month';
916
+ selected.setFullYear( dt.getFullYear() );
917
+ } else if ( scope.mode === 'month' ) {
918
+ scope.mode = 'day';
919
+ selected.setMonth( dt.getMonth() );
920
+ } else if ( scope.mode === 'day' ) {
921
+ scope.model = new Date(selected);
922
+ }
923
+ };
924
+ scope.move = function(step) {
925
+ if (scope.mode === 'day') {
926
+ selected.setMonth( selected.getMonth() + step );
927
+ } else if (scope.mode === 'month') {
928
+ selected.setFullYear( selected.getFullYear() + step );
929
+ } else if (scope.mode === 'year') {
930
+ selected.setFullYear( selected.getFullYear() + step * yearRange );
931
+ }
932
+ refill();
933
+ };
934
+ scope.toggleMode = function() {
935
+ scope.mode = ( scope.mode === 'day' ) ? 'month' : ( scope.mode === 'month' ) ? 'year' : 'day';
936
+ };
937
+ scope.getWeekNumber = function(row) {
938
+ if ( scope.mode !== 'day' || ! scope.showWeekNumbers || row.length !== 7 ) {
939
+ return;
940
+ }
941
+
942
+ var index = ( startingDay > 4 ) ? 11 - startingDay : 4 - startingDay; // Thursday
943
+ var d = new Date( row[ index ].date );
944
+ d.setHours(0, 0, 0);
945
+ return Math.ceil((((d - new Date(d.getFullYear(), 0, 1)) / 86400000) + 1) / 7); // 86400000 = 1000*60*60*24;
946
+ };
947
+ }
948
+ };
949
+ }]);
613
950
  // The `$dialogProvider` can be used to configure global defaults for your
614
951
  // `$dialog` service.
615
952
  var dialogModule = angular.module('ui.bootstrap.dialog', ['ui.bootstrap.transition']);
@@ -632,7 +969,6 @@ dialogModule.provider("$dialog", function(){
632
969
  backdropClass: 'modal-backdrop',
633
970
  transitionClass: 'fade',
634
971
  triggerClass: 'in',
635
- dialogOpenClass: 'modal-open',
636
972
  resolve:{},
637
973
  backdropFade: false,
638
974
  dialogFade:false,
@@ -744,7 +1080,6 @@ dialogModule.provider("$dialog", function(){
744
1080
 
745
1081
  $compile(self.modalEl)($scope);
746
1082
  self._addElementsToDom();
747
- body.addClass(self.options.dialogOpenClass);
748
1083
 
749
1084
  // trigger tranisitions
750
1085
  setTimeout(function(){
@@ -764,7 +1099,6 @@ dialogModule.provider("$dialog", function(){
764
1099
  var self = this;
765
1100
  var fadingElements = this._getFadingElements();
766
1101
 
767
- body.removeClass(self.options.dialogOpenClass);
768
1102
  if(fadingElements.length > 0){
769
1103
  for (var i = fadingElements.length - 1; i >= 0; i--) {
770
1104
  $transition(fadingElements[i], removeTriggerClass).then(onCloseComplete);
@@ -800,8 +1134,6 @@ dialogModule.provider("$dialog", function(){
800
1134
  Dialog.prototype._bindEvents = function() {
801
1135
  if(this.options.keyboard){ body.bind('keydown', this.handledEscapeKey); }
802
1136
  if(this.options.backdrop && this.options.backdropClick){ this.backdropEl.bind('click', this.handleBackDropClick); }
803
-
804
- this.$scope.$on('$locationChangeSuccess', this.handleLocationChange);
805
1137
  };
806
1138
 
807
1139
  Dialog.prototype._unbindEvents = function() {
@@ -899,7 +1231,7 @@ dialogModule.provider("$dialog", function(){
899
1231
  };
900
1232
  }];
901
1233
  });
902
-
1234
+
903
1235
  /*
904
1236
  * dropdownToggle - Provides dropdown menu functionality in place of bootstrap js
905
1237
  * @restrict class or attribute
@@ -914,8 +1246,7 @@ dialogModule.provider("$dialog", function(){
914
1246
  </li>
915
1247
  */
916
1248
 
917
- angular.module('ui.bootstrap.dropdownToggle', []).directive('dropdownToggle',
918
- ['$document', '$location', '$window', function ($document, $location, $window) {
1249
+ angular.module('ui.bootstrap.dropdownToggle', []).directive('dropdownToggle', ['$document', '$location', function ($document, $location) {
919
1250
  var openElement = null,
920
1251
  closeMenu = angular.noop;
921
1252
  return {
@@ -923,13 +1254,18 @@ angular.module('ui.bootstrap.dropdownToggle', []).directive('dropdownToggle',
923
1254
  link: function(scope, element, attrs) {
924
1255
  scope.$watch('$location.path', function() { closeMenu(); });
925
1256
  element.parent().bind('click', function() { closeMenu(); });
926
- element.bind('click', function(event) {
1257
+ element.bind('click', function (event) {
1258
+
1259
+ var elementWasOpen = (element === openElement);
1260
+
927
1261
  event.preventDefault();
928
1262
  event.stopPropagation();
929
- var elementWasOpen = (element === openElement);
1263
+
930
1264
  if (!!openElement) {
931
- closeMenu(); }
932
- if (!elementWasOpen){
1265
+ closeMenu();
1266
+ }
1267
+
1268
+ if (!elementWasOpen) {
933
1269
  element.parent().addClass('open');
934
1270
  openElement = element;
935
1271
  closeMenu = function (event) {
@@ -939,7 +1275,7 @@ angular.module('ui.bootstrap.dropdownToggle', []).directive('dropdownToggle',
939
1275
  }
940
1276
  $document.unbind('click', closeMenu);
941
1277
  element.parent().removeClass('open');
942
- closeMenu = angular.noop;
1278
+ closeMenu = angular.noop;
943
1279
  openElement = null;
944
1280
  };
945
1281
  $document.bind('click', closeMenu);
@@ -947,7 +1283,7 @@ angular.module('ui.bootstrap.dropdownToggle', []).directive('dropdownToggle',
947
1283
  });
948
1284
  }
949
1285
  };
950
- }]);
1286
+ }]);
951
1287
  angular.module('ui.bootstrap.modal', ['ui.bootstrap.dialog'])
952
1288
  .directive('modal', ['$parse', '$dialog', function($parse, $dialog) {
953
1289
  return {
@@ -994,16 +1330,38 @@ angular.module('ui.bootstrap.modal', ['ui.bootstrap.dialog'])
994
1330
  });
995
1331
  }
996
1332
  };
997
- }]);
1333
+ }]);
998
1334
  angular.module('ui.bootstrap.pagination', [])
999
1335
 
1336
+ .controller('PaginationController', ['$scope', function (scope) {
1337
+
1338
+ scope.noPrevious = function() {
1339
+ return scope.currentPage === 1;
1340
+ };
1341
+ scope.noNext = function() {
1342
+ return scope.currentPage === scope.numPages;
1343
+ };
1344
+
1345
+ scope.isActive = function(page) {
1346
+ return scope.currentPage === page;
1347
+ };
1348
+
1349
+ scope.selectPage = function(page) {
1350
+ if ( ! scope.isActive(page) && page > 0 && page <= scope.numPages) {
1351
+ scope.currentPage = page;
1352
+ scope.onSelectPage({ page: page });
1353
+ }
1354
+ };
1355
+ }])
1356
+
1000
1357
  .constant('paginationConfig', {
1001
1358
  boundaryLinks: false,
1002
1359
  directionLinks: true,
1003
1360
  firstText: 'First',
1004
1361
  previousText: 'Previous',
1005
1362
  nextText: 'Next',
1006
- lastText: 'Last'
1363
+ lastText: 'Last',
1364
+ rotate: true
1007
1365
  })
1008
1366
 
1009
1367
  .directive('pagination', ['paginationConfig', function(paginationConfig) {
@@ -1015,6 +1373,7 @@ angular.module('ui.bootstrap.pagination', [])
1015
1373
  maxSize: '=',
1016
1374
  onSelectPage: '&'
1017
1375
  },
1376
+ controller: 'PaginationController',
1018
1377
  templateUrl: 'template/pagination/pagination.html',
1019
1378
  replace: true,
1020
1379
  link: function(scope, element, attrs) {
@@ -1022,10 +1381,11 @@ angular.module('ui.bootstrap.pagination', [])
1022
1381
  // Setup configuration parameters
1023
1382
  var boundaryLinks = angular.isDefined(attrs.boundaryLinks) ? scope.$eval(attrs.boundaryLinks) : paginationConfig.boundaryLinks;
1024
1383
  var directionLinks = angular.isDefined(attrs.directionLinks) ? scope.$eval(attrs.directionLinks) : paginationConfig.directionLinks;
1025
- var firstText = angular.isDefined(attrs.firstText) ? attrs.firstText : paginationConfig.firstText;
1026
- var previousText = angular.isDefined(attrs.previousText) ? attrs.previousText : paginationConfig.previousText;
1027
- var nextText = angular.isDefined(attrs.nextText) ? attrs.nextText : paginationConfig.nextText;
1028
- var lastText = angular.isDefined(attrs.lastText) ? attrs.lastText : paginationConfig.lastText;
1384
+ var firstText = angular.isDefined(attrs.firstText) ? scope.$parent.$eval(attrs.firstText) : paginationConfig.firstText;
1385
+ var previousText = angular.isDefined(attrs.previousText) ? scope.$parent.$eval(attrs.previousText) : paginationConfig.previousText;
1386
+ var nextText = angular.isDefined(attrs.nextText) ? scope.$parent.$eval(attrs.nextText) : paginationConfig.nextText;
1387
+ var lastText = angular.isDefined(attrs.lastText) ? scope.$parent.$eval(attrs.lastText) : paginationConfig.lastText;
1388
+ var rotate = angular.isDefined(attrs.rotate) ? scope.$eval(attrs.rotate) : paginationConfig.rotate;
1029
1389
 
1030
1390
  // Create page object used in template
1031
1391
  function makePage(number, text, isActive, isDisabled) {
@@ -1042,16 +1402,26 @@ angular.module('ui.bootstrap.pagination', [])
1042
1402
 
1043
1403
  // Default page limits
1044
1404
  var startPage = 1, endPage = scope.numPages;
1405
+ var isMaxSized = ( angular.isDefined(scope.maxSize) && scope.maxSize < scope.numPages );
1045
1406
 
1046
1407
  // recompute if maxSize
1047
- if ( scope.maxSize && scope.maxSize < scope.numPages ) {
1048
- startPage = Math.max(scope.currentPage - Math.floor(scope.maxSize/2), 1);
1049
- endPage = startPage + scope.maxSize - 1;
1050
-
1051
- // Adjust if limit is exceeded
1052
- if (endPage > scope.numPages) {
1053
- endPage = scope.numPages;
1054
- startPage = endPage - scope.maxSize + 1;
1408
+ if ( isMaxSized ) {
1409
+ if ( rotate ) {
1410
+ // Current page is displayed in the middle of the visible ones
1411
+ startPage = Math.max(scope.currentPage - Math.floor(scope.maxSize/2), 1);
1412
+ endPage = startPage + scope.maxSize - 1;
1413
+
1414
+ // Adjust if limit is exceeded
1415
+ if (endPage > scope.numPages) {
1416
+ endPage = scope.numPages;
1417
+ startPage = endPage - scope.maxSize + 1;
1418
+ }
1419
+ } else {
1420
+ // Visible pages are paginated with maxSize
1421
+ startPage = ((Math.ceil(scope.currentPage / scope.maxSize) - 1) * scope.maxSize) + 1;
1422
+
1423
+ // Adjust last page if limit is exceeded
1424
+ endPage = Math.min(startPage + scope.maxSize - 1, scope.numPages);
1055
1425
  }
1056
1426
  }
1057
1427
 
@@ -1061,6 +1431,19 @@ angular.module('ui.bootstrap.pagination', [])
1061
1431
  scope.pages.push(page);
1062
1432
  }
1063
1433
 
1434
+ // Add links to move between page sets
1435
+ if ( isMaxSized && ! rotate ) {
1436
+ if ( startPage > 1 ) {
1437
+ var previousPageSet = makePage(startPage - 1, '...', false, false);
1438
+ scope.pages.unshift(previousPageSet);
1439
+ }
1440
+
1441
+ if ( endPage < scope.numPages ) {
1442
+ var nextPageSet = makePage(endPage + 1, '...', false, false);
1443
+ scope.pages.push(nextPageSet);
1444
+ }
1445
+ }
1446
+
1064
1447
  // Add previous & next links
1065
1448
  if (directionLinks) {
1066
1449
  var previousPage = makePage(scope.currentPage - 1, previousText, false, scope.noPrevious());
@@ -1079,116 +1462,167 @@ angular.module('ui.bootstrap.pagination', [])
1079
1462
  scope.pages.push(lastPage);
1080
1463
  }
1081
1464
 
1082
-
1083
1465
  if ( scope.currentPage > scope.numPages ) {
1084
1466
  scope.selectPage(scope.numPages);
1085
1467
  }
1086
1468
  });
1087
- scope.noPrevious = function() {
1088
- return scope.currentPage === 1;
1089
- };
1090
- scope.noNext = function() {
1091
- return scope.currentPage === scope.numPages;
1092
- };
1093
- scope.isActive = function(page) {
1094
- return scope.currentPage === page;
1095
- };
1469
+ }
1470
+ };
1471
+ }])
1472
+
1473
+ .constant('pagerConfig', {
1474
+ previousText: '« Previous',
1475
+ nextText: 'Next »',
1476
+ align: true
1477
+ })
1478
+
1479
+ .directive('pager', ['pagerConfig', function(config) {
1480
+ return {
1481
+ restrict: 'EA',
1482
+ scope: {
1483
+ numPages: '=',
1484
+ currentPage: '=',
1485
+ onSelectPage: '&'
1486
+ },
1487
+ controller: 'PaginationController',
1488
+ templateUrl: 'template/pagination/pager.html',
1489
+ replace: true,
1490
+ link: function(scope, element, attrs, paginationCtrl) {
1491
+
1492
+ // Setup configuration parameters
1493
+ var previousText = angular.isDefined(attrs.previousText) ? scope.$parent.$eval(attrs.previousText) : config.previousText;
1494
+ var nextText = angular.isDefined(attrs.nextText) ? scope.$parent.$eval(attrs.nextText) : config.nextText;
1495
+ var align = angular.isDefined(attrs.align) ? scope.$parent.$eval(attrs.align) : config.align;
1496
+
1497
+ // Create page object used in template
1498
+ function makePage(number, text, isDisabled, isPrevious, isNext) {
1499
+ return {
1500
+ number: number,
1501
+ text: text,
1502
+ disabled: isDisabled,
1503
+ previous: ( align && isPrevious ),
1504
+ next: ( align && isNext )
1505
+ };
1506
+ }
1096
1507
 
1097
- scope.selectPage = function(page) {
1098
- if ( ! scope.isActive(page) && page > 0 && page <= scope.numPages) {
1099
- scope.currentPage = page;
1100
- scope.onSelectPage({ page: page });
1508
+ scope.$watch('numPages + currentPage', function() {
1509
+ scope.pages = [];
1510
+
1511
+ // Add previous & next links
1512
+ var previousPage = makePage(scope.currentPage - 1, previousText, scope.noPrevious(), true, false);
1513
+ scope.pages.unshift(previousPage);
1514
+
1515
+ var nextPage = makePage(scope.currentPage + 1, nextText, scope.noNext(), false, true);
1516
+ scope.pages.push(nextPage);
1517
+
1518
+ if ( scope.currentPage > scope.numPages ) {
1519
+ scope.selectPage(scope.numPages);
1101
1520
  }
1102
- };
1521
+ });
1103
1522
  }
1104
1523
  };
1105
- }]);
1106
- angular.module('ui.bootstrap.position', [])
1107
-
1108
- /**
1109
- * A set of utility methods that can be use to retrieve position of DOM elements.
1110
- * It is meant to be used where we need to absolute-position DOM elements in
1111
- * relation to other, existing elements (this is the case for tooltips, popovers,
1112
- * typeahead suggestions etc.).
1113
- */
1114
- .factory('$position', ['$document', '$window', function ($document, $window) {
1115
-
1116
- function getStyle(el, cssprop) {
1117
- if (el.currentStyle) { //IE
1118
- return el.currentStyle[cssprop];
1119
- } else if ($window.getComputedStyle) {
1120
- return $window.getComputedStyle(el)[cssprop];
1121
- }
1122
- // finally try and get inline style
1123
- return el.style[cssprop];
1124
- }
1125
-
1126
- /**
1127
- * Checks if a given element is statically positioned
1128
- * @param element - raw DOM element
1129
- */
1130
- function isStaticPositioned(element) {
1131
- return (getStyle(element, "position") || 'static' ) === 'static';
1132
- }
1133
-
1134
- /**
1135
- * returns the closest, non-statically positioned parentOffset of a given element
1136
- * @param element
1137
- */
1138
- var parentOffsetEl = function (element) {
1139
- var docDomEl = $document[0];
1140
- var offsetParent = element.offsetParent || docDomEl;
1141
- while (offsetParent && offsetParent !== docDomEl && isStaticPositioned(offsetParent) ) {
1142
- offsetParent = offsetParent.offsetParent;
1143
- }
1144
- return offsetParent || docDomEl;
1145
- };
1146
-
1147
- return {
1148
- /**
1149
- * Provides read-only equivalent of jQuery's position function:
1150
- * http://api.jquery.com/position/
1151
- */
1152
- position: function (element) {
1153
- var elBCR = this.offset(element);
1154
- var offsetParentBCR = { top: 0, left: 0 };
1155
- var offsetParentEl = parentOffsetEl(element[0]);
1156
- if (offsetParentEl != $document[0]) {
1157
- offsetParentBCR = this.offset(angular.element(offsetParentEl));
1158
- offsetParentBCR.top += offsetParentEl.clientTop;
1159
- offsetParentBCR.left += offsetParentEl.clientLeft;
1160
- }
1161
-
1162
- return {
1163
- width: element.prop('offsetWidth'),
1164
- height: element.prop('offsetHeight'),
1165
- top: elBCR.top - offsetParentBCR.top,
1166
- left: elBCR.left - offsetParentBCR.left
1167
- };
1168
- },
1169
-
1170
- /**
1171
- * Provides read-only equivalent of jQuery's offset function:
1172
- * http://api.jquery.com/offset/
1173
- */
1174
- offset: function (element) {
1175
- var boundingClientRect = element[0].getBoundingClientRect();
1176
- return {
1177
- width: element.prop('offsetWidth'),
1178
- height: element.prop('offsetHeight'),
1179
- top: boundingClientRect.top + ($window.pageYOffset || $document[0].body.scrollTop),
1180
- left: boundingClientRect.left + ($window.pageXOffset || $document[0].body.scrollLeft)
1181
- };
1182
- }
1183
- };
1184
- }]);
1185
-
1524
+ }]);
1525
+
1526
+ angular.module('ui.bootstrap.position', [])
1527
+
1186
1528
  /**
1187
- * The following features are still outstanding: animation as a
1188
- * function, placement as a function, inside, support for more triggers than
1189
- * just mouse enter/leave, html tooltips, and selector delegation.
1529
+ * A set of utility methods that can be use to retrieve position of DOM elements.
1530
+ * It is meant to be used where we need to absolute-position DOM elements in
1531
+ * relation to other, existing elements (this is the case for tooltips, popovers,
1532
+ * typeahead suggestions etc.).
1190
1533
  */
1191
- angular.module( 'ui.bootstrap.tooltip', [ 'ui.bootstrap.position' ] )
1534
+ .factory('$position', ['$document', '$window', function ($document, $window) {
1535
+
1536
+ var mouseX, mouseY;
1537
+
1538
+ $document.bind('mousemove', function mouseMoved(event) {
1539
+ mouseX = event.pageX;
1540
+ mouseY = event.pageY;
1541
+ });
1542
+
1543
+ function getStyle(el, cssprop) {
1544
+ if (el.currentStyle) { //IE
1545
+ return el.currentStyle[cssprop];
1546
+ } else if ($window.getComputedStyle) {
1547
+ return $window.getComputedStyle(el)[cssprop];
1548
+ }
1549
+ // finally try and get inline style
1550
+ return el.style[cssprop];
1551
+ }
1552
+
1553
+ /**
1554
+ * Checks if a given element is statically positioned
1555
+ * @param element - raw DOM element
1556
+ */
1557
+ function isStaticPositioned(element) {
1558
+ return (getStyle(element, "position") || 'static' ) === 'static';
1559
+ }
1560
+
1561
+ /**
1562
+ * returns the closest, non-statically positioned parentOffset of a given element
1563
+ * @param element
1564
+ */
1565
+ var parentOffsetEl = function (element) {
1566
+ var docDomEl = $document[0];
1567
+ var offsetParent = element.offsetParent || docDomEl;
1568
+ while (offsetParent && offsetParent !== docDomEl && isStaticPositioned(offsetParent) ) {
1569
+ offsetParent = offsetParent.offsetParent;
1570
+ }
1571
+ return offsetParent || docDomEl;
1572
+ };
1573
+
1574
+ return {
1575
+ /**
1576
+ * Provides read-only equivalent of jQuery's position function:
1577
+ * http://api.jquery.com/position/
1578
+ */
1579
+ position: function (element) {
1580
+ var elBCR = this.offset(element);
1581
+ var offsetParentBCR = { top: 0, left: 0 };
1582
+ var offsetParentEl = parentOffsetEl(element[0]);
1583
+ if (offsetParentEl != $document[0]) {
1584
+ offsetParentBCR = this.offset(angular.element(offsetParentEl));
1585
+ offsetParentBCR.top += offsetParentEl.clientTop;
1586
+ offsetParentBCR.left += offsetParentEl.clientLeft;
1587
+ }
1588
+
1589
+ return {
1590
+ width: element.prop('offsetWidth'),
1591
+ height: element.prop('offsetHeight'),
1592
+ top: elBCR.top - offsetParentBCR.top,
1593
+ left: elBCR.left - offsetParentBCR.left
1594
+ };
1595
+ },
1596
+
1597
+ /**
1598
+ * Provides read-only equivalent of jQuery's offset function:
1599
+ * http://api.jquery.com/offset/
1600
+ */
1601
+ offset: function (element) {
1602
+ var boundingClientRect = element[0].getBoundingClientRect();
1603
+ return {
1604
+ width: element.prop('offsetWidth'),
1605
+ height: element.prop('offsetHeight'),
1606
+ top: boundingClientRect.top + ($window.pageYOffset || $document[0].body.scrollTop),
1607
+ left: boundingClientRect.left + ($window.pageXOffset || $document[0].body.scrollLeft)
1608
+ };
1609
+ },
1610
+
1611
+ /**
1612
+ * Provides the coordinates of the mouse
1613
+ */
1614
+ mouse: function () {
1615
+ return {x: mouseX, y: mouseY};
1616
+ }
1617
+ };
1618
+ }]);
1619
+
1620
+ /**
1621
+ * The following features are still outstanding: animation as a
1622
+ * function, placement as a function, inside, support for more triggers than
1623
+ * just mouse enter/leave, html tooltips, and selector delegation.
1624
+ */
1625
+ angular.module( 'ui.bootstrap.tooltip', [ 'ui.bootstrap.position' ] )
1192
1626
 
1193
1627
  /**
1194
1628
  * The $tooltip service creates tooltip- and popover-like directives as well as
@@ -1225,6 +1659,15 @@ angular.module( 'ui.bootstrap.tooltip', [ 'ui.bootstrap.position' ] )
1225
1659
  angular.extend( globalOptions, value );
1226
1660
  };
1227
1661
 
1662
+ /**
1663
+ * This allows you to extend the set of trigger mappings available. E.g.:
1664
+ *
1665
+ * $tooltipProvider.setTriggers( 'openTrigger': 'closeTrigger' );
1666
+ */
1667
+ this.setTriggers = function setTriggers ( triggers ) {
1668
+ angular.extend( triggerMap, triggers );
1669
+ };
1670
+
1228
1671
  /**
1229
1672
  * This is a helper function for translating camel-case to snake-case.
1230
1673
  */
@@ -1240,7 +1683,7 @@ angular.module( 'ui.bootstrap.tooltip', [ 'ui.bootstrap.position' ] )
1240
1683
  * Returns the actual instance of the $tooltip service.
1241
1684
  * TODO support multiple triggers
1242
1685
  */
1243
- this.$get = [ '$window', '$compile', '$timeout', '$parse', '$document', '$position', function ( $window, $compile, $timeout, $parse, $document, $position ) {
1686
+ this.$get = [ '$window', '$compile', '$timeout', '$parse', '$document', '$position', '$interpolate', function ( $window, $compile, $timeout, $parse, $document, $position, $interpolate ) {
1244
1687
  return function $tooltip ( type, prefix, defaultTriggerShow ) {
1245
1688
  var options = angular.extend( {}, defaultOptions, globalOptions );
1246
1689
 
@@ -1277,11 +1720,13 @@ angular.module( 'ui.bootstrap.tooltip', [ 'ui.bootstrap.position' ] )
1277
1720
  var directiveName = snake_case( type );
1278
1721
  var triggers = setTriggers( undefined );
1279
1722
 
1723
+ var startSym = $interpolate.startSymbol();
1724
+ var endSym = $interpolate.endSymbol();
1280
1725
  var template =
1281
1726
  '<'+ directiveName +'-popup '+
1282
- 'title="{{tt_title}}" '+
1283
- 'content="{{tt_content}}" '+
1284
- 'placement="{{tt_placement}}" '+
1727
+ 'title="'+startSym+'tt_title'+endSym+'" '+
1728
+ 'content="'+startSym+'tt_content'+endSym+'" '+
1729
+ 'placement="'+startSym+'tt_placement'+endSym+'" '+
1285
1730
  'animation="tt_animation()" '+
1286
1731
  'is-open="tt_isOpen"'+
1287
1732
  '>'+
@@ -1295,6 +1740,7 @@ angular.module( 'ui.bootstrap.tooltip', [ 'ui.bootstrap.position' ] )
1295
1740
  var transitionTimeout;
1296
1741
  var popupTimeout;
1297
1742
  var $body;
1743
+ var appendToBody = angular.isDefined( options.appendToBody ) ? options.appendToBody : false;
1298
1744
 
1299
1745
  // By default, the tooltip is not open.
1300
1746
  // TODO add ability to start tooltip opened
@@ -1346,15 +1792,15 @@ angular.module( 'ui.bootstrap.tooltip', [ 'ui.bootstrap.position' ] )
1346
1792
 
1347
1793
  // Now we add it to the DOM because need some info about it. But it's not
1348
1794
  // visible yet anyway.
1349
- if ( options.appendToBody ) {
1795
+ if ( appendToBody ) {
1350
1796
  $body = $body || $document.find( 'body' );
1351
1797
  $body.append( tooltip );
1352
1798
  } else {
1353
1799
  element.after( tooltip );
1354
1800
  }
1355
-
1801
+
1356
1802
  // Get the position of the directive element.
1357
- position = $position.position( element );
1803
+ position = options.appendToBody ? $position.offset( element ) : $position.position( element );
1358
1804
 
1359
1805
  // Get the height and width of the tooltip so we can center it.
1360
1806
  ttWidth = tooltip.prop( 'offsetWidth' );
@@ -1363,32 +1809,42 @@ angular.module( 'ui.bootstrap.tooltip', [ 'ui.bootstrap.position' ] )
1363
1809
  // Calculate the tooltip's top and left coordinates to center it with
1364
1810
  // this directive.
1365
1811
  switch ( scope.tt_placement ) {
1812
+ case 'mouse':
1813
+ var mousePos = $position.mouse();
1814
+ ttPosition = {
1815
+ top: mousePos.y,
1816
+ left: mousePos.x
1817
+ };
1818
+ break;
1366
1819
  case 'right':
1367
1820
  ttPosition = {
1368
- top: (position.top + position.height / 2 - ttHeight / 2) + 'px',
1369
- left: (position.left + position.width) + 'px'
1821
+ top: position.top + position.height / 2 - ttHeight / 2,
1822
+ left: position.left + position.width
1370
1823
  };
1371
1824
  break;
1372
1825
  case 'bottom':
1373
1826
  ttPosition = {
1374
- top: (position.top + position.height) + 'px',
1375
- left: (position.left + position.width / 2 - ttWidth / 2) + 'px'
1827
+ top: position.top + position.height,
1828
+ left: position.left + position.width / 2 - ttWidth / 2
1376
1829
  };
1377
1830
  break;
1378
1831
  case 'left':
1379
1832
  ttPosition = {
1380
- top: (position.top + position.height / 2 - ttHeight / 2) + 'px',
1381
- left: (position.left - ttWidth) + 'px'
1833
+ top: position.top + position.height / 2 - ttHeight / 2,
1834
+ left: position.left - ttWidth
1382
1835
  };
1383
1836
  break;
1384
1837
  default:
1385
1838
  ttPosition = {
1386
- top: (position.top - ttHeight) + 'px',
1387
- left: (position.left + position.width / 2 - ttWidth / 2) + 'px'
1839
+ top: position.top - ttHeight,
1840
+ left: position.left + position.width / 2 - ttWidth / 2
1388
1841
  };
1389
1842
  break;
1390
1843
  }
1391
-
1844
+
1845
+ ttPosition.top += 'px';
1846
+ ttPosition.left += 'px';
1847
+
1392
1848
  // Now set the calculated positioning.
1393
1849
  tooltip.css( ttPosition );
1394
1850
 
@@ -1451,6 +1907,30 @@ angular.module( 'ui.bootstrap.tooltip', [ 'ui.bootstrap.position' ] )
1451
1907
  element.bind( triggers.hide, hideTooltipBind );
1452
1908
  }
1453
1909
  });
1910
+
1911
+ attrs.$observe( prefix+'AppendToBody', function ( val ) {
1912
+ appendToBody = angular.isDefined( val ) ? $parse( val )( scope ) : appendToBody;
1913
+ });
1914
+
1915
+ // if a tooltip is attached to <body> we need to remove it on
1916
+ // location change as its parent scope will probably not be destroyed
1917
+ // by the change.
1918
+ if ( appendToBody ) {
1919
+ scope.$on('$locationChangeSuccess', function closeTooltipOnLocationChangeSuccess () {
1920
+ if ( scope.tt_isOpen ) {
1921
+ hide();
1922
+ }
1923
+ });
1924
+ }
1925
+
1926
+ // Make sure tooltip is destroyed and removed.
1927
+ scope.$on('$destroy', function onDestroyTooltip() {
1928
+ if ( scope.tt_isOpen ) {
1929
+ hide();
1930
+ } else {
1931
+ tooltip.remove();
1932
+ }
1933
+ });
1454
1934
  }
1455
1935
  };
1456
1936
  };
@@ -1481,11 +1961,8 @@ angular.module( 'ui.bootstrap.tooltip', [ 'ui.bootstrap.position' ] )
1481
1961
 
1482
1962
  .directive( 'tooltipHtmlUnsafe', [ '$tooltip', function ( $tooltip ) {
1483
1963
  return $tooltip( 'tooltipHtmlUnsafe', 'tooltip', 'mouseenter' );
1484
- }])
1485
-
1486
- ;
1964
+ }]);
1487
1965
 
1488
-
1489
1966
  /**
1490
1967
  * The following features are still outstanding: popup delay, animation as a
1491
1968
  * function, placement as a function, inside, support for more triggers than
@@ -1504,7 +1981,7 @@ angular.module( 'ui.bootstrap.popover', [ 'ui.bootstrap.tooltip' ] )
1504
1981
  return $tooltip( 'popover', 'popover', 'click' );
1505
1982
  }]);
1506
1983
 
1507
-
1984
+
1508
1985
  angular.module('ui.bootstrap.progressbar', ['ui.bootstrap.transition'])
1509
1986
 
1510
1987
  .constant('progressConfig', {
@@ -1556,7 +2033,7 @@ angular.module('ui.bootstrap.progressbar', ['ui.bootstrap.transition'])
1556
2033
  replace: true,
1557
2034
  controller: 'ProgressBarController',
1558
2035
  scope: {
1559
- value: '=',
2036
+ value: '=percent',
1560
2037
  onFull: '&',
1561
2038
  onEmpty: '&'
1562
2039
  },
@@ -1610,7 +2087,7 @@ angular.module('ui.bootstrap.progressbar', ['ui.bootstrap.transition'])
1610
2087
  });
1611
2088
  }
1612
2089
  };
1613
- }]);
2090
+ }]);
1614
2091
  angular.module('ui.bootstrap.rating', [])
1615
2092
 
1616
2093
  .constant('ratingConfig', {
@@ -1663,320 +2140,787 @@ angular.module('ui.bootstrap.rating', [])
1663
2140
  }
1664
2141
  }
1665
2142
  };
1666
- }]);
2143
+ }]);
2144
+
2145
+ /**
2146
+ * @ngdoc overview
2147
+ * @name ui.bootstrap.tabs
2148
+ *
2149
+ * @description
2150
+ * AngularJS version of the tabs directive.
2151
+ */
2152
+
1667
2153
  angular.module('ui.bootstrap.tabs', [])
1668
- .controller('TabsController', ['$scope', '$element', function($scope, $element) {
1669
- var panes = $scope.panes = [];
1670
2154
 
1671
- this.select = $scope.select = function selectPane(pane) {
1672
- angular.forEach(panes, function(pane) {
1673
- pane.selected = false;
1674
- });
1675
- pane.selected = true;
2155
+ .directive('tabs', function() {
2156
+ return function() {
2157
+ 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");
2158
+ };
2159
+ })
2160
+
2161
+ .controller('TabsetController', ['$scope', '$element',
2162
+ function TabsetCtrl($scope, $element) {
2163
+ var ctrl = this,
2164
+ tabs = ctrl.tabs = $scope.tabs = [];
2165
+
2166
+ ctrl.select = function(tab) {
2167
+ angular.forEach(tabs, function(tab) {
2168
+ tab.active = false;
2169
+ });
2170
+ tab.active = true;
1676
2171
  };
1677
2172
 
1678
- this.addPane = function addPane(pane) {
1679
- if (!panes.length) {
1680
- $scope.select(pane);
2173
+ ctrl.addTab = function addTab(tab) {
2174
+ tabs.push(tab);
2175
+ if (tabs.length == 1) {
2176
+ ctrl.select(tab);
1681
2177
  }
1682
- panes.push(pane);
1683
2178
  };
1684
2179
 
1685
- this.removePane = function removePane(pane) {
1686
- var index = panes.indexOf(pane);
1687
- panes.splice(index, 1);
1688
- //Select a new pane if removed pane was selected
1689
- if (pane.selected && panes.length > 0) {
1690
- $scope.select(panes[index < panes.length ? index : index-1]);
2180
+ ctrl.removeTab = function removeTab(tab) {
2181
+ var index = tabs.indexOf(tab);
2182
+ //Select a new tab if the tab to be removed is selected
2183
+ if (tab.active && tabs.length > 1) {
2184
+ //If this is the last tab, select the previous tab. else, the next tab.
2185
+ var newActiveIndex = index == tabs.length - 1 ? index - 1 : index + 1;
2186
+ ctrl.select(tabs[newActiveIndex]);
1691
2187
  }
2188
+ tabs.splice(index, 1);
1692
2189
  };
1693
2190
  }])
1694
- .directive('tabs', function() {
2191
+
2192
+ /**
2193
+ * @ngdoc directive
2194
+ * @name ui.bootstrap.tabs.directive:tabset
2195
+ * @restrict EA
2196
+ *
2197
+ * @description
2198
+ * Tabset is the outer container for the tabs directive
2199
+ *
2200
+ * @param {boolean=} vertical Whether or not to use vertical styling for the tabs.
2201
+ *
2202
+ * @example
2203
+ <example module="ui.bootstrap">
2204
+ <file name="index.html">
2205
+ <tabset>
2206
+ <tab heading="Vertical Tab 1"><b>First</b> Content!</tab>
2207
+ <tab heading="Vertical Tab 2"><i>Second</i> Content!</tab>
2208
+ </tabset>
2209
+ <hr />
2210
+ <tabset vertical="true">
2211
+ <tab heading="Vertical Tab 1"><b>First</b> Vertical Content!</tab>
2212
+ <tab heading="Vertical Tab 2"><i>Second</i> Vertical Content!</tab>
2213
+ </tabset>
2214
+ </file>
2215
+ </example>
2216
+ */
2217
+ .directive('tabset', function() {
1695
2218
  return {
1696
2219
  restrict: 'EA',
1697
2220
  transclude: true,
1698
2221
  scope: {},
1699
- controller: 'TabsController',
1700
- templateUrl: 'template/tabs/tabs.html',
1701
- replace: true
2222
+ controller: 'TabsetController',
2223
+ templateUrl: 'template/tabs/tabset.html',
2224
+ link: function(scope, element, attrs) {
2225
+ scope.vertical = angular.isDefined(attrs.vertical) ? scope.$eval(attrs.vertical) : false;
2226
+ scope.type = angular.isDefined(attrs.type) ? scope.$parent.$eval(attrs.type) : 'tabs';
2227
+ }
1702
2228
  };
1703
2229
  })
1704
- .directive('pane', ['$parse', function($parse) {
2230
+
2231
+ /**
2232
+ * @ngdoc directive
2233
+ * @name ui.bootstrap.tabs.directive:tab
2234
+ * @restrict EA
2235
+ *
2236
+ * @param {string=} heading The visible heading, or title, of the tab. Set HTML headings with {@link ui.bootstrap.tabs.directive:tabHeading tabHeading}.
2237
+ * @param {string=} select An expression to evaluate when the tab is selected.
2238
+ * @param {boolean=} active A binding, telling whether or not this tab is selected.
2239
+ * @param {boolean=} disabled A binding, telling whether or not this tab is disabled.
2240
+ *
2241
+ * @description
2242
+ * Creates a tab with a heading and content. Must be placed within a {@link ui.bootstrap.tabs.directive:tabset tabset}.
2243
+ *
2244
+ * @example
2245
+ <example module="ui.bootstrap">
2246
+ <file name="index.html">
2247
+ <div ng-controller="TabsDemoCtrl">
2248
+ <button class="btn btn-small" ng-click="items[0].active = true">
2249
+ Select item 1, using active binding
2250
+ </button>
2251
+ <button class="btn btn-small" ng-click="items[1].disabled = !items[1].disabled">
2252
+ Enable/disable item 2, using disabled binding
2253
+ </button>
2254
+ <br />
2255
+ <tabset>
2256
+ <tab heading="Tab 1">First Tab</tab>
2257
+ <tab select="alertMe()">
2258
+ <tab-heading><i class="icon-bell"></i> Alert me!</tab-heading>
2259
+ Second Tab, with alert callback and html heading!
2260
+ </tab>
2261
+ <tab ng-repeat="item in items"
2262
+ heading="{{item.title}}"
2263
+ disabled="item.disabled"
2264
+ active="item.active">
2265
+ {{item.content}}
2266
+ </tab>
2267
+ </tabset>
2268
+ </div>
2269
+ </file>
2270
+ <file name="script.js">
2271
+ function TabsDemoCtrl($scope) {
2272
+ $scope.items = [
2273
+ { title:"Dynamic Title 1", content:"Dynamic Item 0" },
2274
+ { title:"Dynamic Title 2", content:"Dynamic Item 1", disabled: true }
2275
+ ];
2276
+
2277
+ $scope.alertMe = function() {
2278
+ setTimeout(function() {
2279
+ alert("You've selected the alert tab!");
2280
+ });
2281
+ };
2282
+ };
2283
+ </file>
2284
+ </example>
2285
+ */
2286
+
2287
+ /**
2288
+ * @ngdoc directive
2289
+ * @name ui.bootstrap.tabs.directive:tabHeading
2290
+ * @restrict EA
2291
+ *
2292
+ * @description
2293
+ * Creates an HTML heading for a {@link ui.bootstrap.tabs.directive:tab tab}. Must be placed as a child of a tab element.
2294
+ *
2295
+ * @example
2296
+ <example module="ui.bootstrap">
2297
+ <file name="index.html">
2298
+ <tabset>
2299
+ <tab>
2300
+ <tab-heading><b>HTML</b> in my titles?!</tab-heading>
2301
+ And some content, too!
2302
+ </tab>
2303
+ <tab>
2304
+ <tab-heading><i class="icon-heart"></i> Icon heading?!?</tab-heading>
2305
+ That's right.
2306
+ </tab>
2307
+ </tabset>
2308
+ </file>
2309
+ </example>
2310
+ */
2311
+ .directive('tab', ['$parse', '$http', '$templateCache', '$compile',
2312
+ function($parse, $http, $templateCache, $compile) {
1705
2313
  return {
1706
- require: '^tabs',
2314
+ require: '^tabset',
1707
2315
  restrict: 'EA',
2316
+ replace: true,
2317
+ templateUrl: 'template/tabs/tab.html',
1708
2318
  transclude: true,
1709
- scope:{
1710
- heading:'@'
2319
+ scope: {
2320
+ heading: '@',
2321
+ onSelect: '&select' //This callback is called in contentHeadingTransclude
2322
+ //once it inserts the tab's content into the dom
1711
2323
  },
1712
- link: function(scope, element, attrs, tabsCtrl) {
1713
- var getSelected, setSelected;
1714
- scope.selected = false;
1715
- if (attrs.active) {
1716
- getSelected = $parse(attrs.active);
1717
- setSelected = getSelected.assign;
1718
- scope.$watch(
1719
- function watchSelected() {return getSelected(scope.$parent);},
1720
- function updateSelected(value) {scope.selected = value;}
1721
- );
1722
- scope.selected = getSelected ? getSelected(scope.$parent) : false;
1723
- }
1724
- scope.$watch('selected', function(selected) {
1725
- if(selected) {
1726
- tabsCtrl.select(scope);
2324
+ controller: function() {
2325
+ //Empty controller so other directives can require being 'under' a tab
2326
+ },
2327
+ compile: function(elm, attrs, transclude) {
2328
+ return function postLink(scope, elm, attrs, tabsetCtrl) {
2329
+ var getActive, setActive;
2330
+ scope.active = false; // default value
2331
+ if (attrs.active) {
2332
+ getActive = $parse(attrs.active);
2333
+ setActive = getActive.assign;
2334
+ scope.$parent.$watch(getActive, function updateActive(value) {
2335
+ if ( !!value && scope.disabled ) {
2336
+ setActive(scope.$parent, false); // Prevent active assignment
2337
+ } else {
2338
+ scope.active = !!value;
2339
+ }
2340
+ });
2341
+ } else {
2342
+ setActive = getActive = angular.noop;
1727
2343
  }
1728
- if(setSelected) {
1729
- setSelected(scope.$parent, selected);
2344
+
2345
+ scope.$watch('active', function(active) {
2346
+ setActive(scope.$parent, active);
2347
+ if (active) {
2348
+ tabsetCtrl.select(scope);
2349
+ scope.onSelect();
2350
+ }
2351
+ });
2352
+
2353
+ scope.disabled = false;
2354
+ if ( attrs.disabled ) {
2355
+ scope.$parent.$watch($parse(attrs.disabled), function(value) {
2356
+ scope.disabled = !! value;
2357
+ });
2358
+ }
2359
+
2360
+ scope.select = function() {
2361
+ if ( ! scope.disabled ) {
2362
+ scope.active = true;
2363
+ }
2364
+ };
2365
+
2366
+ tabsetCtrl.addTab(scope);
2367
+ scope.$on('$destroy', function() {
2368
+ tabsetCtrl.removeTab(scope);
2369
+ });
2370
+ //If the tabset sets this tab to active, set the parent scope's active
2371
+ //binding too. We do this so the watch for the parent's initial active
2372
+ //value won't overwrite what is initially set by the tabset
2373
+ if (scope.active) {
2374
+ setActive(scope.$parent, true);
2375
+ }
2376
+
2377
+ //Transclude the collection of sibling elements. Use forEach to find
2378
+ //the heading if it exists. We don't use a directive for tab-heading
2379
+ //because it is problematic. Discussion @ http://git.io/MSNPwQ
2380
+ transclude(scope.$parent, function(clone) {
2381
+ //Look at every element in the clone collection. If it's tab-heading,
2382
+ //mark it as that. If it's not tab-heading, mark it as tab contents
2383
+ var contents = [], heading;
2384
+ angular.forEach(clone, function(el) {
2385
+ //See if it's a tab-heading attr or element directive
2386
+ //First make sure it's a normal element, one that has a tagName
2387
+ if (el.tagName &&
2388
+ (el.hasAttribute("tab-heading") ||
2389
+ el.hasAttribute("data-tab-heading") ||
2390
+ el.tagName.toLowerCase() == "tab-heading" ||
2391
+ el.tagName.toLowerCase() == "data-tab-heading"
2392
+ )) {
2393
+ heading = el;
2394
+ } else {
2395
+ contents.push(el);
2396
+ }
2397
+ });
2398
+ //Share what we found on the scope, so our tabHeadingTransclude and
2399
+ //tabContentTransclude directives can find out what the heading and
2400
+ //contents are.
2401
+ if (heading) {
2402
+ scope.headingElement = angular.element(heading);
2403
+ }
2404
+ scope.contentElement = angular.element(contents);
2405
+ });
2406
+ };
2407
+ }
2408
+ };
2409
+ }])
2410
+
2411
+ .directive('tabHeadingTransclude', [function() {
2412
+ return {
2413
+ restrict: 'A',
2414
+ require: '^tab',
2415
+ link: function(scope, elm, attrs, tabCtrl) {
2416
+ scope.$watch('headingElement', function updateHeadingElement(heading) {
2417
+ if (heading) {
2418
+ elm.html('');
2419
+ elm.append(heading);
1730
2420
  }
1731
2421
  });
2422
+ }
2423
+ };
2424
+ }])
1732
2425
 
1733
- tabsCtrl.addPane(scope);
1734
- scope.$on('$destroy', function() {
1735
- tabsCtrl.removePane(scope);
2426
+ .directive('tabContentTransclude', ['$parse', function($parse) {
2427
+ return {
2428
+ restrict: 'A',
2429
+ require: '^tabset',
2430
+ link: function(scope, elm, attrs, tabsetCtrl) {
2431
+ scope.$watch($parse(attrs.tabContentTransclude), function(tab) {
2432
+ elm.html('');
2433
+ if (tab) {
2434
+ elm.append(tab.contentElement);
2435
+ }
1736
2436
  });
2437
+ }
2438
+ };
2439
+ }])
2440
+
2441
+ ;
2442
+
2443
+
2444
+ angular.module('ui.bootstrap.timepicker', [])
2445
+
2446
+ .filter('pad', function() {
2447
+ return function(input) {
2448
+ if ( angular.isDefined(input) && input.toString().length < 2 ) {
2449
+ input = '0' + input;
2450
+ }
2451
+ return input;
2452
+ };
2453
+ })
2454
+
2455
+ .constant('timepickerConfig', {
2456
+ hourStep: 1,
2457
+ minuteStep: 1,
2458
+ showMeridian: true,
2459
+ meridians: ['AM', 'PM'],
2460
+ readonlyInput: false,
2461
+ mousewheel: true
2462
+ })
2463
+
2464
+ .directive('timepicker', ['padFilter', '$parse', 'timepickerConfig', function (padFilter, $parse, timepickerConfig) {
2465
+ return {
2466
+ restrict: 'EA',
2467
+ require:'ngModel',
2468
+ replace: true,
2469
+ templateUrl: 'template/timepicker/timepicker.html',
2470
+ scope: {
2471
+ model: '=ngModel'
1737
2472
  },
1738
- templateUrl: 'template/tabs/pane.html',
1739
- replace: true
2473
+ link: function(scope, element, attrs, ngModelCtrl) {
2474
+ var selected = new Date(), meridians = timepickerConfig.meridians;
2475
+
2476
+ var hourStep = timepickerConfig.hourStep;
2477
+ if (attrs.hourStep) {
2478
+ scope.$parent.$watch($parse(attrs.hourStep), function(value) {
2479
+ hourStep = parseInt(value, 10);
2480
+ });
2481
+ }
2482
+
2483
+ var minuteStep = timepickerConfig.minuteStep;
2484
+ if (attrs.minuteStep) {
2485
+ scope.$parent.$watch($parse(attrs.minuteStep), function(value) {
2486
+ minuteStep = parseInt(value, 10);
2487
+ });
2488
+ }
2489
+
2490
+ // 12H / 24H mode
2491
+ scope.showMeridian = timepickerConfig.showMeridian;
2492
+ if (attrs.showMeridian) {
2493
+ scope.$parent.$watch($parse(attrs.showMeridian), function(value) {
2494
+ scope.showMeridian = !! value;
2495
+
2496
+ if ( ! scope.model ) {
2497
+ // Reset
2498
+ var dt = new Date( selected );
2499
+ var hours = getScopeHours();
2500
+ if (angular.isDefined( hours )) {
2501
+ dt.setHours( hours );
2502
+ }
2503
+ scope.model = new Date( dt );
2504
+ } else {
2505
+ refreshTemplate();
2506
+ }
2507
+ });
2508
+ }
2509
+
2510
+ // Get scope.hours in 24H mode if valid
2511
+ function getScopeHours ( ) {
2512
+ var hours = parseInt( scope.hours, 10 );
2513
+ var valid = ( scope.showMeridian ) ? (hours > 0 && hours < 13) : (hours >= 0 && hours < 24);
2514
+ if ( !valid ) {
2515
+ return;
2516
+ }
2517
+
2518
+ if ( scope.showMeridian ) {
2519
+ if ( hours === 12 ) {
2520
+ hours = 0;
2521
+ }
2522
+ if ( scope.meridian === meridians[1] ) {
2523
+ hours = hours + 12;
2524
+ }
2525
+ }
2526
+ return hours;
2527
+ }
2528
+
2529
+ // Input elements
2530
+ var inputs = element.find('input');
2531
+ var hoursInputEl = inputs.eq(0), minutesInputEl = inputs.eq(1);
2532
+
2533
+ // Respond on mousewheel spin
2534
+ var mousewheel = (angular.isDefined(attrs.mousewheel)) ? scope.$eval(attrs.mousewheel) : timepickerConfig.mousewheel;
2535
+ if ( mousewheel ) {
2536
+
2537
+ var isScrollingUp = function(e) {
2538
+ if (e.originalEvent) {
2539
+ e = e.originalEvent;
2540
+ }
2541
+ return (e.detail || e.wheelDelta > 0);
2542
+ };
2543
+
2544
+ hoursInputEl.bind('mousewheel', function(e) {
2545
+ scope.$apply( (isScrollingUp(e)) ? scope.incrementHours() : scope.decrementHours() );
2546
+ e.preventDefault();
2547
+ });
2548
+
2549
+ minutesInputEl.bind('mousewheel', function(e) {
2550
+ scope.$apply( (isScrollingUp(e)) ? scope.incrementMinutes() : scope.decrementMinutes() );
2551
+ e.preventDefault();
2552
+ });
2553
+ }
2554
+
2555
+ var keyboardChange = false;
2556
+ scope.readonlyInput = (angular.isDefined(attrs.readonlyInput)) ? scope.$eval(attrs.readonlyInput) : timepickerConfig.readonlyInput;
2557
+ if ( ! scope.readonlyInput ) {
2558
+ scope.updateHours = function() {
2559
+ var hours = getScopeHours();
2560
+
2561
+ if ( angular.isDefined(hours) ) {
2562
+ keyboardChange = 'h';
2563
+ if ( scope.model === null ) {
2564
+ scope.model = new Date( selected );
2565
+ }
2566
+ scope.model.setHours( hours );
2567
+ } else {
2568
+ scope.model = null;
2569
+ scope.validHours = false;
2570
+ }
2571
+ };
2572
+
2573
+ hoursInputEl.bind('blur', function(e) {
2574
+ if ( scope.validHours && scope.hours < 10) {
2575
+ scope.$apply( function() {
2576
+ scope.hours = padFilter( scope.hours );
2577
+ });
2578
+ }
2579
+ });
2580
+
2581
+ scope.updateMinutes = function() {
2582
+ var minutes = parseInt(scope.minutes, 10);
2583
+ if ( minutes >= 0 && minutes < 60 ) {
2584
+ keyboardChange = 'm';
2585
+ if ( scope.model === null ) {
2586
+ scope.model = new Date( selected );
2587
+ }
2588
+ scope.model.setMinutes( minutes );
2589
+ } else {
2590
+ scope.model = null;
2591
+ scope.validMinutes = false;
2592
+ }
2593
+ };
2594
+
2595
+ minutesInputEl.bind('blur', function(e) {
2596
+ if ( scope.validMinutes && scope.minutes < 10 ) {
2597
+ scope.$apply( function() {
2598
+ scope.minutes = padFilter( scope.minutes );
2599
+ });
2600
+ }
2601
+ });
2602
+ } else {
2603
+ scope.updateHours = angular.noop;
2604
+ scope.updateMinutes = angular.noop;
2605
+ }
2606
+
2607
+ scope.$watch( function getModelTimestamp() {
2608
+ return +scope.model;
2609
+ }, function( timestamp ) {
2610
+ if ( !isNaN( timestamp ) && timestamp > 0 ) {
2611
+ selected = new Date( timestamp );
2612
+ refreshTemplate();
2613
+ }
2614
+ });
2615
+
2616
+ function refreshTemplate() {
2617
+ var hours = selected.getHours();
2618
+ if ( scope.showMeridian ) {
2619
+ // Convert 24 to 12 hour system
2620
+ hours = ( hours === 0 || hours === 12 ) ? 12 : hours % 12;
2621
+ }
2622
+ scope.hours = ( keyboardChange === 'h' ) ? hours : padFilter(hours);
2623
+ scope.validHours = true;
2624
+
2625
+ var minutes = selected.getMinutes();
2626
+ scope.minutes = ( keyboardChange === 'm' ) ? minutes : padFilter(minutes);
2627
+ scope.validMinutes = true;
2628
+
2629
+ scope.meridian = ( scope.showMeridian ) ? (( selected.getHours() < 12 ) ? meridians[0] : meridians[1]) : '';
2630
+
2631
+ keyboardChange = false;
2632
+ }
2633
+
2634
+ function addMinutes( minutes ) {
2635
+ var dt = new Date( selected.getTime() + minutes * 60000 );
2636
+ if ( dt.getDate() !== selected.getDate()) {
2637
+ dt.setDate( dt.getDate() - 1 );
2638
+ }
2639
+ selected.setTime( dt.getTime() );
2640
+ scope.model = new Date( selected );
2641
+ }
2642
+
2643
+ scope.incrementHours = function() {
2644
+ addMinutes( hourStep * 60 );
2645
+ };
2646
+ scope.decrementHours = function() {
2647
+ addMinutes( - hourStep * 60 );
2648
+ };
2649
+ scope.incrementMinutes = function() {
2650
+ addMinutes( minuteStep );
2651
+ };
2652
+ scope.decrementMinutes = function() {
2653
+ addMinutes( - minuteStep );
2654
+ };
2655
+ scope.toggleMeridian = function() {
2656
+ addMinutes( 12 * 60 * (( selected.getHours() < 12 ) ? 1 : -1) );
2657
+ };
2658
+ }
1740
2659
  };
1741
2660
  }]);
1742
-
1743
- angular.module('ui.bootstrap.typeahead', ['ui.bootstrap.position'])
1744
-
1745
- /**
1746
- * A helper service that can parse typeahead's syntax (string provided by users)
1747
- * Extracted to a separate service for ease of unit testing
1748
- */
1749
- .factory('typeaheadParser', ['$parse', function ($parse) {
1750
-
1751
- // 00000111000000000000022200000000000000003333333333333330000000000044000
1752
- var TYPEAHEAD_REGEXP = /^\s*(.*?)(?:\s+as\s+(.*?))?\s+for\s+(?:([\$\w][\$\w\d]*))\s+in\s+(.*)$/;
1753
-
1754
- return {
1755
- parse:function (input) {
1756
-
1757
- var match = input.match(TYPEAHEAD_REGEXP), modelMapper, viewMapper, source;
1758
- if (!match) {
1759
- throw new Error(
1760
- "Expected typeahead specification in form of '_modelValue_ (as _label_)? for _item_ in _collection_'" +
1761
- " but got '" + input + "'.");
1762
- }
1763
-
1764
- return {
1765
- itemName:match[3],
1766
- source:$parse(match[4]),
1767
- viewMapper:$parse(match[2] || match[1]),
1768
- modelMapper:$parse(match[1])
1769
- };
1770
- }
1771
- };
1772
- }])
1773
-
1774
- .directive('typeahead', ['$compile', '$parse', '$q', '$document', '$position', 'typeaheadParser', function ($compile, $parse, $q, $document, $position, typeaheadParser) {
1775
-
1776
- var HOT_KEYS = [9, 13, 27, 38, 40];
1777
-
1778
- return {
1779
- require:'ngModel',
1780
- link:function (originalScope, element, attrs, modelCtrl) {
1781
-
1782
- var selected;
1783
-
1784
- //minimal no of characters that needs to be entered before typeahead kicks-in
1785
- var minSearch = originalScope.$eval(attrs.typeaheadMinLength) || 1;
1786
-
1787
- //expressions used by typeahead
1788
- var parserResult = typeaheadParser.parse(attrs.typeahead);
1789
-
1790
- //should it restrict model values to the ones selected from the popup only?
1791
- var isEditable = originalScope.$eval(attrs.typeaheadEditable) !== false;
1792
-
1793
- var isLoadingSetter = $parse(attrs.typeaheadLoading).assign || angular.noop;
1794
-
1795
- //pop-up element used to display matches
1796
- var popUpEl = angular.element(
1797
- "<typeahead-popup " +
1798
- "matches='matches' " +
1799
- "active='activeIdx' " +
1800
- "select='select(activeIdx)' "+
1801
- "query='query' "+
1802
- "position='position'>"+
1803
- "</typeahead-popup>");
1804
-
1805
- //create a child scope for the typeahead directive so we are not polluting original scope
1806
- //with typeahead-specific data (matches, query etc.)
1807
- var scope = originalScope.$new();
1808
- originalScope.$on('$destroy', function(){
1809
- scope.$destroy();
1810
- });
1811
-
1812
- var resetMatches = function() {
1813
- scope.matches = [];
1814
- scope.activeIdx = -1;
1815
- };
1816
-
1817
- var getMatchesAsync = function(inputValue) {
1818
-
1819
- var locals = {$viewValue: inputValue};
1820
- isLoadingSetter(originalScope, true);
1821
- $q.when(parserResult.source(scope, locals)).then(function(matches) {
1822
-
1823
- //it might happen that several async queries were in progress if a user were typing fast
1824
- //but we are interested only in responses that correspond to the current view value
1825
- if (inputValue === modelCtrl.$viewValue) {
1826
- if (matches.length > 0) {
1827
-
1828
- scope.activeIdx = 0;
1829
- scope.matches.length = 0;
1830
-
1831
- //transform labels
1832
- for(var i=0; i<matches.length; i++) {
1833
- locals[parserResult.itemName] = matches[i];
1834
- scope.matches.push({
1835
- label: parserResult.viewMapper(scope, locals),
1836
- model: matches[i]
1837
- });
1838
- }
1839
-
1840
- scope.query = inputValue;
1841
- //position pop-up with matches - we need to re-calculate its position each time we are opening a window
1842
- //with matches as a pop-up might be absolute-positioned and position of an input might have changed on a page
1843
- //due to other elements being rendered
1844
- scope.position = $position.position(element);
1845
- scope.position.top = scope.position.top + element.prop('offsetHeight');
1846
-
1847
- } else {
1848
- resetMatches();
1849
- }
1850
- isLoadingSetter(originalScope, false);
1851
- }
1852
- }, function(){
1853
- resetMatches();
1854
- isLoadingSetter(originalScope, false);
1855
- });
1856
- };
1857
-
1858
- resetMatches();
1859
-
1860
- //we need to propagate user's query so we can higlight matches
1861
- scope.query = undefined;
1862
-
1863
- //plug into $parsers pipeline to open a typeahead on view changes initiated from DOM
1864
- //$parsers kick-in on all the changes coming from the view as well as manually triggered by $setViewValue
1865
- modelCtrl.$parsers.push(function (inputValue) {
1866
-
1867
- resetMatches();
1868
- if (selected) {
1869
- return inputValue;
1870
- } else {
1871
- if (inputValue && inputValue.length >= minSearch) {
1872
- getMatchesAsync(inputValue);
1873
- }
1874
- }
1875
-
1876
- return isEditable ? inputValue : undefined;
1877
- });
1878
-
1879
- modelCtrl.$render = function () {
1880
- var locals = {};
1881
- locals[parserResult.itemName] = selected || modelCtrl.$viewValue;
1882
- element.val(parserResult.viewMapper(scope, locals) || modelCtrl.$viewValue);
1883
- selected = undefined;
1884
- };
1885
-
1886
- scope.select = function (activeIdx) {
1887
- //called from within the $digest() cycle
1888
- var locals = {};
1889
- locals[parserResult.itemName] = selected = scope.matches[activeIdx].model;
1890
-
1891
- modelCtrl.$setViewValue(parserResult.modelMapper(scope, locals));
1892
- modelCtrl.$render();
1893
- };
1894
-
1895
- //bind keyboard events: arrows up(38) / down(40), enter(13) and tab(9), esc(27)
1896
- element.bind('keydown', function (evt) {
1897
-
1898
- //typeahead is open and an "interesting" key was pressed
1899
- if (scope.matches.length === 0 || HOT_KEYS.indexOf(evt.which) === -1) {
1900
- return;
1901
- }
1902
-
1903
- evt.preventDefault();
1904
-
1905
- if (evt.which === 40) {
1906
- scope.activeIdx = (scope.activeIdx + 1) % scope.matches.length;
1907
- scope.$digest();
1908
-
1909
- } else if (evt.which === 38) {
1910
- scope.activeIdx = (scope.activeIdx ? scope.activeIdx : scope.matches.length) - 1;
1911
- scope.$digest();
1912
-
1913
- } else if (evt.which === 13 || evt.which === 9) {
1914
- scope.$apply(function () {
1915
- scope.select(scope.activeIdx);
1916
- });
1917
-
1918
- } else if (evt.which === 27) {
1919
- evt.stopPropagation();
1920
-
1921
- resetMatches();
1922
- scope.$digest();
1923
- }
1924
- });
1925
-
1926
- $document.bind('click', function(){
1927
- resetMatches();
1928
- scope.$digest();
1929
- });
1930
-
1931
- element.after($compile(popUpEl)(scope));
1932
- }
1933
- };
1934
-
1935
- }])
1936
-
1937
- .directive('typeaheadPopup', function () {
1938
- return {
1939
- restrict:'E',
1940
- scope:{
1941
- matches:'=',
1942
- query:'=',
1943
- active:'=',
1944
- position:'=',
1945
- select:'&'
1946
- },
1947
- replace:true,
1948
- templateUrl:'template/typeahead/typeahead.html',
1949
- link:function (scope, element, attrs) {
1950
-
1951
- scope.isOpen = function () {
1952
- return scope.matches.length > 0;
1953
- };
1954
-
1955
- scope.isActive = function (matchIdx) {
1956
- return scope.active == matchIdx;
1957
- };
1958
-
1959
- scope.selectActive = function (matchIdx) {
1960
- scope.active = matchIdx;
1961
- };
1962
-
1963
- scope.selectMatch = function (activeIdx) {
1964
- scope.select({activeIdx:activeIdx});
1965
- };
1966
- }
1967
- };
1968
- })
1969
-
1970
- .filter('typeaheadHighlight', function() {
1971
-
1972
- function escapeRegexp(queryToEscape) {
1973
- return queryToEscape.replace(/([.?*+^$[\]\\(){}|-])/g, "\\$1");
1974
- }
1975
-
1976
- return function(matchItem, query) {
1977
- return query ? matchItem.replace(new RegExp(escapeRegexp(query), 'gi'), '<strong>$&</strong>') : query;
1978
- };
1979
- });
2661
+ angular.module('ui.bootstrap.typeahead', ['ui.bootstrap.position'])
2662
+
2663
+ /**
2664
+ * A helper service that can parse typeahead's syntax (string provided by users)
2665
+ * Extracted to a separate service for ease of unit testing
2666
+ */
2667
+ .factory('typeaheadParser', ['$parse', function ($parse) {
2668
+
2669
+ // 00000111000000000000022200000000000000003333333333333330000000000044000
2670
+ var TYPEAHEAD_REGEXP = /^\s*(.*?)(?:\s+as\s+(.*?))?\s+for\s+(?:([\$\w][\$\w\d]*))\s+in\s+(.*)$/;
2671
+
2672
+ return {
2673
+ parse:function (input) {
2674
+
2675
+ var match = input.match(TYPEAHEAD_REGEXP), modelMapper, viewMapper, source;
2676
+ if (!match) {
2677
+ throw new Error(
2678
+ "Expected typeahead specification in form of '_modelValue_ (as _label_)? for _item_ in _collection_'" +
2679
+ " but got '" + input + "'.");
2680
+ }
2681
+
2682
+ return {
2683
+ itemName:match[3],
2684
+ source:$parse(match[4]),
2685
+ viewMapper:$parse(match[2] || match[1]),
2686
+ modelMapper:$parse(match[1])
2687
+ };
2688
+ }
2689
+ };
2690
+ }])
2691
+
2692
+ .directive('typeahead', ['$compile', '$parse', '$q', '$timeout', '$document', '$position', 'typeaheadParser', function ($compile, $parse, $q, $timeout, $document, $position, typeaheadParser) {
2693
+
2694
+ var HOT_KEYS = [9, 13, 27, 38, 40];
2695
+
2696
+ return {
2697
+ require:'ngModel',
2698
+ link:function (originalScope, element, attrs, modelCtrl) {
2699
+
2700
+ var selected;
2701
+
2702
+ //minimal no of characters that needs to be entered before typeahead kicks-in
2703
+ var minSearch = originalScope.$eval(attrs.typeaheadMinLength) || 1;
2704
+
2705
+ //minimal wait time after last character typed before typehead kicks-in
2706
+ var waitTime = originalScope.$eval(attrs.typeaheadWaitMs) || 0;
2707
+
2708
+ //expressions used by typeahead
2709
+ var parserResult = typeaheadParser.parse(attrs.typeahead);
2710
+
2711
+ //should it restrict model values to the ones selected from the popup only?
2712
+ var isEditable = originalScope.$eval(attrs.typeaheadEditable) !== false;
2713
+
2714
+ var isLoadingSetter = $parse(attrs.typeaheadLoading).assign || angular.noop;
2715
+
2716
+ var onSelectCallback = $parse(attrs.typeaheadOnSelect);
2717
+
2718
+ //pop-up element used to display matches
2719
+ var popUpEl = angular.element('<typeahead-popup></typeahead-popup>');
2720
+ popUpEl.attr({
2721
+ matches: 'matches',
2722
+ active: 'activeIdx',
2723
+ select: 'select(activeIdx)',
2724
+ query: 'query',
2725
+ position: 'position'
2726
+ });
2727
+
2728
+ //create a child scope for the typeahead directive so we are not polluting original scope
2729
+ //with typeahead-specific data (matches, query etc.)
2730
+ var scope = originalScope.$new();
2731
+ originalScope.$on('$destroy', function(){
2732
+ scope.$destroy();
2733
+ });
2734
+
2735
+ var resetMatches = function() {
2736
+ scope.matches = [];
2737
+ scope.activeIdx = -1;
2738
+ };
2739
+
2740
+ var getMatchesAsync = function(inputValue) {
2741
+
2742
+ var locals = {$viewValue: inputValue};
2743
+ isLoadingSetter(originalScope, true);
2744
+ $q.when(parserResult.source(scope, locals)).then(function(matches) {
2745
+
2746
+ //it might happen that several async queries were in progress if a user were typing fast
2747
+ //but we are interested only in responses that correspond to the current view value
2748
+ if (inputValue === modelCtrl.$viewValue) {
2749
+ if (matches.length > 0) {
2750
+
2751
+ scope.activeIdx = 0;
2752
+ scope.matches.length = 0;
2753
+
2754
+ //transform labels
2755
+ for(var i=0; i<matches.length; i++) {
2756
+ locals[parserResult.itemName] = matches[i];
2757
+ scope.matches.push({
2758
+ label: parserResult.viewMapper(scope, locals),
2759
+ model: matches[i]
2760
+ });
2761
+ }
2762
+
2763
+ scope.query = inputValue;
2764
+ //position pop-up with matches - we need to re-calculate its position each time we are opening a window
2765
+ //with matches as a pop-up might be absolute-positioned and position of an input might have changed on a page
2766
+ //due to other elements being rendered
2767
+ scope.position = $position.position(element);
2768
+ scope.position.top = scope.position.top + element.prop('offsetHeight');
2769
+
2770
+ } else {
2771
+ resetMatches();
2772
+ }
2773
+ isLoadingSetter(originalScope, false);
2774
+ }
2775
+ }, function(){
2776
+ resetMatches();
2777
+ isLoadingSetter(originalScope, false);
2778
+ });
2779
+ };
2780
+
2781
+ resetMatches();
2782
+
2783
+ //we need to propagate user's query so we can higlight matches
2784
+ scope.query = undefined;
2785
+
2786
+ //plug into $parsers pipeline to open a typeahead on view changes initiated from DOM
2787
+ //$parsers kick-in on all the changes coming from the view as well as manually triggered by $setViewValue
2788
+ modelCtrl.$parsers.push(function (inputValue) {
2789
+
2790
+ var timeoutId;
2791
+
2792
+ resetMatches();
2793
+ if (selected) {
2794
+ return inputValue;
2795
+ } else {
2796
+ if (inputValue && inputValue.length >= minSearch) {
2797
+ if (waitTime > 0) {
2798
+ if (timeoutId) {
2799
+ $timeout.cancel(timeoutId);//cancel previous timeout
2800
+ }
2801
+ timeoutId = $timeout(function () {
2802
+ getMatchesAsync(inputValue);
2803
+ }, waitTime);
2804
+ } else {
2805
+ getMatchesAsync(inputValue);
2806
+ }
2807
+ }
2808
+ }
2809
+
2810
+ return isEditable ? inputValue : undefined;
2811
+ });
2812
+
2813
+ modelCtrl.$render = function () {
2814
+ var locals = {};
2815
+ locals[parserResult.itemName] = selected || modelCtrl.$viewValue;
2816
+ element.val(parserResult.viewMapper(scope, locals) || modelCtrl.$viewValue);
2817
+ selected = undefined;
2818
+ };
2819
+
2820
+ scope.select = function (activeIdx) {
2821
+ //called from within the $digest() cycle
2822
+ var locals = {};
2823
+ var model, item;
2824
+ locals[parserResult.itemName] = item = selected = scope.matches[activeIdx].model;
2825
+
2826
+ model = parserResult.modelMapper(scope, locals);
2827
+ modelCtrl.$setViewValue(model);
2828
+ modelCtrl.$render();
2829
+ onSelectCallback(scope, {
2830
+ $item: item,
2831
+ $model: model,
2832
+ $label: parserResult.viewMapper(scope, locals)
2833
+ });
2834
+
2835
+ element[0].focus();
2836
+ };
2837
+
2838
+ //bind keyboard events: arrows up(38) / down(40), enter(13) and tab(9), esc(27)
2839
+ element.bind('keydown', function (evt) {
2840
+
2841
+ //typeahead is open and an "interesting" key was pressed
2842
+ if (scope.matches.length === 0 || HOT_KEYS.indexOf(evt.which) === -1) {
2843
+ return;
2844
+ }
2845
+
2846
+ evt.preventDefault();
2847
+
2848
+ if (evt.which === 40) {
2849
+ scope.activeIdx = (scope.activeIdx + 1) % scope.matches.length;
2850
+ scope.$digest();
2851
+
2852
+ } else if (evt.which === 38) {
2853
+ scope.activeIdx = (scope.activeIdx ? scope.activeIdx : scope.matches.length) - 1;
2854
+ scope.$digest();
2855
+
2856
+ } else if (evt.which === 13 || evt.which === 9) {
2857
+ scope.$apply(function () {
2858
+ scope.select(scope.activeIdx);
2859
+ });
2860
+
2861
+ } else if (evt.which === 27) {
2862
+ evt.stopPropagation();
2863
+
2864
+ resetMatches();
2865
+ scope.$digest();
2866
+ }
2867
+ });
2868
+
2869
+ $document.bind('click', function(){
2870
+ resetMatches();
2871
+ scope.$digest();
2872
+ });
2873
+
2874
+ element.after($compile(popUpEl)(scope));
2875
+ }
2876
+ };
2877
+
2878
+ }])
2879
+
2880
+ .directive('typeaheadPopup', function () {
2881
+ return {
2882
+ restrict:'E',
2883
+ scope:{
2884
+ matches:'=',
2885
+ query:'=',
2886
+ active:'=',
2887
+ position:'=',
2888
+ select:'&'
2889
+ },
2890
+ replace:true,
2891
+ templateUrl:'template/typeahead/typeahead.html',
2892
+ link:function (scope, element, attrs) {
2893
+
2894
+ scope.isOpen = function () {
2895
+ return scope.matches.length > 0;
2896
+ };
2897
+
2898
+ scope.isActive = function (matchIdx) {
2899
+ return scope.active == matchIdx;
2900
+ };
2901
+
2902
+ scope.selectActive = function (matchIdx) {
2903
+ scope.active = matchIdx;
2904
+ };
2905
+
2906
+ scope.selectMatch = function (activeIdx) {
2907
+ scope.select({activeIdx:activeIdx});
2908
+ };
2909
+ }
2910
+ };
2911
+ })
2912
+
2913
+ .filter('typeaheadHighlight', function() {
2914
+
2915
+ function escapeRegexp(queryToEscape) {
2916
+ return queryToEscape.replace(/([.?*+^$[\]\\(){}|-])/g, "\\$1");
2917
+ }
2918
+
2919
+ return function(matchItem, query) {
2920
+ return query ? matchItem.replace(new RegExp(escapeRegexp(query), 'gi'), '<strong>$&</strong>') : query;
2921
+ };
2922
+ });
2923
+
1980
2924
  angular.module("template/accordion/accordion-group.html", []).run(["$templateCache", function($templateCache) {
1981
2925
  $templateCache.put("template/accordion/accordion-group.html",
1982
2926
  "<div class=\"accordion-group\">\n" +
@@ -1985,12 +2929,12 @@ angular.module("template/accordion/accordion-group.html", []).run(["$templateCac
1985
2929
  " <div class=\"accordion-inner\" ng-transclude></div> </div>\n" +
1986
2930
  "</div>");
1987
2931
  }]);
1988
-
2932
+
1989
2933
  angular.module("template/accordion/accordion.html", []).run(["$templateCache", function($templateCache) {
1990
2934
  $templateCache.put("template/accordion/accordion.html",
1991
2935
  "<div class=\"accordion\" ng-transclude></div>");
1992
2936
  }]);
1993
-
2937
+
1994
2938
  angular.module("template/alert/alert.html", []).run(["$templateCache", function($templateCache) {
1995
2939
  $templateCache.put("template/alert/alert.html",
1996
2940
  "<div class='alert' ng-class='type && \"alert-\" + type'>\n" +
@@ -1999,7 +2943,7 @@ angular.module("template/alert/alert.html", []).run(["$templateCache", function(
1999
2943
  "</div>\n" +
2000
2944
  "");
2001
2945
  }]);
2002
-
2946
+
2003
2947
  angular.module("template/carousel/carousel.html", []).run(["$templateCache", function($templateCache) {
2004
2948
  $templateCache.put("template/carousel/carousel.html",
2005
2949
  "<div ng-mouseenter=\"pause()\" ng-mouseleave=\"play()\" class=\"carousel\">\n" +
@@ -2012,7 +2956,7 @@ angular.module("template/carousel/carousel.html", []).run(["$templateCache", fun
2012
2956
  "</div>\n" +
2013
2957
  "");
2014
2958
  }]);
2015
-
2959
+
2016
2960
  angular.module("template/carousel/slide.html", []).run(["$templateCache", function($templateCache) {
2017
2961
  $templateCache.put("template/carousel/slide.html",
2018
2962
  "<div ng-class=\"{\n" +
@@ -2024,21 +2968,67 @@ angular.module("template/carousel/slide.html", []).run(["$templateCache", functi
2024
2968
  " }\" class=\"item\" ng-transclude></div>\n" +
2025
2969
  "");
2026
2970
  }]);
2027
-
2971
+
2972
+ angular.module("template/datepicker/datepicker.html", []).run(["$templateCache", function($templateCache) {
2973
+ $templateCache.put("template/datepicker/datepicker.html",
2974
+ "<table class=\"well well-large\">\n" +
2975
+ " <thead>\n" +
2976
+ " <tr class=\"text-center\">\n" +
2977
+ " <th><button class=\"btn pull-left\" ng-click=\"move(-1)\"><i class=\"icon-chevron-left\"></i></button></th>\n" +
2978
+ " <th colspan=\"{{rows[0].length - 2 + showWeekNumbers}}\"><button class=\"btn btn-block\" ng-click=\"toggleMode()\"><strong>{{title}}</strong></button></th>\n" +
2979
+ " <th><button class=\"btn pull-right\" ng-click=\"move(1)\"><i class=\"icon-chevron-right\"></i></button></th>\n" +
2980
+ " </tr>\n" +
2981
+ " <tr class=\"text-center\" ng-show=\"labels.length > 0\">\n" +
2982
+ " <th ng-show=\"showWeekNumbers\">#</th>\n" +
2983
+ " <th ng-repeat=\"label in labels\">{{label}}</th>\n" +
2984
+ " </tr>\n" +
2985
+ " </thead>\n" +
2986
+ " <tbody>\n" +
2987
+ " <tr ng-repeat=\"row in rows\">\n" +
2988
+ " <td ng-show=\"showWeekNumbers\" class=\"text-center\"><em>{{ getWeekNumber(row) }}</em></td>\n" +
2989
+ " <td ng-repeat=\"dt in row\" class=\"text-center\">\n" +
2990
+ " <button style=\"width:100%;\" class=\"btn\" ng-class=\"{'btn-info': dt.isSelected}\" ng-click=\"select(dt.date)\" ng-disabled=\"dt.disabled\"><span ng-class=\"{muted: ! dt.isCurrent}\">{{dt.label}}</span></button>\n" +
2991
+ " </td>\n" +
2992
+ " </tr>\n" +
2993
+ " </tbody>\n" +
2994
+ "</table>\n" +
2995
+ "");
2996
+ }]);
2997
+
2028
2998
  angular.module("template/dialog/message.html", []).run(["$templateCache", function($templateCache) {
2029
2999
  $templateCache.put("template/dialog/message.html",
2030
3000
  "<div class=\"modal-header\">\n" +
2031
- " <h1>{{ title }}</h1>\n" +
3001
+ " <h3>{{ title }}</h3>\n" +
2032
3002
  "</div>\n" +
2033
3003
  "<div class=\"modal-body\">\n" +
2034
3004
  " <p>{{ message }}</p>\n" +
2035
3005
  "</div>\n" +
2036
3006
  "<div class=\"modal-footer\">\n" +
2037
- " <button ng-repeat=\"btn in buttons\" ng-click=\"close(btn.result)\" class=btn ng-class=\"btn.cssClass\">{{ btn.label }}</button>\n" +
3007
+ " <button ng-repeat=\"btn in buttons\" ng-click=\"close(btn.result)\" class=\"btn\" ng-class=\"btn.cssClass\">{{ btn.label }}</button>\n" +
2038
3008
  "</div>\n" +
2039
3009
  "");
2040
3010
  }]);
2041
-
3011
+
3012
+ angular.module("template/modal/backdrop.html", []).run(["$templateCache", function($templateCache) {
3013
+ $templateCache.put("template/modal/backdrop.html",
3014
+ "<div class=\"modal-backdrop\"></div>");
3015
+ }]);
3016
+
3017
+ angular.module("template/modal/window.html", []).run(["$templateCache", function($templateCache) {
3018
+ $templateCache.put("template/modal/window.html",
3019
+ "<div class=\"modal in\" ng-transclude></div>");
3020
+ }]);
3021
+
3022
+ angular.module("template/pagination/pager.html", []).run(["$templateCache", function($templateCache) {
3023
+ $templateCache.put("template/pagination/pager.html",
3024
+ "<div class=\"pager\">\n" +
3025
+ " <ul>\n" +
3026
+ " <li ng-repeat=\"page in pages\" ng-class=\"{disabled: page.disabled, previous: page.previous, next: page.next}\"><a ng-click=\"selectPage(page.number)\">{{page.text}}</a></li>\n" +
3027
+ " </ul>\n" +
3028
+ "</div>\n" +
3029
+ "");
3030
+ }]);
3031
+
2042
3032
  angular.module("template/pagination/pagination.html", []).run(["$templateCache", function($templateCache) {
2043
3033
  $templateCache.put("template/pagination/pagination.html",
2044
3034
  "<div class=\"pagination\"><ul>\n" +
@@ -2047,7 +3037,7 @@ angular.module("template/pagination/pagination.html", []).run(["$templateCache",
2047
3037
  "</div>\n" +
2048
3038
  "");
2049
3039
  }]);
2050
-
3040
+
2051
3041
  angular.module("template/tooltip/tooltip-html-unsafe-popup.html", []).run(["$templateCache", function($templateCache) {
2052
3042
  $templateCache.put("template/tooltip/tooltip-html-unsafe-popup.html",
2053
3043
  "<div class=\"tooltip {{placement}}\" ng-class=\"{ in: isOpen(), fade: animation() }\">\n" +
@@ -2056,7 +3046,7 @@ angular.module("template/tooltip/tooltip-html-unsafe-popup.html", []).run(["$tem
2056
3046
  "</div>\n" +
2057
3047
  "");
2058
3048
  }]);
2059
-
3049
+
2060
3050
  angular.module("template/tooltip/tooltip-popup.html", []).run(["$templateCache", function($templateCache) {
2061
3051
  $templateCache.put("template/tooltip/tooltip-popup.html",
2062
3052
  "<div class=\"tooltip {{placement}}\" ng-class=\"{ in: isOpen(), fade: animation() }\">\n" +
@@ -2065,7 +3055,7 @@ angular.module("template/tooltip/tooltip-popup.html", []).run(["$templateCache",
2065
3055
  "</div>\n" +
2066
3056
  "");
2067
3057
  }]);
2068
-
3058
+
2069
3059
  angular.module("template/popover/popover.html", []).run(["$templateCache", function($templateCache) {
2070
3060
  $templateCache.put("template/popover/popover.html",
2071
3061
  "<div class=\"popover {{placement}}\" ng-class=\"{ in: isOpen(), fade: animation() }\">\n" +
@@ -2078,17 +3068,17 @@ angular.module("template/popover/popover.html", []).run(["$templateCache", funct
2078
3068
  "</div>\n" +
2079
3069
  "");
2080
3070
  }]);
2081
-
3071
+
2082
3072
  angular.module("template/progressbar/bar.html", []).run(["$templateCache", function($templateCache) {
2083
3073
  $templateCache.put("template/progressbar/bar.html",
2084
3074
  "<div class=\"bar\" ng-class='type && \"bar-\" + type'></div>");
2085
3075
  }]);
2086
-
3076
+
2087
3077
  angular.module("template/progressbar/progress.html", []).run(["$templateCache", function($templateCache) {
2088
3078
  $templateCache.put("template/progressbar/progress.html",
2089
3079
  "<div class=\"progress\"><progressbar ng-repeat=\"bar in bars\" width=\"bar.to\" old=\"bar.from\" animate=\"bar.animate\" type=\"bar.type\"></progressbar></div>");
2090
3080
  }]);
2091
-
3081
+
2092
3082
  angular.module("template/rating/rating.html", []).run(["$templateCache", function($templateCache) {
2093
3083
  $templateCache.put("template/rating/rating.html",
2094
3084
  "<span ng-mouseleave=\"reset()\">\n" +
@@ -2096,13 +3086,21 @@ angular.module("template/rating/rating.html", []).run(["$templateCache", functio
2096
3086
  "</span>\n" +
2097
3087
  "");
2098
3088
  }]);
2099
-
3089
+
2100
3090
  angular.module("template/tabs/pane.html", []).run(["$templateCache", function($templateCache) {
2101
3091
  $templateCache.put("template/tabs/pane.html",
2102
3092
  "<div class=\"tab-pane\" ng-class=\"{active: selected}\" ng-show=\"selected\" ng-transclude></div>\n" +
2103
3093
  "");
2104
3094
  }]);
2105
-
3095
+
3096
+ angular.module("template/tabs/tab.html", []).run(["$templateCache", function($templateCache) {
3097
+ $templateCache.put("template/tabs/tab.html",
3098
+ "<li ng-class=\"{active: active, disabled: disabled}\">\n" +
3099
+ " <a ng-click=\"select()\" tab-heading-transclude>{{heading}}</a>\n" +
3100
+ "</li>\n" +
3101
+ "");
3102
+ }]);
3103
+
2106
3104
  angular.module("template/tabs/tabs.html", []).run(["$templateCache", function($templateCache) {
2107
3105
  $templateCache.put("template/tabs/tabs.html",
2108
3106
  "<div class=\"tabbable\">\n" +
@@ -2115,12 +3113,48 @@ angular.module("template/tabs/tabs.html", []).run(["$templateCache", function($t
2115
3113
  "</div>\n" +
2116
3114
  "");
2117
3115
  }]);
2118
-
2119
- angular.module("template/typeahead/match.html", []).run(["$templateCache", function($templateCache){
2120
- $templateCache.put("template/typeahead/match.html",
2121
- "<a tabindex=\"-1\" ng-bind-html-unsafe=\"match.label | typeaheadHighlight:query\"></a>");
2122
- }]);
2123
-
3116
+
3117
+ angular.module("template/tabs/tabset.html", []).run(["$templateCache", function($templateCache) {
3118
+ $templateCache.put("template/tabs/tabset.html",
3119
+ "\n" +
3120
+ "<div class=\"tabbable\">\n" +
3121
+ " <ul class=\"nav {{type && 'nav-' + type}}\" ng-class=\"{'nav-stacked': vertical}\" ng-transclude>\n" +
3122
+ " </ul>\n" +
3123
+ " <div class=\"tab-content\">\n" +
3124
+ " <div class=\"tab-pane\" \n" +
3125
+ " ng-repeat=\"tab in tabs\" \n" +
3126
+ " ng-class=\"{active: tab.active}\"\n" +
3127
+ " tab-content-transclude=\"tab\" tt=\"tab\">\n" +
3128
+ " </div>\n" +
3129
+ " </div>\n" +
3130
+ "</div>\n" +
3131
+ "");
3132
+ }]);
3133
+
3134
+ angular.module("template/timepicker/timepicker.html", []).run(["$templateCache", function($templateCache) {
3135
+ $templateCache.put("template/timepicker/timepicker.html",
3136
+ "<table class=\"form-inline\">\n" +
3137
+ " <tr class=\"text-center\">\n" +
3138
+ " <td><a ng-click=\"incrementHours()\" class=\"btn btn-link\"><i class=\"icon-chevron-up\"></i></a></td>\n" +
3139
+ " <td>&nbsp;</td>\n" +
3140
+ " <td><a ng-click=\"incrementMinutes()\" class=\"btn btn-link\"><i class=\"icon-chevron-up\"></i></a></td>\n" +
3141
+ " <td ng-show=\"showMeridian\"></td>\n" +
3142
+ " </tr>\n" +
3143
+ " <tr>\n" +
3144
+ " <td class=\"control-group\" ng-class=\"{'error': !validHours}\"><input type=\"text\" ng-model=\"hours\" ng-change=\"updateHours()\" class=\"span1 text-center\" ng-mousewheel=\"incrementHours()\" ng-readonly=\"readonlyInput\" maxlength=\"2\" /></td>\n" +
3145
+ " <td>:</td>\n" +
3146
+ " <td class=\"control-group\" ng-class=\"{'error': !validMinutes}\"><input type=\"text\" ng-model=\"minutes\" ng-change=\"updateMinutes()\" class=\"span1 text-center\" ng-readonly=\"readonlyInput\" maxlength=\"2\"></td>\n" +
3147
+ " <td ng-show=\"showMeridian\"><button ng-click=\"toggleMeridian()\" class=\"btn text-center\">{{meridian}}</button></td>\n" +
3148
+ " </tr>\n" +
3149
+ " <tr class=\"text-center\">\n" +
3150
+ " <td><a ng-click=\"decrementHours()\" class=\"btn btn-link\"><i class=\"icon-chevron-down\"></i></a></td>\n" +
3151
+ " <td>&nbsp;</td>\n" +
3152
+ " <td><a ng-click=\"decrementMinutes()\" class=\"btn btn-link\"><i class=\"icon-chevron-down\"></i></a></td>\n" +
3153
+ " <td ng-show=\"showMeridian\"></td>\n" +
3154
+ " </tr>\n" +
3155
+ "</table>");
3156
+ }]);
3157
+
2124
3158
  angular.module("template/typeahead/typeahead.html", []).run(["$templateCache", function($templateCache) {
2125
3159
  $templateCache.put("template/typeahead/typeahead.html",
2126
3160
  "<ul class=\"typeahead dropdown-menu\" ng-style=\"{display: isOpen()&&'block' || 'none', top: position.top+'px', left: position.left+'px'}\">\n" +
@@ -2128,4 +3162,4 @@ angular.module("template/typeahead/typeahead.html", []).run(["$templateCache", f
2128
3162
  " <a tabindex=\"-1\" ng-click=\"selectMatch($index)\" ng-bind-html-unsafe=\"match.label | typeaheadHighlight:query\"></a>\n" +
2129
3163
  " </li>\n" +
2130
3164
  "</ul>");
2131
- }]);
3165
+ }]);