angular-ui-bootstrap-rails 0.1.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,1318 @@
1
+ angular.module("ui.bootstrap", ["ui.bootstrap.accordion","ui.bootstrap.alert","ui.bootstrap.carousel","ui.bootstrap.collapse","ui.bootstrap.dialog","ui.bootstrap.dropdownToggle","ui.bootstrap.modal","ui.bootstrap.pagination","ui.bootstrap.popover","ui.bootstrap.tabs","ui.bootstrap.tooltip","ui.bootstrap.transition"]);
2
+
3
+ angular.module('ui.bootstrap.accordion', ['ui.bootstrap.collapse'])
4
+
5
+ .constant('accordionConfig', {
6
+ closeOthers: true
7
+ })
8
+
9
+ .controller('AccordionController', ['$scope', '$attrs', 'accordionConfig', function ($scope, $attrs, accordionConfig) {
10
+
11
+ // This array keeps track of the accordion groups
12
+ this.groups = [];
13
+
14
+ // Ensure that all the groups in this accordion are closed, unless close-others explicitly says not to
15
+ this.closeOthers = function(openGroup) {
16
+ var closeOthers = angular.isDefined($attrs.closeOthers) ? $scope.$eval($attrs.closeOthers) : accordionConfig.closeOthers;
17
+ if ( closeOthers ) {
18
+ angular.forEach(this.groups, function (group) {
19
+ if ( group !== openGroup ) {
20
+ group.isOpen = false;
21
+ }
22
+ });
23
+ }
24
+ };
25
+
26
+ // This is called from the accordion-group directive to add itself to the accordion
27
+ this.addGroup = function(groupScope) {
28
+ var that = this;
29
+ this.groups.push(groupScope);
30
+
31
+ groupScope.$on('$destroy', function (event) {
32
+ that.removeGroup(groupScope);
33
+ });
34
+ };
35
+
36
+ // This is called from the accordion-group directive when to remove itself
37
+ this.removeGroup = function(group) {
38
+ var index = this.groups.indexOf(group);
39
+ if ( index !== -1 ) {
40
+ this.groups.splice(this.groups.indexOf(group), 1);
41
+ }
42
+ };
43
+
44
+ }]);
45
+
46
+ // The accordion directive simply sets up the directive controller
47
+ // and adds an accordion CSS class to itself element.
48
+ angular.module('ui.bootstrap.accordion').directive('accordion', function () {
49
+ return {
50
+ restrict:'EA',
51
+ controller:'AccordionController',
52
+ transclude: true,
53
+ replace: false,
54
+ templateUrl: 'template/accordion/accordion.html'
55
+ };
56
+ });
57
+
58
+ // The accordion-group directive indicates a block of html that will expand and collapse in an accordion
59
+ angular.module('ui.bootstrap.accordion').directive('accordionGroup', ['$parse', '$transition', '$timeout', function($parse, $transition, $timeout) {
60
+ return {
61
+ require:'^accordion', // We need this directive to be inside an accordion
62
+ restrict:'EA',
63
+ transclude:true, // It transcludes the contents of the directive into the template
64
+ replace: true, // The element containing the directive will be replaced with the template
65
+ templateUrl:'template/accordion/accordion-group.html',
66
+ scope:{ heading:'@' }, // Create an isolated scope and interpolate the heading attribute onto this scope
67
+ link: function(scope, element, attrs, accordionCtrl) {
68
+ var getIsOpen, setIsOpen;
69
+
70
+ accordionCtrl.addGroup(scope);
71
+
72
+ scope.isOpen = false;
73
+
74
+ if ( attrs.isOpen ) {
75
+ getIsOpen = $parse(attrs.isOpen);
76
+ setIsOpen = getIsOpen.assign;
77
+
78
+ scope.$watch(
79
+ function watchIsOpen() { return getIsOpen(scope.$parent); },
80
+ function updateOpen(value) { scope.isOpen = value; }
81
+ );
82
+
83
+ scope.isOpen = getIsOpen ? getIsOpen(scope.$parent) : false;
84
+ }
85
+
86
+ scope.$watch('isOpen', function(value) {
87
+ if ( value ) {
88
+ accordionCtrl.closeOthers(scope);
89
+ }
90
+ if ( setIsOpen ) {
91
+ setIsOpen(scope.$parent, value);
92
+ }
93
+ });
94
+
95
+ }
96
+ };
97
+ }]);
98
+
99
+ angular.module("ui.bootstrap.alert", []).directive('alert', function () {
100
+ return {
101
+ restrict:'EA',
102
+ templateUrl:'template/alert/alert.html',
103
+ transclude:true,
104
+ replace:true,
105
+ scope:{
106
+ type:'=',
107
+ close:'&'
108
+ }
109
+ };
110
+ });
111
+ /*
112
+ *
113
+ * Angular Bootstrap Carousel
114
+ *
115
+ * The carousel has all of the function that the original Bootstrap carousel has, except for animations.
116
+ *
117
+ * For no interval set the interval to non-number, or milliseconds of desired interval
118
+ * Template: <carousel interval="none"><slide>{{anything}}</slide></carousel>
119
+ * To change the carousel's active slide set the active attribute to true
120
+ * Template: <carousel interval="none"><slide active="someModel">{{anything}}</slide></carousel>
121
+ */
122
+ angular.module('ui.bootstrap.carousel', ['ui.bootstrap.transition'])
123
+ .controller('CarouselController', ['$scope', '$timeout', '$transition', '$q', function ($scope, $timeout, $transition, $q) {
124
+ var self = this,
125
+ slides = self.slides = [],
126
+ currentIndex = -1,
127
+ currentTimeout, isPlaying;
128
+ self.currentSlide = null;
129
+
130
+ /* direction: "prev" or "next" */
131
+ self.select = function(nextSlide, direction) {
132
+ var nextIndex = slides.indexOf(nextSlide);
133
+ //Decide direction if it's not given
134
+ if (direction === undefined) {
135
+ direction = nextIndex > currentIndex ? "next" : "prev";
136
+ }
137
+ if (nextSlide && nextSlide !== self.currentSlide) {
138
+ if ($scope.$currentTransition) {
139
+ $scope.$currentTransition.cancel();
140
+ //Timeout so ng-class in template has time to fix classes for finished slide
141
+ $timeout(goNext);
142
+ } else {
143
+ goNext();
144
+ }
145
+ }
146
+ function goNext() {
147
+ //If we have a slide to transition from and we have a transition type and we're allowed, go
148
+ if (self.currentSlide && angular.isString(direction) && !$scope.noTransition && nextSlide.$element) {
149
+ //We shouldn't do class manip in here, but it's the same weird thing bootstrap does. need to fix sometime
150
+ nextSlide.$element.addClass(direction);
151
+ nextSlide.$element[0].offsetWidth = nextSlide.$element[0].offsetWidth; //force reflow
152
+
153
+ //Set all other slides to stop doing their stuff for the new transition
154
+ angular.forEach(slides, function(slide) {
155
+ angular.extend(slide, {direction: '', entering: false, leaving: false, active: false});
156
+ });
157
+ angular.extend(nextSlide, {direction: direction, active: true, entering: true});
158
+ angular.extend(self.currentSlide||{}, {direction: direction, leaving: true});
159
+
160
+ $scope.$currentTransition = $transition(nextSlide.$element, {});
161
+ //We have to create new pointers inside a closure since next & current will change
162
+ (function(next,current) {
163
+ $scope.$currentTransition.then(
164
+ function(){ transitionDone(next, current); },
165
+ function(){ transitionDone(next, current); }
166
+ );
167
+ }(nextSlide, self.currentSlide));
168
+ } else {
169
+ transitionDone(nextSlide, self.currentSlide);
170
+ }
171
+ self.currentSlide = nextSlide;
172
+ currentIndex = nextIndex;
173
+ //every time you change slides, reset the timer
174
+ restartTimer();
175
+ }
176
+ function transitionDone(next, current) {
177
+ angular.extend(next, {direction: '', active: true, leaving: false, entering: false});
178
+ angular.extend(current||{}, {direction: '', active: false, leaving: false, entering: false});
179
+ $scope.$currentTransition = null;
180
+ }
181
+ };
182
+
183
+ /* Allow outside people to call indexOf on slides array */
184
+ self.indexOfSlide = function(slide) {
185
+ return slides.indexOf(slide);
186
+ };
187
+
188
+ $scope.next = function() {
189
+ var newIndex = (currentIndex + 1) % slides.length;
190
+ return self.select(slides[newIndex], 'next');
191
+ };
192
+
193
+ $scope.prev = function() {
194
+ var newIndex = currentIndex - 1 < 0 ? slides.length - 1 : currentIndex - 1;
195
+ return self.select(slides[newIndex], 'prev');
196
+ };
197
+
198
+ $scope.$watch('interval', restartTimer);
199
+ function restartTimer() {
200
+ if (currentTimeout) {
201
+ $timeout.cancel(currentTimeout);
202
+ }
203
+ function go() {
204
+ if (isPlaying) {
205
+ $scope.next();
206
+ restartTimer();
207
+ } else {
208
+ $scope.pause();
209
+ }
210
+ }
211
+ var interval = +$scope.interval;
212
+ if (!isNaN(interval) && interval>=0) {
213
+ currentTimeout = $timeout(go, interval);
214
+ }
215
+ }
216
+ $scope.play = function() {
217
+ if (!isPlaying) {
218
+ isPlaying = true;
219
+ restartTimer();
220
+ }
221
+ };
222
+ $scope.pause = function() {
223
+ isPlaying = false;
224
+ if (currentTimeout) {
225
+ $timeout.cancel(currentTimeout);
226
+ }
227
+ };
228
+
229
+ self.addSlide = function(slide, element) {
230
+ slide.$element = element;
231
+ slides.push(slide);
232
+ //if this is the first slide or the slide is set to active, select it
233
+ if(slides.length === 1 || slide.active) {
234
+ self.select(slides[slides.length-1]);
235
+ if (slides.length == 1) {
236
+ $scope.play();
237
+ }
238
+ } else {
239
+ slide.active = false;
240
+ }
241
+ };
242
+
243
+ self.removeSlide = function(slide) {
244
+ //get the index of the slide inside the carousel
245
+ var index = slides.indexOf(slide);
246
+ slides.splice(index, 1);
247
+ if (slides.length > 0 && slide.active) {
248
+ if (index >= slides.length) {
249
+ self.select(slides[index-1]);
250
+ } else {
251
+ self.select(slides[index]);
252
+ }
253
+ }
254
+ };
255
+ }])
256
+ .directive('carousel', [function() {
257
+ return {
258
+ restrict: 'EA',
259
+ transclude: true,
260
+ replace: true,
261
+ controller: 'CarouselController',
262
+ require: 'carousel',
263
+ templateUrl: 'template/carousel/carousel.html',
264
+ scope: {
265
+ interval: '=',
266
+ noTransition: '='
267
+ }
268
+ };
269
+ }])
270
+ .directive('slide', [function() {
271
+ return {
272
+ require: '^carousel',
273
+ restrict: 'EA',
274
+ transclude: true,
275
+ replace: true,
276
+ templateUrl: 'template/carousel/slide.html',
277
+ scope: {
278
+ active: '='
279
+ },
280
+ link: function (scope, element, attrs, carouselCtrl) {
281
+ carouselCtrl.addSlide(scope, element);
282
+ //when the scope is destroyed then remove the slide from the current slides array
283
+ scope.$on('$destroy', function() {
284
+ carouselCtrl.removeSlide(scope);
285
+ });
286
+
287
+ scope.$watch('active', function(active) {
288
+ if (active) {
289
+ carouselCtrl.select(scope);
290
+ }
291
+ });
292
+ }
293
+ };
294
+ }]);
295
+
296
+ angular.module('ui.bootstrap.collapse',['ui.bootstrap.transition'])
297
+
298
+ // The collapsible directive indicates a block of html that will expand and collapse
299
+ .directive('collapse', ['$transition', function($transition) {
300
+ // CSS transitions don't work with height: auto, so we have to manually change the height to a
301
+ // specific value and then once the animation completes, we can reset the height to auto.
302
+ // Unfortunately if you do this while the CSS transitions are specified (i.e. in the CSS class
303
+ // "collapse") then you trigger a change to height 0 in between.
304
+ // The fix is to remove the "collapse" CSS class while changing the height back to auto - phew!
305
+ var fixUpHeight = function(scope, element, height) {
306
+ // We remove the collapse CSS class to prevent a transition when we change to height: auto
307
+ element.removeClass('collapse');
308
+ element.css({ height: height });
309
+ // It appears that reading offsetWidth makes the browser realise that we have changed the
310
+ // height already :-/
311
+ var x = element[0].offsetWidth;
312
+ element.addClass('collapse');
313
+ };
314
+
315
+ return {
316
+ link: function(scope, element, attrs) {
317
+
318
+ var isCollapsed;
319
+ var initialAnimSkip = true;
320
+ scope.$watch(function (){ return element[0].scrollHeight; }, function (value) {
321
+ //The listener is called when scollHeight changes
322
+ //It actually does on 2 scenarios:
323
+ // 1. Parent is set to display none
324
+ // 2. angular bindings inside are resolved
325
+ //When we have a change of scrollHeight we are setting again the correct height if the group is opened
326
+ if (element[0].scrollHeight !== 0) {
327
+ if (!isCollapsed) {
328
+ fixUpHeight(scope, element, element[0].scrollHeight + 'px');
329
+ }
330
+ }
331
+ });
332
+
333
+ scope.$watch(attrs.collapse, function(value) {
334
+ if (value) {
335
+ collapse();
336
+ } else {
337
+ expand();
338
+ }
339
+ });
340
+
341
+
342
+ var currentTransition;
343
+ var doTransition = function(change) {
344
+ if ( currentTransition ) {
345
+ currentTransition.cancel();
346
+ }
347
+ currentTransition = $transition(element,change);
348
+ currentTransition.then(
349
+ function() { currentTransition = undefined; },
350
+ function() { currentTransition = undefined; }
351
+ );
352
+ return currentTransition;
353
+ };
354
+
355
+ var expand = function() {
356
+ if (initialAnimSkip) {
357
+ initialAnimSkip = false;
358
+ if ( !isCollapsed ) {
359
+ fixUpHeight(scope, element, 'auto');
360
+ }
361
+ } else {
362
+ doTransition({ height : element[0].scrollHeight + 'px' })
363
+ .then(function() {
364
+ // This check ensures that we don't accidentally update the height if the user has closed
365
+ // the group while the animation was still running
366
+ if ( !isCollapsed ) {
367
+ fixUpHeight(scope, element, 'auto');
368
+ }
369
+ });
370
+ }
371
+ isCollapsed = false;
372
+ };
373
+
374
+ var collapse = function() {
375
+ isCollapsed = true;
376
+ if (initialAnimSkip) {
377
+ initialAnimSkip = false;
378
+ fixUpHeight(scope, element, 0);
379
+ } else {
380
+ fixUpHeight(scope, element, element[0].scrollHeight + 'px');
381
+ doTransition({'height':'0'});
382
+ }
383
+ };
384
+ }
385
+ };
386
+ }]);
387
+
388
+ // The `$dialogProvider` can be used to configure global defaults for your
389
+ // `$dialog` service.
390
+ var dialogModule = angular.module('ui.bootstrap.dialog', ['ui.bootstrap.transition']);
391
+
392
+ dialogModule.controller('MessageBoxController', ['$scope', 'dialog', 'model', function($scope, dialog, model){
393
+ $scope.title = model.title;
394
+ $scope.message = model.message;
395
+ $scope.buttons = model.buttons;
396
+ $scope.close = function(res){
397
+ dialog.close(res);
398
+ };
399
+ }]);
400
+
401
+ dialogModule.provider("$dialog", function(){
402
+
403
+ // The default options for all dialogs.
404
+ var defaults = {
405
+ backdrop: true,
406
+ modalClass: 'modal',
407
+ backdropClass: 'modal-backdrop',
408
+ transitionClass: 'fade',
409
+ triggerClass: 'in',
410
+ resolve:{},
411
+ backdropFade: false,
412
+ modalFade:false,
413
+ keyboard: true, // close with esc key
414
+ backdropClick: true // only in conjunction with backdrop=true
415
+ /* other options: template, templateUrl, controller */
416
+ };
417
+
418
+ var globalOptions = {};
419
+
420
+ // The `options({})` allows global configuration of all dialogs in the application.
421
+ //
422
+ // var app = angular.module('App', ['ui.bootstrap.dialog'], function($dialogProvider){
423
+ // // don't close dialog when backdrop is clicked by default
424
+ // $dialogProvider.options({backdropClick: false});
425
+ // });
426
+ this.options = function(value){
427
+ globalOptions = value;
428
+ };
429
+
430
+ // Returns the actual `$dialog` service that is injected in controllers
431
+ this.$get = ["$http", "$document", "$compile", "$rootScope", "$controller", "$templateCache", "$q", "$transition",
432
+ function ($http, $document, $compile, $rootScope, $controller, $templateCache, $q, $transition) {
433
+
434
+ var body = $document.find('body');
435
+
436
+ function createElement(clazz) {
437
+ var el = angular.element("<div>");
438
+ el.addClass(clazz);
439
+ return el;
440
+ }
441
+
442
+ // The `Dialog` class represents a modal dialog. The dialog class can be invoked by providing an options object
443
+ // containing at lest template or templateUrl and controller:
444
+ //
445
+ // var d = new Dialog({templateUrl: 'foo.html', controller: 'BarController'});
446
+ //
447
+ // Dialogs can also be created using templateUrl and controller as distinct arguments:
448
+ //
449
+ // var d = new Dialog('path/to/dialog.html', MyDialogController);
450
+ function Dialog(opts) {
451
+
452
+ var self = this, options = this.options = angular.extend({}, defaults, globalOptions, opts);
453
+
454
+ this.backdropEl = createElement(options.backdropClass);
455
+ if(options.backdropFade){
456
+ this.backdropEl.addClass(options.transitionClass);
457
+ this.backdropEl.removeClass(options.triggerClass);
458
+ }
459
+
460
+ this.modalEl = createElement(options.modalClass);
461
+ if(options.modalFade){
462
+ this.modalEl.addClass(options.transitionClass);
463
+ this.modalEl.removeClass(options.triggerClass);
464
+ }
465
+
466
+ this.handledEscapeKey = function(e) {
467
+ if (e.which === 27) {
468
+ self.close();
469
+ e.preventDefault();
470
+ self.$scope.$apply();
471
+ }
472
+ };
473
+
474
+ this.handleBackDropClick = function(e) {
475
+ self.close();
476
+ e.preventDefault();
477
+ self.$scope.$apply();
478
+ };
479
+ }
480
+
481
+ // The `isOpen()` method returns wether the dialog is currently visible.
482
+ Dialog.prototype.isOpen = function(){
483
+ return this._open;
484
+ };
485
+
486
+ // The `open(templateUrl, controller)` method opens the dialog.
487
+ // Use the `templateUrl` and `controller` arguments if specifying them at dialog creation time is not desired.
488
+ Dialog.prototype.open = function(templateUrl, controller){
489
+ var self = this, options = this.options;
490
+
491
+ if(templateUrl){
492
+ options.templateUrl = templateUrl;
493
+ }
494
+ if(controller){
495
+ options.controller = controller;
496
+ }
497
+
498
+ if(!(options.template || options.templateUrl)) {
499
+ throw new Error('Dialog.open expected template or templateUrl, neither found. Use options or open method to specify them.');
500
+ }
501
+
502
+ this._loadResolves().then(function(locals) {
503
+ var $scope = locals.$scope = self.$scope = $rootScope.$new();
504
+
505
+ self.modalEl.html(locals.$template);
506
+
507
+ if (self.options.controller) {
508
+ var ctrl = $controller(self.options.controller, locals);
509
+ self.modalEl.contents().data('ngControllerController', ctrl);
510
+ }
511
+
512
+ $compile(self.modalEl.contents())($scope);
513
+ self._addElementsToDom();
514
+
515
+ // trigger tranisitions
516
+ setTimeout(function(){
517
+ if(self.options.modalFade){ self.modalEl.addClass(self.options.triggerClass); }
518
+ if(self.options.backdropFade){ self.backdropEl.addClass(self.options.triggerClass); }
519
+ });
520
+
521
+ self._bindEvents();
522
+ });
523
+
524
+ this.deferred = $q.defer();
525
+ return this.deferred.promise;
526
+ };
527
+
528
+ // closes the dialog and resolves the promise returned by the `open` method with the specified result.
529
+ Dialog.prototype.close = function(result){
530
+ var self = this;
531
+ var fadingElements = this._getFadingElements();
532
+
533
+ if(fadingElements.length > 0){
534
+ for (var i = fadingElements.length - 1; i >= 0; i--) {
535
+ $transition(fadingElements[i], removeTriggerClass).then(onCloseComplete);
536
+ }
537
+ return;
538
+ }
539
+
540
+ this._onCloseComplete(result);
541
+
542
+ function removeTriggerClass(el){
543
+ el.removeClass(self.options.triggerClass);
544
+ }
545
+
546
+ function onCloseComplete(){
547
+ if(self._open){
548
+ self._onCloseComplete(result);
549
+ }
550
+ }
551
+ };
552
+
553
+ Dialog.prototype._getFadingElements = function(){
554
+ var elements = [];
555
+ if(this.options.modalFade){
556
+ elements.push(this.modalEl);
557
+ }
558
+ if(this.options.backdropFade){
559
+ elements.push(this.backdropEl);
560
+ }
561
+
562
+ return elements;
563
+ };
564
+
565
+ Dialog.prototype._bindEvents = function() {
566
+ if(this.options.keyboard){ body.bind('keydown', this.handledEscapeKey); }
567
+ if(this.options.backdrop && this.options.backdropClick){ this.backdropEl.bind('click', this.handleBackDropClick); }
568
+ };
569
+
570
+ Dialog.prototype._unbindEvents = function() {
571
+ if(this.options.keyboard){ body.unbind('keydown', this.handledEscapeKey); }
572
+ if(this.options.backdrop && this.options.backdropClick){ this.backdropEl.unbind('click', this.handleBackDropClick); }
573
+ };
574
+
575
+ Dialog.prototype._onCloseComplete = function(result) {
576
+ this._removeElementsFromDom();
577
+ this._unbindEvents();
578
+
579
+ this.deferred.resolve(result);
580
+ };
581
+
582
+ Dialog.prototype._addElementsToDom = function(){
583
+ body.append(this.modalEl);
584
+ if(this.options.backdrop) { body.append(this.backdropEl); }
585
+ this._open = true;
586
+ };
587
+
588
+ Dialog.prototype._removeElementsFromDom = function(){
589
+ this.modalEl.remove();
590
+ if(this.options.backdrop) { this.backdropEl.remove(); }
591
+ this._open = false;
592
+ };
593
+
594
+ // Loads all `options.resolve` members to be used as locals for the controller associated with the dialog.
595
+ Dialog.prototype._loadResolves = function(){
596
+ var values = [], keys = [], templatePromise, self = this;
597
+
598
+ if (this.options.template) {
599
+ templatePromise = $q.when(this.options.template);
600
+ } else if (this.options.templateUrl) {
601
+ templatePromise = $http.get(this.options.templateUrl, {cache:$templateCache})
602
+ .then(function(response) { return response.data; });
603
+ }
604
+
605
+ angular.forEach(this.options.resolve || [], function(value, key) {
606
+ keys.push(key);
607
+ values.push(value);
608
+ });
609
+
610
+ keys.push('$template');
611
+ values.push(templatePromise);
612
+
613
+ return $q.all(values).then(function(values) {
614
+ var locals = {};
615
+ angular.forEach(values, function(value, index) {
616
+ locals[keys[index]] = value;
617
+ });
618
+ locals.dialog = self;
619
+ return locals;
620
+ });
621
+ };
622
+
623
+ // The actual `$dialog` service that is injected in controllers.
624
+ return {
625
+ // Creates a new `Dialog` with the specified options.
626
+ dialog: function(opts){
627
+ return new Dialog(opts);
628
+ },
629
+ // creates a new `Dialog` tied to the default message box template and controller.
630
+ //
631
+ // Arguments `title` and `message` are rendered in the modal header and body sections respectively.
632
+ // The `buttons` array holds an object with the following members for each button to include in the
633
+ // modal footer section:
634
+ //
635
+ // * `result`: the result to pass to the `close` method of the dialog when the button is clicked
636
+ // * `label`: the label of the button
637
+ // * `cssClass`: additional css class(es) to apply to the button for styling
638
+ messageBox: function(title, message, buttons){
639
+ return new Dialog({templateUrl: 'template/dialog/message.html', controller: 'MessageBoxController', resolve: {model: {
640
+ title: title,
641
+ message: message,
642
+ buttons: buttons
643
+ }}});
644
+ }
645
+ };
646
+ }];
647
+ });
648
+
649
+ /*
650
+ * dropdownToggle - Provides dropdown menu functionality in place of bootstrap js
651
+ * @restrict class or attribute
652
+ * @example:
653
+ <li class="dropdown">
654
+ <a class="dropdown-toggle">My Dropdown Menu</a>
655
+ <ul class="dropdown-menu">
656
+ <li ng-repeat="choice in dropChoices">
657
+ <a ng-href="{{choice.href}}">{{choice.text}}</a>
658
+ </li>
659
+ </ul>
660
+ </li>
661
+ */
662
+
663
+ angular.module('ui.bootstrap.dropdownToggle', []).directive('dropdownToggle',
664
+ ['$document', '$location', '$window', function ($document, $location, $window) {
665
+ var openElement = null, close;
666
+ return {
667
+ restrict: 'CA',
668
+ link: function(scope, element, attrs) {
669
+ scope.$watch(function dropdownTogglePathWatch(){return $location.path();}, function dropdownTogglePathWatchAction() {
670
+ if (close) { close(); }
671
+ });
672
+
673
+ element.parent().bind('click', function(event) {
674
+ if (close) { close(); }
675
+ });
676
+
677
+ element.bind('click', function(event) {
678
+ event.preventDefault();
679
+ event.stopPropagation();
680
+
681
+ var iWasOpen = false;
682
+
683
+ if (openElement) {
684
+ iWasOpen = openElement === element;
685
+ close();
686
+ }
687
+
688
+ if (!iWasOpen){
689
+ element.parent().addClass('open');
690
+ openElement = element;
691
+
692
+ close = function (event) {
693
+ if (event) {
694
+ event.preventDefault();
695
+ event.stopPropagation();
696
+ }
697
+ $document.unbind('click', close);
698
+ element.parent().removeClass('open');
699
+ close = null;
700
+ openElement = null;
701
+ };
702
+
703
+ $document.bind('click', close);
704
+ }
705
+ });
706
+ }
707
+ };
708
+ }]);
709
+
710
+ angular.module('ui.bootstrap.modal', []).directive('modal', ['$parse',function($parse) {
711
+ var backdropEl;
712
+ var body = angular.element(document.getElementsByTagName('body')[0]);
713
+ var defaultOpts = {
714
+ backdrop: true,
715
+ escape: true
716
+ };
717
+ return {
718
+ restrict: 'EA',
719
+ link: function(scope, elm, attrs) {
720
+ var opts = angular.extend(defaultOpts, scope.$eval(attrs.uiOptions || attrs.bsOptions || attrs.options));
721
+ var shownExpr = attrs.modal || attrs.show;
722
+ var setClosed;
723
+
724
+ if (attrs.close) {
725
+ setClosed = function() {
726
+ scope.$apply(attrs.close);
727
+ };
728
+ } else {
729
+ setClosed = function() {
730
+ scope.$apply(function() {
731
+ $parse(shownExpr).assign(scope, false);
732
+ });
733
+ };
734
+ }
735
+ elm.addClass('modal');
736
+
737
+ if (opts.backdrop && !backdropEl) {
738
+ backdropEl = angular.element('<div class="modal-backdrop"></div>');
739
+ backdropEl.css('display','none');
740
+ body.append(backdropEl);
741
+ }
742
+
743
+ function setShown(shown) {
744
+ scope.$apply(function() {
745
+ model.assign(scope, shown);
746
+ });
747
+ }
748
+
749
+ function escapeClose(evt) {
750
+ if (evt.which === 27) { setClosed(); }
751
+ }
752
+ function clickClose() {
753
+ setClosed();
754
+ }
755
+
756
+ function close() {
757
+ if (opts.escape) { body.unbind('keyup', escapeClose); }
758
+ if (opts.backdrop) {
759
+ backdropEl.css('display', 'none').removeClass('in');
760
+ backdropEl.unbind('click', clickClose);
761
+ }
762
+ elm.css('display', 'none').removeClass('in');
763
+ body.removeClass('modal-open');
764
+ }
765
+ function open() {
766
+ if (opts.escape) { body.bind('keyup', escapeClose); }
767
+ if (opts.backdrop) {
768
+ backdropEl.css('display', 'block').addClass('in');
769
+ if(opts.backdrop != "static") {
770
+ backdropEl.bind('click', clickClose);
771
+ }
772
+ }
773
+ elm.css('display', 'block').addClass('in');
774
+ body.addClass('modal-open');
775
+ }
776
+
777
+ scope.$watch(shownExpr, function(isShown, oldShown) {
778
+ if (isShown) {
779
+ open();
780
+ } else {
781
+ close();
782
+ }
783
+ });
784
+ }
785
+ };
786
+ }]);
787
+
788
+ angular.module('ui.bootstrap.pagination', [])
789
+
790
+ .directive('pagination', function() {
791
+ return {
792
+ restrict: 'EA',
793
+ scope: {
794
+ numPages: '=',
795
+ currentPage: '=',
796
+ maxSize: '=',
797
+ onSelectPage: '&',
798
+ nextText: '@',
799
+ previousText: '@'
800
+ },
801
+ templateUrl: 'template/pagination/pagination.html',
802
+ replace: true,
803
+ link: function(scope) {
804
+ scope.$watch('numPages + currentPage + maxSize', function() {
805
+ scope.pages = [];
806
+
807
+ //set the default maxSize to numPages
808
+ var maxSize = ( scope.maxSize && scope.maxSize < scope.numPages ) ? scope.maxSize : scope.numPages;
809
+ var startPage = scope.currentPage - Math.floor(maxSize/2);
810
+
811
+ //adjust the startPage within boundary
812
+ if(startPage < 1) {
813
+ startPage = 1;
814
+ }
815
+ if ((startPage + maxSize - 1) > scope.numPages) {
816
+ startPage = startPage - ((startPage + maxSize - 1) - scope.numPages );
817
+ }
818
+
819
+ for(var i=0; i < maxSize && i < scope.numPages ;i++) {
820
+ scope.pages.push(startPage + i);
821
+ }
822
+ if ( scope.currentPage > scope.numPages ) {
823
+ scope.selectPage(scope.numPages);
824
+ }
825
+ });
826
+ scope.noPrevious = function() {
827
+ return scope.currentPage === 1;
828
+ };
829
+ scope.noNext = function() {
830
+ return scope.currentPage === scope.numPages;
831
+ };
832
+ scope.isActive = function(page) {
833
+ return scope.currentPage === page;
834
+ };
835
+
836
+ scope.selectPage = function(page) {
837
+ if ( ! scope.isActive(page) ) {
838
+ scope.currentPage = page;
839
+ scope.onSelectPage({ page: page });
840
+ }
841
+ };
842
+
843
+ scope.selectPrevious = function() {
844
+ if ( !scope.noPrevious() ) {
845
+ scope.selectPage(scope.currentPage-1);
846
+ }
847
+ };
848
+ scope.selectNext = function() {
849
+ if ( !scope.noNext() ) {
850
+ scope.selectPage(scope.currentPage+1);
851
+ }
852
+ };
853
+ }
854
+ };
855
+ });
856
+ /**
857
+ * The following features are still outstanding: popup delay, animation as a
858
+ * function, placement as a function, inside, support for more triggers than
859
+ * just mouse enter/leave, html popovers, and selector delegatation.
860
+ */
861
+ angular.module( 'ui.bootstrap.popover', [] )
862
+ .directive( 'popoverPopup', function () {
863
+ return {
864
+ restrict: 'EA',
865
+ replace: true,
866
+ scope: { popoverTitle: '@', popoverContent: '@', placement: '@', animation: '&', isOpen: '&' },
867
+ templateUrl: 'template/popover/popover.html'
868
+ };
869
+ })
870
+ .directive( 'popover', [ '$compile', '$timeout', '$parse', function ( $compile, $timeout, $parse ) {
871
+
872
+ var template =
873
+ '<popover-popup '+
874
+ 'popover-title="{{tt_title}}" '+
875
+ 'popover-content="{{tt_popover}}" '+
876
+ 'placement="{{tt_placement}}" '+
877
+ 'animation="tt_animation()" '+
878
+ 'is-open="tt_isOpen"'+
879
+ '>'+
880
+ '</popover-popup>';
881
+
882
+ return {
883
+ scope: true,
884
+ link: function ( scope, element, attr ) {
885
+ var popover = $compile( template )( scope ),
886
+ transitionTimeout;
887
+
888
+ attr.$observe( 'popover', function ( val ) {
889
+ scope.tt_popover = val;
890
+ });
891
+
892
+ attr.$observe( 'popoverTitle', function ( val ) {
893
+ scope.tt_title = val;
894
+ });
895
+
896
+ attr.$observe( 'popoverPlacement', function ( val ) {
897
+ // If no placement was provided, default to 'top'.
898
+ scope.tt_placement = val || 'top';
899
+ });
900
+
901
+ attr.$observe( 'popoverAnimation', function ( val ) {
902
+ scope.tt_animation = $parse( val );
903
+ });
904
+
905
+ // By default, the popover is not open.
906
+ scope.tt_isOpen = false;
907
+
908
+ // Calculate the current position and size of the directive element.
909
+ function getPosition() {
910
+ return {
911
+ width: element.prop( 'offsetWidth' ),
912
+ height: element.prop( 'offsetHeight' ),
913
+ top: element.prop( 'offsetTop' ),
914
+ left: element.prop( 'offsetLeft' )
915
+ };
916
+ }
917
+
918
+ // Show the popover popup element.
919
+ function show() {
920
+ var position,
921
+ ttWidth,
922
+ ttHeight,
923
+ ttPosition;
924
+
925
+ // If there is a pending remove transition, we must cancel it, lest the
926
+ // toolip be mysteriously removed.
927
+ if ( transitionTimeout ) {
928
+ $timeout.cancel( transitionTimeout );
929
+ }
930
+
931
+ // Set the initial positioning.
932
+ popover.css({ top: 0, left: 0, display: 'block' });
933
+
934
+ // Now we add it to the DOM because need some info about it. But it's not
935
+ // visible yet anyway.
936
+ element.after( popover );
937
+
938
+ // Get the position of the directive element.
939
+ position = getPosition();
940
+
941
+ // Get the height and width of the popover so we can center it.
942
+ ttWidth = popover.prop( 'offsetWidth' );
943
+ ttHeight = popover.prop( 'offsetHeight' );
944
+
945
+ // Calculate the popover's top and left coordinates to center it with
946
+ // this directive.
947
+ switch ( scope.tt_placement ) {
948
+ case 'right':
949
+ ttPosition = {
950
+ top: (position.top + position.height / 2 - ttHeight / 2) + 'px',
951
+ left: (position.left + position.width) + 'px'
952
+ };
953
+ break;
954
+ case 'bottom':
955
+ ttPosition = {
956
+ top: (position.top + position.height) + 'px',
957
+ left: (position.left + position.width / 2 - ttWidth / 2) + 'px'
958
+ };
959
+ break;
960
+ case 'left':
961
+ ttPosition = {
962
+ top: (position.top + position.height / 2 - ttHeight / 2) + 'px',
963
+ left: (position.left - ttWidth) + 'px'
964
+ };
965
+ break;
966
+ default:
967
+ ttPosition = {
968
+ top: (position.top - ttHeight) + 'px',
969
+ left: (position.left + position.width / 2 - ttWidth / 2) + 'px'
970
+ };
971
+ break;
972
+ }
973
+
974
+ // Now set the calculated positioning.
975
+ popover.css( ttPosition );
976
+
977
+ // And show the popover.
978
+ scope.tt_isOpen = true;
979
+ }
980
+
981
+ // Hide the popover popup element.
982
+ function hide() {
983
+ // First things first: we don't show it anymore.
984
+ //popover.removeClass( 'in' );
985
+ scope.tt_isOpen = false;
986
+
987
+ // And now we remove it from the DOM. However, if we have animation, we
988
+ // need to wait for it to expire beforehand.
989
+ // FIXME: this is a placeholder for a port of the transitions library.
990
+ if ( angular.isDefined( scope.tt_animation ) && scope.tt_animation() ) {
991
+ transitionTimeout = $timeout( function () { popover.remove(); }, 500 );
992
+ } else {
993
+ popover.remove();
994
+ }
995
+ }
996
+
997
+ // Register the event listeners.
998
+ element.bind( 'click', function() {
999
+ if(scope.tt_isOpen){
1000
+ scope.$apply( hide );
1001
+ } else {
1002
+ scope.$apply( show );
1003
+ }
1004
+
1005
+ });
1006
+ }
1007
+ };
1008
+ }]);
1009
+
1010
+
1011
+ angular.module('ui.bootstrap.tabs', [])
1012
+ .controller('TabsController', ['$scope', '$element', function($scope, $element) {
1013
+ var panes = $scope.panes = [];
1014
+
1015
+ this.select = $scope.select = function selectPane(pane) {
1016
+ angular.forEach(panes, function(pane) {
1017
+ pane.selected = false;
1018
+ });
1019
+ pane.selected = true;
1020
+ };
1021
+
1022
+ this.addPane = function addPane(pane) {
1023
+ if (!panes.length) {
1024
+ $scope.select(pane);
1025
+ }
1026
+ panes.push(pane);
1027
+ };
1028
+
1029
+ this.removePane = function removePane(pane) {
1030
+ var index = panes.indexOf(pane);
1031
+ panes.splice(index, 1);
1032
+ //Select a new pane if removed pane was selected
1033
+ if (pane.selected && panes.length > 0) {
1034
+ $scope.select(panes[index < panes.length ? index : index-1]);
1035
+ }
1036
+ };
1037
+ }])
1038
+ .directive('tabs', function() {
1039
+ return {
1040
+ restrict: 'EA',
1041
+ transclude: true,
1042
+ scope: {},
1043
+ controller: 'TabsController',
1044
+ templateUrl: 'template/tabs/tabs.html',
1045
+ replace: true
1046
+ };
1047
+ })
1048
+ .directive('pane', ['$parse', function($parse) {
1049
+ return {
1050
+ require: '^tabs',
1051
+ restrict: 'EA',
1052
+ transclude: true,
1053
+ scope:{
1054
+ heading:'@'
1055
+ },
1056
+ link: function(scope, element, attrs, tabsCtrl) {
1057
+ var getSelected, setSelected;
1058
+ scope.selected = false;
1059
+ if (attrs.active) {
1060
+ getSelected = $parse(attrs.active);
1061
+ setSelected = getSelected.assign;
1062
+ scope.$watch(
1063
+ function watchSelected() {return getSelected(scope.$parent);},
1064
+ function updateSelected(value) {scope.selected = value;}
1065
+ );
1066
+ scope.selected = getSelected ? getSelected(scope.$parent) : false;
1067
+ }
1068
+ scope.$watch('selected', function(selected) {
1069
+ if(selected) {
1070
+ tabsCtrl.select(scope);
1071
+ }
1072
+ if(setSelected) {
1073
+ setSelected(scope.$parent, selected);
1074
+ }
1075
+ });
1076
+
1077
+ tabsCtrl.addPane(scope);
1078
+ scope.$on('$destroy', function() {
1079
+ tabsCtrl.removePane(scope);
1080
+ });
1081
+ },
1082
+ templateUrl: 'template/tabs/pane.html',
1083
+ replace: true
1084
+ };
1085
+ }]);
1086
+
1087
+ /**
1088
+ * The following features are still outstanding: popup delay, animation as a
1089
+ * function, placement as a function, inside, support for more triggers than
1090
+ * just mouse enter/leave, html tooltips, and selector delegatation.
1091
+ */
1092
+ angular.module( 'ui.bootstrap.tooltip', [] )
1093
+ .directive( 'tooltipPopup', function () {
1094
+ return {
1095
+ restrict: 'EA',
1096
+ replace: true,
1097
+ scope: { tooltipTitle: '@', placement: '@', animation: '&', isOpen: '&' },
1098
+ templateUrl: 'template/tooltip/tooltip-popup.html'
1099
+ };
1100
+ })
1101
+ .directive( 'tooltip', [ '$compile', '$timeout', '$parse', function ( $compile, $timeout, $parse ) {
1102
+
1103
+ var template =
1104
+ '<tooltip-popup '+
1105
+ 'tooltip-title="{{tt_tooltip}}" '+
1106
+ 'placement="{{tt_placement}}" '+
1107
+ 'animation="tt_animation()" '+
1108
+ 'is-open="tt_isOpen"'+
1109
+ '>'+
1110
+ '</tooltip-popup>';
1111
+
1112
+ return {
1113
+ scope: true,
1114
+ link: function ( scope, element, attr ) {
1115
+ var tooltip = $compile( template )( scope ),
1116
+ transitionTimeout;
1117
+
1118
+ attr.$observe( 'tooltip', function ( val ) {
1119
+ scope.tt_tooltip = val;
1120
+ });
1121
+
1122
+ attr.$observe( 'tooltipPlacement', function ( val ) {
1123
+ // If no placement was provided, default to 'top'.
1124
+ scope.tt_placement = val || 'top';
1125
+ });
1126
+
1127
+ attr.$observe( 'tooltipAnimation', function ( val ) {
1128
+ scope.tt_animation = $parse( val );
1129
+ });
1130
+
1131
+ // By default, the tooltip is not open.
1132
+ scope.tt_isOpen = false;
1133
+
1134
+ // Calculate the current position and size of the directive element.
1135
+ function getPosition() {
1136
+ return {
1137
+ width: element.prop( 'offsetWidth' ),
1138
+ height: element.prop( 'offsetHeight' ),
1139
+ top: element.prop( 'offsetTop' ),
1140
+ left: element.prop( 'offsetLeft' )
1141
+ };
1142
+ }
1143
+
1144
+ // Show the tooltip popup element.
1145
+ function show() {
1146
+ var position,
1147
+ ttWidth,
1148
+ ttHeight,
1149
+ ttPosition;
1150
+
1151
+ // If there is a pending remove transition, we must cancel it, lest the
1152
+ // toolip be mysteriously removed.
1153
+ if ( transitionTimeout ) {
1154
+ $timeout.cancel( transitionTimeout );
1155
+ }
1156
+
1157
+ // Set the initial positioning.
1158
+ tooltip.css({ top: 0, left: 0, display: 'block' });
1159
+
1160
+ // Now we add it to the DOM because need some info about it. But it's not
1161
+ // visible yet anyway.
1162
+ element.after( tooltip );
1163
+
1164
+ // Get the position of the directive element.
1165
+ position = getPosition();
1166
+
1167
+ // Get the height and width of the tooltip so we can center it.
1168
+ ttWidth = tooltip.prop( 'offsetWidth' );
1169
+ ttHeight = tooltip.prop( 'offsetHeight' );
1170
+
1171
+ // Calculate the tooltip's top and left coordinates to center it with
1172
+ // this directive.
1173
+ switch ( scope.tt_placement ) {
1174
+ case 'right':
1175
+ ttPosition = {
1176
+ top: (position.top + position.height / 2 - ttHeight / 2) + 'px',
1177
+ left: (position.left + position.width) + 'px'
1178
+ };
1179
+ break;
1180
+ case 'bottom':
1181
+ ttPosition = {
1182
+ top: (position.top + position.height) + 'px',
1183
+ left: (position.left + position.width / 2 - ttWidth / 2) + 'px'
1184
+ };
1185
+ break;
1186
+ case 'left':
1187
+ ttPosition = {
1188
+ top: (position.top + position.height / 2 - ttHeight / 2) + 'px',
1189
+ left: (position.left - ttWidth) + 'px'
1190
+ };
1191
+ break;
1192
+ default:
1193
+ ttPosition = {
1194
+ top: (position.top - ttHeight) + 'px',
1195
+ left: (position.left + position.width / 2 - ttWidth / 2) + 'px'
1196
+ };
1197
+ break;
1198
+ }
1199
+
1200
+ // Now set the calculated positioning.
1201
+ tooltip.css( ttPosition );
1202
+
1203
+ // And show the tooltip.
1204
+ scope.tt_isOpen = true;
1205
+ }
1206
+
1207
+ // Hide the tooltip popup element.
1208
+ function hide() {
1209
+ // First things first: we don't show it anymore.
1210
+ //tooltip.removeClass( 'in' );
1211
+ scope.tt_isOpen = false;
1212
+
1213
+ // And now we remove it from the DOM. However, if we have animation, we
1214
+ // need to wait for it to expire beforehand.
1215
+ // FIXME: this is a placeholder for a port of the transitions library.
1216
+ if ( angular.isDefined( scope.tt_animation ) && scope.tt_animation() ) {
1217
+ transitionTimeout = $timeout( function () { tooltip.remove(); }, 500 );
1218
+ } else {
1219
+ tooltip.remove();
1220
+ }
1221
+ }
1222
+
1223
+ // Register the event listeners.
1224
+ element.bind( 'mouseenter', function() {
1225
+ scope.$apply( show );
1226
+ });
1227
+ element.bind( 'mouseleave', function() {
1228
+ scope.$apply( hide );
1229
+ });
1230
+ }
1231
+ };
1232
+ }]);
1233
+
1234
+
1235
+ angular.module('ui.bootstrap.transition', [])
1236
+
1237
+ /**
1238
+ * $transition service provides a consistent interface to trigger CSS 3 transitions and to be informed when they complete.
1239
+ * @param {DOMElement} element The DOMElement that will be animated.
1240
+ * @param {string|object|function} trigger The thing that will cause the transition to start:
1241
+ * - As a string, it represents the css class to be added to the element.
1242
+ * - As an object, it represents a hash of style attributes to be applied to the element.
1243
+ * - As a function, it represents a function to be called that will cause the transition to occur.
1244
+ * @return {Promise} A promise that is resolved when the transition finishes.
1245
+ */
1246
+ .factory('$transition', ['$q', '$timeout', '$rootScope', function($q, $timeout, $rootScope) {
1247
+
1248
+ var $transition = function(element, trigger, options) {
1249
+ options = options || {};
1250
+ var deferred = $q.defer();
1251
+ var endEventName = $transition[options.animation ? "animationEndEventName" : "transitionEndEventName"];
1252
+
1253
+ var transitionEndHandler = function(event) {
1254
+ $rootScope.$apply(function() {
1255
+ element.unbind(endEventName, transitionEndHandler);
1256
+ deferred.resolve(element);
1257
+ });
1258
+ };
1259
+
1260
+ if (endEventName) {
1261
+ element.bind(endEventName, transitionEndHandler);
1262
+ }
1263
+
1264
+ // Wrap in a timeout to allow the browser time to update the DOM before the transition is to occur
1265
+ $timeout(function() {
1266
+ if ( angular.isString(trigger) ) {
1267
+ element.addClass(trigger);
1268
+ } else if ( angular.isFunction(trigger) ) {
1269
+ trigger(element);
1270
+ } else if ( angular.isObject(trigger) ) {
1271
+ element.css(trigger);
1272
+ }
1273
+ //If browser does not support transitions, instantly resolve
1274
+ if ( !endEventName ) {
1275
+ deferred.resolve(element);
1276
+ }
1277
+ });
1278
+
1279
+ // Add our custom cancel function to the promise that is returned
1280
+ // We can call this if we are about to run a new transition, which we know will prevent this transition from ending,
1281
+ // i.e. it will therefore never raise a transitionEnd event for that transition
1282
+ deferred.promise.cancel = function() {
1283
+ if ( endEventName ) {
1284
+ element.unbind(endEventName, transitionEndHandler);
1285
+ }
1286
+ deferred.reject('Transition cancelled');
1287
+ };
1288
+
1289
+ return deferred.promise;
1290
+ };
1291
+
1292
+ // Work out the name of the transitionEnd event
1293
+ var transElement = document.createElement('trans');
1294
+ var transitionEndEventNames = {
1295
+ 'WebkitTransition': 'webkitTransitionEnd',
1296
+ 'MozTransition': 'transitionend',
1297
+ 'OTransition': 'oTransitionEnd',
1298
+ 'msTransition': 'MSTransitionEnd',
1299
+ 'transition': 'transitionend'
1300
+ };
1301
+ var animationEndEventNames = {
1302
+ 'WebkitTransition': 'webkitAnimationEnd',
1303
+ 'MozTransition': 'animationend',
1304
+ 'OTransition': 'oAnimationEnd',
1305
+ 'msTransition': 'MSAnimationEnd',
1306
+ 'transition': 'animationend'
1307
+ };
1308
+ function findEndEventName(endEventNames) {
1309
+ for (var name in endEventNames){
1310
+ if (transElement.style[name] !== undefined) {
1311
+ return endEventNames[name];
1312
+ }
1313
+ }
1314
+ }
1315
+ $transition.transitionEndEventName = findEndEventName(transitionEndEventNames);
1316
+ $transition.animationEndEventName = findEndEventName(animationEndEventNames);
1317
+ return $transition;
1318
+ }]);