angular-ui-bootstrap-rails 0.3.0.1 → 0.4.0.0

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