angular-ui-bootstrap-rails 0.1.1

Sign up to get free protection for your applications and to get access to all the features.
@@ -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
+ }]);