bosun.org@v0.0.0-20210513094433-e25bc3e69a1f/cmd/bosun/web/static/js/angular-strap.js (about)

     1  /**
     2   * angular-strap
     3   * @version v2.3.9 - 2016-06-10
     4   * @link http://mgcrea.github.io/angular-strap
     5   * @author Olivier Louvignes <olivier@mg-crea.com> (https://github.com/mgcrea)
     6   * @license MIT License, http://www.opensource.org/licenses/MIT
     7   */
     8  (function(window, document, undefined) {
     9    'use strict';
    10    bsCompilerService.$inject = [ '$q', '$http', '$injector', '$compile', '$controller', '$templateCache' ];
    11    angular.module('mgcrea.ngStrap.typeahead', [ 'mgcrea.ngStrap.tooltip', 'mgcrea.ngStrap.helpers.parseOptions' ]).provider('$typeahead', function() {
    12      var defaults = this.defaults = {
    13        animation: 'am-fade',
    14        prefixClass: 'typeahead',
    15        prefixEvent: '$typeahead',
    16        placement: 'bottom-left',
    17        templateUrl: 'typeahead/typeahead.tpl.html',
    18        trigger: 'focus',
    19        container: false,
    20        keyboard: true,
    21        html: false,
    22        delay: 0,
    23        minLength: 1,
    24        filter: 'bsAsyncFilter',
    25        limit: 6,
    26        autoSelect: false,
    27        comparator: '',
    28        trimValue: true
    29      };
    30      this.$get = [ '$window', '$rootScope', '$tooltip', '$$rAF', '$timeout', function($window, $rootScope, $tooltip, $$rAF, $timeout) {
    31        function TypeaheadFactory(element, controller, config) {
    32          var $typeahead = {};
    33          var options = angular.extend({}, defaults, config);
    34          $typeahead = $tooltip(element, options);
    35          var parentScope = config.scope;
    36          var scope = $typeahead.$scope;
    37          scope.$resetMatches = function() {
    38            scope.$matches = [];
    39            scope.$activeIndex = options.autoSelect ? 0 : -1;
    40          };
    41          scope.$resetMatches();
    42          scope.$activate = function(index) {
    43            scope.$$postDigest(function() {
    44              $typeahead.activate(index);
    45            });
    46          };
    47          scope.$select = function(index, evt) {
    48            scope.$$postDigest(function() {
    49              $typeahead.select(index);
    50            });
    51          };
    52          scope.$isVisible = function() {
    53            return $typeahead.$isVisible();
    54          };
    55          $typeahead.update = function(matches) {
    56            scope.$matches = matches;
    57            if (scope.$activeIndex >= matches.length) {
    58              scope.$activeIndex = options.autoSelect ? 0 : -1;
    59            }
    60            safeDigest(scope);
    61            $$rAF($typeahead.$applyPlacement);
    62          };
    63          $typeahead.activate = function(index) {
    64            scope.$activeIndex = index;
    65          };
    66          $typeahead.select = function(index) {
    67            if (index === -1) return;
    68            var value = scope.$matches[index].value;
    69            controller.$setViewValue(value);
    70            controller.$render();
    71            scope.$resetMatches();
    72            if (parentScope) parentScope.$digest();
    73            scope.$emit(options.prefixEvent + '.select', value, index, $typeahead);
    74            if (angular.isDefined(options.onSelect) && angular.isFunction(options.onSelect)) {
    75              options.onSelect(value, index, $typeahead);
    76            }
    77          };
    78          $typeahead.$isVisible = function() {
    79            if (!options.minLength || !controller) {
    80              return !!scope.$matches.length;
    81            }
    82            return scope.$matches.length && angular.isString(controller.$viewValue) && controller.$viewValue.length >= options.minLength;
    83          };
    84          $typeahead.$getIndex = function(value) {
    85            var index;
    86            for (index = scope.$matches.length; index--; ) {
    87              if (angular.equals(scope.$matches[index].value, value)) break;
    88            }
    89            return index;
    90          };
    91          $typeahead.$onMouseDown = function(evt) {
    92            evt.preventDefault();
    93            evt.stopPropagation();
    94          };
    95          $typeahead.$onKeyDown = function(evt) {
    96            if (!/(38|40|13)/.test(evt.keyCode)) return;
    97            if ($typeahead.$isVisible() && !(evt.keyCode === 13 && scope.$activeIndex === -1)) {
    98              evt.preventDefault();
    99              evt.stopPropagation();
   100            }
   101            if (evt.keyCode === 13 && scope.$matches.length) {
   102              $typeahead.select(scope.$activeIndex);
   103            } else if (evt.keyCode === 38 && scope.$activeIndex > 0) {
   104              scope.$activeIndex--;
   105            } else if (evt.keyCode === 40 && scope.$activeIndex < scope.$matches.length - 1) {
   106              scope.$activeIndex++;
   107            } else if (angular.isUndefined(scope.$activeIndex)) {
   108              scope.$activeIndex = 0;
   109            }
   110            scope.$digest();
   111          };
   112          var show = $typeahead.show;
   113          $typeahead.show = function() {
   114            show();
   115            $timeout(function() {
   116              if ($typeahead.$element) {
   117                $typeahead.$element.on('mousedown', $typeahead.$onMouseDown);
   118                if (options.keyboard) {
   119                  if (element) element.on('keydown', $typeahead.$onKeyDown);
   120                }
   121              }
   122            }, 0, false);
   123          };
   124          var hide = $typeahead.hide;
   125          $typeahead.hide = function() {
   126            if ($typeahead.$element) $typeahead.$element.off('mousedown', $typeahead.$onMouseDown);
   127            if (options.keyboard) {
   128              if (element) element.off('keydown', $typeahead.$onKeyDown);
   129            }
   130            if (!options.autoSelect) {
   131              $typeahead.activate(-1);
   132            }
   133            hide();
   134          };
   135          return $typeahead;
   136        }
   137        function safeDigest(scope) {
   138          scope.$$phase || scope.$root && scope.$root.$$phase || scope.$digest();
   139        }
   140        TypeaheadFactory.defaults = defaults;
   141        return TypeaheadFactory;
   142      } ];
   143    }).filter('bsAsyncFilter', [ '$filter', function($filter) {
   144      return function(array, expression, comparator) {
   145        if (array && angular.isFunction(array.then)) {
   146          return array.then(function(results) {
   147            return $filter('filter')(results, expression, comparator);
   148          });
   149        }
   150        return $filter('filter')(array, expression, comparator);
   151      };
   152    } ]).directive('bsTypeahead', [ '$window', '$parse', '$q', '$typeahead', '$parseOptions', function($window, $parse, $q, $typeahead, $parseOptions) {
   153      var defaults = $typeahead.defaults;
   154      return {
   155        restrict: 'EAC',
   156        require: 'ngModel',
   157        link: function postLink(scope, element, attr, controller) {
   158          element.off('change');
   159          var options = {
   160            scope: scope
   161          };
   162          angular.forEach([ 'template', 'templateUrl', 'controller', 'controllerAs', 'placement', 'container', 'delay', 'trigger', 'keyboard', 'html', 'animation', 'filter', 'limit', 'minLength', 'watchOptions', 'selectMode', 'autoSelect', 'comparator', 'id', 'prefixEvent', 'prefixClass' ], function(key) {
   163            if (angular.isDefined(attr[key])) options[key] = attr[key];
   164          });
   165          var falseValueRegExp = /^(false|0|)$/i;
   166          angular.forEach([ 'html', 'container', 'trimValue', 'filter' ], function(key) {
   167            if (angular.isDefined(attr[key]) && falseValueRegExp.test(attr[key])) options[key] = false;
   168          });
   169          angular.forEach([ 'onBeforeShow', 'onShow', 'onBeforeHide', 'onHide', 'onSelect' ], function(key) {
   170            var bsKey = 'bs' + key.charAt(0).toUpperCase() + key.slice(1);
   171            if (angular.isDefined(attr[bsKey])) {
   172              options[key] = scope.$eval(attr[bsKey]);
   173            }
   174          });
   175          if (!element.attr('autocomplete')) element.attr('autocomplete', 'off');
   176          var filter = angular.isDefined(options.filter) ? options.filter : defaults.filter;
   177          var limit = options.limit || defaults.limit;
   178          var comparator = options.comparator || defaults.comparator;
   179          var bsOptions = attr.bsOptions;
   180          if (filter) {
   181            bsOptions += ' | ' + filter + ':$viewValue';
   182            if (comparator) bsOptions += ':' + comparator;
   183          }
   184          if (limit) bsOptions += ' | limitTo:' + limit;
   185          var parsedOptions = $parseOptions(bsOptions);
   186          var typeahead = $typeahead(element, controller, options);
   187          if (options.watchOptions) {
   188            var watchedOptions = parsedOptions.$match[7].replace(/\|.+/, '').replace(/\(.*\)/g, '').trim();
   189            scope.$watchCollection(watchedOptions, function(newValue, oldValue) {
   190              parsedOptions.valuesFn(scope, controller).then(function(values) {
   191                typeahead.update(values);
   192                controller.$render();
   193              });
   194            });
   195          }
   196          scope.$watch(attr.ngModel, function(newValue, oldValue) {
   197            scope.$modelValue = newValue;
   198            parsedOptions.valuesFn(scope, controller).then(function(values) {
   199              if (options.selectMode && !values.length && newValue.length > 0) {
   200                controller.$setViewValue(controller.$viewValue.substring(0, controller.$viewValue.length - 1));
   201                return;
   202              }
   203              if (values.length > limit) values = values.slice(0, limit);
   204              typeahead.update(values);
   205              controller.$render();
   206            });
   207          });
   208          controller.$formatters.push(function(modelValue) {
   209            var displayValue = parsedOptions.displayValue(modelValue);
   210            if (displayValue) {
   211              return displayValue;
   212            }
   213            if (angular.isDefined(modelValue) && typeof modelValue !== 'object') {
   214              return modelValue;
   215            }
   216            return '';
   217          });
   218          controller.$render = function() {
   219            if (controller.$isEmpty(controller.$viewValue)) {
   220              return element.val('');
   221            }
   222            var index = typeahead.$getIndex(controller.$modelValue);
   223            var selected = index !== -1 ? typeahead.$scope.$matches[index].label : controller.$viewValue;
   224            selected = angular.isObject(selected) ? parsedOptions.displayValue(selected) : selected;
   225            var value = selected ? selected.toString().replace(/<(?:.|\n)*?>/gm, '') : '';
   226            var ss = element[0].selectionStart;
   227            var sd = element[0].selectionEnd;
   228            element.val(options.trimValue === false ? value : value.trim());
   229            element[0].setSelectionRange(ss, sd);
   230          };
   231          scope.$on('$destroy', function() {
   232            if (typeahead) typeahead.destroy();
   233            options = null;
   234            typeahead = null;
   235          });
   236        }
   237      };
   238    } ]);
   239    angular.module('mgcrea.ngStrap.tab', []).provider('$tab', function() {
   240      var defaults = this.defaults = {
   241        animation: 'am-fade',
   242        template: 'tab/tab.tpl.html',
   243        navClass: 'nav-tabs',
   244        activeClass: 'active'
   245      };
   246      var controller = this.controller = function($scope, $element, $attrs) {
   247        var self = this;
   248        self.$options = angular.copy(defaults);
   249        angular.forEach([ 'animation', 'navClass', 'activeClass' ], function(key) {
   250          if (angular.isDefined($attrs[key])) self.$options[key] = $attrs[key];
   251        });
   252        $scope.$navClass = self.$options.navClass;
   253        $scope.$activeClass = self.$options.activeClass;
   254        self.$panes = $scope.$panes = [];
   255        self.$activePaneChangeListeners = self.$viewChangeListeners = [];
   256        self.$push = function(pane) {
   257          if (angular.isUndefined(self.$panes.$active)) {
   258            $scope.$setActive(pane.name || 0);
   259          }
   260          self.$panes.push(pane);
   261        };
   262        self.$remove = function(pane) {
   263          var index = self.$panes.indexOf(pane);
   264          var active = self.$panes.$active;
   265          var activeIndex;
   266          if (angular.isString(active)) {
   267            activeIndex = self.$panes.map(function(pane) {
   268              return pane.name;
   269            }).indexOf(active);
   270          } else {
   271            activeIndex = self.$panes.$active;
   272          }
   273          self.$panes.splice(index, 1);
   274          if (index < activeIndex) {
   275            activeIndex--;
   276          } else if (index === activeIndex && activeIndex === self.$panes.length) {
   277            activeIndex--;
   278          }
   279          if (activeIndex >= 0 && activeIndex < self.$panes.length) {
   280            self.$setActive(self.$panes[activeIndex].name || activeIndex);
   281          } else {
   282            self.$setActive();
   283          }
   284        };
   285        self.$setActive = $scope.$setActive = function(value) {
   286          self.$panes.$active = value;
   287          self.$activePaneChangeListeners.forEach(function(fn) {
   288            fn();
   289          });
   290        };
   291        self.$isActive = $scope.$isActive = function($pane, $index) {
   292          return self.$panes.$active === $pane.name || self.$panes.$active === $index;
   293        };
   294      };
   295      this.$get = function() {
   296        var $tab = {};
   297        $tab.defaults = defaults;
   298        $tab.controller = controller;
   299        return $tab;
   300      };
   301    }).directive('bsTabs', [ '$window', '$animate', '$tab', '$parse', function($window, $animate, $tab, $parse) {
   302      var defaults = $tab.defaults;
   303      return {
   304        require: [ '?ngModel', 'bsTabs' ],
   305        transclude: true,
   306        scope: true,
   307        controller: [ '$scope', '$element', '$attrs', $tab.controller ],
   308        templateUrl: function(element, attr) {
   309          return attr.template || defaults.template;
   310        },
   311        link: function postLink(scope, element, attrs, controllers) {
   312          var ngModelCtrl = controllers[0];
   313          var bsTabsCtrl = controllers[1];
   314          if (ngModelCtrl) {
   315            bsTabsCtrl.$activePaneChangeListeners.push(function() {
   316              ngModelCtrl.$setViewValue(bsTabsCtrl.$panes.$active);
   317            });
   318            ngModelCtrl.$formatters.push(function(modelValue) {
   319              bsTabsCtrl.$setActive(modelValue);
   320              return modelValue;
   321            });
   322          }
   323          if (attrs.bsActivePane) {
   324            var parsedBsActivePane = $parse(attrs.bsActivePane);
   325            bsTabsCtrl.$activePaneChangeListeners.push(function() {
   326              parsedBsActivePane.assign(scope, bsTabsCtrl.$panes.$active);
   327            });
   328            scope.$watch(attrs.bsActivePane, function(newValue, oldValue) {
   329              bsTabsCtrl.$setActive(newValue);
   330            }, true);
   331          }
   332        }
   333      };
   334    } ]).directive('bsPane', [ '$window', '$animate', '$sce', function($window, $animate, $sce) {
   335      return {
   336        require: [ '^?ngModel', '^bsTabs' ],
   337        scope: true,
   338        link: function postLink(scope, element, attrs, controllers) {
   339          var bsTabsCtrl = controllers[1];
   340          element.addClass('tab-pane');
   341          attrs.$observe('title', function(newValue, oldValue) {
   342            scope.title = $sce.trustAsHtml(newValue);
   343          });
   344          scope.name = attrs.name;
   345          if (bsTabsCtrl.$options.animation) {
   346            element.addClass(bsTabsCtrl.$options.animation);
   347          }
   348          attrs.$observe('disabled', function(newValue, oldValue) {
   349            scope.disabled = scope.$eval(newValue);
   350          });
   351          bsTabsCtrl.$push(scope);
   352          scope.$on('$destroy', function() {
   353            bsTabsCtrl.$remove(scope);
   354          });
   355          function render() {
   356            var index = bsTabsCtrl.$panes.indexOf(scope);
   357            $animate[bsTabsCtrl.$isActive(scope, index) ? 'addClass' : 'removeClass'](element, bsTabsCtrl.$options.activeClass);
   358          }
   359          bsTabsCtrl.$activePaneChangeListeners.push(function() {
   360            render();
   361          });
   362          render();
   363        }
   364      };
   365    } ]);
   366    angular.module('mgcrea.ngStrap.tooltip', [ 'mgcrea.ngStrap.core', 'mgcrea.ngStrap.helpers.dimensions' ]).provider('$tooltip', function() {
   367      var defaults = this.defaults = {
   368        animation: 'am-fade',
   369        customClass: '',
   370        prefixClass: 'tooltip',
   371        prefixEvent: 'tooltip',
   372        container: false,
   373        target: false,
   374        placement: 'top',
   375        templateUrl: 'tooltip/tooltip.tpl.html',
   376        template: '',
   377        titleTemplate: false,
   378        trigger: 'hover focus',
   379        keyboard: false,
   380        html: false,
   381        show: false,
   382        title: '',
   383        type: '',
   384        delay: 0,
   385        autoClose: false,
   386        bsEnabled: true,
   387        mouseDownPreventDefault: true,
   388        mouseDownStopPropagation: true,
   389        viewport: {
   390          selector: 'body',
   391          padding: 0
   392        }
   393      };
   394      this.$get = [ '$window', '$rootScope', '$bsCompiler', '$q', '$templateCache', '$http', '$animate', '$sce', 'dimensions', '$$rAF', '$timeout', function($window, $rootScope, $bsCompiler, $q, $templateCache, $http, $animate, $sce, dimensions, $$rAF, $timeout) {
   395        var isNative = /(ip[ao]d|iphone|android)/gi.test($window.navigator.userAgent);
   396        var isTouch = 'createTouch' in $window.document && isNative;
   397        var $body = angular.element($window.document);
   398        function TooltipFactory(element, config) {
   399          var $tooltip = {};
   400          var options = $tooltip.$options = angular.extend({}, defaults, config);
   401          var promise = $tooltip.$promise = $bsCompiler.compile(options);
   402          var scope = $tooltip.$scope = options.scope && options.scope.$new() || $rootScope.$new();
   403          var nodeName = element[0].nodeName.toLowerCase();
   404          if (options.delay && angular.isString(options.delay)) {
   405            var split = options.delay.split(',').map(parseFloat);
   406            options.delay = split.length > 1 ? {
   407              show: split[0],
   408              hide: split[1]
   409            } : split[0];
   410          }
   411          $tooltip.$id = options.id || element.attr('id') || '';
   412          if (options.title) {
   413            scope.title = $sce.trustAsHtml(options.title);
   414          }
   415          scope.$setEnabled = function(isEnabled) {
   416            scope.$$postDigest(function() {
   417              $tooltip.setEnabled(isEnabled);
   418            });
   419          };
   420          scope.$hide = function() {
   421            scope.$$postDigest(function() {
   422              $tooltip.hide();
   423            });
   424          };
   425          scope.$show = function() {
   426            scope.$$postDigest(function() {
   427              $tooltip.show();
   428            });
   429          };
   430          scope.$toggle = function() {
   431            scope.$$postDigest(function() {
   432              $tooltip.toggle();
   433            });
   434          };
   435          $tooltip.$isShown = scope.$isShown = false;
   436          var timeout;
   437          var hoverState;
   438          var compileData;
   439          var tipElement;
   440          var tipContainer;
   441          var tipScope;
   442          promise.then(function(data) {
   443            compileData = data;
   444            $tooltip.init();
   445          });
   446          $tooltip.init = function() {
   447            if (options.delay && angular.isNumber(options.delay)) {
   448              options.delay = {
   449                show: options.delay,
   450                hide: options.delay
   451              };
   452            }
   453            if (options.container === 'self') {
   454              tipContainer = element;
   455            } else if (angular.isElement(options.container)) {
   456              tipContainer = options.container;
   457            } else if (options.container) {
   458              tipContainer = findElement(options.container);
   459            }
   460            bindTriggerEvents();
   461            if (options.target) {
   462              options.target = angular.isElement(options.target) ? options.target : findElement(options.target);
   463            }
   464            if (options.show) {
   465              scope.$$postDigest(function() {
   466                if (options.trigger === 'focus') {
   467                  element[0].focus();
   468                } else {
   469                  $tooltip.show();
   470                }
   471              });
   472            }
   473          };
   474          $tooltip.destroy = function() {
   475            unbindTriggerEvents();
   476            destroyTipElement();
   477            scope.$destroy();
   478          };
   479          $tooltip.enter = function() {
   480            clearTimeout(timeout);
   481            hoverState = 'in';
   482            if (!options.delay || !options.delay.show) {
   483              return $tooltip.show();
   484            }
   485            timeout = setTimeout(function() {
   486              if (hoverState === 'in') $tooltip.show();
   487            }, options.delay.show);
   488          };
   489          $tooltip.show = function() {
   490            if (!options.bsEnabled || $tooltip.$isShown) return;
   491            scope.$emit(options.prefixEvent + '.show.before', $tooltip);
   492            if (angular.isDefined(options.onBeforeShow) && angular.isFunction(options.onBeforeShow)) {
   493              options.onBeforeShow($tooltip);
   494            }
   495            var parent;
   496            var after;
   497            if (options.container) {
   498              parent = tipContainer;
   499              if (tipContainer[0].lastChild) {
   500                after = angular.element(tipContainer[0].lastChild);
   501              } else {
   502                after = null;
   503              }
   504            } else {
   505              parent = null;
   506              after = element;
   507            }
   508            if (tipElement) destroyTipElement();
   509            tipScope = $tooltip.$scope.$new();
   510            tipElement = $tooltip.$element = compileData.link(tipScope, function(clonedElement, scope) {});
   511            tipElement.css({
   512              top: '-9999px',
   513              left: '-9999px',
   514              right: 'auto',
   515              display: 'block',
   516              visibility: 'hidden'
   517            });
   518            if (options.animation) tipElement.addClass(options.animation);
   519            if (options.type) tipElement.addClass(options.prefixClass + '-' + options.type);
   520            if (options.customClass) tipElement.addClass(options.customClass);
   521            if (after) {
   522              after.after(tipElement);
   523            } else {
   524              parent.prepend(tipElement);
   525            }
   526            $tooltip.$isShown = scope.$isShown = true;
   527            safeDigest(scope);
   528            $tooltip.$applyPlacement();
   529            if (angular.version.minor <= 2) {
   530              $animate.enter(tipElement, parent, after, enterAnimateCallback);
   531            } else {
   532              $animate.enter(tipElement, parent, after).then(enterAnimateCallback);
   533            }
   534            safeDigest(scope);
   535            $$rAF(function() {
   536              if (tipElement) tipElement.css({
   537                visibility: 'visible'
   538              });
   539              if (options.keyboard) {
   540                if (options.trigger !== 'focus') {
   541                  $tooltip.focus();
   542                }
   543                bindKeyboardEvents();
   544              }
   545            });
   546            if (options.autoClose) {
   547              bindAutoCloseEvents();
   548            }
   549          };
   550          function enterAnimateCallback() {
   551            scope.$emit(options.prefixEvent + '.show', $tooltip);
   552            if (angular.isDefined(options.onShow) && angular.isFunction(options.onShow)) {
   553              options.onShow($tooltip);
   554            }
   555          }
   556          $tooltip.leave = function() {
   557            clearTimeout(timeout);
   558            hoverState = 'out';
   559            if (!options.delay || !options.delay.hide) {
   560              return $tooltip.hide();
   561            }
   562            timeout = setTimeout(function() {
   563              if (hoverState === 'out') {
   564                $tooltip.hide();
   565              }
   566            }, options.delay.hide);
   567          };
   568          var _blur;
   569          var _tipToHide;
   570          $tooltip.hide = function(blur) {
   571            if (!$tooltip.$isShown) return;
   572            scope.$emit(options.prefixEvent + '.hide.before', $tooltip);
   573            if (angular.isDefined(options.onBeforeHide) && angular.isFunction(options.onBeforeHide)) {
   574              options.onBeforeHide($tooltip);
   575            }
   576            _blur = blur;
   577            _tipToHide = tipElement;
   578            if (angular.version.minor <= 2) {
   579              $animate.leave(tipElement, leaveAnimateCallback);
   580            } else {
   581              $animate.leave(tipElement).then(leaveAnimateCallback);
   582            }
   583            $tooltip.$isShown = scope.$isShown = false;
   584            safeDigest(scope);
   585            if (options.keyboard && tipElement !== null) {
   586              unbindKeyboardEvents();
   587            }
   588            if (options.autoClose && tipElement !== null) {
   589              unbindAutoCloseEvents();
   590            }
   591          };
   592          function leaveAnimateCallback() {
   593            scope.$emit(options.prefixEvent + '.hide', $tooltip);
   594            if (angular.isDefined(options.onHide) && angular.isFunction(options.onHide)) {
   595              options.onHide($tooltip);
   596            }
   597            if (tipElement === _tipToHide) {
   598              if (_blur && options.trigger === 'focus') {
   599                return element[0].blur();
   600              }
   601              destroyTipElement();
   602            }
   603          }
   604          $tooltip.toggle = function(evt) {
   605            if (evt) {
   606              evt.preventDefault();
   607            }
   608            if ($tooltip.$isShown) {
   609              $tooltip.leave();
   610            } else {
   611              $tooltip.enter();
   612            }
   613          };
   614          $tooltip.focus = function() {
   615            tipElement[0].focus();
   616          };
   617          $tooltip.setEnabled = function(isEnabled) {
   618            options.bsEnabled = isEnabled;
   619          };
   620          $tooltip.setViewport = function(viewport) {
   621            options.viewport = viewport;
   622          };
   623          $tooltip.$applyPlacement = function() {
   624            if (!tipElement) return;
   625            var placement = options.placement;
   626            var autoToken = /\s?auto?\s?/i;
   627            var autoPlace = autoToken.test(placement);
   628            if (autoPlace) {
   629              placement = placement.replace(autoToken, '') || defaults.placement;
   630            }
   631            tipElement.addClass(options.placement);
   632            var elementPosition = getPosition();
   633            var tipWidth = tipElement.prop('offsetWidth');
   634            var tipHeight = tipElement.prop('offsetHeight');
   635            $tooltip.$viewport = options.viewport && findElement(options.viewport.selector || options.viewport);
   636            if (autoPlace) {
   637              var originalPlacement = placement;
   638              var viewportPosition = getPosition($tooltip.$viewport);
   639              if (/bottom/.test(originalPlacement) && elementPosition.bottom + tipHeight > viewportPosition.bottom) {
   640                placement = originalPlacement.replace('bottom', 'top');
   641              } else if (/top/.test(originalPlacement) && elementPosition.top - tipHeight < viewportPosition.top) {
   642                placement = originalPlacement.replace('top', 'bottom');
   643              }
   644              if (/left/.test(originalPlacement) && elementPosition.left - tipWidth < viewportPosition.left) {
   645                placement = placement.replace('left', 'right');
   646              } else if (/right/.test(originalPlacement) && elementPosition.right + tipWidth > viewportPosition.width) {
   647                placement = placement.replace('right', 'left');
   648              }
   649              tipElement.removeClass(originalPlacement).addClass(placement);
   650            }
   651            var tipPosition = getCalculatedOffset(placement, elementPosition, tipWidth, tipHeight);
   652            applyPlacement(tipPosition, placement);
   653          };
   654          $tooltip.$onKeyUp = function(evt) {
   655            if (evt.which === 27 && $tooltip.$isShown) {
   656              $tooltip.hide();
   657              evt.stopPropagation();
   658            }
   659          };
   660          $tooltip.$onFocusKeyUp = function(evt) {
   661            if (evt.which === 27) {
   662              element[0].blur();
   663              evt.stopPropagation();
   664            }
   665          };
   666          $tooltip.$onFocusElementMouseDown = function(evt) {
   667            if (options.mouseDownPreventDefault) {
   668              evt.preventDefault();
   669            }
   670            if (options.mouseDownStopPropagation) {
   671              evt.stopPropagation();
   672            }
   673            if ($tooltip.$isShown) {
   674              element[0].blur();
   675            } else {
   676              element[0].focus();
   677            }
   678          };
   679          function bindTriggerEvents() {
   680            var triggers = options.trigger.split(' ');
   681            angular.forEach(triggers, function(trigger) {
   682              if (trigger === 'click' || trigger === 'contextmenu') {
   683                element.on(trigger, $tooltip.toggle);
   684              } else if (trigger !== 'manual') {
   685                element.on(trigger === 'hover' ? 'mouseenter' : 'focus', $tooltip.enter);
   686                element.on(trigger === 'hover' ? 'mouseleave' : 'blur', $tooltip.leave);
   687                if (nodeName === 'button' && trigger !== 'hover') {
   688                  element.on(isTouch ? 'touchstart' : 'mousedown', $tooltip.$onFocusElementMouseDown);
   689                }
   690              }
   691            });
   692          }
   693          function unbindTriggerEvents() {
   694            var triggers = options.trigger.split(' ');
   695            for (var i = triggers.length; i--; ) {
   696              var trigger = triggers[i];
   697              if (trigger === 'click' || trigger === 'contextmenu') {
   698                element.off(trigger, $tooltip.toggle);
   699              } else if (trigger !== 'manual') {
   700                element.off(trigger === 'hover' ? 'mouseenter' : 'focus', $tooltip.enter);
   701                element.off(trigger === 'hover' ? 'mouseleave' : 'blur', $tooltip.leave);
   702                if (nodeName === 'button' && trigger !== 'hover') {
   703                  element.off(isTouch ? 'touchstart' : 'mousedown', $tooltip.$onFocusElementMouseDown);
   704                }
   705              }
   706            }
   707          }
   708          function bindKeyboardEvents() {
   709            if (options.trigger !== 'focus') {
   710              tipElement.on('keyup', $tooltip.$onKeyUp);
   711            } else {
   712              element.on('keyup', $tooltip.$onFocusKeyUp);
   713            }
   714          }
   715          function unbindKeyboardEvents() {
   716            if (options.trigger !== 'focus') {
   717              tipElement.off('keyup', $tooltip.$onKeyUp);
   718            } else {
   719              element.off('keyup', $tooltip.$onFocusKeyUp);
   720            }
   721          }
   722          var _autoCloseEventsBinded = false;
   723          function bindAutoCloseEvents() {
   724            $timeout(function() {
   725              tipElement.on('click', stopEventPropagation);
   726              $body.on('click', $tooltip.hide);
   727              _autoCloseEventsBinded = true;
   728            }, 0, false);
   729          }
   730          function unbindAutoCloseEvents() {
   731            if (_autoCloseEventsBinded) {
   732              tipElement.off('click', stopEventPropagation);
   733              $body.off('click', $tooltip.hide);
   734              _autoCloseEventsBinded = false;
   735            }
   736          }
   737          function stopEventPropagation(event) {
   738            event.stopPropagation();
   739          }
   740          function getPosition($element) {
   741            $element = $element || (options.target || element);
   742            var el = $element[0];
   743            var isBody = el.tagName === 'BODY';
   744            var elRect = el.getBoundingClientRect();
   745            var rect = {};
   746            for (var p in elRect) {
   747              rect[p] = elRect[p];
   748            }
   749            if (rect.width === null) {
   750              rect = angular.extend({}, rect, {
   751                width: elRect.right - elRect.left,
   752                height: elRect.bottom - elRect.top
   753              });
   754            }
   755            var elOffset = isBody ? {
   756              top: 0,
   757              left: 0
   758            } : dimensions.offset(el);
   759            var scroll = {
   760              scroll: isBody ? document.documentElement.scrollTop || document.body.scrollTop : $element.prop('scrollTop') || 0
   761            };
   762            var outerDims = isBody ? {
   763              width: document.documentElement.clientWidth,
   764              height: $window.innerHeight
   765            } : null;
   766            return angular.extend({}, rect, scroll, outerDims, elOffset);
   767          }
   768          function getCalculatedOffset(placement, position, actualWidth, actualHeight) {
   769            var offset;
   770            var split = placement.split('-');
   771            switch (split[0]) {
   772             case 'right':
   773              offset = {
   774                top: position.top + position.height / 2 - actualHeight / 2,
   775                left: position.left + position.width
   776              };
   777              break;
   778  
   779             case 'bottom':
   780              offset = {
   781                top: position.top + position.height,
   782                left: position.left + position.width / 2 - actualWidth / 2
   783              };
   784              break;
   785  
   786             case 'left':
   787              offset = {
   788                top: position.top + position.height / 2 - actualHeight / 2,
   789                left: position.left - actualWidth
   790              };
   791              break;
   792  
   793             default:
   794              offset = {
   795                top: position.top - actualHeight,
   796                left: position.left + position.width / 2 - actualWidth / 2
   797              };
   798              break;
   799            }
   800            if (!split[1]) {
   801              return offset;
   802            }
   803            if (split[0] === 'top' || split[0] === 'bottom') {
   804              switch (split[1]) {
   805               case 'left':
   806                offset.left = position.left;
   807                break;
   808  
   809               case 'right':
   810                offset.left = position.left + position.width - actualWidth;
   811                break;
   812  
   813               default:
   814                break;
   815              }
   816            } else if (split[0] === 'left' || split[0] === 'right') {
   817              switch (split[1]) {
   818               case 'top':
   819                offset.top = position.top - actualHeight + position.height;
   820                break;
   821  
   822               case 'bottom':
   823                offset.top = position.top;
   824                break;
   825  
   826               default:
   827                break;
   828              }
   829            }
   830            return offset;
   831          }
   832          function applyPlacement(offset, placement) {
   833            var tip = tipElement[0];
   834            var width = tip.offsetWidth;
   835            var height = tip.offsetHeight;
   836            var marginTop = parseInt(dimensions.css(tip, 'margin-top'), 10);
   837            var marginLeft = parseInt(dimensions.css(tip, 'margin-left'), 10);
   838            if (isNaN(marginTop)) marginTop = 0;
   839            if (isNaN(marginLeft)) marginLeft = 0;
   840            offset.top = offset.top + marginTop;
   841            offset.left = offset.left + marginLeft;
   842            dimensions.setOffset(tip, angular.extend({
   843              using: function(props) {
   844                tipElement.css({
   845                  top: Math.round(props.top) + 'px',
   846                  left: Math.round(props.left) + 'px',
   847                  right: ''
   848                });
   849              }
   850            }, offset), 0);
   851            var actualWidth = tip.offsetWidth;
   852            var actualHeight = tip.offsetHeight;
   853            if (placement === 'top' && actualHeight !== height) {
   854              offset.top = offset.top + height - actualHeight;
   855            }
   856            if (/top-left|top-right|bottom-left|bottom-right/.test(placement)) return;
   857            var delta = getViewportAdjustedDelta(placement, offset, actualWidth, actualHeight);
   858            if (delta.left) {
   859              offset.left += delta.left;
   860            } else {
   861              offset.top += delta.top;
   862            }
   863            dimensions.setOffset(tip, offset);
   864            if (/top|right|bottom|left/.test(placement)) {
   865              var isVertical = /top|bottom/.test(placement);
   866              var arrowDelta = isVertical ? delta.left * 2 - width + actualWidth : delta.top * 2 - height + actualHeight;
   867              var arrowOffsetPosition = isVertical ? 'offsetWidth' : 'offsetHeight';
   868              replaceArrow(arrowDelta, tip[arrowOffsetPosition], isVertical);
   869            }
   870          }
   871          function getViewportAdjustedDelta(placement, position, actualWidth, actualHeight) {
   872            var delta = {
   873              top: 0,
   874              left: 0
   875            };
   876            if (!$tooltip.$viewport) return delta;
   877            var viewportPadding = options.viewport && options.viewport.padding || 0;
   878            var viewportDimensions = getPosition($tooltip.$viewport);
   879            if (/right|left/.test(placement)) {
   880              var topEdgeOffset = position.top - viewportPadding - viewportDimensions.scroll;
   881              var bottomEdgeOffset = position.top + viewportPadding - viewportDimensions.scroll + actualHeight;
   882              if (topEdgeOffset < viewportDimensions.top) {
   883                delta.top = viewportDimensions.top - topEdgeOffset;
   884              } else if (bottomEdgeOffset > viewportDimensions.top + viewportDimensions.height) {
   885                delta.top = viewportDimensions.top + viewportDimensions.height - bottomEdgeOffset;
   886              }
   887            } else {
   888              var leftEdgeOffset = position.left - viewportPadding;
   889              var rightEdgeOffset = position.left + viewportPadding + actualWidth;
   890              if (leftEdgeOffset < viewportDimensions.left) {
   891                delta.left = viewportDimensions.left - leftEdgeOffset;
   892              } else if (rightEdgeOffset > viewportDimensions.right) {
   893                delta.left = viewportDimensions.left + viewportDimensions.width - rightEdgeOffset;
   894              }
   895            }
   896            return delta;
   897          }
   898          function replaceArrow(delta, dimension, isHorizontal) {
   899            var $arrow = findElement('.tooltip-arrow, .arrow', tipElement[0]);
   900            $arrow.css(isHorizontal ? 'left' : 'top', 50 * (1 - delta / dimension) + '%').css(isHorizontal ? 'top' : 'left', '');
   901          }
   902          function destroyTipElement() {
   903            clearTimeout(timeout);
   904            if ($tooltip.$isShown && tipElement !== null) {
   905              if (options.autoClose) {
   906                unbindAutoCloseEvents();
   907              }
   908              if (options.keyboard) {
   909                unbindKeyboardEvents();
   910              }
   911            }
   912            if (tipScope) {
   913              tipScope.$destroy();
   914              tipScope = null;
   915            }
   916            if (tipElement) {
   917              tipElement.remove();
   918              tipElement = $tooltip.$element = null;
   919            }
   920          }
   921          return $tooltip;
   922        }
   923        function safeDigest(scope) {
   924          scope.$$phase || scope.$root && scope.$root.$$phase || scope.$digest();
   925        }
   926        function findElement(query, element) {
   927          return angular.element((element || document).querySelectorAll(query));
   928        }
   929        return TooltipFactory;
   930      } ];
   931    }).directive('bsTooltip', [ '$window', '$location', '$sce', '$parse', '$tooltip', '$$rAF', function($window, $location, $sce, $parse, $tooltip, $$rAF) {
   932      return {
   933        restrict: 'EAC',
   934        scope: true,
   935        link: function postLink(scope, element, attr, transclusion) {
   936          var tooltip;
   937          var options = {
   938            scope: scope
   939          };
   940          angular.forEach([ 'template', 'templateUrl', 'controller', 'controllerAs', 'titleTemplate', 'placement', 'container', 'delay', 'trigger', 'html', 'animation', 'backdropAnimation', 'type', 'customClass', 'id' ], function(key) {
   941            if (angular.isDefined(attr[key])) options[key] = attr[key];
   942          });
   943          var falseValueRegExp = /^(false|0|)$/i;
   944          angular.forEach([ 'html', 'container' ], function(key) {
   945            if (angular.isDefined(attr[key]) && falseValueRegExp.test(attr[key])) {
   946              options[key] = false;
   947            }
   948          });
   949          angular.forEach([ 'onBeforeShow', 'onShow', 'onBeforeHide', 'onHide' ], function(key) {
   950            var bsKey = 'bs' + key.charAt(0).toUpperCase() + key.slice(1);
   951            if (angular.isDefined(attr[bsKey])) {
   952              options[key] = scope.$eval(attr[bsKey]);
   953            }
   954          });
   955          var dataTarget = element.attr('data-target');
   956          if (angular.isDefined(dataTarget)) {
   957            if (falseValueRegExp.test(dataTarget)) {
   958              options.target = false;
   959            } else {
   960              options.target = dataTarget;
   961            }
   962          }
   963          if (!scope.hasOwnProperty('title')) {
   964            scope.title = '';
   965          }
   966          attr.$observe('title', function(newValue) {
   967            if (angular.isDefined(newValue) || !scope.hasOwnProperty('title')) {
   968              var oldValue = scope.title;
   969              scope.title = $sce.trustAsHtml(newValue);
   970              if (angular.isDefined(oldValue)) {
   971                $$rAF(function() {
   972                  if (tooltip) tooltip.$applyPlacement();
   973                });
   974              }
   975            }
   976          });
   977          attr.$observe('disabled', function(newValue) {
   978            if (newValue && tooltip.$isShown) {
   979              tooltip.hide();
   980            }
   981          });
   982          if (attr.bsTooltip) {
   983            scope.$watch(attr.bsTooltip, function(newValue, oldValue) {
   984              if (angular.isObject(newValue)) {
   985                angular.extend(scope, newValue);
   986              } else {
   987                scope.title = newValue;
   988              }
   989              if (angular.isDefined(oldValue)) {
   990                $$rAF(function() {
   991                  if (tooltip) tooltip.$applyPlacement();
   992                });
   993              }
   994            }, true);
   995          }
   996          if (attr.bsShow) {
   997            scope.$watch(attr.bsShow, function(newValue, oldValue) {
   998              if (!tooltip || !angular.isDefined(newValue)) return;
   999              if (angular.isString(newValue)) newValue = !!newValue.match(/true|,?(tooltip),?/i);
  1000              if (newValue === true) {
  1001                tooltip.show();
  1002              } else {
  1003                tooltip.hide();
  1004              }
  1005            });
  1006          }
  1007          if (attr.bsEnabled) {
  1008            scope.$watch(attr.bsEnabled, function(newValue, oldValue) {
  1009              if (!tooltip || !angular.isDefined(newValue)) return;
  1010              if (angular.isString(newValue)) newValue = !!newValue.match(/true|1|,?(tooltip),?/i);
  1011              if (newValue === false) {
  1012                tooltip.setEnabled(false);
  1013              } else {
  1014                tooltip.setEnabled(true);
  1015              }
  1016            });
  1017          }
  1018          if (attr.viewport) {
  1019            scope.$watch(attr.viewport, function(newValue) {
  1020              if (!tooltip || !angular.isDefined(newValue)) return;
  1021              tooltip.setViewport(newValue);
  1022            });
  1023          }
  1024          tooltip = $tooltip(element, options);
  1025          scope.$on('$destroy', function() {
  1026            if (tooltip) tooltip.destroy();
  1027            options = null;
  1028            tooltip = null;
  1029          });
  1030        }
  1031      };
  1032    } ]);
  1033    angular.module('mgcrea.ngStrap.timepicker', [ 'mgcrea.ngStrap.helpers.dateParser', 'mgcrea.ngStrap.helpers.dateFormatter', 'mgcrea.ngStrap.tooltip' ]).provider('$timepicker', function() {
  1034      var defaults = this.defaults = {
  1035        animation: 'am-fade',
  1036        defaultDate: 'auto',
  1037        prefixClass: 'timepicker',
  1038        placement: 'bottom-left',
  1039        templateUrl: 'timepicker/timepicker.tpl.html',
  1040        trigger: 'focus',
  1041        container: false,
  1042        keyboard: true,
  1043        html: false,
  1044        delay: 0,
  1045        useNative: true,
  1046        timeType: 'date',
  1047        timeFormat: 'shortTime',
  1048        timezone: null,
  1049        modelTimeFormat: null,
  1050        autoclose: false,
  1051        minTime: -Infinity,
  1052        maxTime: +Infinity,
  1053        length: 5,
  1054        hourStep: 1,
  1055        minuteStep: 5,
  1056        secondStep: 5,
  1057        roundDisplay: false,
  1058        iconUp: 'glyphicon glyphicon-chevron-up',
  1059        iconDown: 'glyphicon glyphicon-chevron-down',
  1060        arrowBehavior: 'pager'
  1061      };
  1062      this.$get = [ '$window', '$document', '$rootScope', '$sce', '$dateFormatter', '$tooltip', '$timeout', function($window, $document, $rootScope, $sce, $dateFormatter, $tooltip, $timeout) {
  1063        var isNative = /(ip[ao]d|iphone|android)/gi.test($window.navigator.userAgent);
  1064        var isTouch = 'createTouch' in $window.document && isNative;
  1065        if (!defaults.lang) {
  1066          defaults.lang = $dateFormatter.getDefaultLocale();
  1067        }
  1068        function timepickerFactory(element, controller, config) {
  1069          var $timepicker = $tooltip(element, angular.extend({}, defaults, config));
  1070          var parentScope = config.scope;
  1071          var options = $timepicker.$options;
  1072          var scope = $timepicker.$scope;
  1073          var lang = options.lang;
  1074          var formatDate = function(date, format, timezone) {
  1075            return $dateFormatter.formatDate(date, format, lang, timezone);
  1076          };
  1077          function floorMinutes(time) {
  1078            var coeff = 1e3 * 60 * options.minuteStep;
  1079            return new Date(Math.floor(time.getTime() / coeff) * coeff);
  1080          }
  1081          var selectedIndex = 0;
  1082          var defaultDate = options.roundDisplay ? floorMinutes(new Date()) : new Date();
  1083          var startDate = controller.$dateValue || defaultDate;
  1084          var viewDate = {
  1085            hour: startDate.getHours(),
  1086            meridian: startDate.getHours() < 12,
  1087            minute: startDate.getMinutes(),
  1088            second: startDate.getSeconds(),
  1089            millisecond: startDate.getMilliseconds()
  1090          };
  1091          var format = $dateFormatter.getDatetimeFormat(options.timeFormat, lang);
  1092          var hoursFormat = $dateFormatter.hoursFormat(format);
  1093          var timeSeparator = $dateFormatter.timeSeparator(format);
  1094          var minutesFormat = $dateFormatter.minutesFormat(format);
  1095          var secondsFormat = $dateFormatter.secondsFormat(format);
  1096          var showSeconds = $dateFormatter.showSeconds(format);
  1097          var showAM = $dateFormatter.showAM(format);
  1098          scope.$iconUp = options.iconUp;
  1099          scope.$iconDown = options.iconDown;
  1100          scope.$select = function(date, index) {
  1101            $timepicker.select(date, index);
  1102          };
  1103          scope.$moveIndex = function(value, index) {
  1104            $timepicker.$moveIndex(value, index);
  1105          };
  1106          scope.$switchMeridian = function(date) {
  1107            $timepicker.switchMeridian(date);
  1108          };
  1109          $timepicker.update = function(date) {
  1110            if (angular.isDate(date) && !isNaN(date.getTime())) {
  1111              $timepicker.$date = date;
  1112              angular.extend(viewDate, {
  1113                hour: date.getHours(),
  1114                minute: date.getMinutes(),
  1115                second: date.getSeconds(),
  1116                millisecond: date.getMilliseconds()
  1117              });
  1118              $timepicker.$build();
  1119            } else if (!$timepicker.$isBuilt) {
  1120              $timepicker.$build();
  1121            }
  1122          };
  1123          $timepicker.select = function(date, index, keep) {
  1124            if (!controller.$dateValue || isNaN(controller.$dateValue.getTime())) {
  1125              controller.$dateValue = options.defaultDate === 'today' ? new Date() : new Date(1970, 0, 1);
  1126            }
  1127            if (!angular.isDate(date)) date = new Date(date);
  1128            if (index === 0) controller.$dateValue.setHours(date.getHours()); else if (index === 1) controller.$dateValue.setMinutes(date.getMinutes()); else if (index === 2) controller.$dateValue.setSeconds(date.getSeconds());
  1129            controller.$setViewValue(angular.copy(controller.$dateValue));
  1130            controller.$render();
  1131            if (options.autoclose && !keep) {
  1132              $timeout(function() {
  1133                $timepicker.hide(true);
  1134              });
  1135            }
  1136          };
  1137          $timepicker.switchMeridian = function(date) {
  1138            if (!controller.$dateValue || isNaN(controller.$dateValue.getTime())) {
  1139              return;
  1140            }
  1141            var hours = (date || controller.$dateValue).getHours();
  1142            controller.$dateValue.setHours(hours < 12 ? hours + 12 : hours - 12);
  1143            controller.$setViewValue(angular.copy(controller.$dateValue));
  1144            controller.$render();
  1145          };
  1146          $timepicker.$build = function() {
  1147            var i;
  1148            var midIndex = scope.midIndex = parseInt(options.length / 2, 10);
  1149            var hours = [];
  1150            var hour;
  1151            for (i = 0; i < options.length; i++) {
  1152              hour = new Date(1970, 0, 1, viewDate.hour - (midIndex - i) * options.hourStep);
  1153              hours.push({
  1154                date: hour,
  1155                label: formatDate(hour, hoursFormat),
  1156                selected: $timepicker.$date && $timepicker.$isSelected(hour, 0),
  1157                disabled: $timepicker.$isDisabled(hour, 0)
  1158              });
  1159            }
  1160            var minutes = [];
  1161            var minute;
  1162            for (i = 0; i < options.length; i++) {
  1163              minute = new Date(1970, 0, 1, 0, viewDate.minute - (midIndex - i) * options.minuteStep);
  1164              minutes.push({
  1165                date: minute,
  1166                label: formatDate(minute, minutesFormat),
  1167                selected: $timepicker.$date && $timepicker.$isSelected(minute, 1),
  1168                disabled: $timepicker.$isDisabled(minute, 1)
  1169              });
  1170            }
  1171            var seconds = [];
  1172            var second;
  1173            for (i = 0; i < options.length; i++) {
  1174              second = new Date(1970, 0, 1, 0, 0, viewDate.second - (midIndex - i) * options.secondStep);
  1175              seconds.push({
  1176                date: second,
  1177                label: formatDate(second, secondsFormat),
  1178                selected: $timepicker.$date && $timepicker.$isSelected(second, 2),
  1179                disabled: $timepicker.$isDisabled(second, 2)
  1180              });
  1181            }
  1182            var rows = [];
  1183            for (i = 0; i < options.length; i++) {
  1184              if (showSeconds) {
  1185                rows.push([ hours[i], minutes[i], seconds[i] ]);
  1186              } else {
  1187                rows.push([ hours[i], minutes[i] ]);
  1188              }
  1189            }
  1190            scope.rows = rows;
  1191            scope.showSeconds = showSeconds;
  1192            scope.showAM = showAM;
  1193            scope.isAM = ($timepicker.$date || hours[midIndex].date).getHours() < 12;
  1194            scope.timeSeparator = timeSeparator;
  1195            $timepicker.$isBuilt = true;
  1196          };
  1197          $timepicker.$isSelected = function(date, index) {
  1198            if (!$timepicker.$date) return false; else if (index === 0) {
  1199              return date.getHours() === $timepicker.$date.getHours();
  1200            } else if (index === 1) {
  1201              return date.getMinutes() === $timepicker.$date.getMinutes();
  1202            } else if (index === 2) {
  1203              return date.getSeconds() === $timepicker.$date.getSeconds();
  1204            }
  1205          };
  1206          $timepicker.$isDisabled = function(date, index) {
  1207            var selectedTime;
  1208            if (index === 0) {
  1209              selectedTime = date.getTime() + viewDate.minute * 6e4 + viewDate.second * 1e3;
  1210            } else if (index === 1) {
  1211              selectedTime = date.getTime() + viewDate.hour * 36e5 + viewDate.second * 1e3;
  1212            } else if (index === 2) {
  1213              selectedTime = date.getTime() + viewDate.hour * 36e5 + viewDate.minute * 6e4;
  1214            }
  1215            return selectedTime < options.minTime * 1 || selectedTime > options.maxTime * 1;
  1216          };
  1217          scope.$arrowAction = function(value, index) {
  1218            if (options.arrowBehavior === 'picker') {
  1219              $timepicker.$setTimeByStep(value, index);
  1220            } else {
  1221              $timepicker.$moveIndex(value, index);
  1222            }
  1223          };
  1224          $timepicker.$setTimeByStep = function(value, index) {
  1225            var newDate = new Date($timepicker.$date || startDate);
  1226            var hours = newDate.getHours();
  1227            var minutes = newDate.getMinutes();
  1228            var seconds = newDate.getSeconds();
  1229            if (index === 0) {
  1230              newDate.setHours(hours - parseInt(options.hourStep, 10) * value);
  1231            } else if (index === 1) {
  1232              newDate.setMinutes(minutes - parseInt(options.minuteStep, 10) * value);
  1233            } else if (index === 2) {
  1234              newDate.setSeconds(seconds - parseInt(options.secondStep, 10) * value);
  1235            }
  1236            $timepicker.select(newDate, index, true);
  1237          };
  1238          $timepicker.$moveIndex = function(value, index) {
  1239            var targetDate;
  1240            if (index === 0) {
  1241              targetDate = new Date(1970, 0, 1, viewDate.hour + value * options.length, viewDate.minute, viewDate.second);
  1242              angular.extend(viewDate, {
  1243                hour: targetDate.getHours()
  1244              });
  1245            } else if (index === 1) {
  1246              targetDate = new Date(1970, 0, 1, viewDate.hour, viewDate.minute + value * options.length * options.minuteStep, viewDate.second);
  1247              angular.extend(viewDate, {
  1248                minute: targetDate.getMinutes()
  1249              });
  1250            } else if (index === 2) {
  1251              targetDate = new Date(1970, 0, 1, viewDate.hour, viewDate.minute, viewDate.second + value * options.length * options.secondStep);
  1252              angular.extend(viewDate, {
  1253                second: targetDate.getSeconds()
  1254              });
  1255            }
  1256            $timepicker.$build();
  1257          };
  1258          $timepicker.$onMouseDown = function(evt) {
  1259            if (evt.target.nodeName.toLowerCase() !== 'input') evt.preventDefault();
  1260            evt.stopPropagation();
  1261            if (isTouch) {
  1262              var targetEl = angular.element(evt.target);
  1263              if (targetEl[0].nodeName.toLowerCase() !== 'button') {
  1264                targetEl = targetEl.parent();
  1265              }
  1266              targetEl.triggerHandler('click');
  1267            }
  1268          };
  1269          $timepicker.$onKeyDown = function(evt) {
  1270            if (!/(38|37|39|40|13)/.test(evt.keyCode) || evt.shiftKey || evt.altKey) return;
  1271            evt.preventDefault();
  1272            evt.stopPropagation();
  1273            if (evt.keyCode === 13) {
  1274              $timepicker.hide(true);
  1275              return;
  1276            }
  1277            var newDate = new Date($timepicker.$date);
  1278            var hours = newDate.getHours();
  1279            var hoursLength = formatDate(newDate, hoursFormat).length;
  1280            var minutes = newDate.getMinutes();
  1281            var minutesLength = formatDate(newDate, minutesFormat).length;
  1282            var seconds = newDate.getSeconds();
  1283            var secondsLength = formatDate(newDate, secondsFormat).length;
  1284            var sepLength = 1;
  1285            var lateralMove = /(37|39)/.test(evt.keyCode);
  1286            var count = 2 + showSeconds * 1 + showAM * 1;
  1287            if (lateralMove) {
  1288              if (evt.keyCode === 37) selectedIndex = selectedIndex < 1 ? count - 1 : selectedIndex - 1; else if (evt.keyCode === 39) selectedIndex = selectedIndex < count - 1 ? selectedIndex + 1 : 0;
  1289            }
  1290            var selectRange = [ 0, hoursLength ];
  1291            var incr = 0;
  1292            if (evt.keyCode === 38) incr = -1;
  1293            if (evt.keyCode === 40) incr = +1;
  1294            var isSeconds = selectedIndex === 2 && showSeconds;
  1295            var isMeridian = selectedIndex === 2 && !showSeconds || selectedIndex === 3 && showSeconds;
  1296            if (selectedIndex === 0) {
  1297              newDate.setHours(hours + incr * parseInt(options.hourStep, 10));
  1298              hoursLength = formatDate(newDate, hoursFormat).length;
  1299              selectRange = [ 0, hoursLength ];
  1300            } else if (selectedIndex === 1) {
  1301              newDate.setMinutes(minutes + incr * parseInt(options.minuteStep, 10));
  1302              minutesLength = formatDate(newDate, minutesFormat).length;
  1303              selectRange = [ hoursLength + sepLength, minutesLength ];
  1304            } else if (isSeconds) {
  1305              newDate.setSeconds(seconds + incr * parseInt(options.secondStep, 10));
  1306              secondsLength = formatDate(newDate, secondsFormat).length;
  1307              selectRange = [ hoursLength + sepLength + minutesLength + sepLength, secondsLength ];
  1308            } else if (isMeridian) {
  1309              if (!lateralMove) $timepicker.switchMeridian();
  1310              selectRange = [ hoursLength + sepLength + minutesLength + sepLength + (secondsLength + sepLength) * showSeconds, 2 ];
  1311            }
  1312            $timepicker.select(newDate, selectedIndex, true);
  1313            createSelection(selectRange[0], selectRange[1]);
  1314            parentScope.$digest();
  1315          };
  1316          function createSelection(start, length) {
  1317            var end = start + length;
  1318            if (element[0].createTextRange) {
  1319              var selRange = element[0].createTextRange();
  1320              selRange.collapse(true);
  1321              selRange.moveStart('character', start);
  1322              selRange.moveEnd('character', end);
  1323              selRange.select();
  1324            } else if (element[0].setSelectionRange) {
  1325              element[0].setSelectionRange(start, end);
  1326            } else if (angular.isUndefined(element[0].selectionStart)) {
  1327              element[0].selectionStart = start;
  1328              element[0].selectionEnd = end;
  1329            }
  1330          }
  1331          function focusElement() {
  1332            element[0].focus();
  1333          }
  1334          var _init = $timepicker.init;
  1335          $timepicker.init = function() {
  1336            if (isNative && options.useNative) {
  1337              element.prop('type', 'time');
  1338              element.css('-webkit-appearance', 'textfield');
  1339              return;
  1340            } else if (isTouch) {
  1341              element.prop('type', 'text');
  1342              element.attr('readonly', 'true');
  1343              element.on('click', focusElement);
  1344            }
  1345            _init();
  1346          };
  1347          var _destroy = $timepicker.destroy;
  1348          $timepicker.destroy = function() {
  1349            if (isNative && options.useNative) {
  1350              element.off('click', focusElement);
  1351            }
  1352            _destroy();
  1353          };
  1354          var _show = $timepicker.show;
  1355          $timepicker.show = function() {
  1356            if (!isTouch && element.attr('readonly') || element.attr('disabled')) return;
  1357            _show();
  1358            $timeout(function() {
  1359              if ($timepicker.$element) $timepicker.$element.on(isTouch ? 'touchstart' : 'mousedown', $timepicker.$onMouseDown);
  1360              if (options.keyboard) {
  1361                if (element) element.on('keydown', $timepicker.$onKeyDown);
  1362              }
  1363            }, 0, false);
  1364          };
  1365          var _hide = $timepicker.hide;
  1366          $timepicker.hide = function(blur) {
  1367            if (!$timepicker.$isShown) return;
  1368            if ($timepicker.$element) $timepicker.$element.off(isTouch ? 'touchstart' : 'mousedown', $timepicker.$onMouseDown);
  1369            if (options.keyboard) {
  1370              if (element) element.off('keydown', $timepicker.$onKeyDown);
  1371            }
  1372            _hide(blur);
  1373          };
  1374          return $timepicker;
  1375        }
  1376        timepickerFactory.defaults = defaults;
  1377        return timepickerFactory;
  1378      } ];
  1379    }).directive('bsTimepicker', [ '$window', '$parse', '$q', '$dateFormatter', '$dateParser', '$timepicker', function($window, $parse, $q, $dateFormatter, $dateParser, $timepicker) {
  1380      var defaults = $timepicker.defaults;
  1381      var isNative = /(ip[ao]d|iphone|android)/gi.test($window.navigator.userAgent);
  1382      return {
  1383        restrict: 'EAC',
  1384        require: 'ngModel',
  1385        link: function postLink(scope, element, attr, controller) {
  1386          var options = {
  1387            scope: scope
  1388          };
  1389          angular.forEach([ 'template', 'templateUrl', 'controller', 'controllerAs', 'placement', 'container', 'delay', 'trigger', 'keyboard', 'html', 'animation', 'autoclose', 'timeType', 'timeFormat', 'timezone', 'modelTimeFormat', 'useNative', 'hourStep', 'minuteStep', 'secondStep', 'length', 'arrowBehavior', 'iconUp', 'iconDown', 'roundDisplay', 'id', 'prefixClass', 'prefixEvent', 'defaultDate' ], function(key) {
  1390            if (angular.isDefined(attr[key])) options[key] = attr[key];
  1391          });
  1392          var falseValueRegExp = /^(false|0|)$/i;
  1393          angular.forEach([ 'html', 'container', 'autoclose', 'useNative', 'roundDisplay' ], function(key) {
  1394            if (angular.isDefined(attr[key]) && falseValueRegExp.test(attr[key])) {
  1395              options[key] = false;
  1396            }
  1397          });
  1398          angular.forEach([ 'onBeforeShow', 'onShow', 'onBeforeHide', 'onHide' ], function(key) {
  1399            var bsKey = 'bs' + key.charAt(0).toUpperCase() + key.slice(1);
  1400            if (angular.isDefined(attr[bsKey])) {
  1401              options[key] = scope.$eval(attr[bsKey]);
  1402            }
  1403          });
  1404          if (isNative && (options.useNative || defaults.useNative)) options.timeFormat = 'HH:mm';
  1405          var timepicker = $timepicker(element, controller, options);
  1406          options = timepicker.$options;
  1407          var lang = options.lang;
  1408          var formatDate = function(date, format, timezone) {
  1409            return $dateFormatter.formatDate(date, format, lang, timezone);
  1410          };
  1411          if (attr.bsShow) {
  1412            scope.$watch(attr.bsShow, function(newValue, oldValue) {
  1413              if (!timepicker || !angular.isDefined(newValue)) return;
  1414              if (angular.isString(newValue)) newValue = !!newValue.match(/true|,?(timepicker),?/i);
  1415              if (newValue === true) {
  1416                timepicker.show();
  1417              } else {
  1418                timepicker.hide();
  1419              }
  1420            });
  1421          }
  1422          var dateParser = $dateParser({
  1423            format: options.timeFormat,
  1424            lang: lang
  1425          });
  1426          angular.forEach([ 'minTime', 'maxTime' ], function(key) {
  1427            if (angular.isDefined(attr[key])) {
  1428              attr.$observe(key, function(newValue) {
  1429                timepicker.$options[key] = dateParser.getTimeForAttribute(key, newValue);
  1430                if (!isNaN(timepicker.$options[key])) timepicker.$build();
  1431                validateAgainstMinMaxTime(controller.$dateValue);
  1432              });
  1433            }
  1434          });
  1435          scope.$watch(attr.ngModel, function(newValue, oldValue) {
  1436            timepicker.update(controller.$dateValue);
  1437          }, true);
  1438          function validateAgainstMinMaxTime(parsedTime) {
  1439            if (!angular.isDate(parsedTime)) return;
  1440            var isMinValid = isNaN(options.minTime) || new Date(parsedTime.getTime()).setFullYear(1970, 0, 1) >= options.minTime;
  1441            var isMaxValid = isNaN(options.maxTime) || new Date(parsedTime.getTime()).setFullYear(1970, 0, 1) <= options.maxTime;
  1442            var isValid = isMinValid && isMaxValid;
  1443            controller.$setValidity('date', isValid);
  1444            controller.$setValidity('min', isMinValid);
  1445            controller.$setValidity('max', isMaxValid);
  1446            if (!isValid) {
  1447              return;
  1448            }
  1449            controller.$dateValue = parsedTime;
  1450          }
  1451          controller.$parsers.unshift(function(viewValue) {
  1452            var date;
  1453            if (!viewValue) {
  1454              controller.$setValidity('date', true);
  1455              return null;
  1456            }
  1457            var parsedTime = angular.isDate(viewValue) ? viewValue : dateParser.parse(viewValue, controller.$dateValue);
  1458            if (!parsedTime || isNaN(parsedTime.getTime())) {
  1459              controller.$setValidity('date', false);
  1460              return undefined;
  1461            }
  1462            validateAgainstMinMaxTime(parsedTime);
  1463            if (options.timeType === 'string') {
  1464              date = dateParser.timezoneOffsetAdjust(parsedTime, options.timezone, true);
  1465              return formatDate(date, options.modelTimeFormat || options.timeFormat);
  1466            }
  1467            date = dateParser.timezoneOffsetAdjust(controller.$dateValue, options.timezone, true);
  1468            if (options.timeType === 'number') {
  1469              return date.getTime();
  1470            } else if (options.timeType === 'unix') {
  1471              return date.getTime() / 1e3;
  1472            } else if (options.timeType === 'iso') {
  1473              return date.toISOString();
  1474            }
  1475            return new Date(date);
  1476          });
  1477          controller.$formatters.push(function(modelValue) {
  1478            var date;
  1479            if (angular.isUndefined(modelValue) || modelValue === null) {
  1480              date = NaN;
  1481            } else if (angular.isDate(modelValue)) {
  1482              date = modelValue;
  1483            } else if (options.timeType === 'string') {
  1484              date = dateParser.parse(modelValue, null, options.modelTimeFormat);
  1485            } else if (options.timeType === 'unix') {
  1486              date = new Date(modelValue * 1e3);
  1487            } else {
  1488              date = new Date(modelValue);
  1489            }
  1490            controller.$dateValue = dateParser.timezoneOffsetAdjust(date, options.timezone);
  1491            return getTimeFormattedString();
  1492          });
  1493          controller.$render = function() {
  1494            element.val(getTimeFormattedString());
  1495          };
  1496          function getTimeFormattedString() {
  1497            return !controller.$dateValue || isNaN(controller.$dateValue.getTime()) ? '' : formatDate(controller.$dateValue, options.timeFormat);
  1498          }
  1499          scope.$on('$destroy', function() {
  1500            if (timepicker) timepicker.destroy();
  1501            options = null;
  1502            timepicker = null;
  1503          });
  1504        }
  1505      };
  1506    } ]);
  1507    angular.module('mgcrea.ngStrap.scrollspy', [ 'mgcrea.ngStrap.helpers.debounce', 'mgcrea.ngStrap.helpers.dimensions' ]).provider('$scrollspy', function() {
  1508      var spies = this.$$spies = {};
  1509      var defaults = this.defaults = {
  1510        debounce: 150,
  1511        throttle: 100,
  1512        offset: 100
  1513      };
  1514      this.$get = [ '$window', '$document', '$rootScope', 'dimensions', 'debounce', 'throttle', function($window, $document, $rootScope, dimensions, debounce, throttle) {
  1515        var windowEl = angular.element($window);
  1516        var docEl = angular.element($document.prop('documentElement'));
  1517        var bodyEl = angular.element($window.document.body);
  1518        function nodeName(element, name) {
  1519          return element[0].nodeName && element[0].nodeName.toLowerCase() === name.toLowerCase();
  1520        }
  1521        function ScrollSpyFactory(config) {
  1522          var options = angular.extend({}, defaults, config);
  1523          if (!options.element) options.element = bodyEl;
  1524          var isWindowSpy = nodeName(options.element, 'body');
  1525          var scrollEl = isWindowSpy ? windowEl : options.element;
  1526          var scrollId = isWindowSpy ? 'window' : options.id;
  1527          if (spies[scrollId]) {
  1528            spies[scrollId].$$count++;
  1529            return spies[scrollId];
  1530          }
  1531          var $scrollspy = {};
  1532          var unbindViewContentLoaded;
  1533          var unbindIncludeContentLoaded;
  1534          var trackedElements = $scrollspy.$trackedElements = [];
  1535          var sortedElements = [];
  1536          var activeTarget;
  1537          var debouncedCheckPosition;
  1538          var throttledCheckPosition;
  1539          var debouncedCheckOffsets;
  1540          var viewportHeight;
  1541          var scrollTop;
  1542          $scrollspy.init = function() {
  1543            this.$$count = 1;
  1544            debouncedCheckPosition = debounce(this.checkPosition, options.debounce);
  1545            throttledCheckPosition = throttle(this.checkPosition, options.throttle);
  1546            scrollEl.on('click', this.checkPositionWithEventLoop);
  1547            windowEl.on('resize', debouncedCheckPosition);
  1548            scrollEl.on('scroll', throttledCheckPosition);
  1549            debouncedCheckOffsets = debounce(this.checkOffsets, options.debounce);
  1550            unbindViewContentLoaded = $rootScope.$on('$viewContentLoaded', debouncedCheckOffsets);
  1551            unbindIncludeContentLoaded = $rootScope.$on('$includeContentLoaded', debouncedCheckOffsets);
  1552            debouncedCheckOffsets();
  1553            if (scrollId) {
  1554              spies[scrollId] = $scrollspy;
  1555            }
  1556          };
  1557          $scrollspy.destroy = function() {
  1558            this.$$count--;
  1559            if (this.$$count > 0) {
  1560              return;
  1561            }
  1562            scrollEl.off('click', this.checkPositionWithEventLoop);
  1563            windowEl.off('resize', debouncedCheckPosition);
  1564            scrollEl.off('scroll', throttledCheckPosition);
  1565            unbindViewContentLoaded();
  1566            unbindIncludeContentLoaded();
  1567            if (scrollId) {
  1568              delete spies[scrollId];
  1569            }
  1570          };
  1571          $scrollspy.checkPosition = function() {
  1572            if (!sortedElements.length) return;
  1573            scrollTop = (isWindowSpy ? $window.pageYOffset : scrollEl.prop('scrollTop')) || 0;
  1574            viewportHeight = Math.max($window.innerHeight, docEl.prop('clientHeight'));
  1575            if (scrollTop < sortedElements[0].offsetTop && activeTarget !== sortedElements[0].target) {
  1576              return $scrollspy.$activateElement(sortedElements[0]);
  1577            }
  1578            for (var i = sortedElements.length; i--; ) {
  1579              if (angular.isUndefined(sortedElements[i].offsetTop) || sortedElements[i].offsetTop === null) continue;
  1580              if (activeTarget === sortedElements[i].target) continue;
  1581              if (scrollTop < sortedElements[i].offsetTop) continue;
  1582              if (sortedElements[i + 1] && scrollTop > sortedElements[i + 1].offsetTop) continue;
  1583              return $scrollspy.$activateElement(sortedElements[i]);
  1584            }
  1585          };
  1586          $scrollspy.checkPositionWithEventLoop = function() {
  1587            setTimeout($scrollspy.checkPosition, 1);
  1588          };
  1589          $scrollspy.$activateElement = function(element) {
  1590            if (activeTarget) {
  1591              var activeElement = $scrollspy.$getTrackedElement(activeTarget);
  1592              if (activeElement) {
  1593                activeElement.source.removeClass('active');
  1594                if (nodeName(activeElement.source, 'li') && nodeName(activeElement.source.parent().parent(), 'li')) {
  1595                  activeElement.source.parent().parent().removeClass('active');
  1596                }
  1597              }
  1598            }
  1599            activeTarget = element.target;
  1600            element.source.addClass('active');
  1601            if (nodeName(element.source, 'li') && nodeName(element.source.parent().parent(), 'li')) {
  1602              element.source.parent().parent().addClass('active');
  1603            }
  1604          };
  1605          $scrollspy.$getTrackedElement = function(target) {
  1606            return trackedElements.filter(function(obj) {
  1607              return obj.target === target;
  1608            })[0];
  1609          };
  1610          $scrollspy.checkOffsets = function() {
  1611            angular.forEach(trackedElements, function(trackedElement) {
  1612              var targetElement = document.querySelector(trackedElement.target);
  1613              trackedElement.offsetTop = targetElement ? dimensions.offset(targetElement).top : null;
  1614              if (options.offset && trackedElement.offsetTop !== null) trackedElement.offsetTop -= options.offset * 1;
  1615            });
  1616            sortedElements = trackedElements.filter(function(el) {
  1617              return el.offsetTop !== null;
  1618            }).sort(function(a, b) {
  1619              return a.offsetTop - b.offsetTop;
  1620            });
  1621            debouncedCheckPosition();
  1622          };
  1623          $scrollspy.trackElement = function(target, source) {
  1624            trackedElements.push({
  1625              target: target,
  1626              source: source
  1627            });
  1628          };
  1629          $scrollspy.untrackElement = function(target, source) {
  1630            var toDelete;
  1631            for (var i = trackedElements.length; i--; ) {
  1632              if (trackedElements[i].target === target && trackedElements[i].source === source) {
  1633                toDelete = i;
  1634                break;
  1635              }
  1636            }
  1637            trackedElements.splice(toDelete, 1);
  1638          };
  1639          $scrollspy.activate = function(i) {
  1640            trackedElements[i].addClass('active');
  1641          };
  1642          $scrollspy.init();
  1643          return $scrollspy;
  1644        }
  1645        return ScrollSpyFactory;
  1646      } ];
  1647    }).directive('bsScrollspy', [ '$rootScope', 'debounce', 'dimensions', '$scrollspy', function($rootScope, debounce, dimensions, $scrollspy) {
  1648      return {
  1649        restrict: 'EAC',
  1650        link: function postLink(scope, element, attr) {
  1651          var options = {
  1652            scope: scope
  1653          };
  1654          angular.forEach([ 'offset', 'target' ], function(key) {
  1655            if (angular.isDefined(attr[key])) options[key] = attr[key];
  1656          });
  1657          var scrollspy = $scrollspy(options);
  1658          scrollspy.trackElement(options.target, element);
  1659          scope.$on('$destroy', function() {
  1660            if (scrollspy) {
  1661              scrollspy.untrackElement(options.target, element);
  1662              scrollspy.destroy();
  1663            }
  1664            options = null;
  1665            scrollspy = null;
  1666          });
  1667        }
  1668      };
  1669    } ]).directive('bsScrollspyList', [ '$rootScope', 'debounce', 'dimensions', '$scrollspy', function($rootScope, debounce, dimensions, $scrollspy) {
  1670      return {
  1671        restrict: 'A',
  1672        compile: function postLink(element, attr) {
  1673          var children = element[0].querySelectorAll('li > a[href]');
  1674          angular.forEach(children, function(child) {
  1675            var childEl = angular.element(child);
  1676            childEl.parent().attr('bs-scrollspy', '').attr('data-target', childEl.attr('href'));
  1677          });
  1678        }
  1679      };
  1680    } ]);
  1681    angular.module('mgcrea.ngStrap.select', [ 'mgcrea.ngStrap.tooltip', 'mgcrea.ngStrap.helpers.parseOptions' ]).provider('$select', function() {
  1682      var defaults = this.defaults = {
  1683        animation: 'am-fade',
  1684        prefixClass: 'select',
  1685        prefixEvent: '$select',
  1686        placement: 'bottom-left',
  1687        templateUrl: 'select/select.tpl.html',
  1688        trigger: 'focus',
  1689        container: false,
  1690        keyboard: true,
  1691        html: false,
  1692        delay: 0,
  1693        multiple: false,
  1694        allNoneButtons: false,
  1695        sort: true,
  1696        caretHtml: '&nbsp;<span class="caret"></span>',
  1697        placeholder: 'Choose among the following...',
  1698        allText: 'All',
  1699        noneText: 'None',
  1700        maxLength: 3,
  1701        maxLengthHtml: 'selected',
  1702        iconCheckmark: 'glyphicon glyphicon-ok',
  1703        toggle: false
  1704      };
  1705      this.$get = [ '$window', '$document', '$rootScope', '$tooltip', '$timeout', function($window, $document, $rootScope, $tooltip, $timeout) {
  1706        var isNative = /(ip[ao]d|iphone|android)/gi.test($window.navigator.userAgent);
  1707        var isTouch = 'createTouch' in $window.document && isNative;
  1708        function SelectFactory(element, controller, config) {
  1709          var $select = {};
  1710          var options = angular.extend({}, defaults, config);
  1711          $select = $tooltip(element, options);
  1712          var scope = $select.$scope;
  1713          scope.$matches = [];
  1714          if (options.multiple) {
  1715            scope.$activeIndex = [];
  1716          } else {
  1717            scope.$activeIndex = -1;
  1718          }
  1719          scope.$isMultiple = options.multiple;
  1720          scope.$showAllNoneButtons = options.allNoneButtons && options.multiple;
  1721          scope.$iconCheckmark = options.iconCheckmark;
  1722          scope.$allText = options.allText;
  1723          scope.$noneText = options.noneText;
  1724          scope.$activate = function(index) {
  1725            scope.$$postDigest(function() {
  1726              $select.activate(index);
  1727            });
  1728          };
  1729          scope.$select = function(index, evt) {
  1730            scope.$$postDigest(function() {
  1731              $select.select(index);
  1732            });
  1733          };
  1734          scope.$isVisible = function() {
  1735            return $select.$isVisible();
  1736          };
  1737          scope.$isActive = function(index) {
  1738            return $select.$isActive(index);
  1739          };
  1740          scope.$selectAll = function() {
  1741            for (var i = 0; i < scope.$matches.length; i++) {
  1742              if (!scope.$isActive(i)) {
  1743                scope.$select(i);
  1744              }
  1745            }
  1746          };
  1747          scope.$selectNone = function() {
  1748            for (var i = 0; i < scope.$matches.length; i++) {
  1749              if (scope.$isActive(i)) {
  1750                scope.$select(i);
  1751              }
  1752            }
  1753          };
  1754          $select.update = function(matches) {
  1755            scope.$matches = matches;
  1756            $select.$updateActiveIndex();
  1757          };
  1758          $select.activate = function(index) {
  1759            if (options.multiple) {
  1760              if ($select.$isActive(index)) {
  1761                scope.$activeIndex.splice(scope.$activeIndex.indexOf(index), 1);
  1762              } else {
  1763                scope.$activeIndex.push(index);
  1764              }
  1765              if (options.sort) scope.$activeIndex.sort(function(a, b) {
  1766                return a - b;
  1767              });
  1768            } else {
  1769              scope.$activeIndex = index;
  1770            }
  1771            return scope.$activeIndex;
  1772          };
  1773          $select.select = function(index) {
  1774            if (angular.isUndefined(index) || index < 0 || index >= scope.$matches.length) {
  1775              return;
  1776            }
  1777            var value = scope.$matches[index].value;
  1778            scope.$apply(function() {
  1779              $select.activate(index);
  1780              if (options.multiple) {
  1781                controller.$setViewValue(scope.$activeIndex.map(function(index) {
  1782                  if (angular.isUndefined(scope.$matches[index])) {
  1783                    return null;
  1784                  }
  1785                  return scope.$matches[index].value;
  1786                }));
  1787              } else {
  1788                if (options.toggle) {
  1789                  controller.$setViewValue(value === controller.$modelValue ? undefined : value);
  1790                } else {
  1791                  controller.$setViewValue(value);
  1792                }
  1793                $select.hide();
  1794              }
  1795            });
  1796            scope.$emit(options.prefixEvent + '.select', value, index, $select);
  1797            if (angular.isDefined(options.onSelect) && angular.isFunction(options.onSelect)) {
  1798              options.onSelect(value, index, $select);
  1799            }
  1800          };
  1801          $select.$updateActiveIndex = function() {
  1802            if (options.multiple) {
  1803              if (angular.isArray(controller.$modelValue)) {
  1804                scope.$activeIndex = controller.$modelValue.map(function(value) {
  1805                  return $select.$getIndex(value);
  1806                });
  1807              } else {
  1808                scope.$activeIndex = [];
  1809              }
  1810            } else {
  1811              if (angular.isDefined(controller.$modelValue) && scope.$matches.length) {
  1812                scope.$activeIndex = $select.$getIndex(controller.$modelValue);
  1813              } else {
  1814                scope.$activeIndex = -1;
  1815              }
  1816            }
  1817          };
  1818          $select.$isVisible = function() {
  1819            if (!options.minLength || !controller) {
  1820              return scope.$matches.length;
  1821            }
  1822            return scope.$matches.length && controller.$viewValue.length >= options.minLength;
  1823          };
  1824          $select.$isActive = function(index) {
  1825            if (options.multiple) {
  1826              return scope.$activeIndex.indexOf(index) !== -1;
  1827            }
  1828            return scope.$activeIndex === index;
  1829          };
  1830          $select.$getIndex = function(value) {
  1831            var index;
  1832            for (index = scope.$matches.length; index--; ) {
  1833              if (angular.equals(scope.$matches[index].value, value)) break;
  1834            }
  1835            return index;
  1836          };
  1837          $select.$onMouseDown = function(evt) {
  1838            evt.preventDefault();
  1839            evt.stopPropagation();
  1840            if (isTouch) {
  1841              var targetEl = angular.element(evt.target);
  1842              targetEl.triggerHandler('click');
  1843            }
  1844          };
  1845          $select.$onKeyDown = function(evt) {
  1846            if (!/(9|13|38|40)/.test(evt.keyCode)) return;
  1847            if (evt.keyCode !== 9) {
  1848              evt.preventDefault();
  1849              evt.stopPropagation();
  1850            }
  1851            if (options.multiple && evt.keyCode === 9) {
  1852              return $select.hide();
  1853            }
  1854            if (!options.multiple && (evt.keyCode === 13 || evt.keyCode === 9)) {
  1855              return $select.select(scope.$activeIndex);
  1856            }
  1857            if (!options.multiple) {
  1858              if (evt.keyCode === 38 && scope.$activeIndex > 0) scope.$activeIndex--; else if (evt.keyCode === 38 && scope.$activeIndex < 0) scope.$activeIndex = scope.$matches.length - 1; else if (evt.keyCode === 40 && scope.$activeIndex < scope.$matches.length - 1) scope.$activeIndex++; else if (angular.isUndefined(scope.$activeIndex)) scope.$activeIndex = 0;
  1859              scope.$digest();
  1860            }
  1861          };
  1862          $select.$isIE = function() {
  1863            var ua = $window.navigator.userAgent;
  1864            return ua.indexOf('MSIE ') > 0 || ua.indexOf('Trident/') > 0 || ua.indexOf('Edge/') > 0;
  1865          };
  1866          $select.$selectScrollFix = function(e) {
  1867            if ($document[0].activeElement.tagName === 'UL') {
  1868              e.preventDefault();
  1869              e.stopImmediatePropagation();
  1870              e.target.focus();
  1871            }
  1872          };
  1873          var _show = $select.show;
  1874          $select.show = function() {
  1875            _show();
  1876            if (options.multiple) {
  1877              $select.$element.addClass('select-multiple');
  1878            }
  1879            $timeout(function() {
  1880              $select.$element.on(isTouch ? 'touchstart' : 'mousedown', $select.$onMouseDown);
  1881              if (options.keyboard) {
  1882                element.on('keydown', $select.$onKeyDown);
  1883              }
  1884            }, 0, false);
  1885          };
  1886          var _hide = $select.hide;
  1887          $select.hide = function() {
  1888            if (!options.multiple && angular.isUndefined(controller.$modelValue)) {
  1889              scope.$activeIndex = -1;
  1890            }
  1891            $select.$element.off(isTouch ? 'touchstart' : 'mousedown', $select.$onMouseDown);
  1892            if (options.keyboard) {
  1893              element.off('keydown', $select.$onKeyDown);
  1894            }
  1895            _hide(true);
  1896          };
  1897          return $select;
  1898        }
  1899        SelectFactory.defaults = defaults;
  1900        return SelectFactory;
  1901      } ];
  1902    }).directive('bsSelect', [ '$window', '$parse', '$q', '$select', '$parseOptions', function($window, $parse, $q, $select, $parseOptions) {
  1903      var defaults = $select.defaults;
  1904      return {
  1905        restrict: 'EAC',
  1906        require: 'ngModel',
  1907        link: function postLink(scope, element, attr, controller) {
  1908          var options = {
  1909            scope: scope,
  1910            placeholder: defaults.placeholder
  1911          };
  1912          angular.forEach([ 'template', 'templateUrl', 'controller', 'controllerAs', 'placement', 'container', 'delay', 'trigger', 'keyboard', 'html', 'animation', 'placeholder', 'allNoneButtons', 'maxLength', 'maxLengthHtml', 'allText', 'noneText', 'iconCheckmark', 'autoClose', 'id', 'sort', 'caretHtml', 'prefixClass', 'prefixEvent', 'toggle' ], function(key) {
  1913            if (angular.isDefined(attr[key])) options[key] = attr[key];
  1914          });
  1915          var falseValueRegExp = /^(false|0|)$/i;
  1916          angular.forEach([ 'html', 'container', 'allNoneButtons', 'sort' ], function(key) {
  1917            if (angular.isDefined(attr[key]) && falseValueRegExp.test(attr[key])) {
  1918              options[key] = false;
  1919            }
  1920          });
  1921          angular.forEach([ 'onBeforeShow', 'onShow', 'onBeforeHide', 'onHide', 'onSelect' ], function(key) {
  1922            var bsKey = 'bs' + key.charAt(0).toUpperCase() + key.slice(1);
  1923            if (angular.isDefined(attr[bsKey])) {
  1924              options[key] = scope.$eval(attr[bsKey]);
  1925            }
  1926          });
  1927          var dataMultiple = element.attr('data-multiple');
  1928          if (angular.isDefined(dataMultiple)) {
  1929            if (falseValueRegExp.test(dataMultiple)) {
  1930              options.multiple = false;
  1931            } else {
  1932              options.multiple = dataMultiple;
  1933            }
  1934          }
  1935          if (element[0].nodeName.toLowerCase() === 'select') {
  1936            var inputEl = element;
  1937            inputEl.css('display', 'none');
  1938            element = angular.element('<button type="button" class="btn btn-default"></button>');
  1939            inputEl.after(element);
  1940          }
  1941          var parsedOptions = $parseOptions(attr.bsOptions);
  1942          var select = $select(element, controller, options);
  1943          if (select.$isIE()) {
  1944            element[0].addEventListener('blur', select.$selectScrollFix);
  1945          }
  1946          var watchedOptions = parsedOptions.$match[7].replace(/\|.+/, '').trim();
  1947          scope.$watch(watchedOptions, function(newValue, oldValue) {
  1948            parsedOptions.valuesFn(scope, controller).then(function(values) {
  1949              select.update(values);
  1950              controller.$render();
  1951            });
  1952          }, true);
  1953          scope.$watch(attr.ngModel, function(newValue, oldValue) {
  1954            select.$updateActiveIndex();
  1955            controller.$render();
  1956          }, true);
  1957          controller.$render = function() {
  1958            var selected;
  1959            var index;
  1960            if (options.multiple && angular.isArray(controller.$modelValue)) {
  1961              selected = controller.$modelValue.map(function(value) {
  1962                index = select.$getIndex(value);
  1963                return index !== -1 ? select.$scope.$matches[index].label : false;
  1964              }).filter(angular.isDefined);
  1965              if (selected.length > (options.maxLength || defaults.maxLength)) {
  1966                selected = selected.length + ' ' + (options.maxLengthHtml || defaults.maxLengthHtml);
  1967              } else {
  1968                selected = selected.join(', ');
  1969              }
  1970            } else {
  1971              index = select.$getIndex(controller.$modelValue);
  1972              selected = index !== -1 ? select.$scope.$matches[index].label : false;
  1973            }
  1974            element.html((selected || options.placeholder) + (options.caretHtml || defaults.caretHtml));
  1975          };
  1976          if (options.multiple) {
  1977            controller.$isEmpty = function(value) {
  1978              return !value || value.length === 0;
  1979            };
  1980          }
  1981          scope.$on('$destroy', function() {
  1982            if (select) select.destroy();
  1983            options = null;
  1984            select = null;
  1985          });
  1986        }
  1987      };
  1988    } ]);
  1989    angular.module('mgcrea.ngStrap.popover', [ 'mgcrea.ngStrap.tooltip' ]).provider('$popover', function() {
  1990      var defaults = this.defaults = {
  1991        animation: 'am-fade',
  1992        customClass: '',
  1993        container: false,
  1994        target: false,
  1995        placement: 'right',
  1996        templateUrl: 'popover/popover.tpl.html',
  1997        contentTemplate: false,
  1998        trigger: 'click',
  1999        keyboard: true,
  2000        html: false,
  2001        title: '',
  2002        content: '',
  2003        delay: 0,
  2004        autoClose: false
  2005      };
  2006      this.$get = [ '$tooltip', function($tooltip) {
  2007        function PopoverFactory(element, config) {
  2008          var options = angular.extend({}, defaults, config);
  2009          var $popover = $tooltip(element, options);
  2010          if (options.content) {
  2011            $popover.$scope.content = options.content;
  2012          }
  2013          return $popover;
  2014        }
  2015        return PopoverFactory;
  2016      } ];
  2017    }).directive('bsPopover', [ '$window', '$sce', '$popover', function($window, $sce, $popover) {
  2018      var requestAnimationFrame = $window.requestAnimationFrame || $window.setTimeout;
  2019      return {
  2020        restrict: 'EAC',
  2021        scope: true,
  2022        link: function postLink(scope, element, attr) {
  2023          var popover;
  2024          var options = {
  2025            scope: scope
  2026          };
  2027          angular.forEach([ 'template', 'templateUrl', 'controller', 'controllerAs', 'contentTemplate', 'placement', 'container', 'delay', 'trigger', 'html', 'animation', 'customClass', 'autoClose', 'id', 'prefixClass', 'prefixEvent' ], function(key) {
  2028            if (angular.isDefined(attr[key])) options[key] = attr[key];
  2029          });
  2030          var falseValueRegExp = /^(false|0|)$/i;
  2031          angular.forEach([ 'html', 'container', 'autoClose' ], function(key) {
  2032            if (angular.isDefined(attr[key]) && falseValueRegExp.test(attr[key])) options[key] = false;
  2033          });
  2034          angular.forEach([ 'onBeforeShow', 'onShow', 'onBeforeHide', 'onHide' ], function(key) {
  2035            var bsKey = 'bs' + key.charAt(0).toUpperCase() + key.slice(1);
  2036            if (angular.isDefined(attr[bsKey])) {
  2037              options[key] = scope.$eval(attr[bsKey]);
  2038            }
  2039          });
  2040          var dataTarget = element.attr('data-target');
  2041          if (angular.isDefined(dataTarget)) {
  2042            if (falseValueRegExp.test(dataTarget)) {
  2043              options.target = false;
  2044            } else {
  2045              options.target = dataTarget;
  2046            }
  2047          }
  2048          angular.forEach([ 'title', 'content' ], function(key) {
  2049            if (attr[key]) {
  2050              attr.$observe(key, function(newValue, oldValue) {
  2051                scope[key] = $sce.trustAsHtml(newValue);
  2052                if (angular.isDefined(oldValue)) {
  2053                  requestAnimationFrame(function() {
  2054                    if (popover) popover.$applyPlacement();
  2055                  });
  2056                }
  2057              });
  2058            }
  2059          });
  2060          if (attr.bsPopover) {
  2061            scope.$watch(attr.bsPopover, function(newValue, oldValue) {
  2062              if (angular.isObject(newValue)) {
  2063                angular.extend(scope, newValue);
  2064              } else {
  2065                scope.content = newValue;
  2066              }
  2067              if (angular.isDefined(oldValue)) {
  2068                requestAnimationFrame(function() {
  2069                  if (popover) popover.$applyPlacement();
  2070                });
  2071              }
  2072            }, true);
  2073          }
  2074          if (attr.bsShow) {
  2075            scope.$watch(attr.bsShow, function(newValue, oldValue) {
  2076              if (!popover || !angular.isDefined(newValue)) return;
  2077              if (angular.isString(newValue)) newValue = !!newValue.match(/true|,?(popover),?/i);
  2078              if (newValue === true) {
  2079                popover.show();
  2080              } else {
  2081                popover.hide();
  2082              }
  2083            });
  2084          }
  2085          if (attr.viewport) {
  2086            scope.$watch(attr.viewport, function(newValue) {
  2087              if (!popover || !angular.isDefined(newValue)) return;
  2088              popover.setViewport(newValue);
  2089            });
  2090          }
  2091          popover = $popover(element, options);
  2092          scope.$on('$destroy', function() {
  2093            if (popover) popover.destroy();
  2094            options = null;
  2095            popover = null;
  2096          });
  2097        }
  2098      };
  2099    } ]);
  2100    angular.module('mgcrea.ngStrap.navbar', []).provider('$navbar', function() {
  2101      var defaults = this.defaults = {
  2102        activeClass: 'active',
  2103        routeAttr: 'data-match-route',
  2104        strict: false
  2105      };
  2106      this.$get = function() {
  2107        return {
  2108          defaults: defaults
  2109        };
  2110      };
  2111    }).directive('bsNavbar', [ '$window', '$location', '$navbar', function($window, $location, $navbar) {
  2112      var defaults = $navbar.defaults;
  2113      return {
  2114        restrict: 'A',
  2115        link: function postLink(scope, element, attr, controller) {
  2116          var options = angular.copy(defaults);
  2117          angular.forEach(Object.keys(defaults), function(key) {
  2118            if (angular.isDefined(attr[key])) options[key] = attr[key];
  2119          });
  2120          scope.$watch(function() {
  2121            return $location.path();
  2122          }, function(newValue, oldValue) {
  2123            var liElements = element[0].querySelectorAll('li[' + options.routeAttr + ']');
  2124            angular.forEach(liElements, function(li) {
  2125              var liElement = angular.element(li);
  2126              var pattern = liElement.attr(options.routeAttr).replace('/', '\\/');
  2127              if (options.strict) {
  2128                pattern = '^' + pattern + '$';
  2129              }
  2130              var regexp = new RegExp(pattern, 'i');
  2131              if (regexp.test(newValue)) {
  2132                liElement.addClass(options.activeClass);
  2133              } else {
  2134                liElement.removeClass(options.activeClass);
  2135              }
  2136            });
  2137          });
  2138        }
  2139      };
  2140    } ]);
  2141    angular.module('mgcrea.ngStrap.modal', [ 'mgcrea.ngStrap.core', 'mgcrea.ngStrap.helpers.dimensions' ]).provider('$modal', function() {
  2142      var defaults = this.defaults = {
  2143        animation: 'am-fade',
  2144        backdropAnimation: 'am-fade',
  2145        customClass: '',
  2146        prefixClass: 'modal',
  2147        prefixEvent: 'modal',
  2148        placement: 'top',
  2149        templateUrl: 'modal/modal.tpl.html',
  2150        template: '',
  2151        contentTemplate: false,
  2152        container: false,
  2153        element: null,
  2154        backdrop: true,
  2155        keyboard: true,
  2156        html: false,
  2157        show: true,
  2158        size: null
  2159      };
  2160      this.$get = [ '$window', '$rootScope', '$bsCompiler', '$animate', '$timeout', '$sce', 'dimensions', function($window, $rootScope, $bsCompiler, $animate, $timeout, $sce, dimensions) {
  2161        var forEach = angular.forEach;
  2162        var requestAnimationFrame = $window.requestAnimationFrame || $window.setTimeout;
  2163        var bodyElement = angular.element($window.document.body);
  2164        var backdropCount = 0;
  2165        var dialogBaseZindex = 1050;
  2166        var backdropBaseZindex = 1040;
  2167        var validSizes = {
  2168          lg: 'modal-lg',
  2169          sm: 'modal-sm'
  2170        };
  2171        function ModalFactory(config) {
  2172          var $modal = {};
  2173          var options = $modal.$options = angular.extend({}, defaults, config);
  2174          var promise = $modal.$promise = $bsCompiler.compile(options);
  2175          var scope = $modal.$scope = options.scope && options.scope.$new() || $rootScope.$new();
  2176          if (!options.element && !options.container) {
  2177            options.container = 'body';
  2178          }
  2179          $modal.$id = options.id || options.element && options.element.attr('id') || '';
  2180          forEach([ 'title', 'content' ], function(key) {
  2181            if (options[key]) scope[key] = $sce.trustAsHtml(options[key]);
  2182          });
  2183          scope.$hide = function() {
  2184            scope.$$postDigest(function() {
  2185              $modal.hide();
  2186            });
  2187          };
  2188          scope.$show = function() {
  2189            scope.$$postDigest(function() {
  2190              $modal.show();
  2191            });
  2192          };
  2193          scope.$toggle = function() {
  2194            scope.$$postDigest(function() {
  2195              $modal.toggle();
  2196            });
  2197          };
  2198          $modal.$isShown = scope.$isShown = false;
  2199          var compileData;
  2200          var modalElement;
  2201          var modalScope;
  2202          var backdropElement = angular.element('<div class="' + options.prefixClass + '-backdrop"/>');
  2203          backdropElement.css({
  2204            position: 'fixed',
  2205            top: '0px',
  2206            left: '0px',
  2207            bottom: '0px',
  2208            right: '0px'
  2209          });
  2210          promise.then(function(data) {
  2211            compileData = data;
  2212            $modal.init();
  2213          });
  2214          $modal.init = function() {
  2215            if (options.show) {
  2216              scope.$$postDigest(function() {
  2217                $modal.show();
  2218              });
  2219            }
  2220          };
  2221          $modal.destroy = function() {
  2222            destroyModalElement();
  2223            if (backdropElement) {
  2224              backdropElement.remove();
  2225              backdropElement = null;
  2226            }
  2227            scope.$destroy();
  2228          };
  2229          $modal.show = function() {
  2230            if ($modal.$isShown) return;
  2231            var parent;
  2232            var after;
  2233            if (angular.isElement(options.container)) {
  2234              parent = options.container;
  2235              after = options.container[0].lastChild ? angular.element(options.container[0].lastChild) : null;
  2236            } else {
  2237              if (options.container) {
  2238                parent = findElement(options.container);
  2239                after = parent[0] && parent[0].lastChild ? angular.element(parent[0].lastChild) : null;
  2240              } else {
  2241                parent = null;
  2242                after = options.element;
  2243              }
  2244            }
  2245            if (modalElement) destroyModalElement();
  2246            modalScope = $modal.$scope.$new();
  2247            modalElement = $modal.$element = compileData.link(modalScope, function(clonedElement, scope) {});
  2248            if (options.backdrop) {
  2249              modalElement.css({
  2250                'z-index': dialogBaseZindex + backdropCount * 20
  2251              });
  2252              backdropElement.css({
  2253                'z-index': backdropBaseZindex + backdropCount * 20
  2254              });
  2255              backdropCount++;
  2256            }
  2257            if (scope.$emit(options.prefixEvent + '.show.before', $modal).defaultPrevented) {
  2258              return;
  2259            }
  2260            if (angular.isDefined(options.onBeforeShow) && angular.isFunction(options.onBeforeShow)) {
  2261              options.onBeforeShow($modal);
  2262            }
  2263            modalElement.css({
  2264              display: 'block'
  2265            }).addClass(options.placement);
  2266            if (options.customClass) {
  2267              modalElement.addClass(options.customClass);
  2268            }
  2269            if (options.size && validSizes[options.size]) {
  2270              angular.element(findElement('.modal-dialog', modalElement[0])).addClass(validSizes[options.size]);
  2271            }
  2272            if (options.animation) {
  2273              if (options.backdrop) {
  2274                backdropElement.addClass(options.backdropAnimation);
  2275              }
  2276              modalElement.addClass(options.animation);
  2277            }
  2278            if (options.backdrop) {
  2279              $animate.enter(backdropElement, bodyElement, null);
  2280            }
  2281            if (angular.version.minor <= 2) {
  2282              $animate.enter(modalElement, parent, after, enterAnimateCallback);
  2283            } else {
  2284              $animate.enter(modalElement, parent, after).then(enterAnimateCallback);
  2285            }
  2286            $modal.$isShown = scope.$isShown = true;
  2287            safeDigest(scope);
  2288            var el = modalElement[0];
  2289            requestAnimationFrame(function() {
  2290              el.focus();
  2291            });
  2292            bodyElement.addClass(options.prefixClass + '-open');
  2293            if (options.animation) {
  2294              bodyElement.addClass(options.prefixClass + '-with-' + options.animation);
  2295            }
  2296            bindBackdropEvents();
  2297            bindKeyboardEvents();
  2298          };
  2299          function enterAnimateCallback() {
  2300            scope.$emit(options.prefixEvent + '.show', $modal);
  2301            if (angular.isDefined(options.onShow) && angular.isFunction(options.onShow)) {
  2302              options.onShow($modal);
  2303            }
  2304          }
  2305          $modal.hide = function() {
  2306            if (!$modal.$isShown) return;
  2307            if (scope.$emit(options.prefixEvent + '.hide.before', $modal).defaultPrevented) {
  2308              return;
  2309            }
  2310            if (angular.isDefined(options.onBeforeHide) && angular.isFunction(options.onBeforeHide)) {
  2311              options.onBeforeHide($modal);
  2312            }
  2313            if (angular.version.minor <= 2) {
  2314              $animate.leave(modalElement, leaveAnimateCallback);
  2315            } else {
  2316              $animate.leave(modalElement).then(leaveAnimateCallback);
  2317            }
  2318            if (options.backdrop) {
  2319              backdropCount--;
  2320              $animate.leave(backdropElement);
  2321            }
  2322            $modal.$isShown = scope.$isShown = false;
  2323            safeDigest(scope);
  2324            unbindBackdropEvents();
  2325            unbindKeyboardEvents();
  2326          };
  2327          function leaveAnimateCallback() {
  2328            scope.$emit(options.prefixEvent + '.hide', $modal);
  2329            if (angular.isDefined(options.onHide) && angular.isFunction(options.onHide)) {
  2330              options.onHide($modal);
  2331            }
  2332            bodyElement.removeClass(options.prefixClass + '-open');
  2333            if (options.animation) {
  2334              bodyElement.removeClass(options.prefixClass + '-with-' + options.animation);
  2335            }
  2336          }
  2337          $modal.toggle = function() {
  2338            if ($modal.$isShown) {
  2339              $modal.hide();
  2340            } else {
  2341              $modal.show();
  2342            }
  2343          };
  2344          $modal.focus = function() {
  2345            modalElement[0].focus();
  2346          };
  2347          $modal.$onKeyUp = function(evt) {
  2348            if (evt.which === 27 && $modal.$isShown) {
  2349              $modal.hide();
  2350              evt.stopPropagation();
  2351            }
  2352          };
  2353          function bindBackdropEvents() {
  2354            if (options.backdrop) {
  2355              modalElement.on('click', hideOnBackdropClick);
  2356              backdropElement.on('click', hideOnBackdropClick);
  2357              backdropElement.on('wheel', preventEventDefault);
  2358            }
  2359          }
  2360          function unbindBackdropEvents() {
  2361            if (options.backdrop) {
  2362              modalElement.off('click', hideOnBackdropClick);
  2363              backdropElement.off('click', hideOnBackdropClick);
  2364              backdropElement.off('wheel', preventEventDefault);
  2365            }
  2366          }
  2367          function bindKeyboardEvents() {
  2368            if (options.keyboard) {
  2369              modalElement.on('keyup', $modal.$onKeyUp);
  2370            }
  2371          }
  2372          function unbindKeyboardEvents() {
  2373            if (options.keyboard) {
  2374              modalElement.off('keyup', $modal.$onKeyUp);
  2375            }
  2376          }
  2377          function hideOnBackdropClick(evt) {
  2378            if (evt.target !== evt.currentTarget) return;
  2379            if (options.backdrop === 'static') {
  2380              $modal.focus();
  2381            } else {
  2382              $modal.hide();
  2383            }
  2384          }
  2385          function preventEventDefault(evt) {
  2386            evt.preventDefault();
  2387          }
  2388          function destroyModalElement() {
  2389            if ($modal.$isShown && modalElement !== null) {
  2390              unbindBackdropEvents();
  2391              unbindKeyboardEvents();
  2392            }
  2393            if (modalScope) {
  2394              modalScope.$destroy();
  2395              modalScope = null;
  2396            }
  2397            if (modalElement) {
  2398              modalElement.remove();
  2399              modalElement = $modal.$element = null;
  2400            }
  2401          }
  2402          return $modal;
  2403        }
  2404        function safeDigest(scope) {
  2405          scope.$$phase || scope.$root && scope.$root.$$phase || scope.$digest();
  2406        }
  2407        function findElement(query, element) {
  2408          return angular.element((element || document).querySelectorAll(query));
  2409        }
  2410        return ModalFactory;
  2411      } ];
  2412    }).directive('bsModal', [ '$window', '$sce', '$parse', '$modal', function($window, $sce, $parse, $modal) {
  2413      return {
  2414        restrict: 'EAC',
  2415        scope: true,
  2416        link: function postLink(scope, element, attr, transclusion) {
  2417          var options = {
  2418            scope: scope,
  2419            element: element,
  2420            show: false
  2421          };
  2422          angular.forEach([ 'template', 'templateUrl', 'controller', 'controllerAs', 'contentTemplate', 'placement', 'backdrop', 'keyboard', 'html', 'container', 'animation', 'backdropAnimation', 'id', 'prefixEvent', 'prefixClass', 'customClass', 'modalClass', 'size' ], function(key) {
  2423            if (angular.isDefined(attr[key])) options[key] = attr[key];
  2424          });
  2425          if (options.modalClass) {
  2426            options.customClass = options.modalClass;
  2427          }
  2428          var falseValueRegExp = /^(false|0|)$/i;
  2429          angular.forEach([ 'backdrop', 'keyboard', 'html', 'container' ], function(key) {
  2430            if (angular.isDefined(attr[key]) && falseValueRegExp.test(attr[key])) options[key] = false;
  2431          });
  2432          angular.forEach([ 'onBeforeShow', 'onShow', 'onBeforeHide', 'onHide' ], function(key) {
  2433            var bsKey = 'bs' + key.charAt(0).toUpperCase() + key.slice(1);
  2434            if (angular.isDefined(attr[bsKey])) {
  2435              options[key] = scope.$eval(attr[bsKey]);
  2436            }
  2437          });
  2438          angular.forEach([ 'title', 'content' ], function(key) {
  2439            if (attr[key]) {
  2440              attr.$observe(key, function(newValue, oldValue) {
  2441                scope[key] = $sce.trustAsHtml(newValue);
  2442              });
  2443            }
  2444          });
  2445          if (attr.bsModal) {
  2446            scope.$watch(attr.bsModal, function(newValue, oldValue) {
  2447              if (angular.isObject(newValue)) {
  2448                angular.extend(scope, newValue);
  2449              } else {
  2450                scope.content = newValue;
  2451              }
  2452            }, true);
  2453          }
  2454          var modal = $modal(options);
  2455          element.on(attr.trigger || 'click', modal.toggle);
  2456          scope.$on('$destroy', function() {
  2457            if (modal) modal.destroy();
  2458            options = null;
  2459            modal = null;
  2460          });
  2461        }
  2462      };
  2463    } ]);
  2464    angular.module('mgcrea.ngStrap.dropdown', [ 'mgcrea.ngStrap.tooltip' ]).provider('$dropdown', function() {
  2465      var defaults = this.defaults = {
  2466        animation: 'am-fade',
  2467        prefixClass: 'dropdown',
  2468        prefixEvent: 'dropdown',
  2469        placement: 'bottom-left',
  2470        templateUrl: 'dropdown/dropdown.tpl.html',
  2471        trigger: 'click',
  2472        container: false,
  2473        keyboard: true,
  2474        html: false,
  2475        delay: 0
  2476      };
  2477      this.$get = [ '$window', '$rootScope', '$tooltip', '$timeout', function($window, $rootScope, $tooltip, $timeout) {
  2478        var bodyEl = angular.element($window.document.body);
  2479        var matchesSelector = Element.prototype.matchesSelector || Element.prototype.webkitMatchesSelector || Element.prototype.mozMatchesSelector || Element.prototype.msMatchesSelector || Element.prototype.oMatchesSelector;
  2480        function DropdownFactory(element, config) {
  2481          var $dropdown = {};
  2482          var options = angular.extend({}, defaults, config);
  2483          $dropdown.$scope = options.scope && options.scope.$new() || $rootScope.$new();
  2484          $dropdown = $tooltip(element, options);
  2485          var parentEl = element.parent();
  2486          $dropdown.$onKeyDown = function(evt) {
  2487            if (!/(38|40)/.test(evt.keyCode)) return;
  2488            evt.preventDefault();
  2489            evt.stopPropagation();
  2490            var items = angular.element($dropdown.$element[0].querySelectorAll('li:not(.divider) a'));
  2491            if (!items.length) return;
  2492            var index;
  2493            angular.forEach(items, function(el, i) {
  2494              if (matchesSelector && matchesSelector.call(el, ':focus')) index = i;
  2495            });
  2496            if (evt.keyCode === 38 && index > 0) index--; else if (evt.keyCode === 40 && index < items.length - 1) index++; else if (angular.isUndefined(index)) index = 0;
  2497            items.eq(index)[0].focus();
  2498          };
  2499          var show = $dropdown.show;
  2500          $dropdown.show = function() {
  2501            show();
  2502            $timeout(function() {
  2503              if (options.keyboard && $dropdown.$element) $dropdown.$element.on('keydown', $dropdown.$onKeyDown);
  2504              bodyEl.on('click', onBodyClick);
  2505            }, 0, false);
  2506            if (parentEl.hasClass('dropdown')) parentEl.addClass('open');
  2507          };
  2508          var hide = $dropdown.hide;
  2509          $dropdown.hide = function() {
  2510            if (!$dropdown.$isShown) return;
  2511            if (options.keyboard && $dropdown.$element) $dropdown.$element.off('keydown', $dropdown.$onKeyDown);
  2512            bodyEl.off('click', onBodyClick);
  2513            if (parentEl.hasClass('dropdown')) parentEl.removeClass('open');
  2514            hide();
  2515          };
  2516          var destroy = $dropdown.destroy;
  2517          $dropdown.destroy = function() {
  2518            bodyEl.off('click', onBodyClick);
  2519            destroy();
  2520          };
  2521          function onBodyClick(evt) {
  2522            if (evt.target === element[0]) return;
  2523            return evt.target !== element[0] && $dropdown.hide();
  2524          }
  2525          return $dropdown;
  2526        }
  2527        return DropdownFactory;
  2528      } ];
  2529    }).directive('bsDropdown', [ '$window', '$sce', '$dropdown', function($window, $sce, $dropdown) {
  2530      return {
  2531        restrict: 'EAC',
  2532        scope: true,
  2533        compile: function(tElement, tAttrs) {
  2534          if (!tAttrs.bsDropdown) {
  2535            var nextSibling = tElement[0].nextSibling;
  2536            while (nextSibling && nextSibling.nodeType !== 1) {
  2537              nextSibling = nextSibling.nextSibling;
  2538            }
  2539            if (nextSibling && nextSibling.className.split(' ').indexOf('dropdown-menu') >= 0) {
  2540              tAttrs.template = nextSibling.outerHTML;
  2541              tAttrs.templateUrl = undefined;
  2542              nextSibling.parentNode.removeChild(nextSibling);
  2543            }
  2544          }
  2545          return function postLink(scope, element, attr) {
  2546            var options = {
  2547              scope: scope
  2548            };
  2549            angular.forEach([ 'template', 'templateUrl', 'controller', 'controllerAs', 'placement', 'container', 'delay', 'trigger', 'keyboard', 'html', 'animation', 'id', 'autoClose' ], function(key) {
  2550              if (angular.isDefined(tAttrs[key])) options[key] = tAttrs[key];
  2551            });
  2552            var falseValueRegExp = /^(false|0|)$/i;
  2553            angular.forEach([ 'html', 'container' ], function(key) {
  2554              if (angular.isDefined(attr[key]) && falseValueRegExp.test(attr[key])) options[key] = false;
  2555            });
  2556            angular.forEach([ 'onBeforeShow', 'onShow', 'onBeforeHide', 'onHide' ], function(key) {
  2557              var bsKey = 'bs' + key.charAt(0).toUpperCase() + key.slice(1);
  2558              if (angular.isDefined(attr[bsKey])) {
  2559                options[key] = scope.$eval(attr[bsKey]);
  2560              }
  2561            });
  2562            if (attr.bsDropdown) {
  2563              scope.$watch(attr.bsDropdown, function(newValue, oldValue) {
  2564                scope.content = newValue;
  2565              }, true);
  2566            }
  2567            var dropdown = $dropdown(element, options);
  2568            if (attr.bsShow) {
  2569              scope.$watch(attr.bsShow, function(newValue, oldValue) {
  2570                if (!dropdown || !angular.isDefined(newValue)) return;
  2571                if (angular.isString(newValue)) newValue = !!newValue.match(/true|,?(dropdown),?/i);
  2572                if (newValue === true) {
  2573                  dropdown.show();
  2574                } else {
  2575                  dropdown.hide();
  2576                }
  2577              });
  2578            }
  2579            scope.$on('$destroy', function() {
  2580              if (dropdown) dropdown.destroy();
  2581              options = null;
  2582              dropdown = null;
  2583            });
  2584          };
  2585        }
  2586      };
  2587    } ]);
  2588    if (angular.version.minor < 3 && angular.version.dot < 14) {
  2589      angular.module('ng').factory('$$rAF', [ '$window', '$timeout', function($window, $timeout) {
  2590        var requestAnimationFrame = $window.requestAnimationFrame || $window.webkitRequestAnimationFrame || $window.mozRequestAnimationFrame;
  2591        var cancelAnimationFrame = $window.cancelAnimationFrame || $window.webkitCancelAnimationFrame || $window.mozCancelAnimationFrame || $window.webkitCancelRequestAnimationFrame;
  2592        var rafSupported = !!requestAnimationFrame;
  2593        var raf = rafSupported ? function(fn) {
  2594          var id = requestAnimationFrame(fn);
  2595          return function() {
  2596            cancelAnimationFrame(id);
  2597          };
  2598        } : function(fn) {
  2599          var timer = $timeout(fn, 16.66, false);
  2600          return function() {
  2601            $timeout.cancel(timer);
  2602          };
  2603        };
  2604        raf.supported = rafSupported;
  2605        return raf;
  2606      } ]);
  2607    }
  2608    angular.module('mgcrea.ngStrap.helpers.parseOptions', []).provider('$parseOptions', function() {
  2609      var defaults = this.defaults = {
  2610        regexp: /^\s*(.*?)(?:\s+as\s+(.*?))?(?:\s+group\s+by\s+(.*))?\s+for\s+(?:([\$\w][\$\w]*)|(?:\(\s*([\$\w][\$\w]*)\s*,\s*([\$\w][\$\w]*)\s*\)))\s+in\s+(.*?)(?:\s+track\s+by\s+(.*?))?$/
  2611      };
  2612      this.$get = [ '$parse', '$q', function($parse, $q) {
  2613        function ParseOptionsFactory(attr, config) {
  2614          var $parseOptions = {};
  2615          var options = angular.extend({}, defaults, config);
  2616          $parseOptions.$values = [];
  2617          var match;
  2618          var displayFn;
  2619          var valueName;
  2620          var keyName;
  2621          var groupByFn;
  2622          var valueFn;
  2623          var valuesFn;
  2624          $parseOptions.init = function() {
  2625            $parseOptions.$match = match = attr.match(options.regexp);
  2626            displayFn = $parse(match[2] || match[1]);
  2627            valueName = match[4] || match[6];
  2628            keyName = match[5];
  2629            groupByFn = $parse(match[3] || '');
  2630            valueFn = $parse(match[2] ? match[1] : valueName);
  2631            valuesFn = $parse(match[7]);
  2632          };
  2633          $parseOptions.valuesFn = function(scope, controller) {
  2634            return $q.when(valuesFn(scope, controller)).then(function(values) {
  2635              if (!angular.isArray(values)) {
  2636                values = [];
  2637              }
  2638              $parseOptions.$values = values.length ? parseValues(values, scope) : [];
  2639              return $parseOptions.$values;
  2640            });
  2641          };
  2642          $parseOptions.displayValue = function(modelValue) {
  2643            var scope = {};
  2644            scope[valueName] = modelValue;
  2645            return displayFn(scope);
  2646          };
  2647          function parseValues(values, scope) {
  2648            return values.map(function(match, index) {
  2649              var locals = {};
  2650              var label;
  2651              var value;
  2652              locals[valueName] = match;
  2653              label = displayFn(scope, locals);
  2654              value = valueFn(scope, locals);
  2655              return {
  2656                label: label,
  2657                value: value,
  2658                index: index
  2659              };
  2660            });
  2661          }
  2662          $parseOptions.init();
  2663          return $parseOptions;
  2664        }
  2665        return ParseOptionsFactory;
  2666      } ];
  2667    });
  2668    angular.module('mgcrea.ngStrap.helpers.dimensions', []).factory('dimensions', function() {
  2669      var fn = {};
  2670      var nodeName = fn.nodeName = function(element, name) {
  2671        return element.nodeName && element.nodeName.toLowerCase() === name.toLowerCase();
  2672      };
  2673      fn.css = function(element, prop, extra) {
  2674        var value;
  2675        if (element.currentStyle) {
  2676          value = element.currentStyle[prop];
  2677        } else if (window.getComputedStyle) {
  2678          value = window.getComputedStyle(element)[prop];
  2679        } else {
  2680          value = element.style[prop];
  2681        }
  2682        return extra === true ? parseFloat(value) || 0 : value;
  2683      };
  2684      fn.offset = function(element) {
  2685        var boxRect = element.getBoundingClientRect();
  2686        var docElement = element.ownerDocument;
  2687        return {
  2688          width: boxRect.width || element.offsetWidth,
  2689          height: boxRect.height || element.offsetHeight,
  2690          top: boxRect.top + (window.pageYOffset || docElement.documentElement.scrollTop) - (docElement.documentElement.clientTop || 0),
  2691          left: boxRect.left + (window.pageXOffset || docElement.documentElement.scrollLeft) - (docElement.documentElement.clientLeft || 0)
  2692        };
  2693      };
  2694      fn.setOffset = function(element, options, i) {
  2695        var curPosition;
  2696        var curLeft;
  2697        var curCSSTop;
  2698        var curTop;
  2699        var curOffset;
  2700        var curCSSLeft;
  2701        var calculatePosition;
  2702        var position = fn.css(element, 'position');
  2703        var curElem = angular.element(element);
  2704        var props = {};
  2705        if (position === 'static') {
  2706          element.style.position = 'relative';
  2707        }
  2708        curOffset = fn.offset(element);
  2709        curCSSTop = fn.css(element, 'top');
  2710        curCSSLeft = fn.css(element, 'left');
  2711        calculatePosition = (position === 'absolute' || position === 'fixed') && (curCSSTop + curCSSLeft).indexOf('auto') > -1;
  2712        if (calculatePosition) {
  2713          curPosition = fn.position(element);
  2714          curTop = curPosition.top;
  2715          curLeft = curPosition.left;
  2716        } else {
  2717          curTop = parseFloat(curCSSTop) || 0;
  2718          curLeft = parseFloat(curCSSLeft) || 0;
  2719        }
  2720        if (angular.isFunction(options)) {
  2721          options = options.call(element, i, curOffset);
  2722        }
  2723        if (options.top !== null) {
  2724          props.top = options.top - curOffset.top + curTop;
  2725        }
  2726        if (options.left !== null) {
  2727          props.left = options.left - curOffset.left + curLeft;
  2728        }
  2729        if ('using' in options) {
  2730          options.using.call(curElem, props);
  2731        } else {
  2732          curElem.css({
  2733            top: props.top + 'px',
  2734            left: props.left + 'px'
  2735          });
  2736        }
  2737      };
  2738      fn.position = function(element) {
  2739        var offsetParentRect = {
  2740          top: 0,
  2741          left: 0
  2742        };
  2743        var offsetParentEl;
  2744        var offset;
  2745        if (fn.css(element, 'position') === 'fixed') {
  2746          offset = element.getBoundingClientRect();
  2747        } else {
  2748          offsetParentEl = offsetParentElement(element);
  2749          offset = fn.offset(element);
  2750          if (!nodeName(offsetParentEl, 'html')) {
  2751            offsetParentRect = fn.offset(offsetParentEl);
  2752          }
  2753          offsetParentRect.top += fn.css(offsetParentEl, 'borderTopWidth', true);
  2754          offsetParentRect.left += fn.css(offsetParentEl, 'borderLeftWidth', true);
  2755        }
  2756        return {
  2757          width: element.offsetWidth,
  2758          height: element.offsetHeight,
  2759          top: offset.top - offsetParentRect.top - fn.css(element, 'marginTop', true),
  2760          left: offset.left - offsetParentRect.left - fn.css(element, 'marginLeft', true)
  2761        };
  2762      };
  2763      function offsetParentElement(element) {
  2764        var docElement = element.ownerDocument;
  2765        var offsetParent = element.offsetParent || docElement;
  2766        if (nodeName(offsetParent, '#document')) return docElement.documentElement;
  2767        while (offsetParent && !nodeName(offsetParent, 'html') && fn.css(offsetParent, 'position') === 'static') {
  2768          offsetParent = offsetParent.offsetParent;
  2769        }
  2770        return offsetParent || docElement.documentElement;
  2771      }
  2772      fn.height = function(element, outer) {
  2773        var value = element.offsetHeight;
  2774        if (outer) {
  2775          value += fn.css(element, 'marginTop', true) + fn.css(element, 'marginBottom', true);
  2776        } else {
  2777          value -= fn.css(element, 'paddingTop', true) + fn.css(element, 'paddingBottom', true) + fn.css(element, 'borderTopWidth', true) + fn.css(element, 'borderBottomWidth', true);
  2778        }
  2779        return value;
  2780      };
  2781      fn.width = function(element, outer) {
  2782        var value = element.offsetWidth;
  2783        if (outer) {
  2784          value += fn.css(element, 'marginLeft', true) + fn.css(element, 'marginRight', true);
  2785        } else {
  2786          value -= fn.css(element, 'paddingLeft', true) + fn.css(element, 'paddingRight', true) + fn.css(element, 'borderLeftWidth', true) + fn.css(element, 'borderRightWidth', true);
  2787        }
  2788        return value;
  2789      };
  2790      return fn;
  2791    });
  2792    angular.module('mgcrea.ngStrap.helpers.debounce', []).factory('debounce', [ '$timeout', function($timeout) {
  2793      return function(func, wait, immediate) {
  2794        var timeout = null;
  2795        return function() {
  2796          var context = this;
  2797          var args = arguments;
  2798          var callNow = immediate && !timeout;
  2799          if (timeout) {
  2800            $timeout.cancel(timeout);
  2801          }
  2802          timeout = $timeout(function later() {
  2803            timeout = null;
  2804            if (!immediate) {
  2805              func.apply(context, args);
  2806            }
  2807          }, wait, false);
  2808          if (callNow) {
  2809            func.apply(context, args);
  2810          }
  2811          return timeout;
  2812        };
  2813      };
  2814    } ]).factory('throttle', [ '$timeout', function($timeout) {
  2815      return function(func, wait, options) {
  2816        var timeout = null;
  2817        if (!options) options = {};
  2818        return function() {
  2819          var context = this;
  2820          var args = arguments;
  2821          if (!timeout) {
  2822            if (options.leading !== false) {
  2823              func.apply(context, args);
  2824            }
  2825            timeout = $timeout(function later() {
  2826              timeout = null;
  2827              if (options.trailing !== false) {
  2828                func.apply(context, args);
  2829              }
  2830            }, wait, false);
  2831          }
  2832        };
  2833      };
  2834    } ]);
  2835    angular.module('mgcrea.ngStrap.helpers.dateParser', []).provider('$dateParser', [ '$localeProvider', function($localeProvider) {
  2836      function ParseDate() {
  2837        this.year = 1970;
  2838        this.month = 0;
  2839        this.day = 1;
  2840        this.hours = 0;
  2841        this.minutes = 0;
  2842        this.seconds = 0;
  2843        this.milliseconds = 0;
  2844      }
  2845      ParseDate.prototype.setMilliseconds = function(value) {
  2846        this.milliseconds = value;
  2847      };
  2848      ParseDate.prototype.setSeconds = function(value) {
  2849        this.seconds = value;
  2850      };
  2851      ParseDate.prototype.setMinutes = function(value) {
  2852        this.minutes = value;
  2853      };
  2854      ParseDate.prototype.setHours = function(value) {
  2855        this.hours = value;
  2856      };
  2857      ParseDate.prototype.getHours = function() {
  2858        return this.hours;
  2859      };
  2860      ParseDate.prototype.setDate = function(value) {
  2861        this.day = value;
  2862      };
  2863      ParseDate.prototype.setMonth = function(value) {
  2864        this.month = value;
  2865      };
  2866      ParseDate.prototype.setFullYear = function(value) {
  2867        this.year = value;
  2868      };
  2869      ParseDate.prototype.fromDate = function(value) {
  2870        this.year = value.getFullYear();
  2871        this.month = value.getMonth();
  2872        this.day = value.getDate();
  2873        this.hours = value.getHours();
  2874        this.minutes = value.getMinutes();
  2875        this.seconds = value.getSeconds();
  2876        this.milliseconds = value.getMilliseconds();
  2877        return this;
  2878      };
  2879      ParseDate.prototype.toDate = function() {
  2880        return new Date(this.year, this.month, this.day, this.hours, this.minutes, this.seconds, this.milliseconds);
  2881      };
  2882      var proto = ParseDate.prototype;
  2883      function noop() {}
  2884      function isNumeric(n) {
  2885        return !isNaN(parseFloat(n)) && isFinite(n);
  2886      }
  2887      function indexOfCaseInsensitive(array, value) {
  2888        var len = array.length;
  2889        var str = value.toString().toLowerCase();
  2890        for (var i = 0; i < len; i++) {
  2891          if (array[i].toLowerCase() === str) {
  2892            return i;
  2893          }
  2894        }
  2895        return -1;
  2896      }
  2897      var defaults = this.defaults = {
  2898        format: 'shortDate',
  2899        strict: false
  2900      };
  2901      this.$get = [ '$locale', 'dateFilter', function($locale, dateFilter) {
  2902        var DateParserFactory = function(config) {
  2903          var options = angular.extend({}, defaults, config);
  2904          var $dateParser = {};
  2905          var regExpMap = {
  2906            sss: '[0-9]{3}',
  2907            ss: '[0-5][0-9]',
  2908            s: options.strict ? '[1-5]?[0-9]' : '[0-9]|[0-5][0-9]',
  2909            mm: '[0-5][0-9]',
  2910            m: options.strict ? '[1-5]?[0-9]' : '[0-9]|[0-5][0-9]',
  2911            HH: '[01][0-9]|2[0-3]',
  2912            H: options.strict ? '1?[0-9]|2[0-3]' : '[01]?[0-9]|2[0-3]',
  2913            hh: '[0][1-9]|[1][012]',
  2914            h: options.strict ? '[1-9]|1[012]' : '0?[1-9]|1[012]',
  2915            a: 'AM|PM',
  2916            EEEE: $locale.DATETIME_FORMATS.DAY.join('|'),
  2917            EEE: $locale.DATETIME_FORMATS.SHORTDAY.join('|'),
  2918            dd: '0[1-9]|[12][0-9]|3[01]',
  2919            d: options.strict ? '[1-9]|[1-2][0-9]|3[01]' : '0?[1-9]|[1-2][0-9]|3[01]',
  2920            MMMM: $locale.DATETIME_FORMATS.MONTH.join('|'),
  2921            MMM: $locale.DATETIME_FORMATS.SHORTMONTH.join('|'),
  2922            MM: '0[1-9]|1[012]',
  2923            M: options.strict ? '[1-9]|1[012]' : '0?[1-9]|1[012]',
  2924            yyyy: '[1]{1}[0-9]{3}|[2]{1}[0-9]{3}',
  2925            yy: '[0-9]{2}',
  2926            y: options.strict ? '-?(0|[1-9][0-9]{0,3})' : '-?0*[0-9]{1,4}'
  2927          };
  2928          var setFnMap = {
  2929            sss: proto.setMilliseconds,
  2930            ss: proto.setSeconds,
  2931            s: proto.setSeconds,
  2932            mm: proto.setMinutes,
  2933            m: proto.setMinutes,
  2934            HH: proto.setHours,
  2935            H: proto.setHours,
  2936            hh: proto.setHours,
  2937            h: proto.setHours,
  2938            EEEE: noop,
  2939            EEE: noop,
  2940            dd: proto.setDate,
  2941            d: proto.setDate,
  2942            a: function(value) {
  2943              var hours = this.getHours() % 12;
  2944              return this.setHours(value.match(/pm/i) ? hours + 12 : hours);
  2945            },
  2946            MMMM: function(value) {
  2947              return this.setMonth(indexOfCaseInsensitive($locale.DATETIME_FORMATS.MONTH, value));
  2948            },
  2949            MMM: function(value) {
  2950              return this.setMonth(indexOfCaseInsensitive($locale.DATETIME_FORMATS.SHORTMONTH, value));
  2951            },
  2952            MM: function(value) {
  2953              return this.setMonth(1 * value - 1);
  2954            },
  2955            M: function(value) {
  2956              return this.setMonth(1 * value - 1);
  2957            },
  2958            yyyy: proto.setFullYear,
  2959            yy: function(value) {
  2960              return this.setFullYear(2e3 + 1 * value);
  2961            },
  2962            y: function(value) {
  2963              return 1 * value <= 50 && value.length === 2 ? this.setFullYear(2e3 + 1 * value) : this.setFullYear(1 * value);
  2964            }
  2965          };
  2966          var regex;
  2967          var setMap;
  2968          $dateParser.init = function() {
  2969            $dateParser.$format = $locale.DATETIME_FORMATS[options.format] || options.format;
  2970            regex = regExpForFormat($dateParser.$format);
  2971            setMap = setMapForFormat($dateParser.$format);
  2972          };
  2973          $dateParser.isValid = function(date) {
  2974            if (angular.isDate(date)) return !isNaN(date.getTime());
  2975            return regex.test(date);
  2976          };
  2977          $dateParser.parse = function(value, baseDate, format, timezone) {
  2978            if (format) format = $locale.DATETIME_FORMATS[format] || format;
  2979            if (angular.isDate(value)) value = dateFilter(value, format || $dateParser.$format, timezone);
  2980            var formatRegex = format ? regExpForFormat(format) : regex;
  2981            var formatSetMap = format ? setMapForFormat(format) : setMap;
  2982            var matches = formatRegex.exec(value);
  2983            if (!matches) return false;
  2984            var date = baseDate && !isNaN(baseDate.getTime()) ? new ParseDate().fromDate(baseDate) : new ParseDate().fromDate(new Date(1970, 0, 1, 0));
  2985            for (var i = 0; i < matches.length - 1; i++) {
  2986              if (formatSetMap[i]) formatSetMap[i].call(date, matches[i + 1]);
  2987            }
  2988            var newDate = date.toDate();
  2989            if (parseInt(date.day, 10) !== newDate.getDate()) {
  2990              return false;
  2991            }
  2992            return newDate;
  2993          };
  2994          $dateParser.getDateForAttribute = function(key, value) {
  2995            var date;
  2996            if (value === 'today') {
  2997              var today = new Date();
  2998              date = new Date(today.getFullYear(), today.getMonth(), today.getDate() + (key === 'maxDate' ? 1 : 0), 0, 0, 0, key === 'minDate' ? 0 : -1);
  2999            } else if (angular.isString(value) && value.match(/^".+"$/)) {
  3000              date = new Date(value.substr(1, value.length - 2));
  3001            } else if (isNumeric(value)) {
  3002              date = new Date(parseInt(value, 10));
  3003            } else if (angular.isString(value) && value.length === 0) {
  3004              date = key === 'minDate' ? -Infinity : +Infinity;
  3005            } else {
  3006              date = new Date(value);
  3007            }
  3008            return date;
  3009          };
  3010          $dateParser.getTimeForAttribute = function(key, value) {
  3011            var time;
  3012            if (value === 'now') {
  3013              time = new Date().setFullYear(1970, 0, 1);
  3014            } else if (angular.isString(value) && value.match(/^".+"$/)) {
  3015              time = new Date(value.substr(1, value.length - 2)).setFullYear(1970, 0, 1);
  3016            } else if (isNumeric(value)) {
  3017              time = new Date(parseInt(value, 10)).setFullYear(1970, 0, 1);
  3018            } else if (angular.isString(value) && value.length === 0) {
  3019              time = key === 'minTime' ? -Infinity : +Infinity;
  3020            } else {
  3021              time = $dateParser.parse(value, new Date(1970, 0, 1, 0));
  3022            }
  3023            return time;
  3024          };
  3025          $dateParser.daylightSavingAdjust = function(date) {
  3026            if (!date) {
  3027              return null;
  3028            }
  3029            date.setHours(date.getHours() > 12 ? date.getHours() + 2 : 0);
  3030            return date;
  3031          };
  3032          $dateParser.timezoneOffsetAdjust = function(date, timezone, undo) {
  3033            if (!date) {
  3034              return null;
  3035            }
  3036            if (timezone && timezone === 'UTC') {
  3037              date = new Date(date.getTime());
  3038              date.setMinutes(date.getMinutes() + (undo ? -1 : 1) * date.getTimezoneOffset());
  3039            }
  3040            return date;
  3041          };
  3042          function regExpForFormat(format) {
  3043            var re = buildDateAbstractRegex(format);
  3044            return buildDateParseRegex(re);
  3045          }
  3046          function buildDateAbstractRegex(format) {
  3047            var escapedFormat = escapeReservedSymbols(format);
  3048            var escapedLiteralFormat = escapedFormat.replace(/''/g, '\\\'');
  3049            var literalRegex = /('(?:\\'|.)*?')/;
  3050            var formatParts = escapedLiteralFormat.split(literalRegex);
  3051            var dateElements = Object.keys(regExpMap);
  3052            var dateRegexParts = [];
  3053            angular.forEach(formatParts, function(part) {
  3054              if (isFormatStringLiteral(part)) {
  3055                part = trimLiteralEscapeChars(part);
  3056              } else {
  3057                for (var i = 0; i < dateElements.length; i++) {
  3058                  part = part.split(dateElements[i]).join('${' + i + '}');
  3059                }
  3060              }
  3061              dateRegexParts.push(part);
  3062            });
  3063            return dateRegexParts.join('');
  3064          }
  3065          function escapeReservedSymbols(text) {
  3066            return text.replace(/\\/g, '[\\\\]').replace(/-/g, '[-]').replace(/\./g, '[.]').replace(/\*/g, '[*]').replace(/\+/g, '[+]').replace(/\?/g, '[?]').replace(/\$/g, '[$]').replace(/\^/g, '[^]').replace(/\//g, '[/]').replace(/\\s/g, '[\\s]');
  3067          }
  3068          function isFormatStringLiteral(text) {
  3069            return /^'.*'$/.test(text);
  3070          }
  3071          function trimLiteralEscapeChars(text) {
  3072            return text.replace(/^'(.*)'$/, '$1');
  3073          }
  3074          function buildDateParseRegex(abstractRegex) {
  3075            var dateElements = Object.keys(regExpMap);
  3076            var re = abstractRegex;
  3077            for (var i = 0; i < dateElements.length; i++) {
  3078              re = re.split('${' + i + '}').join('(' + regExpMap[dateElements[i]] + ')');
  3079            }
  3080            return new RegExp('^' + re + '$', [ 'i' ]);
  3081          }
  3082          function setMapForFormat(format) {
  3083            var re = buildDateAbstractRegex(format);
  3084            return buildDateParseValuesMap(re);
  3085          }
  3086          function buildDateParseValuesMap(abstractRegex) {
  3087            var dateElements = Object.keys(regExpMap);
  3088            var valuesRegex = new RegExp('\\${(\\d+)}', 'g');
  3089            var valuesMatch;
  3090            var keyIndex;
  3091            var valueKey;
  3092            var valueFunction;
  3093            var valuesFunctionMap = [];
  3094            while ((valuesMatch = valuesRegex.exec(abstractRegex)) !== null) {
  3095              keyIndex = valuesMatch[1];
  3096              valueKey = dateElements[keyIndex];
  3097              valueFunction = setFnMap[valueKey];
  3098              valuesFunctionMap.push(valueFunction);
  3099            }
  3100            return valuesFunctionMap;
  3101          }
  3102          $dateParser.init();
  3103          return $dateParser;
  3104        };
  3105        return DateParserFactory;
  3106      } ];
  3107    } ]);
  3108    angular.module('mgcrea.ngStrap.helpers.dateFormatter', []).service('$dateFormatter', [ '$locale', 'dateFilter', function($locale, dateFilter) {
  3109      this.getDefaultLocale = function() {
  3110        return $locale.id;
  3111      };
  3112      this.getDatetimeFormat = function(format, lang) {
  3113        return $locale.DATETIME_FORMATS[format] || format;
  3114      };
  3115      this.weekdaysShort = function(lang) {
  3116        return $locale.DATETIME_FORMATS.SHORTDAY;
  3117      };
  3118      function splitTimeFormat(format) {
  3119        return /(h+)([:\.])?(m+)([:\.])?(s*)[ ]?(a?)/i.exec(format).slice(1);
  3120      }
  3121      this.hoursFormat = function(timeFormat) {
  3122        return splitTimeFormat(timeFormat)[0];
  3123      };
  3124      this.minutesFormat = function(timeFormat) {
  3125        return splitTimeFormat(timeFormat)[2];
  3126      };
  3127      this.secondsFormat = function(timeFormat) {
  3128        return splitTimeFormat(timeFormat)[4];
  3129      };
  3130      this.timeSeparator = function(timeFormat) {
  3131        return splitTimeFormat(timeFormat)[1];
  3132      };
  3133      this.showSeconds = function(timeFormat) {
  3134        return !!splitTimeFormat(timeFormat)[4];
  3135      };
  3136      this.showAM = function(timeFormat) {
  3137        return !!splitTimeFormat(timeFormat)[5];
  3138      };
  3139      this.formatDate = function(date, format, lang, timezone) {
  3140        return dateFilter(date, format, timezone);
  3141      };
  3142    } ]);
  3143    angular.module('mgcrea.ngStrap.core', []).service('$bsCompiler', bsCompilerService);
  3144    function bsCompilerService($q, $http, $injector, $compile, $controller, $templateCache) {
  3145      this.compile = function(options) {
  3146        if (options.template && /\.html$/.test(options.template)) {
  3147          console.warn('Deprecated use of `template` option to pass a file. Please use the `templateUrl` option instead.');
  3148          options.templateUrl = options.template;
  3149          options.template = '';
  3150        }
  3151        var templateUrl = options.templateUrl;
  3152        var template = options.template || '';
  3153        var controller = options.controller;
  3154        var controllerAs = options.controllerAs;
  3155        var resolve = angular.copy(options.resolve || {});
  3156        var locals = angular.copy(options.locals || {});
  3157        var transformTemplate = options.transformTemplate || angular.identity;
  3158        var bindToController = options.bindToController;
  3159        angular.forEach(resolve, function(value, key) {
  3160          if (angular.isString(value)) {
  3161            resolve[key] = $injector.get(value);
  3162          } else {
  3163            resolve[key] = $injector.invoke(value);
  3164          }
  3165        });
  3166        angular.extend(resolve, locals);
  3167        if (template) {
  3168          resolve.$template = $q.when(template);
  3169        } else if (templateUrl) {
  3170          resolve.$template = fetchTemplate(templateUrl);
  3171        } else {
  3172          throw new Error('Missing `template` / `templateUrl` option.');
  3173        }
  3174        if (options.titleTemplate) {
  3175          resolve.$template = $q.all([ resolve.$template, fetchTemplate(options.titleTemplate) ]).then(function(templates) {
  3176            var templateEl = angular.element(templates[0]);
  3177            findElement('[ng-bind="title"]', templateEl[0]).removeAttr('ng-bind').html(templates[1]);
  3178            return templateEl[0].outerHTML;
  3179          });
  3180        }
  3181        if (options.contentTemplate) {
  3182          resolve.$template = $q.all([ resolve.$template, fetchTemplate(options.contentTemplate) ]).then(function(templates) {
  3183            var templateEl = angular.element(templates[0]);
  3184            var contentEl = findElement('[ng-bind="content"]', templateEl[0]).removeAttr('ng-bind').html(templates[1]);
  3185            if (!options.templateUrl) contentEl.next().remove();
  3186            return templateEl[0].outerHTML;
  3187          });
  3188        }
  3189        return $q.all(resolve).then(function(locals) {
  3190          var template = transformTemplate(locals.$template);
  3191          if (options.html) {
  3192            template = template.replace(/ng-bind="/gi, 'ng-bind-html="');
  3193          }
  3194          var element = angular.element('<div>').html(template.trim()).contents();
  3195          var linkFn = $compile(element);
  3196          return {
  3197            locals: locals,
  3198            element: element,
  3199            link: function link(scope) {
  3200              locals.$scope = scope;
  3201              if (controller) {
  3202                var invokeCtrl = $controller(controller, locals, true);
  3203                if (bindToController) {
  3204                  angular.extend(invokeCtrl.instance, locals);
  3205                }
  3206                var ctrl = angular.isObject(invokeCtrl) ? invokeCtrl : invokeCtrl();
  3207                element.data('$ngControllerController', ctrl);
  3208                element.children().data('$ngControllerController', ctrl);
  3209                if (controllerAs) {
  3210                  scope[controllerAs] = ctrl;
  3211                }
  3212              }
  3213              return linkFn.apply(null, arguments);
  3214            }
  3215          };
  3216        });
  3217      };
  3218      function findElement(query, element) {
  3219        return angular.element((element || document).querySelectorAll(query));
  3220      }
  3221      var fetchPromises = {};
  3222      function fetchTemplate(template) {
  3223        if (fetchPromises[template]) return fetchPromises[template];
  3224        return fetchPromises[template] = $http.get(template, {
  3225          cache: $templateCache
  3226        }).then(function(res) {
  3227          return res.data;
  3228        });
  3229      }
  3230    }
  3231    angular.module('mgcrea.ngStrap.datepicker', [ 'mgcrea.ngStrap.helpers.dateParser', 'mgcrea.ngStrap.helpers.dateFormatter', 'mgcrea.ngStrap.tooltip' ]).provider('$datepicker', function() {
  3232      var defaults = this.defaults = {
  3233        animation: 'am-fade',
  3234        prefixClass: 'datepicker',
  3235        placement: 'bottom-left',
  3236        templateUrl: 'datepicker/datepicker.tpl.html',
  3237        trigger: 'focus',
  3238        container: false,
  3239        keyboard: true,
  3240        html: false,
  3241        delay: 0,
  3242        useNative: false,
  3243        dateType: 'date',
  3244        dateFormat: 'shortDate',
  3245        timezone: null,
  3246        modelDateFormat: null,
  3247        dayFormat: 'dd',
  3248        monthFormat: 'MMM',
  3249        yearFormat: 'yyyy',
  3250        monthTitleFormat: 'MMMM yyyy',
  3251        yearTitleFormat: 'yyyy',
  3252        strictFormat: false,
  3253        autoclose: false,
  3254        minDate: -Infinity,
  3255        maxDate: +Infinity,
  3256        startView: 0,
  3257        minView: 0,
  3258        startWeek: 0,
  3259        daysOfWeekDisabled: '',
  3260        hasToday: false,
  3261        hasClear: false,
  3262        iconLeft: 'glyphicon glyphicon-chevron-left',
  3263        iconRight: 'glyphicon glyphicon-chevron-right'
  3264      };
  3265      this.$get = [ '$window', '$document', '$rootScope', '$sce', '$dateFormatter', 'datepickerViews', '$tooltip', '$timeout', function($window, $document, $rootScope, $sce, $dateFormatter, datepickerViews, $tooltip, $timeout) {
  3266        var isNative = /(ip[ao]d|iphone|android)/gi.test($window.navigator.userAgent);
  3267        var isTouch = 'createTouch' in $window.document && isNative;
  3268        if (!defaults.lang) defaults.lang = $dateFormatter.getDefaultLocale();
  3269        function DatepickerFactory(element, controller, config) {
  3270          var $datepicker = $tooltip(element, angular.extend({}, defaults, config));
  3271          var parentScope = config.scope;
  3272          var options = $datepicker.$options;
  3273          var scope = $datepicker.$scope;
  3274          if (options.startView) options.startView -= options.minView;
  3275          var pickerViews = datepickerViews($datepicker);
  3276          $datepicker.$views = pickerViews.views;
  3277          var viewDate = pickerViews.viewDate;
  3278          scope.$mode = options.startView;
  3279          scope.$iconLeft = options.iconLeft;
  3280          scope.$iconRight = options.iconRight;
  3281          scope.$hasToday = options.hasToday;
  3282          scope.$hasClear = options.hasClear;
  3283          var $picker = $datepicker.$views[scope.$mode];
  3284          scope.$select = function(date) {
  3285            $datepicker.select(date);
  3286          };
  3287          scope.$selectPane = function(value) {
  3288            $datepicker.$selectPane(value);
  3289          };
  3290          scope.$toggleMode = function() {
  3291            $datepicker.setMode((scope.$mode + 1) % $datepicker.$views.length);
  3292          };
  3293          scope.$setToday = function() {
  3294            if (options.autoclose) {
  3295              $datepicker.setMode(0);
  3296              $datepicker.select(new Date());
  3297            } else {
  3298              $datepicker.select(new Date(), true);
  3299            }
  3300          };
  3301          scope.$clear = function() {
  3302            if (options.autoclose) {
  3303              $datepicker.setMode(0);
  3304              $datepicker.select(null);
  3305            } else {
  3306              $datepicker.select(null, true);
  3307            }
  3308          };
  3309          $datepicker.update = function(date) {
  3310            if (angular.isDate(date) && !isNaN(date.getTime())) {
  3311              $datepicker.$date = date;
  3312              $picker.update.call($picker, date);
  3313            }
  3314            $datepicker.$build(true);
  3315          };
  3316          $datepicker.updateDisabledDates = function(dateRanges) {
  3317            options.disabledDateRanges = dateRanges;
  3318            for (var i = 0, l = scope.rows.length; i < l; i++) {
  3319              angular.forEach(scope.rows[i], $datepicker.$setDisabledEl);
  3320            }
  3321          };
  3322          $datepicker.select = function(date, keep) {
  3323            if (angular.isDate(date)) {
  3324              if (!angular.isDate(controller.$dateValue) || isNaN(controller.$dateValue.getTime())) {
  3325                controller.$dateValue = new Date(date);
  3326              }
  3327            } else {
  3328              controller.$dateValue = null;
  3329            }
  3330            if (!scope.$mode || keep) {
  3331              controller.$setViewValue(angular.copy(date));
  3332              controller.$render();
  3333              if (options.autoclose && !keep) {
  3334                $timeout(function() {
  3335                  $datepicker.hide(true);
  3336                });
  3337              }
  3338            } else {
  3339              angular.extend(viewDate, {
  3340                year: date.getFullYear(),
  3341                month: date.getMonth(),
  3342                date: date.getDate()
  3343              });
  3344              $datepicker.setMode(scope.$mode - 1);
  3345              $datepicker.$build();
  3346            }
  3347          };
  3348          $datepicker.setMode = function(mode) {
  3349            scope.$mode = mode;
  3350            $picker = $datepicker.$views[scope.$mode];
  3351            $datepicker.$build();
  3352          };
  3353          $datepicker.$build = function(pristine) {
  3354            if (pristine === true && $picker.built) return;
  3355            if (pristine === false && !$picker.built) return;
  3356            $picker.build.call($picker);
  3357          };
  3358          $datepicker.$updateSelected = function() {
  3359            for (var i = 0, l = scope.rows.length; i < l; i++) {
  3360              angular.forEach(scope.rows[i], updateSelected);
  3361            }
  3362          };
  3363          $datepicker.$isSelected = function(date) {
  3364            return $picker.isSelected(date);
  3365          };
  3366          $datepicker.$setDisabledEl = function(el) {
  3367            el.disabled = $picker.isDisabled(el.date);
  3368          };
  3369          $datepicker.$selectPane = function(value) {
  3370            var steps = $picker.steps;
  3371            var targetDate = new Date(Date.UTC(viewDate.year + (steps.year || 0) * value, viewDate.month + (steps.month || 0) * value, 1));
  3372            angular.extend(viewDate, {
  3373              year: targetDate.getUTCFullYear(),
  3374              month: targetDate.getUTCMonth(),
  3375              date: targetDate.getUTCDate()
  3376            });
  3377            $datepicker.$build();
  3378          };
  3379          $datepicker.$onMouseDown = function(evt) {
  3380            evt.preventDefault();
  3381            evt.stopPropagation();
  3382            if (isTouch) {
  3383              var targetEl = angular.element(evt.target);
  3384              if (targetEl[0].nodeName.toLowerCase() !== 'button') {
  3385                targetEl = targetEl.parent();
  3386              }
  3387              targetEl.triggerHandler('click');
  3388            }
  3389          };
  3390          $datepicker.$onKeyDown = function(evt) {
  3391            if (!/(38|37|39|40|13)/.test(evt.keyCode) || evt.shiftKey || evt.altKey) return;
  3392            evt.preventDefault();
  3393            evt.stopPropagation();
  3394            if (evt.keyCode === 13) {
  3395              if (!scope.$mode) {
  3396                $datepicker.hide(true);
  3397              } else {
  3398                scope.$apply(function() {
  3399                  $datepicker.setMode(scope.$mode - 1);
  3400                });
  3401              }
  3402              return;
  3403            }
  3404            $picker.onKeyDown(evt);
  3405            parentScope.$digest();
  3406          };
  3407          function updateSelected(el) {
  3408            el.selected = $datepicker.$isSelected(el.date);
  3409          }
  3410          function focusElement() {
  3411            element[0].focus();
  3412          }
  3413          var _init = $datepicker.init;
  3414          $datepicker.init = function() {
  3415            if (isNative && options.useNative) {
  3416              element.prop('type', 'date');
  3417              element.css('-webkit-appearance', 'textfield');
  3418              return;
  3419            } else if (isTouch) {
  3420              element.prop('type', 'text');
  3421              element.attr('readonly', 'true');
  3422              element.on('click', focusElement);
  3423            }
  3424            _init();
  3425          };
  3426          var _destroy = $datepicker.destroy;
  3427          $datepicker.destroy = function() {
  3428            if (isNative && options.useNative) {
  3429              element.off('click', focusElement);
  3430            }
  3431            _destroy();
  3432          };
  3433          var _show = $datepicker.show;
  3434          $datepicker.show = function() {
  3435            if (!isTouch && element.attr('readonly') || element.attr('disabled')) return;
  3436            _show();
  3437            $timeout(function() {
  3438              if (!$datepicker.$isShown) return;
  3439              $datepicker.$element.on(isTouch ? 'touchstart' : 'mousedown', $datepicker.$onMouseDown);
  3440              if (options.keyboard) {
  3441                element.on('keydown', $datepicker.$onKeyDown);
  3442              }
  3443            }, 0, false);
  3444          };
  3445          var _hide = $datepicker.hide;
  3446          $datepicker.hide = function(blur) {
  3447            if (!$datepicker.$isShown) return;
  3448            $datepicker.$element.off(isTouch ? 'touchstart' : 'mousedown', $datepicker.$onMouseDown);
  3449            if (options.keyboard) {
  3450              element.off('keydown', $datepicker.$onKeyDown);
  3451            }
  3452            _hide(blur);
  3453          };
  3454          return $datepicker;
  3455        }
  3456        DatepickerFactory.defaults = defaults;
  3457        return DatepickerFactory;
  3458      } ];
  3459    }).directive('bsDatepicker', [ '$window', '$parse', '$q', '$dateFormatter', '$dateParser', '$datepicker', function($window, $parse, $q, $dateFormatter, $dateParser, $datepicker) {
  3460      var isNative = /(ip[ao]d|iphone|android)/gi.test($window.navigator.userAgent);
  3461      return {
  3462        restrict: 'EAC',
  3463        require: 'ngModel',
  3464        link: function postLink(scope, element, attr, controller) {
  3465          var options = {
  3466            scope: scope
  3467          };
  3468          angular.forEach([ 'template', 'templateUrl', 'controller', 'controllerAs', 'placement', 'container', 'delay', 'trigger', 'html', 'animation', 'autoclose', 'dateType', 'dateFormat', 'timezone', 'modelDateFormat', 'dayFormat', 'strictFormat', 'startWeek', 'startDate', 'useNative', 'lang', 'startView', 'minView', 'iconLeft', 'iconRight', 'daysOfWeekDisabled', 'id', 'prefixClass', 'prefixEvent', 'hasToday', 'hasClear' ], function(key) {
  3469            if (angular.isDefined(attr[key])) options[key] = attr[key];
  3470          });
  3471          var falseValueRegExp = /^(false|0|)$/i;
  3472          angular.forEach([ 'html', 'container', 'autoclose', 'useNative', 'hasToday', 'hasClear' ], function(key) {
  3473            if (angular.isDefined(attr[key]) && falseValueRegExp.test(attr[key])) {
  3474              options[key] = false;
  3475            }
  3476          });
  3477          angular.forEach([ 'onBeforeShow', 'onShow', 'onBeforeHide', 'onHide' ], function(key) {
  3478            var bsKey = 'bs' + key.charAt(0).toUpperCase() + key.slice(1);
  3479            if (angular.isDefined(attr[bsKey])) {
  3480              options[key] = scope.$eval(attr[bsKey]);
  3481            }
  3482          });
  3483          var datepicker = $datepicker(element, controller, options);
  3484          options = datepicker.$options;
  3485          if (isNative && options.useNative) options.dateFormat = 'yyyy-MM-dd';
  3486          var lang = options.lang;
  3487          var formatDate = function(date, format) {
  3488            return $dateFormatter.formatDate(date, format, lang);
  3489          };
  3490          var dateParser = $dateParser({
  3491            format: options.dateFormat,
  3492            lang: lang,
  3493            strict: options.strictFormat
  3494          });
  3495          if (attr.bsShow) {
  3496            scope.$watch(attr.bsShow, function(newValue, oldValue) {
  3497              if (!datepicker || !angular.isDefined(newValue)) return;
  3498              if (angular.isString(newValue)) newValue = !!newValue.match(/true|,?(datepicker),?/i);
  3499              if (newValue === true) {
  3500                datepicker.show();
  3501              } else {
  3502                datepicker.hide();
  3503              }
  3504            });
  3505          }
  3506          angular.forEach([ 'minDate', 'maxDate' ], function(key) {
  3507            if (angular.isDefined(attr[key])) {
  3508              attr.$observe(key, function(newValue) {
  3509                datepicker.$options[key] = dateParser.getDateForAttribute(key, newValue);
  3510                if (!isNaN(datepicker.$options[key])) datepicker.$build(false);
  3511                validateAgainstMinMaxDate(controller.$dateValue);
  3512              });
  3513            }
  3514          });
  3515          if (angular.isDefined(attr.dateFormat)) {
  3516            attr.$observe('dateFormat', function(newValue) {
  3517              datepicker.$options.dateFormat = newValue;
  3518            });
  3519          }
  3520          scope.$watch(attr.ngModel, function(newValue, oldValue) {
  3521            datepicker.update(controller.$dateValue);
  3522          }, true);
  3523          function normalizeDateRanges(ranges) {
  3524            if (!ranges || !ranges.length) return null;
  3525            return ranges;
  3526          }
  3527          if (angular.isDefined(attr.disabledDates)) {
  3528            scope.$watch(attr.disabledDates, function(disabledRanges, previousValue) {
  3529              disabledRanges = normalizeDateRanges(disabledRanges);
  3530              previousValue = normalizeDateRanges(previousValue);
  3531              if (disabledRanges) {
  3532                datepicker.updateDisabledDates(disabledRanges);
  3533              }
  3534            });
  3535          }
  3536          function validateAgainstMinMaxDate(parsedDate) {
  3537            if (!angular.isDate(parsedDate)) return;
  3538            var isMinValid = isNaN(datepicker.$options.minDate) || parsedDate.getTime() >= datepicker.$options.minDate;
  3539            var isMaxValid = isNaN(datepicker.$options.maxDate) || parsedDate.getTime() <= datepicker.$options.maxDate;
  3540            var isValid = isMinValid && isMaxValid;
  3541            controller.$setValidity('date', isValid);
  3542            controller.$setValidity('min', isMinValid);
  3543            controller.$setValidity('max', isMaxValid);
  3544            if (isValid) controller.$dateValue = parsedDate;
  3545          }
  3546          controller.$parsers.unshift(function(viewValue) {
  3547            var date;
  3548            if (!viewValue) {
  3549              controller.$setValidity('date', true);
  3550              return null;
  3551            }
  3552            var parsedDate = dateParser.parse(viewValue, controller.$dateValue);
  3553            if (!parsedDate || isNaN(parsedDate.getTime())) {
  3554              controller.$setValidity('date', false);
  3555              return;
  3556            }
  3557            validateAgainstMinMaxDate(parsedDate);
  3558            if (options.dateType === 'string') {
  3559              date = dateParser.timezoneOffsetAdjust(parsedDate, options.timezone, true);
  3560              return formatDate(date, options.modelDateFormat || options.dateFormat);
  3561            }
  3562            date = dateParser.timezoneOffsetAdjust(controller.$dateValue, options.timezone, true);
  3563            if (options.dateType === 'number') {
  3564              return date.getTime();
  3565            } else if (options.dateType === 'unix') {
  3566              return date.getTime() / 1e3;
  3567            } else if (options.dateType === 'iso') {
  3568              return date.toISOString();
  3569            }
  3570            return new Date(date);
  3571          });
  3572          controller.$formatters.push(function(modelValue) {
  3573            var date;
  3574            if (angular.isUndefined(modelValue) || modelValue === null) {
  3575              date = NaN;
  3576            } else if (angular.isDate(modelValue)) {
  3577              date = modelValue;
  3578            } else if (options.dateType === 'string') {
  3579              date = dateParser.parse(modelValue, null, options.modelDateFormat);
  3580            } else if (options.dateType === 'unix') {
  3581              date = new Date(modelValue * 1e3);
  3582            } else {
  3583              date = new Date(modelValue);
  3584            }
  3585            controller.$dateValue = dateParser.timezoneOffsetAdjust(date, options.timezone);
  3586            return getDateFormattedString();
  3587          });
  3588          controller.$render = function() {
  3589            element.val(getDateFormattedString());
  3590          };
  3591          function getDateFormattedString() {
  3592            return !controller.$dateValue || isNaN(controller.$dateValue.getTime()) ? '' : formatDate(controller.$dateValue, options.dateFormat);
  3593          }
  3594          scope.$on('$destroy', function() {
  3595            if (datepicker) datepicker.destroy();
  3596            options = null;
  3597            datepicker = null;
  3598          });
  3599        }
  3600      };
  3601    } ]).provider('datepickerViews', function() {
  3602      function split(arr, size) {
  3603        var arrays = [];
  3604        while (arr.length > 0) {
  3605          arrays.push(arr.splice(0, size));
  3606        }
  3607        return arrays;
  3608      }
  3609      function mod(n, m) {
  3610        return (n % m + m) % m;
  3611      }
  3612      this.$get = [ '$dateFormatter', '$dateParser', '$sce', function($dateFormatter, $dateParser, $sce) {
  3613        return function(picker) {
  3614          var scope = picker.$scope;
  3615          var options = picker.$options;
  3616          var lang = options.lang;
  3617          var formatDate = function(date, format) {
  3618            return $dateFormatter.formatDate(date, format, lang);
  3619          };
  3620          var dateParser = $dateParser({
  3621            format: options.dateFormat,
  3622            lang: lang,
  3623            strict: options.strictFormat
  3624          });
  3625          var weekDaysMin = $dateFormatter.weekdaysShort(lang);
  3626          var weekDaysLabels = weekDaysMin.slice(options.startWeek).concat(weekDaysMin.slice(0, options.startWeek));
  3627          var weekDaysLabelsHtml = $sce.trustAsHtml('<th class="dow text-center">' + weekDaysLabels.join('</th><th class="dow text-center">') + '</th>');
  3628          var startDate = picker.$date || (options.startDate ? dateParser.getDateForAttribute('startDate', options.startDate) : new Date());
  3629          var viewDate = {
  3630            year: startDate.getFullYear(),
  3631            month: startDate.getMonth(),
  3632            date: startDate.getDate()
  3633          };
  3634          var views = [ {
  3635            format: options.dayFormat,
  3636            split: 7,
  3637            steps: {
  3638              month: 1
  3639            },
  3640            update: function(date, force) {
  3641              if (!this.built || force || date.getFullYear() !== viewDate.year || date.getMonth() !== viewDate.month) {
  3642                angular.extend(viewDate, {
  3643                  year: picker.$date.getFullYear(),
  3644                  month: picker.$date.getMonth(),
  3645                  date: picker.$date.getDate()
  3646                });
  3647                picker.$build();
  3648              } else if (date.getDate() !== viewDate.date || date.getDate() === 1) {
  3649                viewDate.date = picker.$date.getDate();
  3650                picker.$updateSelected();
  3651              }
  3652            },
  3653            build: function() {
  3654              var firstDayOfMonth = new Date(viewDate.year, viewDate.month, 1);
  3655              var firstDayOfMonthOffset = firstDayOfMonth.getTimezoneOffset();
  3656              var firstDate = new Date(+firstDayOfMonth - mod(firstDayOfMonth.getDay() - options.startWeek, 7) * 864e5);
  3657              var firstDateOffset = firstDate.getTimezoneOffset();
  3658              var today = dateParser.timezoneOffsetAdjust(new Date(), options.timezone).toDateString();
  3659              if (firstDateOffset !== firstDayOfMonthOffset) firstDate = new Date(+firstDate + (firstDateOffset - firstDayOfMonthOffset) * 6e4);
  3660              var days = [];
  3661              var day;
  3662              for (var i = 0; i < 42; i++) {
  3663                day = dateParser.daylightSavingAdjust(new Date(firstDate.getFullYear(), firstDate.getMonth(), firstDate.getDate() + i));
  3664                days.push({
  3665                  date: day,
  3666                  isToday: day.toDateString() === today,
  3667                  label: formatDate(day, this.format),
  3668                  selected: picker.$date && this.isSelected(day),
  3669                  muted: day.getMonth() !== viewDate.month,
  3670                  disabled: this.isDisabled(day)
  3671                });
  3672              }
  3673              scope.title = formatDate(firstDayOfMonth, options.monthTitleFormat);
  3674              scope.showLabels = true;
  3675              scope.labels = weekDaysLabelsHtml;
  3676              scope.rows = split(days, this.split);
  3677              scope.isTodayDisabled = this.isDisabled(new Date());
  3678              this.built = true;
  3679            },
  3680            isSelected: function(date) {
  3681              return picker.$date && date.getFullYear() === picker.$date.getFullYear() && date.getMonth() === picker.$date.getMonth() && date.getDate() === picker.$date.getDate();
  3682            },
  3683            isDisabled: function(date) {
  3684              var time = date.getTime();
  3685              if (time < options.minDate || time > options.maxDate) return true;
  3686              if (options.daysOfWeekDisabled.indexOf(date.getDay()) !== -1) return true;
  3687              if (options.disabledDateRanges) {
  3688                for (var i = 0; i < options.disabledDateRanges.length; i++) {
  3689                  if (time >= options.disabledDateRanges[i].start && time <= options.disabledDateRanges[i].end) {
  3690                    return true;
  3691                  }
  3692                }
  3693              }
  3694              return false;
  3695            },
  3696            onKeyDown: function(evt) {
  3697              if (!picker.$date) {
  3698                return;
  3699              }
  3700              var actualTime = picker.$date.getTime();
  3701              var newDate;
  3702              if (evt.keyCode === 37) newDate = new Date(actualTime - 1 * 864e5); else if (evt.keyCode === 38) newDate = new Date(actualTime - 7 * 864e5); else if (evt.keyCode === 39) newDate = new Date(actualTime + 1 * 864e5); else if (evt.keyCode === 40) newDate = new Date(actualTime + 7 * 864e5);
  3703              if (!this.isDisabled(newDate)) picker.select(newDate, true);
  3704            }
  3705          }, {
  3706            name: 'month',
  3707            format: options.monthFormat,
  3708            split: 4,
  3709            steps: {
  3710              year: 1
  3711            },
  3712            update: function(date, force) {
  3713              if (!this.built || date.getFullYear() !== viewDate.year) {
  3714                angular.extend(viewDate, {
  3715                  year: picker.$date.getFullYear(),
  3716                  month: picker.$date.getMonth(),
  3717                  date: picker.$date.getDate()
  3718                });
  3719                picker.$build();
  3720              } else if (date.getMonth() !== viewDate.month) {
  3721                angular.extend(viewDate, {
  3722                  month: picker.$date.getMonth(),
  3723                  date: picker.$date.getDate()
  3724                });
  3725                picker.$updateSelected();
  3726              }
  3727            },
  3728            build: function() {
  3729              var months = [];
  3730              var month;
  3731              for (var i = 0; i < 12; i++) {
  3732                month = new Date(viewDate.year, i, 1);
  3733                months.push({
  3734                  date: month,
  3735                  label: formatDate(month, this.format),
  3736                  selected: picker.$isSelected(month),
  3737                  disabled: this.isDisabled(month)
  3738                });
  3739              }
  3740              scope.title = formatDate(month, options.yearTitleFormat);
  3741              scope.showLabels = false;
  3742              scope.rows = split(months, this.split);
  3743              this.built = true;
  3744            },
  3745            isSelected: function(date) {
  3746              return picker.$date && date.getFullYear() === picker.$date.getFullYear() && date.getMonth() === picker.$date.getMonth();
  3747            },
  3748            isDisabled: function(date) {
  3749              var lastDate = +new Date(date.getFullYear(), date.getMonth() + 1, 0);
  3750              return lastDate < options.minDate || date.getTime() > options.maxDate;
  3751            },
  3752            onKeyDown: function(evt) {
  3753              if (!picker.$date) {
  3754                return;
  3755              }
  3756              var actualMonth = picker.$date.getMonth();
  3757              var newDate = new Date(picker.$date);
  3758              if (evt.keyCode === 37) newDate.setMonth(actualMonth - 1); else if (evt.keyCode === 38) newDate.setMonth(actualMonth - 4); else if (evt.keyCode === 39) newDate.setMonth(actualMonth + 1); else if (evt.keyCode === 40) newDate.setMonth(actualMonth + 4);
  3759              if (!this.isDisabled(newDate)) picker.select(newDate, true);
  3760            }
  3761          }, {
  3762            name: 'year',
  3763            format: options.yearFormat,
  3764            split: 4,
  3765            steps: {
  3766              year: 12
  3767            },
  3768            update: function(date, force) {
  3769              if (!this.built || force || parseInt(date.getFullYear() / 20, 10) !== parseInt(viewDate.year / 20, 10)) {
  3770                angular.extend(viewDate, {
  3771                  year: picker.$date.getFullYear(),
  3772                  month: picker.$date.getMonth(),
  3773                  date: picker.$date.getDate()
  3774                });
  3775                picker.$build();
  3776              } else if (date.getFullYear() !== viewDate.year) {
  3777                angular.extend(viewDate, {
  3778                  year: picker.$date.getFullYear(),
  3779                  month: picker.$date.getMonth(),
  3780                  date: picker.$date.getDate()
  3781                });
  3782                picker.$updateSelected();
  3783              }
  3784            },
  3785            build: function() {
  3786              var firstYear = viewDate.year - viewDate.year % (this.split * 3);
  3787              var years = [];
  3788              var year;
  3789              for (var i = 0; i < 12; i++) {
  3790                year = new Date(firstYear + i, 0, 1);
  3791                years.push({
  3792                  date: year,
  3793                  label: formatDate(year, this.format),
  3794                  selected: picker.$isSelected(year),
  3795                  disabled: this.isDisabled(year)
  3796                });
  3797              }
  3798              scope.title = years[0].label + '-' + years[years.length - 1].label;
  3799              scope.showLabels = false;
  3800              scope.rows = split(years, this.split);
  3801              this.built = true;
  3802            },
  3803            isSelected: function(date) {
  3804              return picker.$date && date.getFullYear() === picker.$date.getFullYear();
  3805            },
  3806            isDisabled: function(date) {
  3807              var lastDate = +new Date(date.getFullYear() + 1, 0, 0);
  3808              return lastDate < options.minDate || date.getTime() > options.maxDate;
  3809            },
  3810            onKeyDown: function(evt) {
  3811              if (!picker.$date) {
  3812                return;
  3813              }
  3814              var actualYear = picker.$date.getFullYear();
  3815              var newDate = new Date(picker.$date);
  3816              if (evt.keyCode === 37) newDate.setYear(actualYear - 1); else if (evt.keyCode === 38) newDate.setYear(actualYear - 4); else if (evt.keyCode === 39) newDate.setYear(actualYear + 1); else if (evt.keyCode === 40) newDate.setYear(actualYear + 4);
  3817              if (!this.isDisabled(newDate)) picker.select(newDate, true);
  3818            }
  3819          } ];
  3820          return {
  3821            views: options.minView ? Array.prototype.slice.call(views, options.minView) : views,
  3822            viewDate: viewDate
  3823          };
  3824        };
  3825      } ];
  3826    });
  3827    angular.module('mgcrea.ngStrap.button', []).provider('$button', function() {
  3828      var defaults = this.defaults = {
  3829        activeClass: 'active',
  3830        toggleEvent: 'click'
  3831      };
  3832      this.$get = function() {
  3833        return {
  3834          defaults: defaults
  3835        };
  3836      };
  3837    }).directive('bsCheckboxGroup', function() {
  3838      return {
  3839        restrict: 'A',
  3840        require: 'ngModel',
  3841        compile: function postLink(element, attr) {
  3842          element.attr('data-toggle', 'buttons');
  3843          element.removeAttr('ng-model');
  3844          var children = element[0].querySelectorAll('input[type="checkbox"]');
  3845          angular.forEach(children, function(child) {
  3846            var childEl = angular.element(child);
  3847            childEl.attr('bs-checkbox', '');
  3848            childEl.attr('ng-model', attr.ngModel + '.' + childEl.attr('value'));
  3849          });
  3850        }
  3851      };
  3852    }).directive('bsCheckbox', [ '$button', '$$rAF', function($button, $$rAF) {
  3853      var defaults = $button.defaults;
  3854      var constantValueRegExp = /^(true|false|\d+)$/;
  3855      return {
  3856        restrict: 'A',
  3857        require: 'ngModel',
  3858        link: function postLink(scope, element, attr, controller) {
  3859          var options = defaults;
  3860          var isInput = element[0].nodeName === 'INPUT';
  3861          var activeElement = isInput ? element.parent() : element;
  3862          var trueValue = angular.isDefined(attr.trueValue) ? attr.trueValue : true;
  3863          if (constantValueRegExp.test(attr.trueValue)) {
  3864            trueValue = scope.$eval(attr.trueValue);
  3865          }
  3866          var falseValue = angular.isDefined(attr.falseValue) ? attr.falseValue : false;
  3867          if (constantValueRegExp.test(attr.falseValue)) {
  3868            falseValue = scope.$eval(attr.falseValue);
  3869          }
  3870          var hasExoticValues = typeof trueValue !== 'boolean' || typeof falseValue !== 'boolean';
  3871          if (hasExoticValues) {
  3872            controller.$parsers.push(function(viewValue) {
  3873              return viewValue ? trueValue : falseValue;
  3874            });
  3875            controller.$formatters.push(function(modelValue) {
  3876              return angular.equals(modelValue, trueValue);
  3877            });
  3878            scope.$watch(attr.ngModel, function(newValue, oldValue) {
  3879              controller.$render();
  3880            });
  3881          }
  3882          controller.$render = function() {
  3883            var isActive = angular.equals(controller.$modelValue, trueValue);
  3884            $$rAF(function() {
  3885              if (isInput) element[0].checked = isActive;
  3886              activeElement.toggleClass(options.activeClass, isActive);
  3887            });
  3888          };
  3889          element.bind(options.toggleEvent, function() {
  3890            scope.$apply(function() {
  3891              if (!isInput) {
  3892                controller.$setViewValue(!activeElement.hasClass('active'));
  3893              }
  3894              if (!hasExoticValues) {
  3895                controller.$render();
  3896              }
  3897            });
  3898          });
  3899        }
  3900      };
  3901    } ]).directive('bsRadioGroup', function() {
  3902      return {
  3903        restrict: 'A',
  3904        require: 'ngModel',
  3905        compile: function postLink(element, attr) {
  3906          element.attr('data-toggle', 'buttons');
  3907          element.removeAttr('ng-model');
  3908          var children = element[0].querySelectorAll('input[type="radio"]');
  3909          angular.forEach(children, function(child) {
  3910            angular.element(child).attr('bs-radio', '');
  3911            angular.element(child).attr('ng-model', attr.ngModel);
  3912          });
  3913        }
  3914      };
  3915    }).directive('bsRadio', [ '$button', '$$rAF', function($button, $$rAF) {
  3916      var defaults = $button.defaults;
  3917      var constantValueRegExp = /^(true|false|\d+)$/;
  3918      return {
  3919        restrict: 'A',
  3920        require: 'ngModel',
  3921        link: function postLink(scope, element, attr, controller) {
  3922          var options = defaults;
  3923          var isInput = element[0].nodeName === 'INPUT';
  3924          var activeElement = isInput ? element.parent() : element;
  3925          var value;
  3926          attr.$observe('value', function(v) {
  3927            if (typeof v !== 'boolean' && constantValueRegExp.test(v)) {
  3928              value = scope.$eval(v);
  3929            } else {
  3930              value = v;
  3931            }
  3932            controller.$render();
  3933          });
  3934          controller.$render = function() {
  3935            var isActive = angular.equals(controller.$modelValue, value);
  3936            $$rAF(function() {
  3937              if (isInput) element[0].checked = isActive;
  3938              activeElement.toggleClass(options.activeClass, isActive);
  3939            });
  3940          };
  3941          element.bind(options.toggleEvent, function() {
  3942            scope.$apply(function() {
  3943              controller.$setViewValue(value);
  3944              controller.$render();
  3945            });
  3946          });
  3947        }
  3948      };
  3949    } ]);
  3950    angular.module('mgcrea.ngStrap.collapse', []).provider('$collapse', function() {
  3951      var defaults = this.defaults = {
  3952        animation: 'am-collapse',
  3953        disallowToggle: false,
  3954        activeClass: 'in',
  3955        startCollapsed: false,
  3956        allowMultiple: false
  3957      };
  3958      var controller = this.controller = function($scope, $element, $attrs) {
  3959        var self = this;
  3960        self.$options = angular.copy(defaults);
  3961        angular.forEach([ 'animation', 'disallowToggle', 'activeClass', 'startCollapsed', 'allowMultiple' ], function(key) {
  3962          if (angular.isDefined($attrs[key])) self.$options[key] = $attrs[key];
  3963        });
  3964        var falseValueRegExp = /^(false|0|)$/i;
  3965        angular.forEach([ 'disallowToggle', 'startCollapsed', 'allowMultiple' ], function(key) {
  3966          if (angular.isDefined($attrs[key]) && falseValueRegExp.test($attrs[key])) {
  3967            self.$options[key] = false;
  3968          }
  3969        });
  3970        self.$toggles = [];
  3971        self.$targets = [];
  3972        self.$viewChangeListeners = [];
  3973        self.$registerToggle = function(element) {
  3974          self.$toggles.push(element);
  3975        };
  3976        self.$registerTarget = function(element) {
  3977          self.$targets.push(element);
  3978        };
  3979        self.$unregisterToggle = function(element) {
  3980          var index = self.$toggles.indexOf(element);
  3981          self.$toggles.splice(index, 1);
  3982        };
  3983        self.$unregisterTarget = function(element) {
  3984          var index = self.$targets.indexOf(element);
  3985          self.$targets.splice(index, 1);
  3986          if (self.$options.allowMultiple) {
  3987            deactivateItem(element);
  3988          }
  3989          fixActiveItemIndexes(index);
  3990          self.$viewChangeListeners.forEach(function(fn) {
  3991            fn();
  3992          });
  3993        };
  3994        self.$targets.$active = !self.$options.startCollapsed ? [ 0 ] : [];
  3995        self.$setActive = $scope.$setActive = function(value) {
  3996          if (angular.isArray(value)) {
  3997            self.$targets.$active = value;
  3998          } else if (!self.$options.disallowToggle && isActive(value)) {
  3999            deactivateItem(value);
  4000          } else {
  4001            activateItem(value);
  4002          }
  4003          self.$viewChangeListeners.forEach(function(fn) {
  4004            fn();
  4005          });
  4006        };
  4007        self.$activeIndexes = function() {
  4008          if (self.$options.allowMultiple) {
  4009            return self.$targets.$active;
  4010          }
  4011          return self.$targets.$active.length === 1 ? self.$targets.$active[0] : -1;
  4012        };
  4013        function fixActiveItemIndexes(index) {
  4014          var activeIndexes = self.$targets.$active;
  4015          for (var i = 0; i < activeIndexes.length; i++) {
  4016            if (index < activeIndexes[i]) {
  4017              activeIndexes[i] = activeIndexes[i] - 1;
  4018            }
  4019            if (activeIndexes[i] === self.$targets.length) {
  4020              activeIndexes[i] = self.$targets.length - 1;
  4021            }
  4022          }
  4023        }
  4024        function isActive(value) {
  4025          var activeItems = self.$targets.$active;
  4026          return activeItems.indexOf(value) !== -1;
  4027        }
  4028        function deactivateItem(value) {
  4029          var index = self.$targets.$active.indexOf(value);
  4030          if (index !== -1) {
  4031            self.$targets.$active.splice(index, 1);
  4032          }
  4033        }
  4034        function activateItem(value) {
  4035          if (!self.$options.allowMultiple) {
  4036            self.$targets.$active.splice(0, 1);
  4037          }
  4038          if (self.$targets.$active.indexOf(value) === -1) {
  4039            self.$targets.$active.push(value);
  4040          }
  4041        }
  4042      };
  4043      this.$get = function() {
  4044        var $collapse = {};
  4045        $collapse.defaults = defaults;
  4046        $collapse.controller = controller;
  4047        return $collapse;
  4048      };
  4049    }).directive('bsCollapse', [ '$window', '$animate', '$collapse', function($window, $animate, $collapse) {
  4050      return {
  4051        require: [ '?ngModel', 'bsCollapse' ],
  4052        controller: [ '$scope', '$element', '$attrs', $collapse.controller ],
  4053        link: function postLink(scope, element, attrs, controllers) {
  4054          var ngModelCtrl = controllers[0];
  4055          var bsCollapseCtrl = controllers[1];
  4056          if (ngModelCtrl) {
  4057            bsCollapseCtrl.$viewChangeListeners.push(function() {
  4058              ngModelCtrl.$setViewValue(bsCollapseCtrl.$activeIndexes());
  4059            });
  4060            ngModelCtrl.$formatters.push(function(modelValue) {
  4061              if (angular.isArray(modelValue)) {
  4062                bsCollapseCtrl.$setActive(modelValue);
  4063              } else {
  4064                var activeIndexes = bsCollapseCtrl.$activeIndexes();
  4065                if (angular.isArray(activeIndexes)) {
  4066                  if (activeIndexes.indexOf(modelValue * 1) === -1) {
  4067                    bsCollapseCtrl.$setActive(modelValue * 1);
  4068                  }
  4069                } else if (activeIndexes !== modelValue * 1) {
  4070                  bsCollapseCtrl.$setActive(modelValue * 1);
  4071                }
  4072              }
  4073              return modelValue;
  4074            });
  4075          }
  4076        }
  4077      };
  4078    } ]).directive('bsCollapseToggle', function() {
  4079      return {
  4080        require: [ '^?ngModel', '^bsCollapse' ],
  4081        link: function postLink(scope, element, attrs, controllers) {
  4082          var bsCollapseCtrl = controllers[1];
  4083          element.attr('data-toggle', 'collapse');
  4084          bsCollapseCtrl.$registerToggle(element);
  4085          scope.$on('$destroy', function() {
  4086            bsCollapseCtrl.$unregisterToggle(element);
  4087          });
  4088          element.on('click', function() {
  4089            if (!attrs.disabled) {
  4090              var index = attrs.bsCollapseToggle && attrs.bsCollapseToggle !== 'bs-collapse-toggle' ? attrs.bsCollapseToggle : bsCollapseCtrl.$toggles.indexOf(element);
  4091              bsCollapseCtrl.$setActive(index * 1);
  4092              scope.$apply();
  4093            }
  4094          });
  4095        }
  4096      };
  4097    }).directive('bsCollapseTarget', [ '$animate', function($animate) {
  4098      return {
  4099        require: [ '^?ngModel', '^bsCollapse' ],
  4100        link: function postLink(scope, element, attrs, controllers) {
  4101          var bsCollapseCtrl = controllers[1];
  4102          element.addClass('collapse');
  4103          if (bsCollapseCtrl.$options.animation) {
  4104            element.addClass(bsCollapseCtrl.$options.animation);
  4105          }
  4106          bsCollapseCtrl.$registerTarget(element);
  4107          scope.$on('$destroy', function() {
  4108            bsCollapseCtrl.$unregisterTarget(element);
  4109          });
  4110          function render() {
  4111            var index = bsCollapseCtrl.$targets.indexOf(element);
  4112            var active = bsCollapseCtrl.$activeIndexes();
  4113            var action = 'removeClass';
  4114            if (angular.isArray(active)) {
  4115              if (active.indexOf(index) !== -1) {
  4116                action = 'addClass';
  4117              }
  4118            } else if (index === active) {
  4119              action = 'addClass';
  4120            }
  4121            $animate[action](element, bsCollapseCtrl.$options.activeClass);
  4122          }
  4123          bsCollapseCtrl.$viewChangeListeners.push(function() {
  4124            render();
  4125          });
  4126          render();
  4127        }
  4128      };
  4129    } ]);
  4130    angular.module('mgcrea.ngStrap.aside', [ 'mgcrea.ngStrap.modal' ]).provider('$aside', function() {
  4131      var defaults = this.defaults = {
  4132        animation: 'am-fade-and-slide-right',
  4133        prefixClass: 'aside',
  4134        prefixEvent: 'aside',
  4135        placement: 'right',
  4136        templateUrl: 'aside/aside.tpl.html',
  4137        contentTemplate: false,
  4138        container: false,
  4139        element: null,
  4140        backdrop: true,
  4141        keyboard: true,
  4142        html: false,
  4143        show: true
  4144      };
  4145      this.$get = [ '$modal', function($modal) {
  4146        function AsideFactory(config) {
  4147          var $aside = {};
  4148          var options = angular.extend({}, defaults, config);
  4149          $aside = $modal(options);
  4150          return $aside;
  4151        }
  4152        return AsideFactory;
  4153      } ];
  4154    }).directive('bsAside', [ '$window', '$sce', '$aside', function($window, $sce, $aside) {
  4155      return {
  4156        restrict: 'EAC',
  4157        scope: true,
  4158        link: function postLink(scope, element, attr, transclusion) {
  4159          var options = {
  4160            scope: scope,
  4161            element: element,
  4162            show: false
  4163          };
  4164          angular.forEach([ 'template', 'templateUrl', 'controller', 'controllerAs', 'contentTemplate', 'placement', 'backdrop', 'keyboard', 'html', 'container', 'animation' ], function(key) {
  4165            if (angular.isDefined(attr[key])) options[key] = attr[key];
  4166          });
  4167          var falseValueRegExp = /^(false|0|)$/i;
  4168          angular.forEach([ 'backdrop', 'keyboard', 'html', 'container' ], function(key) {
  4169            if (angular.isDefined(attr[key]) && falseValueRegExp.test(attr[key])) options[key] = false;
  4170          });
  4171          angular.forEach([ 'onBeforeShow', 'onShow', 'onBeforeHide', 'onHide' ], function(key) {
  4172            var bsKey = 'bs' + key.charAt(0).toUpperCase() + key.slice(1);
  4173            if (angular.isDefined(attr[bsKey])) {
  4174              options[key] = scope.$eval(attr[bsKey]);
  4175            }
  4176          });
  4177          angular.forEach([ 'title', 'content' ], function(key) {
  4178            if (attr[key]) {
  4179              attr.$observe(key, function(newValue, oldValue) {
  4180                scope[key] = $sce.trustAsHtml(newValue);
  4181              });
  4182            }
  4183          });
  4184          if (attr.bsAside) {
  4185            scope.$watch(attr.bsAside, function(newValue, oldValue) {
  4186              if (angular.isObject(newValue)) {
  4187                angular.extend(scope, newValue);
  4188              } else {
  4189                scope.content = newValue;
  4190              }
  4191            }, true);
  4192          }
  4193          var aside = $aside(options);
  4194          element.on(attr.trigger || 'click', aside.toggle);
  4195          scope.$on('$destroy', function() {
  4196            if (aside) aside.destroy();
  4197            options = null;
  4198            aside = null;
  4199          });
  4200        }
  4201      };
  4202    } ]);
  4203    angular.module('mgcrea.ngStrap.alert', [ 'mgcrea.ngStrap.modal' ]).provider('$alert', function() {
  4204      var defaults = this.defaults = {
  4205        animation: 'am-fade',
  4206        prefixClass: 'alert',
  4207        prefixEvent: 'alert',
  4208        placement: null,
  4209        templateUrl: 'alert/alert.tpl.html',
  4210        container: false,
  4211        element: null,
  4212        backdrop: false,
  4213        keyboard: true,
  4214        show: true,
  4215        duration: false,
  4216        type: false,
  4217        dismissable: true
  4218      };
  4219      this.$get = [ '$modal', '$timeout', function($modal, $timeout) {
  4220        function AlertFactory(config) {
  4221          var $alert = {};
  4222          var options = angular.extend({}, defaults, config);
  4223          $alert = $modal(options);
  4224          $alert.$scope.dismissable = !!options.dismissable;
  4225          if (options.type) {
  4226            $alert.$scope.type = options.type;
  4227          }
  4228          var show = $alert.show;
  4229          if (options.duration) {
  4230            $alert.show = function() {
  4231              show();
  4232              $timeout(function() {
  4233                $alert.hide();
  4234              }, options.duration * 1e3);
  4235            };
  4236          }
  4237          return $alert;
  4238        }
  4239        return AlertFactory;
  4240      } ];
  4241    }).directive('bsAlert', [ '$window', '$sce', '$alert', function($window, $sce, $alert) {
  4242      return {
  4243        restrict: 'EAC',
  4244        scope: true,
  4245        link: function postLink(scope, element, attr, transclusion) {
  4246          var options = {
  4247            scope: scope,
  4248            element: element,
  4249            show: false
  4250          };
  4251          angular.forEach([ 'template', 'templateUrl', 'controller', 'controllerAs', 'placement', 'keyboard', 'html', 'container', 'animation', 'duration', 'dismissable' ], function(key) {
  4252            if (angular.isDefined(attr[key])) options[key] = attr[key];
  4253          });
  4254          var falseValueRegExp = /^(false|0|)$/i;
  4255          angular.forEach([ 'keyboard', 'html', 'container', 'dismissable' ], function(key) {
  4256            if (angular.isDefined(attr[key]) && falseValueRegExp.test(attr[key])) options[key] = false;
  4257          });
  4258          angular.forEach([ 'onBeforeShow', 'onShow', 'onBeforeHide', 'onHide' ], function(key) {
  4259            var bsKey = 'bs' + key.charAt(0).toUpperCase() + key.slice(1);
  4260            if (angular.isDefined(attr[bsKey])) {
  4261              options[key] = scope.$eval(attr[bsKey]);
  4262            }
  4263          });
  4264          if (!scope.hasOwnProperty('title')) {
  4265            scope.title = '';
  4266          }
  4267          angular.forEach([ 'title', 'content', 'type' ], function(key) {
  4268            if (attr[key]) {
  4269              attr.$observe(key, function(newValue, oldValue) {
  4270                scope[key] = $sce.trustAsHtml(newValue);
  4271              });
  4272            }
  4273          });
  4274          if (attr.bsAlert) {
  4275            scope.$watch(attr.bsAlert, function(newValue, oldValue) {
  4276              if (angular.isObject(newValue)) {
  4277                angular.extend(scope, newValue);
  4278              } else {
  4279                scope.content = newValue;
  4280              }
  4281            }, true);
  4282          }
  4283          var alert = $alert(options);
  4284          element.on(attr.trigger || 'click', alert.toggle);
  4285          scope.$on('$destroy', function() {
  4286            if (alert) alert.destroy();
  4287            options = null;
  4288            alert = null;
  4289          });
  4290        }
  4291      };
  4292    } ]);
  4293    angular.module('mgcrea.ngStrap.affix', [ 'mgcrea.ngStrap.helpers.dimensions', 'mgcrea.ngStrap.helpers.debounce' ]).provider('$affix', function() {
  4294      var defaults = this.defaults = {
  4295        offsetTop: 'auto',
  4296        inlineStyles: true
  4297      };
  4298      this.$get = [ '$window', 'debounce', 'dimensions', function($window, debounce, dimensions) {
  4299        var bodyEl = angular.element($window.document.body);
  4300        var windowEl = angular.element($window);
  4301        function AffixFactory(element, config) {
  4302          var $affix = {};
  4303          var options = angular.extend({}, defaults, config);
  4304          var targetEl = options.target;
  4305          var reset = 'affix affix-top affix-bottom';
  4306          var setWidth = false;
  4307          var initialAffixTop = 0;
  4308          var initialOffsetTop = 0;
  4309          var offsetTop = 0;
  4310          var offsetBottom = 0;
  4311          var affixed = null;
  4312          var unpin = null;
  4313          var parent = element.parent();
  4314          if (options.offsetParent) {
  4315            if (options.offsetParent.match(/^\d+$/)) {
  4316              for (var i = 0; i < options.offsetParent * 1 - 1; i++) {
  4317                parent = parent.parent();
  4318              }
  4319            } else {
  4320              parent = angular.element(options.offsetParent);
  4321            }
  4322          }
  4323          $affix.init = function() {
  4324            this.$parseOffsets();
  4325            initialOffsetTop = dimensions.offset(element[0]).top + initialAffixTop;
  4326            setWidth = !element[0].style.width;
  4327            targetEl.on('scroll', this.checkPosition);
  4328            targetEl.on('click', this.checkPositionWithEventLoop);
  4329            windowEl.on('resize', this.$debouncedOnResize);
  4330            this.checkPosition();
  4331            this.checkPositionWithEventLoop();
  4332          };
  4333          $affix.destroy = function() {
  4334            targetEl.off('scroll', this.checkPosition);
  4335            targetEl.off('click', this.checkPositionWithEventLoop);
  4336            windowEl.off('resize', this.$debouncedOnResize);
  4337          };
  4338          $affix.checkPositionWithEventLoop = function() {
  4339            setTimeout($affix.checkPosition, 1);
  4340          };
  4341          $affix.checkPosition = function() {
  4342            var scrollTop = getScrollTop();
  4343            var position = dimensions.offset(element[0]);
  4344            var elementHeight = dimensions.height(element[0]);
  4345            var affix = getRequiredAffixClass(unpin, position, elementHeight);
  4346            if (affixed === affix) return;
  4347            affixed = affix;
  4348            if (affix === 'top') {
  4349              unpin = null;
  4350              if (setWidth) {
  4351                element.css('width', '');
  4352              }
  4353              if (options.inlineStyles) {
  4354                element.css('position', options.offsetParent ? '' : 'relative');
  4355                element.css('top', '');
  4356              }
  4357            } else if (affix === 'bottom') {
  4358              if (options.offsetUnpin) {
  4359                unpin = -(options.offsetUnpin * 1);
  4360              } else {
  4361                unpin = position.top - scrollTop;
  4362              }
  4363              if (setWidth) {
  4364                element.css('width', '');
  4365              }
  4366              if (options.inlineStyles) {
  4367                element.css('position', options.offsetParent ? '' : 'relative');
  4368                element.css('top', options.offsetParent ? '' : bodyEl[0].offsetHeight - offsetBottom - elementHeight - initialOffsetTop + 'px');
  4369              }
  4370            } else {
  4371              unpin = null;
  4372              if (setWidth) {
  4373                element.css('width', element[0].offsetWidth + 'px');
  4374              }
  4375              if (options.inlineStyles) {
  4376                element.css('position', 'fixed');
  4377                element.css('top', initialAffixTop + 'px');
  4378              }
  4379            }
  4380            element.removeClass(reset).addClass('affix' + (affix !== 'middle' ? '-' + affix : ''));
  4381          };
  4382          $affix.$onResize = function() {
  4383            $affix.$parseOffsets();
  4384            $affix.checkPosition();
  4385          };
  4386          $affix.$debouncedOnResize = debounce($affix.$onResize, 50);
  4387          $affix.$parseOffsets = function() {
  4388            var initialPosition = element.css('position');
  4389            if (options.inlineStyles) {
  4390              element.css('position', options.offsetParent ? '' : 'relative');
  4391            }
  4392            if (options.offsetTop) {
  4393              if (options.offsetTop === 'auto') {
  4394                options.offsetTop = '+0';
  4395              }
  4396              if (options.offsetTop.match(/^[-+]\d+$/)) {
  4397                initialAffixTop = -options.offsetTop * 1;
  4398                if (options.offsetParent) {
  4399                  offsetTop = dimensions.offset(parent[0]).top + options.offsetTop * 1;
  4400                } else {
  4401                  offsetTop = dimensions.offset(element[0]).top - dimensions.css(element[0], 'marginTop', true) + options.offsetTop * 1;
  4402                }
  4403              } else {
  4404                offsetTop = options.offsetTop * 1;
  4405              }
  4406            }
  4407            if (options.offsetBottom) {
  4408              if (options.offsetParent && options.offsetBottom.match(/^[-+]\d+$/)) {
  4409                offsetBottom = getScrollHeight() - (dimensions.offset(parent[0]).top + dimensions.height(parent[0])) + options.offsetBottom * 1 + 1;
  4410              } else {
  4411                offsetBottom = options.offsetBottom * 1;
  4412              }
  4413            }
  4414            if (options.inlineStyles) {
  4415              element.css('position', initialPosition);
  4416            }
  4417          };
  4418          function getRequiredAffixClass(_unpin, position, elementHeight) {
  4419            var scrollTop = getScrollTop();
  4420            var scrollHeight = getScrollHeight();
  4421            if (scrollTop <= offsetTop) {
  4422              return 'top';
  4423            } else if (_unpin !== null && scrollTop + _unpin <= position.top) {
  4424              return 'middle';
  4425            } else if (offsetBottom !== null && position.top + elementHeight + initialAffixTop >= scrollHeight - offsetBottom) {
  4426              return 'bottom';
  4427            }
  4428            return 'middle';
  4429          }
  4430          function getScrollTop() {
  4431            return targetEl[0] === $window ? $window.pageYOffset : targetEl[0].scrollTop;
  4432          }
  4433          function getScrollHeight() {
  4434            return targetEl[0] === $window ? $window.document.body.scrollHeight : targetEl[0].scrollHeight;
  4435          }
  4436          $affix.init();
  4437          return $affix;
  4438        }
  4439        return AffixFactory;
  4440      } ];
  4441    }).directive('bsAffix', [ '$affix', '$window', function($affix, $window) {
  4442      return {
  4443        restrict: 'EAC',
  4444        require: '^?bsAffixTarget',
  4445        link: function postLink(scope, element, attr, affixTarget) {
  4446          var options = {
  4447            scope: scope,
  4448            target: affixTarget ? affixTarget.$element : angular.element($window)
  4449          };
  4450          angular.forEach([ 'offsetTop', 'offsetBottom', 'offsetParent', 'offsetUnpin', 'inlineStyles' ], function(key) {
  4451            if (angular.isDefined(attr[key])) {
  4452              var option = attr[key];
  4453              if (/true/i.test(option)) option = true;
  4454              if (/false/i.test(option)) option = false;
  4455              options[key] = option;
  4456            }
  4457          });
  4458          var affix = $affix(element, options);
  4459          scope.$on('$destroy', function() {
  4460            if (affix) affix.destroy();
  4461            options = null;
  4462            affix = null;
  4463          });
  4464        }
  4465      };
  4466    } ]).directive('bsAffixTarget', function() {
  4467      return {
  4468        controller: [ '$element', function($element) {
  4469          this.$element = $element;
  4470        } ]
  4471      };
  4472    });
  4473    angular.module('mgcrea.ngStrap', [ 'mgcrea.ngStrap.modal', 'mgcrea.ngStrap.aside', 'mgcrea.ngStrap.alert', 'mgcrea.ngStrap.button', 'mgcrea.ngStrap.select', 'mgcrea.ngStrap.datepicker', 'mgcrea.ngStrap.timepicker', 'mgcrea.ngStrap.navbar', 'mgcrea.ngStrap.tooltip', 'mgcrea.ngStrap.popover', 'mgcrea.ngStrap.dropdown', 'mgcrea.ngStrap.typeahead', 'mgcrea.ngStrap.scrollspy', 'mgcrea.ngStrap.affix', 'mgcrea.ngStrap.tab', 'mgcrea.ngStrap.collapse' ]);
  4474  })(window, document);