angular-pack 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,3677 @@
1
+ /*
2
+ * angular-ui-bootstrap
3
+ * http://angular-ui.github.io/bootstrap/
4
+
5
+ * Version: 0.10.0 - 2014-01-13
6
+ * License: MIT
7
+ */
8
+ angular.module("ui.bootstrap", ["ui.bootstrap.tpls", "ui.bootstrap.transition","ui.bootstrap.collapse","ui.bootstrap.accordion","ui.bootstrap.alert","ui.bootstrap.bindHtml","ui.bootstrap.buttons","ui.bootstrap.carousel","ui.bootstrap.position","ui.bootstrap.datepicker","ui.bootstrap.dropdownToggle","ui.bootstrap.modal","ui.bootstrap.pagination","ui.bootstrap.tooltip","ui.bootstrap.popover","ui.bootstrap.progressbar","ui.bootstrap.rating","ui.bootstrap.tabs","ui.bootstrap.timepicker","ui.bootstrap.typeahead"]);
9
+ 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/datepicker/popup.html","template/modal/backdrop.html","template/modal/window.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/progressbar/progressbar.html","template/rating/rating.html","template/tabs/tab.html","template/tabs/tabset.html","template/timepicker/timepicker.html","template/typeahead/typeahead-match.html","template/typeahead/typeahead-popup.html"]);
10
+ angular.module('ui.bootstrap.transition', [])
11
+
12
+ /**
13
+ * $transition service provides a consistent interface to trigger CSS 3 transitions and to be informed when they complete.
14
+ * @param {DOMElement} element The DOMElement that will be animated.
15
+ * @param {string|object|function} trigger The thing that will cause the transition to start:
16
+ * - As a string, it represents the css class to be added to the element.
17
+ * - As an object, it represents a hash of style attributes to be applied to the element.
18
+ * - As a function, it represents a function to be called that will cause the transition to occur.
19
+ * @return {Promise} A promise that is resolved when the transition finishes.
20
+ */
21
+ .factory('$transition', ['$q', '$timeout', '$rootScope', function($q, $timeout, $rootScope) {
22
+
23
+ var $transition = function(element, trigger, options) {
24
+ options = options || {};
25
+ var deferred = $q.defer();
26
+ var endEventName = $transition[options.animation ? "animationEndEventName" : "transitionEndEventName"];
27
+
28
+ var transitionEndHandler = function(event) {
29
+ $rootScope.$apply(function() {
30
+ element.unbind(endEventName, transitionEndHandler);
31
+ deferred.resolve(element);
32
+ });
33
+ };
34
+
35
+ if (endEventName) {
36
+ element.bind(endEventName, transitionEndHandler);
37
+ }
38
+
39
+ // Wrap in a timeout to allow the browser time to update the DOM before the transition is to occur
40
+ $timeout(function() {
41
+ if ( angular.isString(trigger) ) {
42
+ element.addClass(trigger);
43
+ } else if ( angular.isFunction(trigger) ) {
44
+ trigger(element);
45
+ } else if ( angular.isObject(trigger) ) {
46
+ element.css(trigger);
47
+ }
48
+ //If browser does not support transitions, instantly resolve
49
+ if ( !endEventName ) {
50
+ deferred.resolve(element);
51
+ }
52
+ });
53
+
54
+ // Add our custom cancel function to the promise that is returned
55
+ // We can call this if we are about to run a new transition, which we know will prevent this transition from ending,
56
+ // i.e. it will therefore never raise a transitionEnd event for that transition
57
+ deferred.promise.cancel = function() {
58
+ if ( endEventName ) {
59
+ element.unbind(endEventName, transitionEndHandler);
60
+ }
61
+ deferred.reject('Transition cancelled');
62
+ };
63
+
64
+ return deferred.promise;
65
+ };
66
+
67
+ // Work out the name of the transitionEnd event
68
+ var transElement = document.createElement('trans');
69
+ var transitionEndEventNames = {
70
+ 'WebkitTransition': 'webkitTransitionEnd',
71
+ 'MozTransition': 'transitionend',
72
+ 'OTransition': 'oTransitionEnd',
73
+ 'transition': 'transitionend'
74
+ };
75
+ var animationEndEventNames = {
76
+ 'WebkitTransition': 'webkitAnimationEnd',
77
+ 'MozTransition': 'animationend',
78
+ 'OTransition': 'oAnimationEnd',
79
+ 'transition': 'animationend'
80
+ };
81
+ function findEndEventName(endEventNames) {
82
+ for (var name in endEventNames){
83
+ if (transElement.style[name] !== undefined) {
84
+ return endEventNames[name];
85
+ }
86
+ }
87
+ }
88
+ $transition.transitionEndEventName = findEndEventName(transitionEndEventNames);
89
+ $transition.animationEndEventName = findEndEventName(animationEndEventNames);
90
+ return $transition;
91
+ }]);
92
+
93
+ angular.module('ui.bootstrap.collapse', ['ui.bootstrap.transition'])
94
+
95
+ .directive('collapse', ['$transition', function ($transition, $timeout) {
96
+
97
+ return {
98
+ link: function (scope, element, attrs) {
99
+
100
+ var initialAnimSkip = true;
101
+ var currentTransition;
102
+
103
+ function doTransition(change) {
104
+ var newTransition = $transition(element, change);
105
+ if (currentTransition) {
106
+ currentTransition.cancel();
107
+ }
108
+ currentTransition = newTransition;
109
+ newTransition.then(newTransitionDone, newTransitionDone);
110
+ return newTransition;
111
+
112
+ function newTransitionDone() {
113
+ // Make sure it's this transition, otherwise, leave it alone.
114
+ if (currentTransition === newTransition) {
115
+ currentTransition = undefined;
116
+ }
117
+ }
118
+ }
119
+
120
+ function expand() {
121
+ if (initialAnimSkip) {
122
+ initialAnimSkip = false;
123
+ expandDone();
124
+ } else {
125
+ element.removeClass('collapse').addClass('collapsing');
126
+ doTransition({ height: element[0].scrollHeight + 'px' }).then(expandDone);
127
+ }
128
+ }
129
+
130
+ function expandDone() {
131
+ element.removeClass('collapsing');
132
+ element.addClass('collapse in');
133
+ element.css({height: 'auto'});
134
+ }
135
+
136
+ function collapse() {
137
+ if (initialAnimSkip) {
138
+ initialAnimSkip = false;
139
+ collapseDone();
140
+ element.css({height: 0});
141
+ } else {
142
+ // CSS transitions don't work with height: auto, so we have to manually change the height to a specific value
143
+ element.css({ height: element[0].scrollHeight + 'px' });
144
+ //trigger reflow so a browser realizes that height was updated from auto to a specific value
145
+ var x = element[0].offsetWidth;
146
+
147
+ element.removeClass('collapse in').addClass('collapsing');
148
+
149
+ doTransition({ height: 0 }).then(collapseDone);
150
+ }
151
+ }
152
+
153
+ function collapseDone() {
154
+ element.removeClass('collapsing');
155
+ element.addClass('collapse');
156
+ }
157
+
158
+ scope.$watch(attrs.collapse, function (shouldCollapse) {
159
+ if (shouldCollapse) {
160
+ collapse();
161
+ } else {
162
+ expand();
163
+ }
164
+ });
165
+ }
166
+ };
167
+ }]);
168
+
169
+ angular.module('ui.bootstrap.accordion', ['ui.bootstrap.collapse'])
170
+
171
+ .constant('accordionConfig', {
172
+ closeOthers: true
173
+ })
174
+
175
+ .controller('AccordionController', ['$scope', '$attrs', 'accordionConfig', function ($scope, $attrs, accordionConfig) {
176
+
177
+ // This array keeps track of the accordion groups
178
+ this.groups = [];
179
+
180
+ // Ensure that all the groups in this accordion are closed, unless close-others explicitly says not to
181
+ this.closeOthers = function(openGroup) {
182
+ var closeOthers = angular.isDefined($attrs.closeOthers) ? $scope.$eval($attrs.closeOthers) : accordionConfig.closeOthers;
183
+ if ( closeOthers ) {
184
+ angular.forEach(this.groups, function (group) {
185
+ if ( group !== openGroup ) {
186
+ group.isOpen = false;
187
+ }
188
+ });
189
+ }
190
+ };
191
+
192
+ // This is called from the accordion-group directive to add itself to the accordion
193
+ this.addGroup = function(groupScope) {
194
+ var that = this;
195
+ this.groups.push(groupScope);
196
+
197
+ groupScope.$on('$destroy', function (event) {
198
+ that.removeGroup(groupScope);
199
+ });
200
+ };
201
+
202
+ // This is called from the accordion-group directive when to remove itself
203
+ this.removeGroup = function(group) {
204
+ var index = this.groups.indexOf(group);
205
+ if ( index !== -1 ) {
206
+ this.groups.splice(this.groups.indexOf(group), 1);
207
+ }
208
+ };
209
+
210
+ }])
211
+
212
+ // The accordion directive simply sets up the directive controller
213
+ // and adds an accordion CSS class to itself element.
214
+ .directive('accordion', function () {
215
+ return {
216
+ restrict:'EA',
217
+ controller:'AccordionController',
218
+ transclude: true,
219
+ replace: false,
220
+ templateUrl: 'template/accordion/accordion.html'
221
+ };
222
+ })
223
+
224
+ // The accordion-group directive indicates a block of html that will expand and collapse in an accordion
225
+ .directive('accordionGroup', ['$parse', function($parse) {
226
+ return {
227
+ require:'^accordion', // We need this directive to be inside an accordion
228
+ restrict:'EA',
229
+ transclude:true, // It transcludes the contents of the directive into the template
230
+ replace: true, // The element containing the directive will be replaced with the template
231
+ templateUrl:'template/accordion/accordion-group.html',
232
+ scope:{ heading:'@' }, // Create an isolated scope and interpolate the heading attribute onto this scope
233
+ controller: function() {
234
+ this.setHeading = function(element) {
235
+ this.heading = element;
236
+ };
237
+ },
238
+ link: function(scope, element, attrs, accordionCtrl) {
239
+ var getIsOpen, setIsOpen;
240
+
241
+ accordionCtrl.addGroup(scope);
242
+
243
+ scope.isOpen = false;
244
+
245
+ if ( attrs.isOpen ) {
246
+ getIsOpen = $parse(attrs.isOpen);
247
+ setIsOpen = getIsOpen.assign;
248
+
249
+ scope.$parent.$watch(getIsOpen, function(value) {
250
+ scope.isOpen = !!value;
251
+ });
252
+ }
253
+
254
+ scope.$watch('isOpen', function(value) {
255
+ if ( value ) {
256
+ accordionCtrl.closeOthers(scope);
257
+ }
258
+ if ( setIsOpen ) {
259
+ setIsOpen(scope.$parent, value);
260
+ }
261
+ });
262
+ }
263
+ };
264
+ }])
265
+
266
+ // Use accordion-heading below an accordion-group to provide a heading containing HTML
267
+ // <accordion-group>
268
+ // <accordion-heading>Heading containing HTML - <img src="..."></accordion-heading>
269
+ // </accordion-group>
270
+ .directive('accordionHeading', function() {
271
+ return {
272
+ restrict: 'EA',
273
+ transclude: true, // Grab the contents to be used as the heading
274
+ template: '', // In effect remove this element!
275
+ replace: true,
276
+ require: '^accordionGroup',
277
+ compile: function(element, attr, transclude) {
278
+ return function link(scope, element, attr, accordionGroupCtrl) {
279
+ // Pass the heading to the accordion-group controller
280
+ // so that it can be transcluded into the right place in the template
281
+ // [The second parameter to transclude causes the elements to be cloned so that they work in ng-repeat]
282
+ accordionGroupCtrl.setHeading(transclude(scope, function() {}));
283
+ };
284
+ }
285
+ };
286
+ })
287
+
288
+ // Use in the accordion-group template to indicate where you want the heading to be transcluded
289
+ // You must provide the property on the accordion-group controller that will hold the transcluded element
290
+ // <div class="accordion-group">
291
+ // <div class="accordion-heading" ><a ... accordion-transclude="heading">...</a></div>
292
+ // ...
293
+ // </div>
294
+ .directive('accordionTransclude', function() {
295
+ return {
296
+ require: '^accordionGroup',
297
+ link: function(scope, element, attr, controller) {
298
+ scope.$watch(function() { return controller[attr.accordionTransclude]; }, function(heading) {
299
+ if ( heading ) {
300
+ element.html('');
301
+ element.append(heading);
302
+ }
303
+ });
304
+ }
305
+ };
306
+ });
307
+
308
+ angular.module("ui.bootstrap.alert", [])
309
+
310
+ .controller('AlertController', ['$scope', '$attrs', function ($scope, $attrs) {
311
+ $scope.closeable = 'close' in $attrs;
312
+ }])
313
+
314
+ .directive('alert', function () {
315
+ return {
316
+ restrict:'EA',
317
+ controller:'AlertController',
318
+ templateUrl:'template/alert/alert.html',
319
+ transclude:true,
320
+ replace:true,
321
+ scope: {
322
+ type: '=',
323
+ close: '&'
324
+ }
325
+ };
326
+ });
327
+
328
+ angular.module('ui.bootstrap.bindHtml', [])
329
+
330
+ .directive('bindHtmlUnsafe', function () {
331
+ return function (scope, element, attr) {
332
+ element.addClass('ng-binding').data('$binding', attr.bindHtmlUnsafe);
333
+ scope.$watch(attr.bindHtmlUnsafe, function bindHtmlUnsafeWatchAction(value) {
334
+ element.html(value || '');
335
+ });
336
+ };
337
+ });
338
+ angular.module('ui.bootstrap.buttons', [])
339
+
340
+ .constant('buttonConfig', {
341
+ activeClass: 'active',
342
+ toggleEvent: 'click'
343
+ })
344
+
345
+ .controller('ButtonsController', ['buttonConfig', function(buttonConfig) {
346
+ this.activeClass = buttonConfig.activeClass || 'active';
347
+ this.toggleEvent = buttonConfig.toggleEvent || 'click';
348
+ }])
349
+
350
+ .directive('btnRadio', function () {
351
+ return {
352
+ require: ['btnRadio', 'ngModel'],
353
+ controller: 'ButtonsController',
354
+ link: function (scope, element, attrs, ctrls) {
355
+ var buttonsCtrl = ctrls[0], ngModelCtrl = ctrls[1];
356
+
357
+ //model -> UI
358
+ ngModelCtrl.$render = function () {
359
+ element.toggleClass(buttonsCtrl.activeClass, angular.equals(ngModelCtrl.$modelValue, scope.$eval(attrs.btnRadio)));
360
+ };
361
+
362
+ //ui->model
363
+ element.bind(buttonsCtrl.toggleEvent, function () {
364
+ if (!element.hasClass(buttonsCtrl.activeClass)) {
365
+ scope.$apply(function () {
366
+ ngModelCtrl.$setViewValue(scope.$eval(attrs.btnRadio));
367
+ ngModelCtrl.$render();
368
+ });
369
+ }
370
+ });
371
+ }
372
+ };
373
+ })
374
+
375
+ .directive('btnCheckbox', function () {
376
+ return {
377
+ require: ['btnCheckbox', 'ngModel'],
378
+ controller: 'ButtonsController',
379
+ link: function (scope, element, attrs, ctrls) {
380
+ var buttonsCtrl = ctrls[0], ngModelCtrl = ctrls[1];
381
+
382
+ function getTrueValue() {
383
+ return getCheckboxValue(attrs.btnCheckboxTrue, true);
384
+ }
385
+
386
+ function getFalseValue() {
387
+ return getCheckboxValue(attrs.btnCheckboxFalse, false);
388
+ }
389
+
390
+ function getCheckboxValue(attributeValue, defaultValue) {
391
+ var val = scope.$eval(attributeValue);
392
+ return angular.isDefined(val) ? val : defaultValue;
393
+ }
394
+
395
+ //model -> UI
396
+ ngModelCtrl.$render = function () {
397
+ element.toggleClass(buttonsCtrl.activeClass, angular.equals(ngModelCtrl.$modelValue, getTrueValue()));
398
+ };
399
+
400
+ //ui->model
401
+ element.bind(buttonsCtrl.toggleEvent, function () {
402
+ scope.$apply(function () {
403
+ ngModelCtrl.$setViewValue(element.hasClass(buttonsCtrl.activeClass) ? getFalseValue() : getTrueValue());
404
+ ngModelCtrl.$render();
405
+ });
406
+ });
407
+ }
408
+ };
409
+ });
410
+
411
+ /**
412
+ * @ngdoc overview
413
+ * @name ui.bootstrap.carousel
414
+ *
415
+ * @description
416
+ * AngularJS version of an image carousel.
417
+ *
418
+ */
419
+ angular.module('ui.bootstrap.carousel', ['ui.bootstrap.transition'])
420
+ .controller('CarouselController', ['$scope', '$timeout', '$transition', '$q', function ($scope, $timeout, $transition, $q) {
421
+ var self = this,
422
+ slides = self.slides = [],
423
+ currentIndex = -1,
424
+ currentTimeout, isPlaying;
425
+ self.currentSlide = null;
426
+
427
+ var destroyed = false;
428
+ /* direction: "prev" or "next" */
429
+ self.select = function(nextSlide, direction) {
430
+ var nextIndex = slides.indexOf(nextSlide);
431
+ //Decide direction if it's not given
432
+ if (direction === undefined) {
433
+ direction = nextIndex > currentIndex ? "next" : "prev";
434
+ }
435
+ if (nextSlide && nextSlide !== self.currentSlide) {
436
+ if ($scope.$currentTransition) {
437
+ $scope.$currentTransition.cancel();
438
+ //Timeout so ng-class in template has time to fix classes for finished slide
439
+ $timeout(goNext);
440
+ } else {
441
+ goNext();
442
+ }
443
+ }
444
+ function goNext() {
445
+ // Scope has been destroyed, stop here.
446
+ if (destroyed) { return; }
447
+ //If we have a slide to transition from and we have a transition type and we're allowed, go
448
+ if (self.currentSlide && angular.isString(direction) && !$scope.noTransition && nextSlide.$element) {
449
+ //We shouldn't do class manip in here, but it's the same weird thing bootstrap does. need to fix sometime
450
+ nextSlide.$element.addClass(direction);
451
+ var reflow = nextSlide.$element[0].offsetWidth; //force reflow
452
+
453
+ //Set all other slides to stop doing their stuff for the new transition
454
+ angular.forEach(slides, function(slide) {
455
+ angular.extend(slide, {direction: '', entering: false, leaving: false, active: false});
456
+ });
457
+ angular.extend(nextSlide, {direction: direction, active: true, entering: true});
458
+ angular.extend(self.currentSlide||{}, {direction: direction, leaving: true});
459
+
460
+ $scope.$currentTransition = $transition(nextSlide.$element, {});
461
+ //We have to create new pointers inside a closure since next & current will change
462
+ (function(next,current) {
463
+ $scope.$currentTransition.then(
464
+ function(){ transitionDone(next, current); },
465
+ function(){ transitionDone(next, current); }
466
+ );
467
+ }(nextSlide, self.currentSlide));
468
+ } else {
469
+ transitionDone(nextSlide, self.currentSlide);
470
+ }
471
+ self.currentSlide = nextSlide;
472
+ currentIndex = nextIndex;
473
+ //every time you change slides, reset the timer
474
+ restartTimer();
475
+ }
476
+ function transitionDone(next, current) {
477
+ angular.extend(next, {direction: '', active: true, leaving: false, entering: false});
478
+ angular.extend(current||{}, {direction: '', active: false, leaving: false, entering: false});
479
+ $scope.$currentTransition = null;
480
+ }
481
+ };
482
+ $scope.$on('$destroy', function () {
483
+ destroyed = true;
484
+ });
485
+
486
+ /* Allow outside people to call indexOf on slides array */
487
+ self.indexOfSlide = function(slide) {
488
+ return slides.indexOf(slide);
489
+ };
490
+
491
+ $scope.next = function() {
492
+ var newIndex = (currentIndex + 1) % slides.length;
493
+
494
+ //Prevent this user-triggered transition from occurring if there is already one in progress
495
+ if (!$scope.$currentTransition) {
496
+ return self.select(slides[newIndex], 'next');
497
+ }
498
+ };
499
+
500
+ $scope.prev = function() {
501
+ var newIndex = currentIndex - 1 < 0 ? slides.length - 1 : currentIndex - 1;
502
+
503
+ //Prevent this user-triggered transition from occurring if there is already one in progress
504
+ if (!$scope.$currentTransition) {
505
+ return self.select(slides[newIndex], 'prev');
506
+ }
507
+ };
508
+
509
+ $scope.select = function(slide) {
510
+ self.select(slide);
511
+ };
512
+
513
+ $scope.isActive = function(slide) {
514
+ return self.currentSlide === slide;
515
+ };
516
+
517
+ $scope.slides = function() {
518
+ return slides;
519
+ };
520
+
521
+ $scope.$watch('interval', restartTimer);
522
+ $scope.$on('$destroy', resetTimer);
523
+
524
+ function restartTimer() {
525
+ resetTimer();
526
+ var interval = +$scope.interval;
527
+ if (!isNaN(interval) && interval>=0) {
528
+ currentTimeout = $timeout(timerFn, interval);
529
+ }
530
+ }
531
+
532
+ function resetTimer() {
533
+ if (currentTimeout) {
534
+ $timeout.cancel(currentTimeout);
535
+ currentTimeout = null;
536
+ }
537
+ }
538
+
539
+ function timerFn() {
540
+ if (isPlaying) {
541
+ $scope.next();
542
+ restartTimer();
543
+ } else {
544
+ $scope.pause();
545
+ }
546
+ }
547
+
548
+ $scope.play = function() {
549
+ if (!isPlaying) {
550
+ isPlaying = true;
551
+ restartTimer();
552
+ }
553
+ };
554
+ $scope.pause = function() {
555
+ if (!$scope.noPause) {
556
+ isPlaying = false;
557
+ resetTimer();
558
+ }
559
+ };
560
+
561
+ self.addSlide = function(slide, element) {
562
+ slide.$element = element;
563
+ slides.push(slide);
564
+ //if this is the first slide or the slide is set to active, select it
565
+ if(slides.length === 1 || slide.active) {
566
+ self.select(slides[slides.length-1]);
567
+ if (slides.length == 1) {
568
+ $scope.play();
569
+ }
570
+ } else {
571
+ slide.active = false;
572
+ }
573
+ };
574
+
575
+ self.removeSlide = function(slide) {
576
+ //get the index of the slide inside the carousel
577
+ var index = slides.indexOf(slide);
578
+ slides.splice(index, 1);
579
+ if (slides.length > 0 && slide.active) {
580
+ if (index >= slides.length) {
581
+ self.select(slides[index-1]);
582
+ } else {
583
+ self.select(slides[index]);
584
+ }
585
+ } else if (currentIndex > index) {
586
+ currentIndex--;
587
+ }
588
+ };
589
+
590
+ }])
591
+
592
+ /**
593
+ * @ngdoc directive
594
+ * @name ui.bootstrap.carousel.directive:carousel
595
+ * @restrict EA
596
+ *
597
+ * @description
598
+ * Carousel is the outer container for a set of image 'slides' to showcase.
599
+ *
600
+ * @param {number=} interval The time, in milliseconds, that it will take the carousel to go to the next slide.
601
+ * @param {boolean=} noTransition Whether to disable transitions on the carousel.
602
+ * @param {boolean=} noPause Whether to disable pausing on the carousel (by default, the carousel interval pauses on hover).
603
+ *
604
+ * @example
605
+ <example module="ui.bootstrap">
606
+ <file name="index.html">
607
+ <carousel>
608
+ <slide>
609
+ <img src="http://placekitten.com/150/150" style="margin:auto;">
610
+ <div class="carousel-caption">
611
+ <p>Beautiful!</p>
612
+ </div>
613
+ </slide>
614
+ <slide>
615
+ <img src="http://placekitten.com/100/150" style="margin:auto;">
616
+ <div class="carousel-caption">
617
+ <p>D'aww!</p>
618
+ </div>
619
+ </slide>
620
+ </carousel>
621
+ </file>
622
+ <file name="demo.css">
623
+ .carousel-indicators {
624
+ top: auto;
625
+ bottom: 15px;
626
+ }
627
+ </file>
628
+ </example>
629
+ */
630
+ .directive('carousel', [function() {
631
+ return {
632
+ restrict: 'EA',
633
+ transclude: true,
634
+ replace: true,
635
+ controller: 'CarouselController',
636
+ require: 'carousel',
637
+ templateUrl: 'template/carousel/carousel.html',
638
+ scope: {
639
+ interval: '=',
640
+ noTransition: '=',
641
+ noPause: '='
642
+ }
643
+ };
644
+ }])
645
+
646
+ /**
647
+ * @ngdoc directive
648
+ * @name ui.bootstrap.carousel.directive:slide
649
+ * @restrict EA
650
+ *
651
+ * @description
652
+ * Creates a slide inside a {@link ui.bootstrap.carousel.directive:carousel carousel}. Must be placed as a child of a carousel element.
653
+ *
654
+ * @param {boolean=} active Model binding, whether or not this slide is currently active.
655
+ *
656
+ * @example
657
+ <example module="ui.bootstrap">
658
+ <file name="index.html">
659
+ <div ng-controller="CarouselDemoCtrl">
660
+ <carousel>
661
+ <slide ng-repeat="slide in slides" active="slide.active">
662
+ <img ng-src="{{slide.image}}" style="margin:auto;">
663
+ <div class="carousel-caption">
664
+ <h4>Slide {{$index}}</h4>
665
+ <p>{{slide.text}}</p>
666
+ </div>
667
+ </slide>
668
+ </carousel>
669
+ <div class="row-fluid">
670
+ <div class="span6">
671
+ <ul>
672
+ <li ng-repeat="slide in slides">
673
+ <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>
674
+ {{$index}}: {{slide.text}}
675
+ </li>
676
+ </ul>
677
+ <a class="btn" ng-click="addSlide()">Add Slide</a>
678
+ </div>
679
+ <div class="span6">
680
+ Interval, in milliseconds: <input type="number" ng-model="myInterval">
681
+ <br />Enter a negative number to stop the interval.
682
+ </div>
683
+ </div>
684
+ </div>
685
+ </file>
686
+ <file name="script.js">
687
+ function CarouselDemoCtrl($scope) {
688
+ $scope.myInterval = 5000;
689
+ var slides = $scope.slides = [];
690
+ $scope.addSlide = function() {
691
+ var newWidth = 200 + ((slides.length + (25 * slides.length)) % 150);
692
+ slides.push({
693
+ image: 'http://placekitten.com/' + newWidth + '/200',
694
+ text: ['More','Extra','Lots of','Surplus'][slides.length % 4] + ' '
695
+ ['Cats', 'Kittys', 'Felines', 'Cutes'][slides.length % 4]
696
+ });
697
+ };
698
+ for (var i=0; i<4; i++) $scope.addSlide();
699
+ }
700
+ </file>
701
+ <file name="demo.css">
702
+ .carousel-indicators {
703
+ top: auto;
704
+ bottom: 15px;
705
+ }
706
+ </file>
707
+ </example>
708
+ */
709
+
710
+ .directive('slide', ['$parse', function($parse) {
711
+ return {
712
+ require: '^carousel',
713
+ restrict: 'EA',
714
+ transclude: true,
715
+ replace: true,
716
+ templateUrl: 'template/carousel/slide.html',
717
+ scope: {
718
+ },
719
+ link: function (scope, element, attrs, carouselCtrl) {
720
+ //Set up optional 'active' = binding
721
+ if (attrs.active) {
722
+ var getActive = $parse(attrs.active);
723
+ var setActive = getActive.assign;
724
+ var lastValue = scope.active = getActive(scope.$parent);
725
+ scope.$watch(function parentActiveWatch() {
726
+ var parentActive = getActive(scope.$parent);
727
+
728
+ if (parentActive !== scope.active) {
729
+ // we are out of sync and need to copy
730
+ if (parentActive !== lastValue) {
731
+ // parent changed and it has precedence
732
+ lastValue = scope.active = parentActive;
733
+ } else {
734
+ // if the parent can be assigned then do so
735
+ setActive(scope.$parent, parentActive = lastValue = scope.active);
736
+ }
737
+ }
738
+ return parentActive;
739
+ });
740
+ }
741
+
742
+ carouselCtrl.addSlide(scope, element);
743
+ //when the scope is destroyed then remove the slide from the current slides array
744
+ scope.$on('$destroy', function() {
745
+ carouselCtrl.removeSlide(scope);
746
+ });
747
+
748
+ scope.$watch('active', function(active) {
749
+ if (active) {
750
+ carouselCtrl.select(scope);
751
+ }
752
+ });
753
+ }
754
+ };
755
+ }]);
756
+
757
+ angular.module('ui.bootstrap.position', [])
758
+
759
+ /**
760
+ * A set of utility methods that can be use to retrieve position of DOM elements.
761
+ * It is meant to be used where we need to absolute-position DOM elements in
762
+ * relation to other, existing elements (this is the case for tooltips, popovers,
763
+ * typeahead suggestions etc.).
764
+ */
765
+ .factory('$position', ['$document', '$window', function ($document, $window) {
766
+
767
+ function getStyle(el, cssprop) {
768
+ if (el.currentStyle) { //IE
769
+ return el.currentStyle[cssprop];
770
+ } else if ($window.getComputedStyle) {
771
+ return $window.getComputedStyle(el)[cssprop];
772
+ }
773
+ // finally try and get inline style
774
+ return el.style[cssprop];
775
+ }
776
+
777
+ /**
778
+ * Checks if a given element is statically positioned
779
+ * @param element - raw DOM element
780
+ */
781
+ function isStaticPositioned(element) {
782
+ return (getStyle(element, "position") || 'static' ) === 'static';
783
+ }
784
+
785
+ /**
786
+ * returns the closest, non-statically positioned parentOffset of a given element
787
+ * @param element
788
+ */
789
+ var parentOffsetEl = function (element) {
790
+ var docDomEl = $document[0];
791
+ var offsetParent = element.offsetParent || docDomEl;
792
+ while (offsetParent && offsetParent !== docDomEl && isStaticPositioned(offsetParent) ) {
793
+ offsetParent = offsetParent.offsetParent;
794
+ }
795
+ return offsetParent || docDomEl;
796
+ };
797
+
798
+ return {
799
+ /**
800
+ * Provides read-only equivalent of jQuery's position function:
801
+ * http://api.jquery.com/position/
802
+ */
803
+ position: function (element) {
804
+ var elBCR = this.offset(element);
805
+ var offsetParentBCR = { top: 0, left: 0 };
806
+ var offsetParentEl = parentOffsetEl(element[0]);
807
+ if (offsetParentEl != $document[0]) {
808
+ offsetParentBCR = this.offset(angular.element(offsetParentEl));
809
+ offsetParentBCR.top += offsetParentEl.clientTop - offsetParentEl.scrollTop;
810
+ offsetParentBCR.left += offsetParentEl.clientLeft - offsetParentEl.scrollLeft;
811
+ }
812
+
813
+ var boundingClientRect = element[0].getBoundingClientRect();
814
+ return {
815
+ width: boundingClientRect.width || element.prop('offsetWidth'),
816
+ height: boundingClientRect.height || element.prop('offsetHeight'),
817
+ top: elBCR.top - offsetParentBCR.top,
818
+ left: elBCR.left - offsetParentBCR.left
819
+ };
820
+ },
821
+
822
+ /**
823
+ * Provides read-only equivalent of jQuery's offset function:
824
+ * http://api.jquery.com/offset/
825
+ */
826
+ offset: function (element) {
827
+ var boundingClientRect = element[0].getBoundingClientRect();
828
+ return {
829
+ width: boundingClientRect.width || element.prop('offsetWidth'),
830
+ height: boundingClientRect.height || element.prop('offsetHeight'),
831
+ top: boundingClientRect.top + ($window.pageYOffset || $document[0].body.scrollTop || $document[0].documentElement.scrollTop),
832
+ left: boundingClientRect.left + ($window.pageXOffset || $document[0].body.scrollLeft || $document[0].documentElement.scrollLeft)
833
+ };
834
+ }
835
+ };
836
+ }]);
837
+
838
+ angular.module('ui.bootstrap.datepicker', ['ui.bootstrap.position'])
839
+
840
+ .constant('datepickerConfig', {
841
+ dayFormat: 'dd',
842
+ monthFormat: 'MMMM',
843
+ yearFormat: 'yyyy',
844
+ dayHeaderFormat: 'EEE',
845
+ dayTitleFormat: 'MMMM yyyy',
846
+ monthTitleFormat: 'yyyy',
847
+ showWeeks: true,
848
+ startingDay: 0,
849
+ yearRange: 20,
850
+ minDate: null,
851
+ maxDate: null
852
+ })
853
+
854
+ .controller('DatepickerController', ['$scope', '$attrs', 'dateFilter', 'datepickerConfig', function($scope, $attrs, dateFilter, dtConfig) {
855
+ var format = {
856
+ day: getValue($attrs.dayFormat, dtConfig.dayFormat),
857
+ month: getValue($attrs.monthFormat, dtConfig.monthFormat),
858
+ year: getValue($attrs.yearFormat, dtConfig.yearFormat),
859
+ dayHeader: getValue($attrs.dayHeaderFormat, dtConfig.dayHeaderFormat),
860
+ dayTitle: getValue($attrs.dayTitleFormat, dtConfig.dayTitleFormat),
861
+ monthTitle: getValue($attrs.monthTitleFormat, dtConfig.monthTitleFormat)
862
+ },
863
+ startingDay = getValue($attrs.startingDay, dtConfig.startingDay),
864
+ yearRange = getValue($attrs.yearRange, dtConfig.yearRange);
865
+
866
+ this.minDate = dtConfig.minDate ? new Date(dtConfig.minDate) : null;
867
+ this.maxDate = dtConfig.maxDate ? new Date(dtConfig.maxDate) : null;
868
+
869
+ function getValue(value, defaultValue) {
870
+ return angular.isDefined(value) ? $scope.$parent.$eval(value) : defaultValue;
871
+ }
872
+
873
+ function getDaysInMonth( year, month ) {
874
+ return new Date(year, month, 0).getDate();
875
+ }
876
+
877
+ function getDates(startDate, n) {
878
+ var dates = new Array(n);
879
+ var current = startDate, i = 0;
880
+ while (i < n) {
881
+ dates[i++] = new Date(current);
882
+ current.setDate( current.getDate() + 1 );
883
+ }
884
+ return dates;
885
+ }
886
+
887
+ function makeDate(date, format, isSelected, isSecondary) {
888
+ return { date: date, label: dateFilter(date, format), selected: !!isSelected, secondary: !!isSecondary };
889
+ }
890
+
891
+ this.modes = [
892
+ {
893
+ name: 'day',
894
+ getVisibleDates: function(date, selected) {
895
+ var year = date.getFullYear(), month = date.getMonth(), firstDayOfMonth = new Date(year, month, 1);
896
+ var difference = startingDay - firstDayOfMonth.getDay(),
897
+ numDisplayedFromPreviousMonth = (difference > 0) ? 7 - difference : - difference,
898
+ firstDate = new Date(firstDayOfMonth), numDates = 0;
899
+
900
+ if ( numDisplayedFromPreviousMonth > 0 ) {
901
+ firstDate.setDate( - numDisplayedFromPreviousMonth + 1 );
902
+ numDates += numDisplayedFromPreviousMonth; // Previous
903
+ }
904
+ numDates += getDaysInMonth(year, month + 1); // Current
905
+ numDates += (7 - numDates % 7) % 7; // Next
906
+
907
+ var days = getDates(firstDate, numDates), labels = new Array(7);
908
+ for (var i = 0; i < numDates; i ++) {
909
+ var dt = new Date(days[i]);
910
+ days[i] = makeDate(dt, format.day, (selected && selected.getDate() === dt.getDate() && selected.getMonth() === dt.getMonth() && selected.getFullYear() === dt.getFullYear()), dt.getMonth() !== month);
911
+ }
912
+ for (var j = 0; j < 7; j++) {
913
+ labels[j] = dateFilter(days[j].date, format.dayHeader);
914
+ }
915
+ return { objects: days, title: dateFilter(date, format.dayTitle), labels: labels };
916
+ },
917
+ compare: function(date1, date2) {
918
+ return (new Date( date1.getFullYear(), date1.getMonth(), date1.getDate() ) - new Date( date2.getFullYear(), date2.getMonth(), date2.getDate() ) );
919
+ },
920
+ split: 7,
921
+ step: { months: 1 }
922
+ },
923
+ {
924
+ name: 'month',
925
+ getVisibleDates: function(date, selected) {
926
+ var months = new Array(12), year = date.getFullYear();
927
+ for ( var i = 0; i < 12; i++ ) {
928
+ var dt = new Date(year, i, 1);
929
+ months[i] = makeDate(dt, format.month, (selected && selected.getMonth() === i && selected.getFullYear() === year));
930
+ }
931
+ return { objects: months, title: dateFilter(date, format.monthTitle) };
932
+ },
933
+ compare: function(date1, date2) {
934
+ return new Date( date1.getFullYear(), date1.getMonth() ) - new Date( date2.getFullYear(), date2.getMonth() );
935
+ },
936
+ split: 3,
937
+ step: { years: 1 }
938
+ },
939
+ {
940
+ name: 'year',
941
+ getVisibleDates: function(date, selected) {
942
+ var years = new Array(yearRange), year = date.getFullYear(), startYear = parseInt((year - 1) / yearRange, 10) * yearRange + 1;
943
+ for ( var i = 0; i < yearRange; i++ ) {
944
+ var dt = new Date(startYear + i, 0, 1);
945
+ years[i] = makeDate(dt, format.year, (selected && selected.getFullYear() === dt.getFullYear()));
946
+ }
947
+ return { objects: years, title: [years[0].label, years[yearRange - 1].label].join(' - ') };
948
+ },
949
+ compare: function(date1, date2) {
950
+ return date1.getFullYear() - date2.getFullYear();
951
+ },
952
+ split: 5,
953
+ step: { years: yearRange }
954
+ }
955
+ ];
956
+
957
+ this.isDisabled = function(date, mode) {
958
+ var currentMode = this.modes[mode || 0];
959
+ return ((this.minDate && currentMode.compare(date, this.minDate) < 0) || (this.maxDate && currentMode.compare(date, this.maxDate) > 0) || ($scope.dateDisabled && $scope.dateDisabled({date: date, mode: currentMode.name})));
960
+ };
961
+ }])
962
+
963
+ .directive( 'datepicker', ['dateFilter', '$parse', 'datepickerConfig', '$log', function (dateFilter, $parse, datepickerConfig, $log) {
964
+ return {
965
+ restrict: 'EA',
966
+ replace: true,
967
+ templateUrl: 'template/datepicker/datepicker.html',
968
+ scope: {
969
+ dateDisabled: '&'
970
+ },
971
+ require: ['datepicker', '?^ngModel'],
972
+ controller: 'DatepickerController',
973
+ link: function(scope, element, attrs, ctrls) {
974
+ var datepickerCtrl = ctrls[0], ngModel = ctrls[1];
975
+
976
+ if (!ngModel) {
977
+ return; // do nothing if no ng-model
978
+ }
979
+
980
+ // Configuration parameters
981
+ var mode = 0, selected = new Date(), showWeeks = datepickerConfig.showWeeks;
982
+
983
+ if (attrs.showWeeks) {
984
+ scope.$parent.$watch($parse(attrs.showWeeks), function(value) {
985
+ showWeeks = !! value;
986
+ updateShowWeekNumbers();
987
+ });
988
+ } else {
989
+ updateShowWeekNumbers();
990
+ }
991
+
992
+ if (attrs.min) {
993
+ scope.$parent.$watch($parse(attrs.min), function(value) {
994
+ datepickerCtrl.minDate = value ? new Date(value) : null;
995
+ refill();
996
+ });
997
+ }
998
+ if (attrs.max) {
999
+ scope.$parent.$watch($parse(attrs.max), function(value) {
1000
+ datepickerCtrl.maxDate = value ? new Date(value) : null;
1001
+ refill();
1002
+ });
1003
+ }
1004
+
1005
+ function updateShowWeekNumbers() {
1006
+ scope.showWeekNumbers = mode === 0 && showWeeks;
1007
+ }
1008
+
1009
+ // Split array into smaller arrays
1010
+ function split(arr, size) {
1011
+ var arrays = [];
1012
+ while (arr.length > 0) {
1013
+ arrays.push(arr.splice(0, size));
1014
+ }
1015
+ return arrays;
1016
+ }
1017
+
1018
+ function refill( updateSelected ) {
1019
+ var date = null, valid = true;
1020
+
1021
+ if ( ngModel.$modelValue ) {
1022
+ date = new Date( ngModel.$modelValue );
1023
+
1024
+ if ( isNaN(date) ) {
1025
+ valid = false;
1026
+ $log.error('Datepicker directive: "ng-model" value must be a Date object, a number of milliseconds since 01.01.1970 or a string representing an RFC2822 or ISO 8601 date.');
1027
+ } else if ( updateSelected ) {
1028
+ selected = date;
1029
+ }
1030
+ }
1031
+ ngModel.$setValidity('date', valid);
1032
+
1033
+ var currentMode = datepickerCtrl.modes[mode], data = currentMode.getVisibleDates(selected, date);
1034
+ angular.forEach(data.objects, function(obj) {
1035
+ obj.disabled = datepickerCtrl.isDisabled(obj.date, mode);
1036
+ });
1037
+
1038
+ ngModel.$setValidity('date-disabled', (!date || !datepickerCtrl.isDisabled(date)));
1039
+
1040
+ scope.rows = split(data.objects, currentMode.split);
1041
+ scope.labels = data.labels || [];
1042
+ scope.title = data.title;
1043
+ }
1044
+
1045
+ function setMode(value) {
1046
+ mode = value;
1047
+ updateShowWeekNumbers();
1048
+ refill();
1049
+ }
1050
+
1051
+ ngModel.$render = function() {
1052
+ refill( true );
1053
+ };
1054
+
1055
+ scope.select = function( date ) {
1056
+ if ( mode === 0 ) {
1057
+ var dt = ngModel.$modelValue ? new Date( ngModel.$modelValue ) : new Date(0, 0, 0, 0, 0, 0, 0);
1058
+ dt.setFullYear( date.getFullYear(), date.getMonth(), date.getDate() );
1059
+ ngModel.$setViewValue( dt );
1060
+ refill( true );
1061
+ } else {
1062
+ selected = date;
1063
+ setMode( mode - 1 );
1064
+ }
1065
+ };
1066
+ scope.move = function(direction) {
1067
+ var step = datepickerCtrl.modes[mode].step;
1068
+ selected.setMonth( selected.getMonth() + direction * (step.months || 0) );
1069
+ selected.setFullYear( selected.getFullYear() + direction * (step.years || 0) );
1070
+ refill();
1071
+ };
1072
+ scope.toggleMode = function() {
1073
+ setMode( (mode + 1) % datepickerCtrl.modes.length );
1074
+ };
1075
+ scope.getWeekNumber = function(row) {
1076
+ return ( mode === 0 && scope.showWeekNumbers && row.length === 7 ) ? getISO8601WeekNumber(row[0].date) : null;
1077
+ };
1078
+
1079
+ function getISO8601WeekNumber(date) {
1080
+ var checkDate = new Date(date);
1081
+ checkDate.setDate(checkDate.getDate() + 4 - (checkDate.getDay() || 7)); // Thursday
1082
+ var time = checkDate.getTime();
1083
+ checkDate.setMonth(0); // Compare with Jan 1
1084
+ checkDate.setDate(1);
1085
+ return Math.floor(Math.round((time - checkDate) / 86400000) / 7) + 1;
1086
+ }
1087
+ }
1088
+ };
1089
+ }])
1090
+
1091
+ .constant('datepickerPopupConfig', {
1092
+ dateFormat: 'yyyy-MM-dd',
1093
+ currentText: 'Today',
1094
+ toggleWeeksText: 'Weeks',
1095
+ clearText: 'Clear',
1096
+ closeText: 'Done',
1097
+ closeOnDateSelection: true,
1098
+ appendToBody: false,
1099
+ showButtonBar: true
1100
+ })
1101
+
1102
+ .directive('datepickerPopup', ['$compile', '$parse', '$document', '$position', 'dateFilter', 'datepickerPopupConfig', 'datepickerConfig',
1103
+ function ($compile, $parse, $document, $position, dateFilter, datepickerPopupConfig, datepickerConfig) {
1104
+ return {
1105
+ restrict: 'EA',
1106
+ require: 'ngModel',
1107
+ link: function(originalScope, element, attrs, ngModel) {
1108
+ var scope = originalScope.$new(), // create a child scope so we are not polluting original one
1109
+ dateFormat,
1110
+ closeOnDateSelection = angular.isDefined(attrs.closeOnDateSelection) ? originalScope.$eval(attrs.closeOnDateSelection) : datepickerPopupConfig.closeOnDateSelection,
1111
+ appendToBody = angular.isDefined(attrs.datepickerAppendToBody) ? originalScope.$eval(attrs.datepickerAppendToBody) : datepickerPopupConfig.appendToBody;
1112
+
1113
+ attrs.$observe('datepickerPopup', function(value) {
1114
+ dateFormat = value || datepickerPopupConfig.dateFormat;
1115
+ ngModel.$render();
1116
+ });
1117
+
1118
+ scope.showButtonBar = angular.isDefined(attrs.showButtonBar) ? originalScope.$eval(attrs.showButtonBar) : datepickerPopupConfig.showButtonBar;
1119
+
1120
+ originalScope.$on('$destroy', function() {
1121
+ $popup.remove();
1122
+ scope.$destroy();
1123
+ });
1124
+
1125
+ attrs.$observe('currentText', function(text) {
1126
+ scope.currentText = angular.isDefined(text) ? text : datepickerPopupConfig.currentText;
1127
+ });
1128
+ attrs.$observe('toggleWeeksText', function(text) {
1129
+ scope.toggleWeeksText = angular.isDefined(text) ? text : datepickerPopupConfig.toggleWeeksText;
1130
+ });
1131
+ attrs.$observe('clearText', function(text) {
1132
+ scope.clearText = angular.isDefined(text) ? text : datepickerPopupConfig.clearText;
1133
+ });
1134
+ attrs.$observe('closeText', function(text) {
1135
+ scope.closeText = angular.isDefined(text) ? text : datepickerPopupConfig.closeText;
1136
+ });
1137
+
1138
+ var getIsOpen, setIsOpen;
1139
+ if ( attrs.isOpen ) {
1140
+ getIsOpen = $parse(attrs.isOpen);
1141
+ setIsOpen = getIsOpen.assign;
1142
+
1143
+ originalScope.$watch(getIsOpen, function updateOpen(value) {
1144
+ scope.isOpen = !! value;
1145
+ });
1146
+ }
1147
+ scope.isOpen = getIsOpen ? getIsOpen(originalScope) : false; // Initial state
1148
+
1149
+ function setOpen( value ) {
1150
+ if (setIsOpen) {
1151
+ setIsOpen(originalScope, !!value);
1152
+ } else {
1153
+ scope.isOpen = !!value;
1154
+ }
1155
+ }
1156
+
1157
+ var documentClickBind = function(event) {
1158
+ if (scope.isOpen && event.target !== element[0]) {
1159
+ scope.$apply(function() {
1160
+ setOpen(false);
1161
+ });
1162
+ }
1163
+ };
1164
+
1165
+ var elementFocusBind = function() {
1166
+ scope.$apply(function() {
1167
+ setOpen( true );
1168
+ });
1169
+ };
1170
+
1171
+ // popup element used to display calendar
1172
+ var popupEl = angular.element('<div datepicker-popup-wrap><div datepicker></div></div>');
1173
+ popupEl.attr({
1174
+ 'ng-model': 'date',
1175
+ 'ng-change': 'dateSelection()'
1176
+ });
1177
+ var datepickerEl = angular.element(popupEl.children()[0]),
1178
+ datepickerOptions = {};
1179
+ if (attrs.datepickerOptions) {
1180
+ datepickerOptions = originalScope.$eval(attrs.datepickerOptions);
1181
+ datepickerEl.attr(angular.extend({}, datepickerOptions));
1182
+ }
1183
+
1184
+ // TODO: reverse from dateFilter string to Date object
1185
+ function parseDate(viewValue) {
1186
+ if (!viewValue) {
1187
+ ngModel.$setValidity('date', true);
1188
+ return null;
1189
+ } else if (angular.isDate(viewValue)) {
1190
+ ngModel.$setValidity('date', true);
1191
+ return viewValue;
1192
+ } else if (angular.isString(viewValue)) {
1193
+ var date = new Date(viewValue);
1194
+ if (isNaN(date)) {
1195
+ ngModel.$setValidity('date', false);
1196
+ return undefined;
1197
+ } else {
1198
+ ngModel.$setValidity('date', true);
1199
+ return date;
1200
+ }
1201
+ } else {
1202
+ ngModel.$setValidity('date', false);
1203
+ return undefined;
1204
+ }
1205
+ }
1206
+ ngModel.$parsers.unshift(parseDate);
1207
+
1208
+ // Inner change
1209
+ scope.dateSelection = function(dt) {
1210
+ if (angular.isDefined(dt)) {
1211
+ scope.date = dt;
1212
+ }
1213
+ ngModel.$setViewValue(scope.date);
1214
+ ngModel.$render();
1215
+
1216
+ if (closeOnDateSelection) {
1217
+ setOpen( false );
1218
+ }
1219
+ };
1220
+
1221
+ element.bind('input change keyup', function() {
1222
+ scope.$apply(function() {
1223
+ scope.date = ngModel.$modelValue;
1224
+ });
1225
+ });
1226
+
1227
+ // Outter change
1228
+ ngModel.$render = function() {
1229
+ var date = ngModel.$viewValue ? dateFilter(ngModel.$viewValue, dateFormat) : '';
1230
+ element.val(date);
1231
+ scope.date = ngModel.$modelValue;
1232
+ };
1233
+
1234
+ function addWatchableAttribute(attribute, scopeProperty, datepickerAttribute) {
1235
+ if (attribute) {
1236
+ originalScope.$watch($parse(attribute), function(value){
1237
+ scope[scopeProperty] = value;
1238
+ });
1239
+ datepickerEl.attr(datepickerAttribute || scopeProperty, scopeProperty);
1240
+ }
1241
+ }
1242
+ addWatchableAttribute(attrs.min, 'min');
1243
+ addWatchableAttribute(attrs.max, 'max');
1244
+ if (attrs.showWeeks) {
1245
+ addWatchableAttribute(attrs.showWeeks, 'showWeeks', 'show-weeks');
1246
+ } else {
1247
+ scope.showWeeks = 'show-weeks' in datepickerOptions ? datepickerOptions['show-weeks'] : datepickerConfig.showWeeks;
1248
+ datepickerEl.attr('show-weeks', 'showWeeks');
1249
+ }
1250
+ if (attrs.dateDisabled) {
1251
+ datepickerEl.attr('date-disabled', attrs.dateDisabled);
1252
+ }
1253
+
1254
+ function updatePosition() {
1255
+ scope.position = appendToBody ? $position.offset(element) : $position.position(element);
1256
+ scope.position.top = scope.position.top + element.prop('offsetHeight');
1257
+ }
1258
+
1259
+ var documentBindingInitialized = false, elementFocusInitialized = false;
1260
+ scope.$watch('isOpen', function(value) {
1261
+ if (value) {
1262
+ updatePosition();
1263
+ $document.bind('click', documentClickBind);
1264
+ if(elementFocusInitialized) {
1265
+ element.unbind('focus', elementFocusBind);
1266
+ }
1267
+ element[0].focus();
1268
+ documentBindingInitialized = true;
1269
+ } else {
1270
+ if(documentBindingInitialized) {
1271
+ $document.unbind('click', documentClickBind);
1272
+ }
1273
+ element.bind('focus', elementFocusBind);
1274
+ elementFocusInitialized = true;
1275
+ }
1276
+
1277
+ if ( setIsOpen ) {
1278
+ setIsOpen(originalScope, value);
1279
+ }
1280
+ });
1281
+
1282
+ scope.today = function() {
1283
+ scope.dateSelection(new Date());
1284
+ };
1285
+ scope.clear = function() {
1286
+ scope.dateSelection(null);
1287
+ };
1288
+
1289
+ var $popup = $compile(popupEl)(scope);
1290
+ if ( appendToBody ) {
1291
+ $document.find('body').append($popup);
1292
+ } else {
1293
+ element.after($popup);
1294
+ }
1295
+ }
1296
+ };
1297
+ }])
1298
+
1299
+ .directive('datepickerPopupWrap', function() {
1300
+ return {
1301
+ restrict:'EA',
1302
+ replace: true,
1303
+ transclude: true,
1304
+ templateUrl: 'template/datepicker/popup.html',
1305
+ link:function (scope, element, attrs) {
1306
+ element.bind('click', function(event) {
1307
+ event.preventDefault();
1308
+ event.stopPropagation();
1309
+ });
1310
+ }
1311
+ };
1312
+ });
1313
+
1314
+ /*
1315
+ * dropdownToggle - Provides dropdown menu functionality in place of bootstrap js
1316
+ * @restrict class or attribute
1317
+ * @example:
1318
+ <li class="dropdown">
1319
+ <a class="dropdown-toggle">My Dropdown Menu</a>
1320
+ <ul class="dropdown-menu">
1321
+ <li ng-repeat="choice in dropChoices">
1322
+ <a ng-href="{{choice.href}}">{{choice.text}}</a>
1323
+ </li>
1324
+ </ul>
1325
+ </li>
1326
+ */
1327
+
1328
+ angular.module('ui.bootstrap.dropdownToggle', []).directive('dropdownToggle', ['$document', '$location', function ($document, $location) {
1329
+ var openElement = null,
1330
+ closeMenu = angular.noop;
1331
+ return {
1332
+ restrict: 'CA',
1333
+ link: function(scope, element, attrs) {
1334
+ scope.$watch('$location.path', function() { closeMenu(); });
1335
+ element.parent().bind('click', function() { closeMenu(); });
1336
+ element.bind('click', function (event) {
1337
+
1338
+ var elementWasOpen = (element === openElement);
1339
+
1340
+ event.preventDefault();
1341
+ event.stopPropagation();
1342
+
1343
+ if (!!openElement) {
1344
+ closeMenu();
1345
+ }
1346
+
1347
+ if (!elementWasOpen && !element.hasClass('disabled') && !element.prop('disabled')) {
1348
+ element.parent().addClass('open');
1349
+ openElement = element;
1350
+ closeMenu = function (event) {
1351
+ if (event) {
1352
+ event.preventDefault();
1353
+ event.stopPropagation();
1354
+ }
1355
+ $document.unbind('click', closeMenu);
1356
+ element.parent().removeClass('open');
1357
+ closeMenu = angular.noop;
1358
+ openElement = null;
1359
+ };
1360
+ $document.bind('click', closeMenu);
1361
+ }
1362
+ });
1363
+ }
1364
+ };
1365
+ }]);
1366
+
1367
+ angular.module('ui.bootstrap.modal', ['ui.bootstrap.transition'])
1368
+
1369
+ /**
1370
+ * A helper, internal data structure that acts as a map but also allows getting / removing
1371
+ * elements in the LIFO order
1372
+ */
1373
+ .factory('$$stackedMap', function () {
1374
+ return {
1375
+ createNew: function () {
1376
+ var stack = [];
1377
+
1378
+ return {
1379
+ add: function (key, value) {
1380
+ stack.push({
1381
+ key: key,
1382
+ value: value
1383
+ });
1384
+ },
1385
+ get: function (key) {
1386
+ for (var i = 0; i < stack.length; i++) {
1387
+ if (key == stack[i].key) {
1388
+ return stack[i];
1389
+ }
1390
+ }
1391
+ },
1392
+ keys: function() {
1393
+ var keys = [];
1394
+ for (var i = 0; i < stack.length; i++) {
1395
+ keys.push(stack[i].key);
1396
+ }
1397
+ return keys;
1398
+ },
1399
+ top: function () {
1400
+ return stack[stack.length - 1];
1401
+ },
1402
+ remove: function (key) {
1403
+ var idx = -1;
1404
+ for (var i = 0; i < stack.length; i++) {
1405
+ if (key == stack[i].key) {
1406
+ idx = i;
1407
+ break;
1408
+ }
1409
+ }
1410
+ return stack.splice(idx, 1)[0];
1411
+ },
1412
+ removeTop: function () {
1413
+ return stack.splice(stack.length - 1, 1)[0];
1414
+ },
1415
+ length: function () {
1416
+ return stack.length;
1417
+ }
1418
+ };
1419
+ }
1420
+ };
1421
+ })
1422
+
1423
+ /**
1424
+ * A helper directive for the $modal service. It creates a backdrop element.
1425
+ */
1426
+ .directive('modalBackdrop', ['$timeout', function ($timeout) {
1427
+ return {
1428
+ restrict: 'EA',
1429
+ replace: true,
1430
+ templateUrl: 'template/modal/backdrop.html',
1431
+ link: function (scope) {
1432
+
1433
+ scope.animate = false;
1434
+
1435
+ //trigger CSS transitions
1436
+ $timeout(function () {
1437
+ scope.animate = true;
1438
+ });
1439
+ }
1440
+ };
1441
+ }])
1442
+
1443
+ .directive('modalWindow', ['$modalStack', '$timeout', function ($modalStack, $timeout) {
1444
+ return {
1445
+ restrict: 'EA',
1446
+ scope: {
1447
+ index: '@',
1448
+ animate: '='
1449
+ },
1450
+ replace: true,
1451
+ transclude: true,
1452
+ templateUrl: 'template/modal/window.html',
1453
+ link: function (scope, element, attrs) {
1454
+ scope.windowClass = attrs.windowClass || '';
1455
+
1456
+ $timeout(function () {
1457
+ // trigger CSS transitions
1458
+ scope.animate = true;
1459
+ // focus a freshly-opened modal
1460
+ element[0].focus();
1461
+ });
1462
+
1463
+ scope.close = function (evt) {
1464
+ var modal = $modalStack.getTop();
1465
+ if (modal && modal.value.backdrop && modal.value.backdrop != 'static' && (evt.target === evt.currentTarget)) {
1466
+ evt.preventDefault();
1467
+ evt.stopPropagation();
1468
+ $modalStack.dismiss(modal.key, 'backdrop click');
1469
+ }
1470
+ };
1471
+ }
1472
+ };
1473
+ }])
1474
+
1475
+ .factory('$modalStack', ['$transition', '$timeout', '$document', '$compile', '$rootScope', '$$stackedMap',
1476
+ function ($transition, $timeout, $document, $compile, $rootScope, $$stackedMap) {
1477
+
1478
+ var OPENED_MODAL_CLASS = 'modal-open';
1479
+
1480
+ var backdropDomEl, backdropScope;
1481
+ var openedWindows = $$stackedMap.createNew();
1482
+ var $modalStack = {};
1483
+
1484
+ function backdropIndex() {
1485
+ var topBackdropIndex = -1;
1486
+ var opened = openedWindows.keys();
1487
+ for (var i = 0; i < opened.length; i++) {
1488
+ if (openedWindows.get(opened[i]).value.backdrop) {
1489
+ topBackdropIndex = i;
1490
+ }
1491
+ }
1492
+ return topBackdropIndex;
1493
+ }
1494
+
1495
+ $rootScope.$watch(backdropIndex, function(newBackdropIndex){
1496
+ if (backdropScope) {
1497
+ backdropScope.index = newBackdropIndex;
1498
+ }
1499
+ });
1500
+
1501
+ function removeModalWindow(modalInstance) {
1502
+
1503
+ var body = $document.find('body').eq(0);
1504
+ var modalWindow = openedWindows.get(modalInstance).value;
1505
+
1506
+ //clean up the stack
1507
+ openedWindows.remove(modalInstance);
1508
+
1509
+ //remove window DOM element
1510
+ removeAfterAnimate(modalWindow.modalDomEl, modalWindow.modalScope, 300, checkRemoveBackdrop);
1511
+ body.toggleClass(OPENED_MODAL_CLASS, openedWindows.length() > 0);
1512
+ }
1513
+
1514
+ function checkRemoveBackdrop() {
1515
+ //remove backdrop if no longer needed
1516
+ if (backdropDomEl && backdropIndex() == -1) {
1517
+ var backdropScopeRef = backdropScope;
1518
+ removeAfterAnimate(backdropDomEl, backdropScope, 150, function () {
1519
+ backdropScopeRef.$destroy();
1520
+ backdropScopeRef = null;
1521
+ });
1522
+ backdropDomEl = undefined;
1523
+ backdropScope = undefined;
1524
+ }
1525
+ }
1526
+
1527
+ function removeAfterAnimate(domEl, scope, emulateTime, done) {
1528
+ // Closing animation
1529
+ scope.animate = false;
1530
+
1531
+ var transitionEndEventName = $transition.transitionEndEventName;
1532
+ if (transitionEndEventName) {
1533
+ // transition out
1534
+ var timeout = $timeout(afterAnimating, emulateTime);
1535
+
1536
+ domEl.bind(transitionEndEventName, function () {
1537
+ $timeout.cancel(timeout);
1538
+ afterAnimating();
1539
+ scope.$apply();
1540
+ });
1541
+ } else {
1542
+ // Ensure this call is async
1543
+ $timeout(afterAnimating, 0);
1544
+ }
1545
+
1546
+ function afterAnimating() {
1547
+ if (afterAnimating.done) {
1548
+ return;
1549
+ }
1550
+ afterAnimating.done = true;
1551
+
1552
+ domEl.remove();
1553
+ if (done) {
1554
+ done();
1555
+ }
1556
+ }
1557
+ }
1558
+
1559
+ $document.bind('keydown', function (evt) {
1560
+ var modal;
1561
+
1562
+ if (evt.which === 27) {
1563
+ modal = openedWindows.top();
1564
+ if (modal && modal.value.keyboard) {
1565
+ $rootScope.$apply(function () {
1566
+ $modalStack.dismiss(modal.key);
1567
+ });
1568
+ }
1569
+ }
1570
+ });
1571
+
1572
+ $modalStack.open = function (modalInstance, modal) {
1573
+
1574
+ openedWindows.add(modalInstance, {
1575
+ deferred: modal.deferred,
1576
+ modalScope: modal.scope,
1577
+ backdrop: modal.backdrop,
1578
+ keyboard: modal.keyboard
1579
+ });
1580
+
1581
+ var body = $document.find('body').eq(0),
1582
+ currBackdropIndex = backdropIndex();
1583
+
1584
+ if (currBackdropIndex >= 0 && !backdropDomEl) {
1585
+ backdropScope = $rootScope.$new(true);
1586
+ backdropScope.index = currBackdropIndex;
1587
+ backdropDomEl = $compile('<div modal-backdrop></div>')(backdropScope);
1588
+ body.append(backdropDomEl);
1589
+ }
1590
+
1591
+ var angularDomEl = angular.element('<div modal-window></div>');
1592
+ angularDomEl.attr('window-class', modal.windowClass);
1593
+ angularDomEl.attr('index', openedWindows.length() - 1);
1594
+ angularDomEl.attr('animate', 'animate');
1595
+ angularDomEl.html(modal.content);
1596
+
1597
+ var modalDomEl = $compile(angularDomEl)(modal.scope);
1598
+ openedWindows.top().value.modalDomEl = modalDomEl;
1599
+ body.append(modalDomEl);
1600
+ body.addClass(OPENED_MODAL_CLASS);
1601
+ };
1602
+
1603
+ $modalStack.close = function (modalInstance, result) {
1604
+ var modalWindow = openedWindows.get(modalInstance).value;
1605
+ if (modalWindow) {
1606
+ modalWindow.deferred.resolve(result);
1607
+ removeModalWindow(modalInstance);
1608
+ }
1609
+ };
1610
+
1611
+ $modalStack.dismiss = function (modalInstance, reason) {
1612
+ var modalWindow = openedWindows.get(modalInstance).value;
1613
+ if (modalWindow) {
1614
+ modalWindow.deferred.reject(reason);
1615
+ removeModalWindow(modalInstance);
1616
+ }
1617
+ };
1618
+
1619
+ $modalStack.dismissAll = function (reason) {
1620
+ var topModal = this.getTop();
1621
+ while (topModal) {
1622
+ this.dismiss(topModal.key, reason);
1623
+ topModal = this.getTop();
1624
+ }
1625
+ };
1626
+
1627
+ $modalStack.getTop = function () {
1628
+ return openedWindows.top();
1629
+ };
1630
+
1631
+ return $modalStack;
1632
+ }])
1633
+
1634
+ .provider('$modal', function () {
1635
+
1636
+ var $modalProvider = {
1637
+ options: {
1638
+ backdrop: true, //can be also false or 'static'
1639
+ keyboard: true
1640
+ },
1641
+ $get: ['$injector', '$rootScope', '$q', '$http', '$templateCache', '$controller', '$modalStack',
1642
+ function ($injector, $rootScope, $q, $http, $templateCache, $controller, $modalStack) {
1643
+
1644
+ var $modal = {};
1645
+
1646
+ function getTemplatePromise(options) {
1647
+ return options.template ? $q.when(options.template) :
1648
+ $http.get(options.templateUrl, {cache: $templateCache}).then(function (result) {
1649
+ return result.data;
1650
+ });
1651
+ }
1652
+
1653
+ function getResolvePromises(resolves) {
1654
+ var promisesArr = [];
1655
+ angular.forEach(resolves, function (value, key) {
1656
+ if (angular.isFunction(value) || angular.isArray(value)) {
1657
+ promisesArr.push($q.when($injector.invoke(value)));
1658
+ }
1659
+ });
1660
+ return promisesArr;
1661
+ }
1662
+
1663
+ $modal.open = function (modalOptions) {
1664
+
1665
+ var modalResultDeferred = $q.defer();
1666
+ var modalOpenedDeferred = $q.defer();
1667
+
1668
+ //prepare an instance of a modal to be injected into controllers and returned to a caller
1669
+ var modalInstance = {
1670
+ result: modalResultDeferred.promise,
1671
+ opened: modalOpenedDeferred.promise,
1672
+ close: function (result) {
1673
+ $modalStack.close(modalInstance, result);
1674
+ },
1675
+ dismiss: function (reason) {
1676
+ $modalStack.dismiss(modalInstance, reason);
1677
+ }
1678
+ };
1679
+
1680
+ //merge and clean up options
1681
+ modalOptions = angular.extend({}, $modalProvider.options, modalOptions);
1682
+ modalOptions.resolve = modalOptions.resolve || {};
1683
+
1684
+ //verify options
1685
+ if (!modalOptions.template && !modalOptions.templateUrl) {
1686
+ throw new Error('One of template or templateUrl options is required.');
1687
+ }
1688
+
1689
+ var templateAndResolvePromise =
1690
+ $q.all([getTemplatePromise(modalOptions)].concat(getResolvePromises(modalOptions.resolve)));
1691
+
1692
+
1693
+ templateAndResolvePromise.then(function resolveSuccess(tplAndVars) {
1694
+
1695
+ var modalScope = (modalOptions.scope || $rootScope).$new();
1696
+ modalScope.$close = modalInstance.close;
1697
+ modalScope.$dismiss = modalInstance.dismiss;
1698
+
1699
+ var ctrlInstance, ctrlLocals = {};
1700
+ var resolveIter = 1;
1701
+
1702
+ //controllers
1703
+ if (modalOptions.controller) {
1704
+ ctrlLocals.$scope = modalScope;
1705
+ ctrlLocals.$modalInstance = modalInstance;
1706
+ angular.forEach(modalOptions.resolve, function (value, key) {
1707
+ ctrlLocals[key] = tplAndVars[resolveIter++];
1708
+ });
1709
+
1710
+ ctrlInstance = $controller(modalOptions.controller, ctrlLocals);
1711
+ }
1712
+
1713
+ $modalStack.open(modalInstance, {
1714
+ scope: modalScope,
1715
+ deferred: modalResultDeferred,
1716
+ content: tplAndVars[0],
1717
+ backdrop: modalOptions.backdrop,
1718
+ keyboard: modalOptions.keyboard,
1719
+ windowClass: modalOptions.windowClass
1720
+ });
1721
+
1722
+ }, function resolveError(reason) {
1723
+ modalResultDeferred.reject(reason);
1724
+ });
1725
+
1726
+ templateAndResolvePromise.then(function () {
1727
+ modalOpenedDeferred.resolve(true);
1728
+ }, function () {
1729
+ modalOpenedDeferred.reject(false);
1730
+ });
1731
+
1732
+ return modalInstance;
1733
+ };
1734
+
1735
+ return $modal;
1736
+ }]
1737
+ };
1738
+
1739
+ return $modalProvider;
1740
+ });
1741
+
1742
+ angular.module('ui.bootstrap.pagination', [])
1743
+
1744
+ .controller('PaginationController', ['$scope', '$attrs', '$parse', '$interpolate', function ($scope, $attrs, $parse, $interpolate) {
1745
+ var self = this,
1746
+ setNumPages = $attrs.numPages ? $parse($attrs.numPages).assign : angular.noop;
1747
+
1748
+ this.init = function(defaultItemsPerPage) {
1749
+ if ($attrs.itemsPerPage) {
1750
+ $scope.$parent.$watch($parse($attrs.itemsPerPage), function(value) {
1751
+ self.itemsPerPage = parseInt(value, 10);
1752
+ $scope.totalPages = self.calculateTotalPages();
1753
+ });
1754
+ } else {
1755
+ this.itemsPerPage = defaultItemsPerPage;
1756
+ }
1757
+ };
1758
+
1759
+ this.noPrevious = function() {
1760
+ return this.page === 1;
1761
+ };
1762
+ this.noNext = function() {
1763
+ return this.page === $scope.totalPages;
1764
+ };
1765
+
1766
+ this.isActive = function(page) {
1767
+ return this.page === page;
1768
+ };
1769
+
1770
+ this.calculateTotalPages = function() {
1771
+ var totalPages = this.itemsPerPage < 1 ? 1 : Math.ceil($scope.totalItems / this.itemsPerPage);
1772
+ return Math.max(totalPages || 0, 1);
1773
+ };
1774
+
1775
+ this.getAttributeValue = function(attribute, defaultValue, interpolate) {
1776
+ return angular.isDefined(attribute) ? (interpolate ? $interpolate(attribute)($scope.$parent) : $scope.$parent.$eval(attribute)) : defaultValue;
1777
+ };
1778
+
1779
+ this.render = function() {
1780
+ this.page = parseInt($scope.page, 10) || 1;
1781
+ if (this.page > 0 && this.page <= $scope.totalPages) {
1782
+ $scope.pages = this.getPages(this.page, $scope.totalPages);
1783
+ }
1784
+ };
1785
+
1786
+ $scope.selectPage = function(page) {
1787
+ if ( ! self.isActive(page) && page > 0 && page <= $scope.totalPages) {
1788
+ $scope.page = page;
1789
+ $scope.onSelectPage({ page: page });
1790
+ }
1791
+ };
1792
+
1793
+ $scope.$watch('page', function() {
1794
+ self.render();
1795
+ });
1796
+
1797
+ $scope.$watch('totalItems', function() {
1798
+ $scope.totalPages = self.calculateTotalPages();
1799
+ });
1800
+
1801
+ $scope.$watch('totalPages', function(value) {
1802
+ setNumPages($scope.$parent, value); // Readonly variable
1803
+
1804
+ if ( self.page > value ) {
1805
+ $scope.selectPage(value);
1806
+ } else {
1807
+ self.render();
1808
+ }
1809
+ });
1810
+ }])
1811
+
1812
+ .constant('paginationConfig', {
1813
+ itemsPerPage: 10,
1814
+ boundaryLinks: false,
1815
+ directionLinks: true,
1816
+ firstText: 'First',
1817
+ previousText: 'Previous',
1818
+ nextText: 'Next',
1819
+ lastText: 'Last',
1820
+ rotate: true
1821
+ })
1822
+
1823
+ .directive('pagination', ['$parse', 'paginationConfig', function($parse, config) {
1824
+ return {
1825
+ restrict: 'EA',
1826
+ scope: {
1827
+ page: '=',
1828
+ totalItems: '=',
1829
+ onSelectPage:' &'
1830
+ },
1831
+ controller: 'PaginationController',
1832
+ templateUrl: 'template/pagination/pagination.html',
1833
+ replace: true,
1834
+ link: function(scope, element, attrs, paginationCtrl) {
1835
+
1836
+ // Setup configuration parameters
1837
+ var maxSize,
1838
+ boundaryLinks = paginationCtrl.getAttributeValue(attrs.boundaryLinks, config.boundaryLinks ),
1839
+ directionLinks = paginationCtrl.getAttributeValue(attrs.directionLinks, config.directionLinks ),
1840
+ firstText = paginationCtrl.getAttributeValue(attrs.firstText, config.firstText, true),
1841
+ previousText = paginationCtrl.getAttributeValue(attrs.previousText, config.previousText, true),
1842
+ nextText = paginationCtrl.getAttributeValue(attrs.nextText, config.nextText, true),
1843
+ lastText = paginationCtrl.getAttributeValue(attrs.lastText, config.lastText, true),
1844
+ rotate = paginationCtrl.getAttributeValue(attrs.rotate, config.rotate);
1845
+
1846
+ paginationCtrl.init(config.itemsPerPage);
1847
+
1848
+ if (attrs.maxSize) {
1849
+ scope.$parent.$watch($parse(attrs.maxSize), function(value) {
1850
+ maxSize = parseInt(value, 10);
1851
+ paginationCtrl.render();
1852
+ });
1853
+ }
1854
+
1855
+ // Create page object used in template
1856
+ function makePage(number, text, isActive, isDisabled) {
1857
+ return {
1858
+ number: number,
1859
+ text: text,
1860
+ active: isActive,
1861
+ disabled: isDisabled
1862
+ };
1863
+ }
1864
+
1865
+ paginationCtrl.getPages = function(currentPage, totalPages) {
1866
+ var pages = [];
1867
+
1868
+ // Default page limits
1869
+ var startPage = 1, endPage = totalPages;
1870
+ var isMaxSized = ( angular.isDefined(maxSize) && maxSize < totalPages );
1871
+
1872
+ // recompute if maxSize
1873
+ if ( isMaxSized ) {
1874
+ if ( rotate ) {
1875
+ // Current page is displayed in the middle of the visible ones
1876
+ startPage = Math.max(currentPage - Math.floor(maxSize/2), 1);
1877
+ endPage = startPage + maxSize - 1;
1878
+
1879
+ // Adjust if limit is exceeded
1880
+ if (endPage > totalPages) {
1881
+ endPage = totalPages;
1882
+ startPage = endPage - maxSize + 1;
1883
+ }
1884
+ } else {
1885
+ // Visible pages are paginated with maxSize
1886
+ startPage = ((Math.ceil(currentPage / maxSize) - 1) * maxSize) + 1;
1887
+
1888
+ // Adjust last page if limit is exceeded
1889
+ endPage = Math.min(startPage + maxSize - 1, totalPages);
1890
+ }
1891
+ }
1892
+
1893
+ // Add page number links
1894
+ for (var number = startPage; number <= endPage; number++) {
1895
+ var page = makePage(number, number, paginationCtrl.isActive(number), false);
1896
+ pages.push(page);
1897
+ }
1898
+
1899
+ // Add links to move between page sets
1900
+ if ( isMaxSized && ! rotate ) {
1901
+ if ( startPage > 1 ) {
1902
+ var previousPageSet = makePage(startPage - 1, '...', false, false);
1903
+ pages.unshift(previousPageSet);
1904
+ }
1905
+
1906
+ if ( endPage < totalPages ) {
1907
+ var nextPageSet = makePage(endPage + 1, '...', false, false);
1908
+ pages.push(nextPageSet);
1909
+ }
1910
+ }
1911
+
1912
+ // Add previous & next links
1913
+ if (directionLinks) {
1914
+ var previousPage = makePage(currentPage - 1, previousText, false, paginationCtrl.noPrevious());
1915
+ pages.unshift(previousPage);
1916
+
1917
+ var nextPage = makePage(currentPage + 1, nextText, false, paginationCtrl.noNext());
1918
+ pages.push(nextPage);
1919
+ }
1920
+
1921
+ // Add first & last links
1922
+ if (boundaryLinks) {
1923
+ var firstPage = makePage(1, firstText, false, paginationCtrl.noPrevious());
1924
+ pages.unshift(firstPage);
1925
+
1926
+ var lastPage = makePage(totalPages, lastText, false, paginationCtrl.noNext());
1927
+ pages.push(lastPage);
1928
+ }
1929
+
1930
+ return pages;
1931
+ };
1932
+ }
1933
+ };
1934
+ }])
1935
+
1936
+ .constant('pagerConfig', {
1937
+ itemsPerPage: 10,
1938
+ previousText: '« Previous',
1939
+ nextText: 'Next »',
1940
+ align: true
1941
+ })
1942
+
1943
+ .directive('pager', ['pagerConfig', function(config) {
1944
+ return {
1945
+ restrict: 'EA',
1946
+ scope: {
1947
+ page: '=',
1948
+ totalItems: '=',
1949
+ onSelectPage:' &'
1950
+ },
1951
+ controller: 'PaginationController',
1952
+ templateUrl: 'template/pagination/pager.html',
1953
+ replace: true,
1954
+ link: function(scope, element, attrs, paginationCtrl) {
1955
+
1956
+ // Setup configuration parameters
1957
+ var previousText = paginationCtrl.getAttributeValue(attrs.previousText, config.previousText, true),
1958
+ nextText = paginationCtrl.getAttributeValue(attrs.nextText, config.nextText, true),
1959
+ align = paginationCtrl.getAttributeValue(attrs.align, config.align);
1960
+
1961
+ paginationCtrl.init(config.itemsPerPage);
1962
+
1963
+ // Create page object used in template
1964
+ function makePage(number, text, isDisabled, isPrevious, isNext) {
1965
+ return {
1966
+ number: number,
1967
+ text: text,
1968
+ disabled: isDisabled,
1969
+ previous: ( align && isPrevious ),
1970
+ next: ( align && isNext )
1971
+ };
1972
+ }
1973
+
1974
+ paginationCtrl.getPages = function(currentPage) {
1975
+ return [
1976
+ makePage(currentPage - 1, previousText, paginationCtrl.noPrevious(), true, false),
1977
+ makePage(currentPage + 1, nextText, paginationCtrl.noNext(), false, true)
1978
+ ];
1979
+ };
1980
+ }
1981
+ };
1982
+ }]);
1983
+
1984
+ /**
1985
+ * The following features are still outstanding: animation as a
1986
+ * function, placement as a function, inside, support for more triggers than
1987
+ * just mouse enter/leave, html tooltips, and selector delegation.
1988
+ */
1989
+ angular.module( 'ui.bootstrap.tooltip', [ 'ui.bootstrap.position', 'ui.bootstrap.bindHtml' ] )
1990
+
1991
+ /**
1992
+ * The $tooltip service creates tooltip- and popover-like directives as well as
1993
+ * houses global options for them.
1994
+ */
1995
+ .provider( '$tooltip', function () {
1996
+ // The default options tooltip and popover.
1997
+ var defaultOptions = {
1998
+ placement: 'top',
1999
+ animation: true,
2000
+ popupDelay: 0
2001
+ };
2002
+
2003
+ // Default hide triggers for each show trigger
2004
+ var triggerMap = {
2005
+ 'mouseenter': 'mouseleave',
2006
+ 'click': 'click',
2007
+ 'focus': 'blur'
2008
+ };
2009
+
2010
+ // The options specified to the provider globally.
2011
+ var globalOptions = {};
2012
+
2013
+ /**
2014
+ * `options({})` allows global configuration of all tooltips in the
2015
+ * application.
2016
+ *
2017
+ * var app = angular.module( 'App', ['ui.bootstrap.tooltip'], function( $tooltipProvider ) {
2018
+ * // place tooltips left instead of top by default
2019
+ * $tooltipProvider.options( { placement: 'left' } );
2020
+ * });
2021
+ */
2022
+ this.options = function( value ) {
2023
+ angular.extend( globalOptions, value );
2024
+ };
2025
+
2026
+ /**
2027
+ * This allows you to extend the set of trigger mappings available. E.g.:
2028
+ *
2029
+ * $tooltipProvider.setTriggers( 'openTrigger': 'closeTrigger' );
2030
+ */
2031
+ this.setTriggers = function setTriggers ( triggers ) {
2032
+ angular.extend( triggerMap, triggers );
2033
+ };
2034
+
2035
+ /**
2036
+ * This is a helper function for translating camel-case to snake-case.
2037
+ */
2038
+ function snake_case(name){
2039
+ var regexp = /[A-Z]/g;
2040
+ var separator = '-';
2041
+ return name.replace(regexp, function(letter, pos) {
2042
+ return (pos ? separator : '') + letter.toLowerCase();
2043
+ });
2044
+ }
2045
+
2046
+ /**
2047
+ * Returns the actual instance of the $tooltip service.
2048
+ * TODO support multiple triggers
2049
+ */
2050
+ this.$get = [ '$window', '$compile', '$timeout', '$parse', '$document', '$position', '$interpolate', function ( $window, $compile, $timeout, $parse, $document, $position, $interpolate ) {
2051
+ return function $tooltip ( type, prefix, defaultTriggerShow ) {
2052
+ var options = angular.extend( {}, defaultOptions, globalOptions );
2053
+
2054
+ /**
2055
+ * Returns an object of show and hide triggers.
2056
+ *
2057
+ * If a trigger is supplied,
2058
+ * it is used to show the tooltip; otherwise, it will use the `trigger`
2059
+ * option passed to the `$tooltipProvider.options` method; else it will
2060
+ * default to the trigger supplied to this directive factory.
2061
+ *
2062
+ * The hide trigger is based on the show trigger. If the `trigger` option
2063
+ * was passed to the `$tooltipProvider.options` method, it will use the
2064
+ * mapped trigger from `triggerMap` or the passed trigger if the map is
2065
+ * undefined; otherwise, it uses the `triggerMap` value of the show
2066
+ * trigger; else it will just use the show trigger.
2067
+ */
2068
+ function getTriggers ( trigger ) {
2069
+ var show = trigger || options.trigger || defaultTriggerShow;
2070
+ var hide = triggerMap[show] || show;
2071
+ return {
2072
+ show: show,
2073
+ hide: hide
2074
+ };
2075
+ }
2076
+
2077
+ var directiveName = snake_case( type );
2078
+
2079
+ var startSym = $interpolate.startSymbol();
2080
+ var endSym = $interpolate.endSymbol();
2081
+ var template =
2082
+ '<div '+ directiveName +'-popup '+
2083
+ 'title="'+startSym+'tt_title'+endSym+'" '+
2084
+ 'content="'+startSym+'tt_content'+endSym+'" '+
2085
+ 'placement="'+startSym+'tt_placement'+endSym+'" '+
2086
+ 'animation="tt_animation" '+
2087
+ 'is-open="tt_isOpen"'+
2088
+ '>'+
2089
+ '</div>';
2090
+
2091
+ return {
2092
+ restrict: 'EA',
2093
+ scope: true,
2094
+ compile: function (tElem, tAttrs) {
2095
+ var tooltipLinker = $compile( template );
2096
+
2097
+ return function link ( scope, element, attrs ) {
2098
+ var tooltip;
2099
+ var transitionTimeout;
2100
+ var popupTimeout;
2101
+ var appendToBody = angular.isDefined( options.appendToBody ) ? options.appendToBody : false;
2102
+ var triggers = getTriggers( undefined );
2103
+ var hasRegisteredTriggers = false;
2104
+ var hasEnableExp = angular.isDefined(attrs[prefix+'Enable']);
2105
+
2106
+ var positionTooltip = function (){
2107
+ var position,
2108
+ ttWidth,
2109
+ ttHeight,
2110
+ ttPosition;
2111
+ // Get the position of the directive element.
2112
+ position = appendToBody ? $position.offset( element ) : $position.position( element );
2113
+
2114
+ // Get the height and width of the tooltip so we can center it.
2115
+ ttWidth = tooltip.prop( 'offsetWidth' );
2116
+ ttHeight = tooltip.prop( 'offsetHeight' );
2117
+
2118
+ // Calculate the tooltip's top and left coordinates to center it with
2119
+ // this directive.
2120
+ switch ( scope.tt_placement ) {
2121
+ case 'right':
2122
+ ttPosition = {
2123
+ top: position.top + position.height / 2 - ttHeight / 2,
2124
+ left: position.left + position.width
2125
+ };
2126
+ break;
2127
+ case 'bottom':
2128
+ ttPosition = {
2129
+ top: position.top + position.height,
2130
+ left: position.left + position.width / 2 - ttWidth / 2
2131
+ };
2132
+ break;
2133
+ case 'left':
2134
+ ttPosition = {
2135
+ top: position.top + position.height / 2 - ttHeight / 2,
2136
+ left: position.left - ttWidth
2137
+ };
2138
+ break;
2139
+ default:
2140
+ ttPosition = {
2141
+ top: position.top - ttHeight,
2142
+ left: position.left + position.width / 2 - ttWidth / 2
2143
+ };
2144
+ break;
2145
+ }
2146
+
2147
+ ttPosition.top += 'px';
2148
+ ttPosition.left += 'px';
2149
+
2150
+ // Now set the calculated positioning.
2151
+ tooltip.css( ttPosition );
2152
+
2153
+ };
2154
+
2155
+ // By default, the tooltip is not open.
2156
+ // TODO add ability to start tooltip opened
2157
+ scope.tt_isOpen = false;
2158
+
2159
+ function toggleTooltipBind () {
2160
+ if ( ! scope.tt_isOpen ) {
2161
+ showTooltipBind();
2162
+ } else {
2163
+ hideTooltipBind();
2164
+ }
2165
+ }
2166
+
2167
+ // Show the tooltip with delay if specified, otherwise show it immediately
2168
+ function showTooltipBind() {
2169
+ if(hasEnableExp && !scope.$eval(attrs[prefix+'Enable'])) {
2170
+ return;
2171
+ }
2172
+ if ( scope.tt_popupDelay ) {
2173
+ popupTimeout = $timeout( show, scope.tt_popupDelay, false );
2174
+ popupTimeout.then(function(reposition){reposition();});
2175
+ } else {
2176
+ show()();
2177
+ }
2178
+ }
2179
+
2180
+ function hideTooltipBind () {
2181
+ scope.$apply(function () {
2182
+ hide();
2183
+ });
2184
+ }
2185
+
2186
+ // Show the tooltip popup element.
2187
+ function show() {
2188
+
2189
+
2190
+ // Don't show empty tooltips.
2191
+ if ( ! scope.tt_content ) {
2192
+ return angular.noop;
2193
+ }
2194
+
2195
+ createTooltip();
2196
+
2197
+ // If there is a pending remove transition, we must cancel it, lest the
2198
+ // tooltip be mysteriously removed.
2199
+ if ( transitionTimeout ) {
2200
+ $timeout.cancel( transitionTimeout );
2201
+ }
2202
+
2203
+ // Set the initial positioning.
2204
+ tooltip.css({ top: 0, left: 0, display: 'block' });
2205
+
2206
+ // Now we add it to the DOM because need some info about it. But it's not
2207
+ // visible yet anyway.
2208
+ if ( appendToBody ) {
2209
+ $document.find( 'body' ).append( tooltip );
2210
+ } else {
2211
+ element.after( tooltip );
2212
+ }
2213
+
2214
+ positionTooltip();
2215
+
2216
+ // And show the tooltip.
2217
+ scope.tt_isOpen = true;
2218
+ scope.$digest(); // digest required as $apply is not called
2219
+
2220
+ // Return positioning function as promise callback for correct
2221
+ // positioning after draw.
2222
+ return positionTooltip;
2223
+ }
2224
+
2225
+ // Hide the tooltip popup element.
2226
+ function hide() {
2227
+ // First things first: we don't show it anymore.
2228
+ scope.tt_isOpen = false;
2229
+
2230
+ //if tooltip is going to be shown after delay, we must cancel this
2231
+ $timeout.cancel( popupTimeout );
2232
+
2233
+ // And now we remove it from the DOM. However, if we have animation, we
2234
+ // need to wait for it to expire beforehand.
2235
+ // FIXME: this is a placeholder for a port of the transitions library.
2236
+ if ( scope.tt_animation ) {
2237
+ transitionTimeout = $timeout(removeTooltip, 500);
2238
+ } else {
2239
+ removeTooltip();
2240
+ }
2241
+ }
2242
+
2243
+ function createTooltip() {
2244
+ // There can only be one tooltip element per directive shown at once.
2245
+ if (tooltip) {
2246
+ removeTooltip();
2247
+ }
2248
+ tooltip = tooltipLinker(scope, function () {});
2249
+
2250
+ // Get contents rendered into the tooltip
2251
+ scope.$digest();
2252
+ }
2253
+
2254
+ function removeTooltip() {
2255
+ if (tooltip) {
2256
+ tooltip.remove();
2257
+ tooltip = null;
2258
+ }
2259
+ }
2260
+
2261
+ /**
2262
+ * Observe the relevant attributes.
2263
+ */
2264
+ attrs.$observe( type, function ( val ) {
2265
+ scope.tt_content = val;
2266
+
2267
+ if (!val && scope.tt_isOpen ) {
2268
+ hide();
2269
+ }
2270
+ });
2271
+
2272
+ attrs.$observe( prefix+'Title', function ( val ) {
2273
+ scope.tt_title = val;
2274
+ });
2275
+
2276
+ attrs.$observe( prefix+'Placement', function ( val ) {
2277
+ scope.tt_placement = angular.isDefined( val ) ? val : options.placement;
2278
+ });
2279
+
2280
+ attrs.$observe( prefix+'PopupDelay', function ( val ) {
2281
+ var delay = parseInt( val, 10 );
2282
+ scope.tt_popupDelay = ! isNaN(delay) ? delay : options.popupDelay;
2283
+ });
2284
+
2285
+ var unregisterTriggers = function() {
2286
+ if (hasRegisteredTriggers) {
2287
+ element.unbind( triggers.show, showTooltipBind );
2288
+ element.unbind( triggers.hide, hideTooltipBind );
2289
+ }
2290
+ };
2291
+
2292
+ attrs.$observe( prefix+'Trigger', function ( val ) {
2293
+ unregisterTriggers();
2294
+
2295
+ triggers = getTriggers( val );
2296
+
2297
+ if ( triggers.show === triggers.hide ) {
2298
+ element.bind( triggers.show, toggleTooltipBind );
2299
+ } else {
2300
+ element.bind( triggers.show, showTooltipBind );
2301
+ element.bind( triggers.hide, hideTooltipBind );
2302
+ }
2303
+
2304
+ hasRegisteredTriggers = true;
2305
+ });
2306
+
2307
+ var animation = scope.$eval(attrs[prefix + 'Animation']);
2308
+ scope.tt_animation = angular.isDefined(animation) ? !!animation : options.animation;
2309
+
2310
+ attrs.$observe( prefix+'AppendToBody', function ( val ) {
2311
+ appendToBody = angular.isDefined( val ) ? $parse( val )( scope ) : appendToBody;
2312
+ });
2313
+
2314
+ // if a tooltip is attached to <body> we need to remove it on
2315
+ // location change as its parent scope will probably not be destroyed
2316
+ // by the change.
2317
+ if ( appendToBody ) {
2318
+ scope.$on('$locationChangeSuccess', function closeTooltipOnLocationChangeSuccess () {
2319
+ if ( scope.tt_isOpen ) {
2320
+ hide();
2321
+ }
2322
+ });
2323
+ }
2324
+
2325
+ // Make sure tooltip is destroyed and removed.
2326
+ scope.$on('$destroy', function onDestroyTooltip() {
2327
+ $timeout.cancel( transitionTimeout );
2328
+ $timeout.cancel( popupTimeout );
2329
+ unregisterTriggers();
2330
+ removeTooltip();
2331
+ });
2332
+ };
2333
+ }
2334
+ };
2335
+ };
2336
+ }];
2337
+ })
2338
+
2339
+ .directive( 'tooltipPopup', function () {
2340
+ return {
2341
+ restrict: 'EA',
2342
+ replace: true,
2343
+ scope: { content: '@', placement: '@', animation: '&', isOpen: '&' },
2344
+ templateUrl: 'template/tooltip/tooltip-popup.html'
2345
+ };
2346
+ })
2347
+
2348
+ .directive( 'tooltip', [ '$tooltip', function ( $tooltip ) {
2349
+ return $tooltip( 'tooltip', 'tooltip', 'mouseenter' );
2350
+ }])
2351
+
2352
+ .directive( 'tooltipHtmlUnsafePopup', function () {
2353
+ return {
2354
+ restrict: 'EA',
2355
+ replace: true,
2356
+ scope: { content: '@', placement: '@', animation: '&', isOpen: '&' },
2357
+ templateUrl: 'template/tooltip/tooltip-html-unsafe-popup.html'
2358
+ };
2359
+ })
2360
+
2361
+ .directive( 'tooltipHtmlUnsafe', [ '$tooltip', function ( $tooltip ) {
2362
+ return $tooltip( 'tooltipHtmlUnsafe', 'tooltip', 'mouseenter' );
2363
+ }]);
2364
+
2365
+ /**
2366
+ * The following features are still outstanding: popup delay, animation as a
2367
+ * function, placement as a function, inside, support for more triggers than
2368
+ * just mouse enter/leave, html popovers, and selector delegatation.
2369
+ */
2370
+ angular.module( 'ui.bootstrap.popover', [ 'ui.bootstrap.tooltip' ] )
2371
+
2372
+ .directive( 'popoverPopup', function () {
2373
+ return {
2374
+ restrict: 'EA',
2375
+ replace: true,
2376
+ scope: { title: '@', content: '@', placement: '@', animation: '&', isOpen: '&' },
2377
+ templateUrl: 'template/popover/popover.html'
2378
+ };
2379
+ })
2380
+
2381
+ .directive( 'popover', [ '$tooltip', function ( $tooltip ) {
2382
+ return $tooltip( 'popover', 'popover', 'click' );
2383
+ }]);
2384
+
2385
+ angular.module('ui.bootstrap.progressbar', ['ui.bootstrap.transition'])
2386
+
2387
+ .constant('progressConfig', {
2388
+ animate: true,
2389
+ max: 100
2390
+ })
2391
+
2392
+ .controller('ProgressController', ['$scope', '$attrs', 'progressConfig', '$transition', function($scope, $attrs, progressConfig, $transition) {
2393
+ var self = this,
2394
+ bars = [],
2395
+ max = angular.isDefined($attrs.max) ? $scope.$parent.$eval($attrs.max) : progressConfig.max,
2396
+ animate = angular.isDefined($attrs.animate) ? $scope.$parent.$eval($attrs.animate) : progressConfig.animate;
2397
+
2398
+ this.addBar = function(bar, element) {
2399
+ var oldValue = 0, index = bar.$parent.$index;
2400
+ if ( angular.isDefined(index) && bars[index] ) {
2401
+ oldValue = bars[index].value;
2402
+ }
2403
+ bars.push(bar);
2404
+
2405
+ this.update(element, bar.value, oldValue);
2406
+
2407
+ bar.$watch('value', function(value, oldValue) {
2408
+ if (value !== oldValue) {
2409
+ self.update(element, value, oldValue);
2410
+ }
2411
+ });
2412
+
2413
+ bar.$on('$destroy', function() {
2414
+ self.removeBar(bar);
2415
+ });
2416
+ };
2417
+
2418
+ // Update bar element width
2419
+ this.update = function(element, newValue, oldValue) {
2420
+ var percent = this.getPercentage(newValue);
2421
+
2422
+ if (animate) {
2423
+ element.css('width', this.getPercentage(oldValue) + '%');
2424
+ $transition(element, {width: percent + '%'});
2425
+ } else {
2426
+ element.css({'transition': 'none', 'width': percent + '%'});
2427
+ }
2428
+ };
2429
+
2430
+ this.removeBar = function(bar) {
2431
+ bars.splice(bars.indexOf(bar), 1);
2432
+ };
2433
+
2434
+ this.getPercentage = function(value) {
2435
+ return Math.round(100 * value / max);
2436
+ };
2437
+ }])
2438
+
2439
+ .directive('progress', function() {
2440
+ return {
2441
+ restrict: 'EA',
2442
+ replace: true,
2443
+ transclude: true,
2444
+ controller: 'ProgressController',
2445
+ require: 'progress',
2446
+ scope: {},
2447
+ template: '<div class="progress" ng-transclude></div>'
2448
+ //templateUrl: 'template/progressbar/progress.html' // Works in AngularJS 1.2
2449
+ };
2450
+ })
2451
+
2452
+ .directive('bar', function() {
2453
+ return {
2454
+ restrict: 'EA',
2455
+ replace: true,
2456
+ transclude: true,
2457
+ require: '^progress',
2458
+ scope: {
2459
+ value: '=',
2460
+ type: '@'
2461
+ },
2462
+ templateUrl: 'template/progressbar/bar.html',
2463
+ link: function(scope, element, attrs, progressCtrl) {
2464
+ progressCtrl.addBar(scope, element);
2465
+ }
2466
+ };
2467
+ })
2468
+
2469
+ .directive('progressbar', function() {
2470
+ return {
2471
+ restrict: 'EA',
2472
+ replace: true,
2473
+ transclude: true,
2474
+ controller: 'ProgressController',
2475
+ scope: {
2476
+ value: '=',
2477
+ type: '@'
2478
+ },
2479
+ templateUrl: 'template/progressbar/progressbar.html',
2480
+ link: function(scope, element, attrs, progressCtrl) {
2481
+ progressCtrl.addBar(scope, angular.element(element.children()[0]));
2482
+ }
2483
+ };
2484
+ });
2485
+ angular.module('ui.bootstrap.rating', [])
2486
+
2487
+ .constant('ratingConfig', {
2488
+ max: 5,
2489
+ stateOn: null,
2490
+ stateOff: null
2491
+ })
2492
+
2493
+ .controller('RatingController', ['$scope', '$attrs', '$parse', 'ratingConfig', function($scope, $attrs, $parse, ratingConfig) {
2494
+
2495
+ this.maxRange = angular.isDefined($attrs.max) ? $scope.$parent.$eval($attrs.max) : ratingConfig.max;
2496
+ this.stateOn = angular.isDefined($attrs.stateOn) ? $scope.$parent.$eval($attrs.stateOn) : ratingConfig.stateOn;
2497
+ this.stateOff = angular.isDefined($attrs.stateOff) ? $scope.$parent.$eval($attrs.stateOff) : ratingConfig.stateOff;
2498
+
2499
+ this.createRateObjects = function(states) {
2500
+ var defaultOptions = {
2501
+ stateOn: this.stateOn,
2502
+ stateOff: this.stateOff
2503
+ };
2504
+
2505
+ for (var i = 0, n = states.length; i < n; i++) {
2506
+ states[i] = angular.extend({ index: i }, defaultOptions, states[i]);
2507
+ }
2508
+ return states;
2509
+ };
2510
+
2511
+ // Get objects used in template
2512
+ $scope.range = angular.isDefined($attrs.ratingStates) ? this.createRateObjects(angular.copy($scope.$parent.$eval($attrs.ratingStates))): this.createRateObjects(new Array(this.maxRange));
2513
+
2514
+ $scope.rate = function(value) {
2515
+ if ( $scope.value !== value && !$scope.readonly ) {
2516
+ $scope.value = value;
2517
+ }
2518
+ };
2519
+
2520
+ $scope.enter = function(value) {
2521
+ if ( ! $scope.readonly ) {
2522
+ $scope.val = value;
2523
+ }
2524
+ $scope.onHover({value: value});
2525
+ };
2526
+
2527
+ $scope.reset = function() {
2528
+ $scope.val = angular.copy($scope.value);
2529
+ $scope.onLeave();
2530
+ };
2531
+
2532
+ $scope.$watch('value', function(value) {
2533
+ $scope.val = value;
2534
+ });
2535
+
2536
+ $scope.readonly = false;
2537
+ if ($attrs.readonly) {
2538
+ $scope.$parent.$watch($parse($attrs.readonly), function(value) {
2539
+ $scope.readonly = !!value;
2540
+ });
2541
+ }
2542
+ }])
2543
+
2544
+ .directive('rating', function() {
2545
+ return {
2546
+ restrict: 'EA',
2547
+ scope: {
2548
+ value: '=',
2549
+ onHover: '&',
2550
+ onLeave: '&'
2551
+ },
2552
+ controller: 'RatingController',
2553
+ templateUrl: 'template/rating/rating.html',
2554
+ replace: true
2555
+ };
2556
+ });
2557
+
2558
+ /**
2559
+ * @ngdoc overview
2560
+ * @name ui.bootstrap.tabs
2561
+ *
2562
+ * @description
2563
+ * AngularJS version of the tabs directive.
2564
+ */
2565
+
2566
+ angular.module('ui.bootstrap.tabs', [])
2567
+
2568
+ .controller('TabsetController', ['$scope', function TabsetCtrl($scope) {
2569
+ var ctrl = this,
2570
+ tabs = ctrl.tabs = $scope.tabs = [];
2571
+
2572
+ ctrl.select = function(tab) {
2573
+ angular.forEach(tabs, function(tab) {
2574
+ tab.active = false;
2575
+ });
2576
+ tab.active = true;
2577
+ };
2578
+
2579
+ ctrl.addTab = function addTab(tab) {
2580
+ tabs.push(tab);
2581
+ if (tabs.length === 1 || tab.active) {
2582
+ ctrl.select(tab);
2583
+ }
2584
+ };
2585
+
2586
+ ctrl.removeTab = function removeTab(tab) {
2587
+ var index = tabs.indexOf(tab);
2588
+ //Select a new tab if the tab to be removed is selected
2589
+ if (tab.active && tabs.length > 1) {
2590
+ //If this is the last tab, select the previous tab. else, the next tab.
2591
+ var newActiveIndex = index == tabs.length - 1 ? index - 1 : index + 1;
2592
+ ctrl.select(tabs[newActiveIndex]);
2593
+ }
2594
+ tabs.splice(index, 1);
2595
+ };
2596
+ }])
2597
+
2598
+ /**
2599
+ * @ngdoc directive
2600
+ * @name ui.bootstrap.tabs.directive:tabset
2601
+ * @restrict EA
2602
+ *
2603
+ * @description
2604
+ * Tabset is the outer container for the tabs directive
2605
+ *
2606
+ * @param {boolean=} vertical Whether or not to use vertical styling for the tabs.
2607
+ * @param {boolean=} justified Whether or not to use justified styling for the tabs.
2608
+ *
2609
+ * @example
2610
+ <example module="ui.bootstrap">
2611
+ <file name="index.html">
2612
+ <tabset>
2613
+ <tab heading="Tab 1"><b>First</b> Content!</tab>
2614
+ <tab heading="Tab 2"><i>Second</i> Content!</tab>
2615
+ </tabset>
2616
+ <hr />
2617
+ <tabset vertical="true">
2618
+ <tab heading="Vertical Tab 1"><b>First</b> Vertical Content!</tab>
2619
+ <tab heading="Vertical Tab 2"><i>Second</i> Vertical Content!</tab>
2620
+ </tabset>
2621
+ <tabset justified="true">
2622
+ <tab heading="Justified Tab 1"><b>First</b> Justified Content!</tab>
2623
+ <tab heading="Justified Tab 2"><i>Second</i> Justified Content!</tab>
2624
+ </tabset>
2625
+ </file>
2626
+ </example>
2627
+ */
2628
+ .directive('tabset', function() {
2629
+ return {
2630
+ restrict: 'EA',
2631
+ transclude: true,
2632
+ replace: true,
2633
+ scope: {},
2634
+ controller: 'TabsetController',
2635
+ templateUrl: 'template/tabs/tabset.html',
2636
+ link: function(scope, element, attrs) {
2637
+ scope.vertical = angular.isDefined(attrs.vertical) ? scope.$parent.$eval(attrs.vertical) : false;
2638
+ scope.justified = angular.isDefined(attrs.justified) ? scope.$parent.$eval(attrs.justified) : false;
2639
+ scope.type = angular.isDefined(attrs.type) ? scope.$parent.$eval(attrs.type) : 'tabs';
2640
+ }
2641
+ };
2642
+ })
2643
+
2644
+ /**
2645
+ * @ngdoc directive
2646
+ * @name ui.bootstrap.tabs.directive:tab
2647
+ * @restrict EA
2648
+ *
2649
+ * @param {string=} heading The visible heading, or title, of the tab. Set HTML headings with {@link ui.bootstrap.tabs.directive:tabHeading tabHeading}.
2650
+ * @param {string=} select An expression to evaluate when the tab is selected.
2651
+ * @param {boolean=} active A binding, telling whether or not this tab is selected.
2652
+ * @param {boolean=} disabled A binding, telling whether or not this tab is disabled.
2653
+ *
2654
+ * @description
2655
+ * Creates a tab with a heading and content. Must be placed within a {@link ui.bootstrap.tabs.directive:tabset tabset}.
2656
+ *
2657
+ * @example
2658
+ <example module="ui.bootstrap">
2659
+ <file name="index.html">
2660
+ <div ng-controller="TabsDemoCtrl">
2661
+ <button class="btn btn-small" ng-click="items[0].active = true">
2662
+ Select item 1, using active binding
2663
+ </button>
2664
+ <button class="btn btn-small" ng-click="items[1].disabled = !items[1].disabled">
2665
+ Enable/disable item 2, using disabled binding
2666
+ </button>
2667
+ <br />
2668
+ <tabset>
2669
+ <tab heading="Tab 1">First Tab</tab>
2670
+ <tab select="alertMe()">
2671
+ <tab-heading><i class="icon-bell"></i> Alert me!</tab-heading>
2672
+ Second Tab, with alert callback and html heading!
2673
+ </tab>
2674
+ <tab ng-repeat="item in items"
2675
+ heading="{{item.title}}"
2676
+ disabled="item.disabled"
2677
+ active="item.active">
2678
+ {{item.content}}
2679
+ </tab>
2680
+ </tabset>
2681
+ </div>
2682
+ </file>
2683
+ <file name="script.js">
2684
+ function TabsDemoCtrl($scope) {
2685
+ $scope.items = [
2686
+ { title:"Dynamic Title 1", content:"Dynamic Item 0" },
2687
+ { title:"Dynamic Title 2", content:"Dynamic Item 1", disabled: true }
2688
+ ];
2689
+
2690
+ $scope.alertMe = function() {
2691
+ setTimeout(function() {
2692
+ alert("You've selected the alert tab!");
2693
+ });
2694
+ };
2695
+ };
2696
+ </file>
2697
+ </example>
2698
+ */
2699
+
2700
+ /**
2701
+ * @ngdoc directive
2702
+ * @name ui.bootstrap.tabs.directive:tabHeading
2703
+ * @restrict EA
2704
+ *
2705
+ * @description
2706
+ * Creates an HTML heading for a {@link ui.bootstrap.tabs.directive:tab tab}. Must be placed as a child of a tab element.
2707
+ *
2708
+ * @example
2709
+ <example module="ui.bootstrap">
2710
+ <file name="index.html">
2711
+ <tabset>
2712
+ <tab>
2713
+ <tab-heading><b>HTML</b> in my titles?!</tab-heading>
2714
+ And some content, too!
2715
+ </tab>
2716
+ <tab>
2717
+ <tab-heading><i class="icon-heart"></i> Icon heading?!?</tab-heading>
2718
+ That's right.
2719
+ </tab>
2720
+ </tabset>
2721
+ </file>
2722
+ </example>
2723
+ */
2724
+ .directive('tab', ['$parse', function($parse) {
2725
+ return {
2726
+ require: '^tabset',
2727
+ restrict: 'EA',
2728
+ replace: true,
2729
+ templateUrl: 'template/tabs/tab.html',
2730
+ transclude: true,
2731
+ scope: {
2732
+ heading: '@',
2733
+ onSelect: '&select', //This callback is called in contentHeadingTransclude
2734
+ //once it inserts the tab's content into the dom
2735
+ onDeselect: '&deselect'
2736
+ },
2737
+ controller: function() {
2738
+ //Empty controller so other directives can require being 'under' a tab
2739
+ },
2740
+ compile: function(elm, attrs, transclude) {
2741
+ return function postLink(scope, elm, attrs, tabsetCtrl) {
2742
+ var getActive, setActive;
2743
+ if (attrs.active) {
2744
+ getActive = $parse(attrs.active);
2745
+ setActive = getActive.assign;
2746
+ scope.$parent.$watch(getActive, function updateActive(value, oldVal) {
2747
+ // Avoid re-initializing scope.active as it is already initialized
2748
+ // below. (watcher is called async during init with value ===
2749
+ // oldVal)
2750
+ if (value !== oldVal) {
2751
+ scope.active = !!value;
2752
+ }
2753
+ });
2754
+ scope.active = getActive(scope.$parent);
2755
+ } else {
2756
+ setActive = getActive = angular.noop;
2757
+ }
2758
+
2759
+ scope.$watch('active', function(active) {
2760
+ // Note this watcher also initializes and assigns scope.active to the
2761
+ // attrs.active expression.
2762
+ setActive(scope.$parent, active);
2763
+ if (active) {
2764
+ tabsetCtrl.select(scope);
2765
+ scope.onSelect();
2766
+ } else {
2767
+ scope.onDeselect();
2768
+ }
2769
+ });
2770
+
2771
+ scope.disabled = false;
2772
+ if ( attrs.disabled ) {
2773
+ scope.$parent.$watch($parse(attrs.disabled), function(value) {
2774
+ scope.disabled = !! value;
2775
+ });
2776
+ }
2777
+
2778
+ scope.select = function() {
2779
+ if ( ! scope.disabled ) {
2780
+ scope.active = true;
2781
+ }
2782
+ };
2783
+
2784
+ tabsetCtrl.addTab(scope);
2785
+ scope.$on('$destroy', function() {
2786
+ tabsetCtrl.removeTab(scope);
2787
+ });
2788
+
2789
+
2790
+ //We need to transclude later, once the content container is ready.
2791
+ //when this link happens, we're inside a tab heading.
2792
+ scope.$transcludeFn = transclude;
2793
+ };
2794
+ }
2795
+ };
2796
+ }])
2797
+
2798
+ .directive('tabHeadingTransclude', [function() {
2799
+ return {
2800
+ restrict: 'A',
2801
+ require: '^tab',
2802
+ link: function(scope, elm, attrs, tabCtrl) {
2803
+ scope.$watch('headingElement', function updateHeadingElement(heading) {
2804
+ if (heading) {
2805
+ elm.html('');
2806
+ elm.append(heading);
2807
+ }
2808
+ });
2809
+ }
2810
+ };
2811
+ }])
2812
+
2813
+ .directive('tabContentTransclude', function() {
2814
+ return {
2815
+ restrict: 'A',
2816
+ require: '^tabset',
2817
+ link: function(scope, elm, attrs) {
2818
+ var tab = scope.$eval(attrs.tabContentTransclude);
2819
+
2820
+ //Now our tab is ready to be transcluded: both the tab heading area
2821
+ //and the tab content area are loaded. Transclude 'em both.
2822
+ tab.$transcludeFn(tab.$parent, function(contents) {
2823
+ angular.forEach(contents, function(node) {
2824
+ if (isTabHeading(node)) {
2825
+ //Let tabHeadingTransclude know.
2826
+ tab.headingElement = node;
2827
+ } else {
2828
+ elm.append(node);
2829
+ }
2830
+ });
2831
+ });
2832
+ }
2833
+ };
2834
+ function isTabHeading(node) {
2835
+ return node.tagName && (
2836
+ node.hasAttribute('tab-heading') ||
2837
+ node.hasAttribute('data-tab-heading') ||
2838
+ node.tagName.toLowerCase() === 'tab-heading' ||
2839
+ node.tagName.toLowerCase() === 'data-tab-heading'
2840
+ );
2841
+ }
2842
+ })
2843
+
2844
+ ;
2845
+
2846
+ angular.module('ui.bootstrap.timepicker', [])
2847
+
2848
+ .constant('timepickerConfig', {
2849
+ hourStep: 1,
2850
+ minuteStep: 1,
2851
+ showMeridian: true,
2852
+ meridians: null,
2853
+ readonlyInput: false,
2854
+ mousewheel: true
2855
+ })
2856
+
2857
+ .directive('timepicker', ['$parse', '$log', 'timepickerConfig', '$locale', function ($parse, $log, timepickerConfig, $locale) {
2858
+ return {
2859
+ restrict: 'EA',
2860
+ require:'?^ngModel',
2861
+ replace: true,
2862
+ scope: {},
2863
+ templateUrl: 'template/timepicker/timepicker.html',
2864
+ link: function(scope, element, attrs, ngModel) {
2865
+ if ( !ngModel ) {
2866
+ return; // do nothing if no ng-model
2867
+ }
2868
+
2869
+ var selected = new Date(),
2870
+ meridians = angular.isDefined(attrs.meridians) ? scope.$parent.$eval(attrs.meridians) : timepickerConfig.meridians || $locale.DATETIME_FORMATS.AMPMS;
2871
+
2872
+ var hourStep = timepickerConfig.hourStep;
2873
+ if (attrs.hourStep) {
2874
+ scope.$parent.$watch($parse(attrs.hourStep), function(value) {
2875
+ hourStep = parseInt(value, 10);
2876
+ });
2877
+ }
2878
+
2879
+ var minuteStep = timepickerConfig.minuteStep;
2880
+ if (attrs.minuteStep) {
2881
+ scope.$parent.$watch($parse(attrs.minuteStep), function(value) {
2882
+ minuteStep = parseInt(value, 10);
2883
+ });
2884
+ }
2885
+
2886
+ // 12H / 24H mode
2887
+ scope.showMeridian = timepickerConfig.showMeridian;
2888
+ if (attrs.showMeridian) {
2889
+ scope.$parent.$watch($parse(attrs.showMeridian), function(value) {
2890
+ scope.showMeridian = !!value;
2891
+
2892
+ if ( ngModel.$error.time ) {
2893
+ // Evaluate from template
2894
+ var hours = getHoursFromTemplate(), minutes = getMinutesFromTemplate();
2895
+ if (angular.isDefined( hours ) && angular.isDefined( minutes )) {
2896
+ selected.setHours( hours );
2897
+ refresh();
2898
+ }
2899
+ } else {
2900
+ updateTemplate();
2901
+ }
2902
+ });
2903
+ }
2904
+
2905
+ // Get scope.hours in 24H mode if valid
2906
+ function getHoursFromTemplate ( ) {
2907
+ var hours = parseInt( scope.hours, 10 );
2908
+ var valid = ( scope.showMeridian ) ? (hours > 0 && hours < 13) : (hours >= 0 && hours < 24);
2909
+ if ( !valid ) {
2910
+ return undefined;
2911
+ }
2912
+
2913
+ if ( scope.showMeridian ) {
2914
+ if ( hours === 12 ) {
2915
+ hours = 0;
2916
+ }
2917
+ if ( scope.meridian === meridians[1] ) {
2918
+ hours = hours + 12;
2919
+ }
2920
+ }
2921
+ return hours;
2922
+ }
2923
+
2924
+ function getMinutesFromTemplate() {
2925
+ var minutes = parseInt(scope.minutes, 10);
2926
+ return ( minutes >= 0 && minutes < 60 ) ? minutes : undefined;
2927
+ }
2928
+
2929
+ function pad( value ) {
2930
+ return ( angular.isDefined(value) && value.toString().length < 2 ) ? '0' + value : value;
2931
+ }
2932
+
2933
+ // Input elements
2934
+ var inputs = element.find('input'), hoursInputEl = inputs.eq(0), minutesInputEl = inputs.eq(1);
2935
+
2936
+ // Respond on mousewheel spin
2937
+ var mousewheel = (angular.isDefined(attrs.mousewheel)) ? scope.$eval(attrs.mousewheel) : timepickerConfig.mousewheel;
2938
+ if ( mousewheel ) {
2939
+
2940
+ var isScrollingUp = function(e) {
2941
+ if (e.originalEvent) {
2942
+ e = e.originalEvent;
2943
+ }
2944
+ //pick correct delta variable depending on event
2945
+ var delta = (e.wheelDelta) ? e.wheelDelta : -e.deltaY;
2946
+ return (e.detail || delta > 0);
2947
+ };
2948
+
2949
+ hoursInputEl.bind('mousewheel wheel', function(e) {
2950
+ scope.$apply( (isScrollingUp(e)) ? scope.incrementHours() : scope.decrementHours() );
2951
+ e.preventDefault();
2952
+ });
2953
+
2954
+ minutesInputEl.bind('mousewheel wheel', function(e) {
2955
+ scope.$apply( (isScrollingUp(e)) ? scope.incrementMinutes() : scope.decrementMinutes() );
2956
+ e.preventDefault();
2957
+ });
2958
+ }
2959
+
2960
+ scope.readonlyInput = (angular.isDefined(attrs.readonlyInput)) ? scope.$eval(attrs.readonlyInput) : timepickerConfig.readonlyInput;
2961
+ if ( ! scope.readonlyInput ) {
2962
+
2963
+ var invalidate = function(invalidHours, invalidMinutes) {
2964
+ ngModel.$setViewValue( null );
2965
+ ngModel.$setValidity('time', false);
2966
+ if (angular.isDefined(invalidHours)) {
2967
+ scope.invalidHours = invalidHours;
2968
+ }
2969
+ if (angular.isDefined(invalidMinutes)) {
2970
+ scope.invalidMinutes = invalidMinutes;
2971
+ }
2972
+ };
2973
+
2974
+ scope.updateHours = function() {
2975
+ var hours = getHoursFromTemplate();
2976
+
2977
+ if ( angular.isDefined(hours) ) {
2978
+ selected.setHours( hours );
2979
+ refresh( 'h' );
2980
+ } else {
2981
+ invalidate(true);
2982
+ }
2983
+ };
2984
+
2985
+ hoursInputEl.bind('blur', function(e) {
2986
+ if ( !scope.validHours && scope.hours < 10) {
2987
+ scope.$apply( function() {
2988
+ scope.hours = pad( scope.hours );
2989
+ });
2990
+ }
2991
+ });
2992
+
2993
+ scope.updateMinutes = function() {
2994
+ var minutes = getMinutesFromTemplate();
2995
+
2996
+ if ( angular.isDefined(minutes) ) {
2997
+ selected.setMinutes( minutes );
2998
+ refresh( 'm' );
2999
+ } else {
3000
+ invalidate(undefined, true);
3001
+ }
3002
+ };
3003
+
3004
+ minutesInputEl.bind('blur', function(e) {
3005
+ if ( !scope.invalidMinutes && scope.minutes < 10 ) {
3006
+ scope.$apply( function() {
3007
+ scope.minutes = pad( scope.minutes );
3008
+ });
3009
+ }
3010
+ });
3011
+ } else {
3012
+ scope.updateHours = angular.noop;
3013
+ scope.updateMinutes = angular.noop;
3014
+ }
3015
+
3016
+ ngModel.$render = function() {
3017
+ var date = ngModel.$modelValue ? new Date( ngModel.$modelValue ) : null;
3018
+
3019
+ if ( isNaN(date) ) {
3020
+ ngModel.$setValidity('time', false);
3021
+ $log.error('Timepicker directive: "ng-model" value must be a Date object, a number of milliseconds since 01.01.1970 or a string representing an RFC2822 or ISO 8601 date.');
3022
+ } else {
3023
+ if ( date ) {
3024
+ selected = date;
3025
+ }
3026
+ makeValid();
3027
+ updateTemplate();
3028
+ }
3029
+ };
3030
+
3031
+ // Call internally when we know that model is valid.
3032
+ function refresh( keyboardChange ) {
3033
+ makeValid();
3034
+ ngModel.$setViewValue( new Date(selected) );
3035
+ updateTemplate( keyboardChange );
3036
+ }
3037
+
3038
+ function makeValid() {
3039
+ ngModel.$setValidity('time', true);
3040
+ scope.invalidHours = false;
3041
+ scope.invalidMinutes = false;
3042
+ }
3043
+
3044
+ function updateTemplate( keyboardChange ) {
3045
+ var hours = selected.getHours(), minutes = selected.getMinutes();
3046
+
3047
+ if ( scope.showMeridian ) {
3048
+ hours = ( hours === 0 || hours === 12 ) ? 12 : hours % 12; // Convert 24 to 12 hour system
3049
+ }
3050
+ scope.hours = keyboardChange === 'h' ? hours : pad(hours);
3051
+ scope.minutes = keyboardChange === 'm' ? minutes : pad(minutes);
3052
+ scope.meridian = selected.getHours() < 12 ? meridians[0] : meridians[1];
3053
+ }
3054
+
3055
+ function addMinutes( minutes ) {
3056
+ var dt = new Date( selected.getTime() + minutes * 60000 );
3057
+ selected.setHours( dt.getHours(), dt.getMinutes() );
3058
+ refresh();
3059
+ }
3060
+
3061
+ scope.incrementHours = function() {
3062
+ addMinutes( hourStep * 60 );
3063
+ };
3064
+ scope.decrementHours = function() {
3065
+ addMinutes( - hourStep * 60 );
3066
+ };
3067
+ scope.incrementMinutes = function() {
3068
+ addMinutes( minuteStep );
3069
+ };
3070
+ scope.decrementMinutes = function() {
3071
+ addMinutes( - minuteStep );
3072
+ };
3073
+ scope.toggleMeridian = function() {
3074
+ addMinutes( 12 * 60 * (( selected.getHours() < 12 ) ? 1 : -1) );
3075
+ };
3076
+ }
3077
+ };
3078
+ }]);
3079
+
3080
+ angular.module('ui.bootstrap.typeahead', ['ui.bootstrap.position', 'ui.bootstrap.bindHtml'])
3081
+
3082
+ /**
3083
+ * A helper service that can parse typeahead's syntax (string provided by users)
3084
+ * Extracted to a separate service for ease of unit testing
3085
+ */
3086
+ .factory('typeaheadParser', ['$parse', function ($parse) {
3087
+
3088
+ // 00000111000000000000022200000000000000003333333333333330000000000044000
3089
+ var TYPEAHEAD_REGEXP = /^\s*(.*?)(?:\s+as\s+(.*?))?\s+for\s+(?:([\$\w][\$\w\d]*))\s+in\s+(.*)$/;
3090
+
3091
+ return {
3092
+ parse:function (input) {
3093
+
3094
+ var match = input.match(TYPEAHEAD_REGEXP), modelMapper, viewMapper, source;
3095
+ if (!match) {
3096
+ throw new Error(
3097
+ "Expected typeahead specification in form of '_modelValue_ (as _label_)? for _item_ in _collection_'" +
3098
+ " but got '" + input + "'.");
3099
+ }
3100
+
3101
+ return {
3102
+ itemName:match[3],
3103
+ source:$parse(match[4]),
3104
+ viewMapper:$parse(match[2] || match[1]),
3105
+ modelMapper:$parse(match[1])
3106
+ };
3107
+ }
3108
+ };
3109
+ }])
3110
+
3111
+ .directive('typeahead', ['$compile', '$parse', '$q', '$timeout', '$document', '$position', 'typeaheadParser',
3112
+ function ($compile, $parse, $q, $timeout, $document, $position, typeaheadParser) {
3113
+
3114
+ var HOT_KEYS = [9, 13, 27, 38, 40];
3115
+
3116
+ return {
3117
+ require:'ngModel',
3118
+ link:function (originalScope, element, attrs, modelCtrl) {
3119
+
3120
+ //SUPPORTED ATTRIBUTES (OPTIONS)
3121
+
3122
+ //minimal no of characters that needs to be entered before typeahead kicks-in
3123
+ var minSearch = originalScope.$eval(attrs.typeaheadMinLength) || 1;
3124
+
3125
+ //minimal wait time after last character typed before typehead kicks-in
3126
+ var waitTime = originalScope.$eval(attrs.typeaheadWaitMs) || 0;
3127
+
3128
+ //should it restrict model values to the ones selected from the popup only?
3129
+ var isEditable = originalScope.$eval(attrs.typeaheadEditable) !== false;
3130
+
3131
+ //binding to a variable that indicates if matches are being retrieved asynchronously
3132
+ var isLoadingSetter = $parse(attrs.typeaheadLoading).assign || angular.noop;
3133
+
3134
+ //a callback executed when a match is selected
3135
+ var onSelectCallback = $parse(attrs.typeaheadOnSelect);
3136
+
3137
+ var inputFormatter = attrs.typeaheadInputFormatter ? $parse(attrs.typeaheadInputFormatter) : undefined;
3138
+
3139
+ var appendToBody = attrs.typeaheadAppendToBody ? $parse(attrs.typeaheadAppendToBody) : false;
3140
+
3141
+ //INTERNAL VARIABLES
3142
+
3143
+ //model setter executed upon match selection
3144
+ var $setModelValue = $parse(attrs.ngModel).assign;
3145
+
3146
+ //expressions used by typeahead
3147
+ var parserResult = typeaheadParser.parse(attrs.typeahead);
3148
+
3149
+ var hasFocus;
3150
+
3151
+ //pop-up element used to display matches
3152
+ var popUpEl = angular.element('<div typeahead-popup></div>');
3153
+ popUpEl.attr({
3154
+ matches: 'matches',
3155
+ active: 'activeIdx',
3156
+ select: 'select(activeIdx)',
3157
+ query: 'query',
3158
+ position: 'position'
3159
+ });
3160
+ //custom item template
3161
+ if (angular.isDefined(attrs.typeaheadTemplateUrl)) {
3162
+ popUpEl.attr('template-url', attrs.typeaheadTemplateUrl);
3163
+ }
3164
+
3165
+ //create a child scope for the typeahead directive so we are not polluting original scope
3166
+ //with typeahead-specific data (matches, query etc.)
3167
+ var scope = originalScope.$new();
3168
+ originalScope.$on('$destroy', function(){
3169
+ scope.$destroy();
3170
+ });
3171
+
3172
+ var resetMatches = function() {
3173
+ scope.matches = [];
3174
+ scope.activeIdx = -1;
3175
+ };
3176
+
3177
+ var getMatchesAsync = function(inputValue) {
3178
+
3179
+ var locals = {$viewValue: inputValue};
3180
+ isLoadingSetter(originalScope, true);
3181
+ $q.when(parserResult.source(originalScope, locals)).then(function(matches) {
3182
+
3183
+ //it might happen that several async queries were in progress if a user were typing fast
3184
+ //but we are interested only in responses that correspond to the current view value
3185
+ if (inputValue === modelCtrl.$viewValue && hasFocus) {
3186
+ if (matches.length > 0) {
3187
+
3188
+ scope.activeIdx = 0;
3189
+ scope.matches.length = 0;
3190
+
3191
+ //transform labels
3192
+ for(var i=0; i<matches.length; i++) {
3193
+ locals[parserResult.itemName] = matches[i];
3194
+ scope.matches.push({
3195
+ label: parserResult.viewMapper(scope, locals),
3196
+ model: matches[i]
3197
+ });
3198
+ }
3199
+
3200
+ scope.query = inputValue;
3201
+ //position pop-up with matches - we need to re-calculate its position each time we are opening a window
3202
+ //with matches as a pop-up might be absolute-positioned and position of an input might have changed on a page
3203
+ //due to other elements being rendered
3204
+ scope.position = appendToBody ? $position.offset(element) : $position.position(element);
3205
+ scope.position.top = scope.position.top + element.prop('offsetHeight');
3206
+
3207
+ } else {
3208
+ resetMatches();
3209
+ }
3210
+ isLoadingSetter(originalScope, false);
3211
+ }
3212
+ }, function(){
3213
+ resetMatches();
3214
+ isLoadingSetter(originalScope, false);
3215
+ });
3216
+ };
3217
+
3218
+ resetMatches();
3219
+
3220
+ //we need to propagate user's query so we can higlight matches
3221
+ scope.query = undefined;
3222
+
3223
+ //Declare the timeout promise var outside the function scope so that stacked calls can be cancelled later
3224
+ var timeoutPromise;
3225
+
3226
+ //plug into $parsers pipeline to open a typeahead on view changes initiated from DOM
3227
+ //$parsers kick-in on all the changes coming from the view as well as manually triggered by $setViewValue
3228
+ modelCtrl.$parsers.unshift(function (inputValue) {
3229
+
3230
+ hasFocus = true;
3231
+
3232
+ if (inputValue && inputValue.length >= minSearch) {
3233
+ if (waitTime > 0) {
3234
+ if (timeoutPromise) {
3235
+ $timeout.cancel(timeoutPromise);//cancel previous timeout
3236
+ }
3237
+ timeoutPromise = $timeout(function () {
3238
+ getMatchesAsync(inputValue);
3239
+ }, waitTime);
3240
+ } else {
3241
+ getMatchesAsync(inputValue);
3242
+ }
3243
+ } else {
3244
+ isLoadingSetter(originalScope, false);
3245
+ resetMatches();
3246
+ }
3247
+
3248
+ if (isEditable) {
3249
+ return inputValue;
3250
+ } else {
3251
+ if (!inputValue) {
3252
+ // Reset in case user had typed something previously.
3253
+ modelCtrl.$setValidity('editable', true);
3254
+ return inputValue;
3255
+ } else {
3256
+ modelCtrl.$setValidity('editable', false);
3257
+ return undefined;
3258
+ }
3259
+ }
3260
+ });
3261
+
3262
+ modelCtrl.$formatters.push(function (modelValue) {
3263
+
3264
+ var candidateViewValue, emptyViewValue;
3265
+ var locals = {};
3266
+
3267
+ if (inputFormatter) {
3268
+
3269
+ locals['$model'] = modelValue;
3270
+ return inputFormatter(originalScope, locals);
3271
+
3272
+ } else {
3273
+
3274
+ //it might happen that we don't have enough info to properly render input value
3275
+ //we need to check for this situation and simply return model value if we can't apply custom formatting
3276
+ locals[parserResult.itemName] = modelValue;
3277
+ candidateViewValue = parserResult.viewMapper(originalScope, locals);
3278
+ locals[parserResult.itemName] = undefined;
3279
+ emptyViewValue = parserResult.viewMapper(originalScope, locals);
3280
+
3281
+ return candidateViewValue!== emptyViewValue ? candidateViewValue : modelValue;
3282
+ }
3283
+ });
3284
+
3285
+ scope.select = function (activeIdx) {
3286
+ //called from within the $digest() cycle
3287
+ var locals = {};
3288
+ var model, item;
3289
+
3290
+ locals[parserResult.itemName] = item = scope.matches[activeIdx].model;
3291
+ model = parserResult.modelMapper(originalScope, locals);
3292
+ $setModelValue(originalScope, model);
3293
+ modelCtrl.$setValidity('editable', true);
3294
+
3295
+ onSelectCallback(originalScope, {
3296
+ $item: item,
3297
+ $model: model,
3298
+ $label: parserResult.viewMapper(originalScope, locals)
3299
+ });
3300
+
3301
+ resetMatches();
3302
+
3303
+ //return focus to the input element if a mach was selected via a mouse click event
3304
+ element[0].focus();
3305
+ };
3306
+
3307
+ //bind keyboard events: arrows up(38) / down(40), enter(13) and tab(9), esc(27)
3308
+ element.bind('keydown', function (evt) {
3309
+
3310
+ //typeahead is open and an "interesting" key was pressed
3311
+ if (scope.matches.length === 0 || HOT_KEYS.indexOf(evt.which) === -1) {
3312
+ return;
3313
+ }
3314
+
3315
+ evt.preventDefault();
3316
+
3317
+ if (evt.which === 40) {
3318
+ scope.activeIdx = (scope.activeIdx + 1) % scope.matches.length;
3319
+ scope.$digest();
3320
+
3321
+ } else if (evt.which === 38) {
3322
+ scope.activeIdx = (scope.activeIdx ? scope.activeIdx : scope.matches.length) - 1;
3323
+ scope.$digest();
3324
+
3325
+ } else if (evt.which === 13 || evt.which === 9) {
3326
+ scope.$apply(function () {
3327
+ scope.select(scope.activeIdx);
3328
+ });
3329
+
3330
+ } else if (evt.which === 27) {
3331
+ evt.stopPropagation();
3332
+
3333
+ resetMatches();
3334
+ scope.$digest();
3335
+ }
3336
+ });
3337
+
3338
+ element.bind('blur', function (evt) {
3339
+ hasFocus = false;
3340
+ });
3341
+
3342
+ // Keep reference to click handler to unbind it.
3343
+ var dismissClickHandler = function (evt) {
3344
+ if (element[0] !== evt.target) {
3345
+ resetMatches();
3346
+ scope.$digest();
3347
+ }
3348
+ };
3349
+
3350
+ $document.bind('click', dismissClickHandler);
3351
+
3352
+ originalScope.$on('$destroy', function(){
3353
+ $document.unbind('click', dismissClickHandler);
3354
+ });
3355
+
3356
+ var $popup = $compile(popUpEl)(scope);
3357
+ if ( appendToBody ) {
3358
+ $document.find('body').append($popup);
3359
+ } else {
3360
+ element.after($popup);
3361
+ }
3362
+ }
3363
+ };
3364
+
3365
+ }])
3366
+
3367
+ .directive('typeaheadPopup', function () {
3368
+ return {
3369
+ restrict:'EA',
3370
+ scope:{
3371
+ matches:'=',
3372
+ query:'=',
3373
+ active:'=',
3374
+ position:'=',
3375
+ select:'&'
3376
+ },
3377
+ replace:true,
3378
+ templateUrl:'template/typeahead/typeahead-popup.html',
3379
+ link:function (scope, element, attrs) {
3380
+
3381
+ scope.templateUrl = attrs.templateUrl;
3382
+
3383
+ scope.isOpen = function () {
3384
+ return scope.matches.length > 0;
3385
+ };
3386
+
3387
+ scope.isActive = function (matchIdx) {
3388
+ return scope.active == matchIdx;
3389
+ };
3390
+
3391
+ scope.selectActive = function (matchIdx) {
3392
+ scope.active = matchIdx;
3393
+ };
3394
+
3395
+ scope.selectMatch = function (activeIdx) {
3396
+ scope.select({activeIdx:activeIdx});
3397
+ };
3398
+ }
3399
+ };
3400
+ })
3401
+
3402
+ .directive('typeaheadMatch', ['$http', '$templateCache', '$compile', '$parse', function ($http, $templateCache, $compile, $parse) {
3403
+ return {
3404
+ restrict:'EA',
3405
+ scope:{
3406
+ index:'=',
3407
+ match:'=',
3408
+ query:'='
3409
+ },
3410
+ link:function (scope, element, attrs) {
3411
+ var tplUrl = $parse(attrs.templateUrl)(scope.$parent) || 'template/typeahead/typeahead-match.html';
3412
+ $http.get(tplUrl, {cache: $templateCache}).success(function(tplContent){
3413
+ element.replaceWith($compile(tplContent.trim())(scope));
3414
+ });
3415
+ }
3416
+ };
3417
+ }])
3418
+
3419
+ .filter('typeaheadHighlight', function() {
3420
+
3421
+ function escapeRegexp(queryToEscape) {
3422
+ return queryToEscape.replace(/([.?*+^$[\]\\(){}|-])/g, "\\$1");
3423
+ }
3424
+
3425
+ return function(matchItem, query) {
3426
+ return query ? matchItem.replace(new RegExp(escapeRegexp(query), 'gi'), '<strong>$&</strong>') : matchItem;
3427
+ };
3428
+ });
3429
+ angular.module("template/accordion/accordion-group.html", []).run(["$templateCache", function($templateCache) {
3430
+ $templateCache.put("template/accordion/accordion-group.html",
3431
+ "<div class=\"panel panel-default\">\n" +
3432
+ " <div class=\"panel-heading\">\n" +
3433
+ " <h4 class=\"panel-title\">\n" +
3434
+ " <a class=\"accordion-toggle\" ng-click=\"isOpen = !isOpen\" accordion-transclude=\"heading\">{{heading}}</a>\n" +
3435
+ " </h4>\n" +
3436
+ " </div>\n" +
3437
+ " <div class=\"panel-collapse\" collapse=\"!isOpen\">\n" +
3438
+ " <div class=\"panel-body\" ng-transclude></div>\n" +
3439
+ " </div>\n" +
3440
+ "</div>");
3441
+ }]);
3442
+
3443
+ angular.module("template/accordion/accordion.html", []).run(["$templateCache", function($templateCache) {
3444
+ $templateCache.put("template/accordion/accordion.html",
3445
+ "<div class=\"panel-group\" ng-transclude></div>");
3446
+ }]);
3447
+
3448
+ angular.module("template/alert/alert.html", []).run(["$templateCache", function($templateCache) {
3449
+ $templateCache.put("template/alert/alert.html",
3450
+ "<div class='alert' ng-class='\"alert-\" + (type || \"warning\")'>\n" +
3451
+ " <button ng-show='closeable' type='button' class='close' ng-click='close()'>&times;</button>\n" +
3452
+ " <div ng-transclude></div>\n" +
3453
+ "</div>\n" +
3454
+ "");
3455
+ }]);
3456
+
3457
+ angular.module("template/carousel/carousel.html", []).run(["$templateCache", function($templateCache) {
3458
+ $templateCache.put("template/carousel/carousel.html",
3459
+ "<div ng-mouseenter=\"pause()\" ng-mouseleave=\"play()\" class=\"carousel\">\n" +
3460
+ " <ol class=\"carousel-indicators\" ng-show=\"slides().length > 1\">\n" +
3461
+ " <li ng-repeat=\"slide in slides()\" ng-class=\"{active: isActive(slide)}\" ng-click=\"select(slide)\"></li>\n" +
3462
+ " </ol>\n" +
3463
+ " <div class=\"carousel-inner\" ng-transclude></div>\n" +
3464
+ " <a class=\"left carousel-control\" ng-click=\"prev()\" ng-show=\"slides().length > 1\"><span class=\"icon-prev\"></span></a>\n" +
3465
+ " <a class=\"right carousel-control\" ng-click=\"next()\" ng-show=\"slides().length > 1\"><span class=\"icon-next\"></span></a>\n" +
3466
+ "</div>\n" +
3467
+ "");
3468
+ }]);
3469
+
3470
+ angular.module("template/carousel/slide.html", []).run(["$templateCache", function($templateCache) {
3471
+ $templateCache.put("template/carousel/slide.html",
3472
+ "<div ng-class=\"{\n" +
3473
+ " 'active': leaving || (active && !entering),\n" +
3474
+ " 'prev': (next || active) && direction=='prev',\n" +
3475
+ " 'next': (next || active) && direction=='next',\n" +
3476
+ " 'right': direction=='prev',\n" +
3477
+ " 'left': direction=='next'\n" +
3478
+ " }\" class=\"item text-center\" ng-transclude></div>\n" +
3479
+ "");
3480
+ }]);
3481
+
3482
+ angular.module("template/datepicker/datepicker.html", []).run(["$templateCache", function($templateCache) {
3483
+ $templateCache.put("template/datepicker/datepicker.html",
3484
+ "<table>\n" +
3485
+ " <thead>\n" +
3486
+ " <tr>\n" +
3487
+ " <th><button type=\"button\" class=\"btn btn-default btn-sm pull-left\" ng-click=\"move(-1)\"><i class=\"glyphicon glyphicon-chevron-left\"></i></button></th>\n" +
3488
+ " <th colspan=\"{{rows[0].length - 2 + showWeekNumbers}}\"><button type=\"button\" class=\"btn btn-default btn-sm btn-block\" ng-click=\"toggleMode()\"><strong>{{title}}</strong></button></th>\n" +
3489
+ " <th><button type=\"button\" class=\"btn btn-default btn-sm pull-right\" ng-click=\"move(1)\"><i class=\"glyphicon glyphicon-chevron-right\"></i></button></th>\n" +
3490
+ " </tr>\n" +
3491
+ " <tr ng-show=\"labels.length > 0\" class=\"h6\">\n" +
3492
+ " <th ng-show=\"showWeekNumbers\" class=\"text-center\">#</th>\n" +
3493
+ " <th ng-repeat=\"label in labels\" class=\"text-center\">{{label}}</th>\n" +
3494
+ " </tr>\n" +
3495
+ " </thead>\n" +
3496
+ " <tbody>\n" +
3497
+ " <tr ng-repeat=\"row in rows\">\n" +
3498
+ " <td ng-show=\"showWeekNumbers\" class=\"text-center\"><em>{{ getWeekNumber(row) }}</em></td>\n" +
3499
+ " <td ng-repeat=\"dt in row\" class=\"text-center\">\n" +
3500
+ " <button type=\"button\" style=\"width:100%;\" class=\"btn btn-default btn-sm\" ng-class=\"{'btn-info': dt.selected}\" ng-click=\"select(dt.date)\" ng-disabled=\"dt.disabled\"><span ng-class=\"{'text-muted': dt.secondary}\">{{dt.label}}</span></button>\n" +
3501
+ " </td>\n" +
3502
+ " </tr>\n" +
3503
+ " </tbody>\n" +
3504
+ "</table>\n" +
3505
+ "");
3506
+ }]);
3507
+
3508
+ angular.module("template/datepicker/popup.html", []).run(["$templateCache", function($templateCache) {
3509
+ $templateCache.put("template/datepicker/popup.html",
3510
+ "<ul class=\"dropdown-menu\" ng-style=\"{display: (isOpen && 'block') || 'none', top: position.top+'px', left: position.left+'px'}\">\n" +
3511
+ " <li ng-transclude></li>\n" +
3512
+ " <li ng-show=\"showButtonBar\" style=\"padding:10px 9px 2px\">\n" +
3513
+ " <span class=\"btn-group\">\n" +
3514
+ " <button type=\"button\" class=\"btn btn-sm btn-info\" ng-click=\"today()\">{{currentText}}</button>\n" +
3515
+ " <button type=\"button\" class=\"btn btn-sm btn-default\" ng-click=\"showWeeks = ! showWeeks\" ng-class=\"{active: showWeeks}\">{{toggleWeeksText}}</button>\n" +
3516
+ " <button type=\"button\" class=\"btn btn-sm btn-danger\" ng-click=\"clear()\">{{clearText}}</button>\n" +
3517
+ " </span>\n" +
3518
+ " <button type=\"button\" class=\"btn btn-sm btn-success pull-right\" ng-click=\"isOpen = false\">{{closeText}}</button>\n" +
3519
+ " </li>\n" +
3520
+ "</ul>\n" +
3521
+ "");
3522
+ }]);
3523
+
3524
+ angular.module("template/modal/backdrop.html", []).run(["$templateCache", function($templateCache) {
3525
+ $templateCache.put("template/modal/backdrop.html",
3526
+ "<div class=\"modal-backdrop fade\" ng-class=\"{in: animate}\" ng-style=\"{'z-index': 1040 + index*10}\"></div>");
3527
+ }]);
3528
+
3529
+ angular.module("template/modal/window.html", []).run(["$templateCache", function($templateCache) {
3530
+ $templateCache.put("template/modal/window.html",
3531
+ "<div tabindex=\"-1\" class=\"modal fade {{ windowClass }}\" ng-class=\"{in: animate}\" ng-style=\"{'z-index': 1050 + index*10, display: 'block'}\" ng-click=\"close($event)\">\n" +
3532
+ " <div class=\"modal-dialog\"><div class=\"modal-content\" ng-transclude></div></div>\n" +
3533
+ "</div>");
3534
+ }]);
3535
+
3536
+ angular.module("template/pagination/pager.html", []).run(["$templateCache", function($templateCache) {
3537
+ $templateCache.put("template/pagination/pager.html",
3538
+ "<ul class=\"pager\">\n" +
3539
+ " <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" +
3540
+ "</ul>");
3541
+ }]);
3542
+
3543
+ angular.module("template/pagination/pagination.html", []).run(["$templateCache", function($templateCache) {
3544
+ $templateCache.put("template/pagination/pagination.html",
3545
+ "<ul class=\"pagination\">\n" +
3546
+ " <li ng-repeat=\"page in pages\" ng-class=\"{active: page.active, disabled: page.disabled}\"><a ng-click=\"selectPage(page.number)\">{{page.text}}</a></li>\n" +
3547
+ "</ul>");
3548
+ }]);
3549
+
3550
+ angular.module("template/tooltip/tooltip-html-unsafe-popup.html", []).run(["$templateCache", function($templateCache) {
3551
+ $templateCache.put("template/tooltip/tooltip-html-unsafe-popup.html",
3552
+ "<div class=\"tooltip {{placement}}\" ng-class=\"{ in: isOpen(), fade: animation() }\">\n" +
3553
+ " <div class=\"tooltip-arrow\"></div>\n" +
3554
+ " <div class=\"tooltip-inner\" bind-html-unsafe=\"content\"></div>\n" +
3555
+ "</div>\n" +
3556
+ "");
3557
+ }]);
3558
+
3559
+ angular.module("template/tooltip/tooltip-popup.html", []).run(["$templateCache", function($templateCache) {
3560
+ $templateCache.put("template/tooltip/tooltip-popup.html",
3561
+ "<div class=\"tooltip {{placement}}\" ng-class=\"{ in: isOpen(), fade: animation() }\">\n" +
3562
+ " <div class=\"tooltip-arrow\"></div>\n" +
3563
+ " <div class=\"tooltip-inner\" ng-bind=\"content\"></div>\n" +
3564
+ "</div>\n" +
3565
+ "");
3566
+ }]);
3567
+
3568
+ angular.module("template/popover/popover.html", []).run(["$templateCache", function($templateCache) {
3569
+ $templateCache.put("template/popover/popover.html",
3570
+ "<div class=\"popover {{placement}}\" ng-class=\"{ in: isOpen(), fade: animation() }\">\n" +
3571
+ " <div class=\"arrow\"></div>\n" +
3572
+ "\n" +
3573
+ " <div class=\"popover-inner\">\n" +
3574
+ " <h3 class=\"popover-title\" ng-bind=\"title\" ng-show=\"title\"></h3>\n" +
3575
+ " <div class=\"popover-content\" ng-bind=\"content\"></div>\n" +
3576
+ " </div>\n" +
3577
+ "</div>\n" +
3578
+ "");
3579
+ }]);
3580
+
3581
+ angular.module("template/progressbar/bar.html", []).run(["$templateCache", function($templateCache) {
3582
+ $templateCache.put("template/progressbar/bar.html",
3583
+ "<div class=\"progress-bar\" ng-class=\"type && 'progress-bar-' + type\" ng-transclude></div>");
3584
+ }]);
3585
+
3586
+ angular.module("template/progressbar/progress.html", []).run(["$templateCache", function($templateCache) {
3587
+ $templateCache.put("template/progressbar/progress.html",
3588
+ "<div class=\"progress\" ng-transclude></div>");
3589
+ }]);
3590
+
3591
+ angular.module("template/progressbar/progressbar.html", []).run(["$templateCache", function($templateCache) {
3592
+ $templateCache.put("template/progressbar/progressbar.html",
3593
+ "<div class=\"progress\"><div class=\"progress-bar\" ng-class=\"type && 'progress-bar-' + type\" ng-transclude></div></div>");
3594
+ }]);
3595
+
3596
+ angular.module("template/rating/rating.html", []).run(["$templateCache", function($templateCache) {
3597
+ $templateCache.put("template/rating/rating.html",
3598
+ "<span ng-mouseleave=\"reset()\">\n" +
3599
+ " <i ng-repeat=\"r in range\" ng-mouseenter=\"enter($index + 1)\" ng-click=\"rate($index + 1)\" class=\"glyphicon\" ng-class=\"$index < val && (r.stateOn || 'glyphicon-star') || (r.stateOff || 'glyphicon-star-empty')\"></i>\n" +
3600
+ "</span>");
3601
+ }]);
3602
+
3603
+ angular.module("template/tabs/tab.html", []).run(["$templateCache", function($templateCache) {
3604
+ $templateCache.put("template/tabs/tab.html",
3605
+ "<li ng-class=\"{active: active, disabled: disabled}\">\n" +
3606
+ " <a ng-click=\"select()\" tab-heading-transclude>{{heading}}</a>\n" +
3607
+ "</li>\n" +
3608
+ "");
3609
+ }]);
3610
+
3611
+ angular.module("template/tabs/tabset-titles.html", []).run(["$templateCache", function($templateCache) {
3612
+ $templateCache.put("template/tabs/tabset-titles.html",
3613
+ "<ul class=\"nav {{type && 'nav-' + type}}\" ng-class=\"{'nav-stacked': vertical}\">\n" +
3614
+ "</ul>\n" +
3615
+ "");
3616
+ }]);
3617
+
3618
+ angular.module("template/tabs/tabset.html", []).run(["$templateCache", function($templateCache) {
3619
+ $templateCache.put("template/tabs/tabset.html",
3620
+ "\n" +
3621
+ "<div class=\"tabbable\">\n" +
3622
+ " <ul class=\"nav {{type && 'nav-' + type}}\" ng-class=\"{'nav-stacked': vertical, 'nav-justified': justified}\" ng-transclude></ul>\n" +
3623
+ " <div class=\"tab-content\">\n" +
3624
+ " <div class=\"tab-pane\" \n" +
3625
+ " ng-repeat=\"tab in tabs\" \n" +
3626
+ " ng-class=\"{active: tab.active}\"\n" +
3627
+ " tab-content-transclude=\"tab\">\n" +
3628
+ " </div>\n" +
3629
+ " </div>\n" +
3630
+ "</div>\n" +
3631
+ "");
3632
+ }]);
3633
+
3634
+ angular.module("template/timepicker/timepicker.html", []).run(["$templateCache", function($templateCache) {
3635
+ $templateCache.put("template/timepicker/timepicker.html",
3636
+ "<table>\n" +
3637
+ " <tbody>\n" +
3638
+ " <tr class=\"text-center\">\n" +
3639
+ " <td><a ng-click=\"incrementHours()\" class=\"btn btn-link\"><span class=\"glyphicon glyphicon-chevron-up\"></span></a></td>\n" +
3640
+ " <td>&nbsp;</td>\n" +
3641
+ " <td><a ng-click=\"incrementMinutes()\" class=\"btn btn-link\"><span class=\"glyphicon glyphicon-chevron-up\"></span></a></td>\n" +
3642
+ " <td ng-show=\"showMeridian\"></td>\n" +
3643
+ " </tr>\n" +
3644
+ " <tr>\n" +
3645
+ " <td style=\"width:50px;\" class=\"form-group\" ng-class=\"{'has-error': invalidHours}\">\n" +
3646
+ " <input type=\"text\" ng-model=\"hours\" ng-change=\"updateHours()\" class=\"form-control text-center\" ng-mousewheel=\"incrementHours()\" ng-readonly=\"readonlyInput\" maxlength=\"2\">\n" +
3647
+ " </td>\n" +
3648
+ " <td>:</td>\n" +
3649
+ " <td style=\"width:50px;\" class=\"form-group\" ng-class=\"{'has-error': invalidMinutes}\">\n" +
3650
+ " <input type=\"text\" ng-model=\"minutes\" ng-change=\"updateMinutes()\" class=\"form-control text-center\" ng-readonly=\"readonlyInput\" maxlength=\"2\">\n" +
3651
+ " </td>\n" +
3652
+ " <td ng-show=\"showMeridian\"><button type=\"button\" class=\"btn btn-default text-center\" ng-click=\"toggleMeridian()\">{{meridian}}</button></td>\n" +
3653
+ " </tr>\n" +
3654
+ " <tr class=\"text-center\">\n" +
3655
+ " <td><a ng-click=\"decrementHours()\" class=\"btn btn-link\"><span class=\"glyphicon glyphicon-chevron-down\"></span></a></td>\n" +
3656
+ " <td>&nbsp;</td>\n" +
3657
+ " <td><a ng-click=\"decrementMinutes()\" class=\"btn btn-link\"><span class=\"glyphicon glyphicon-chevron-down\"></span></a></td>\n" +
3658
+ " <td ng-show=\"showMeridian\"></td>\n" +
3659
+ " </tr>\n" +
3660
+ " </tbody>\n" +
3661
+ "</table>\n" +
3662
+ "");
3663
+ }]);
3664
+
3665
+ angular.module("template/typeahead/typeahead-match.html", []).run(["$templateCache", function($templateCache) {
3666
+ $templateCache.put("template/typeahead/typeahead-match.html",
3667
+ "<a tabindex=\"-1\" bind-html-unsafe=\"match.label | typeaheadHighlight:query\"></a>");
3668
+ }]);
3669
+
3670
+ angular.module("template/typeahead/typeahead-popup.html", []).run(["$templateCache", function($templateCache) {
3671
+ $templateCache.put("template/typeahead/typeahead-popup.html",
3672
+ "<ul class=\"dropdown-menu\" ng-style=\"{display: isOpen()&&'block' || 'none', top: position.top+'px', left: position.left+'px'}\">\n" +
3673
+ " <li ng-repeat=\"match in matches\" ng-class=\"{active: isActive($index) }\" ng-mouseenter=\"selectActive($index)\" ng-click=\"selectMatch($index)\">\n" +
3674
+ " <div typeahead-match index=\"$index\" match=\"match\" query=\"query\" template-url=\"templateUrl\"></div>\n" +
3675
+ " </li>\n" +
3676
+ "</ul>");
3677
+ }]);