').html(template.trim()).contents();\n var linkFn = $compile(element);\n\n // Return a linking function that can be used later when the element is ready\n return {\n locals: locals,\n element: element,\n link: function link(scope) {\n locals.$scope = scope;\n\n // Instantiate controller if it exists, because we have scope\n if (controller) {\n var invokeCtrl = $controller(controller, locals, true);\n if (bindToController) {\n angular.extend(invokeCtrl.instance, locals);\n }\n // Support angular@~1.2 invokeCtrl\n var ctrl = angular.isObject(invokeCtrl) ? invokeCtrl : invokeCtrl();\n // See angular-route source for this logic\n element.data('$ngControllerController', ctrl);\n element.children().data('$ngControllerController', ctrl);\n\n if (controllerAs) {\n scope[controllerAs] = ctrl;\n }\n }\n\n return linkFn.apply(null, arguments);\n }\n };\n });\n\n };\n\n function findElement(query, element) {\n return angular.element((element || document).querySelectorAll(query));\n }\n\n var fetchPromises = {};\n function fetchTemplate(template) {\n if(fetchPromises[template]) return fetchPromises[template];\n return (fetchPromises[template] = $http.get(template, {cache: $templateCache})\n .then(function(res) {\n return res.data;\n }));\n }\n\n}\n\n// Source: navbar/navbar.js\nangular.module('mgcrea.ngStrap.navbar', [])\n\n .provider('$navbar', function() {\n\n var defaults = this.defaults = {\n activeClass: 'active',\n routeAttr: 'data-match-route',\n strict: false\n };\n\n this.$get = function() {\n return {defaults: defaults};\n };\n\n })\n\n .directive('bsNavbar', function($window, $location, $navbar) {\n\n var defaults = $navbar.defaults;\n\n return {\n restrict: 'A',\n link: function postLink(scope, element, attr, controller) {\n\n // Directive options\n var options = angular.copy(defaults);\n angular.forEach(Object.keys(defaults), function(key) {\n if(angular.isDefined(attr[key])) options[key] = attr[key];\n });\n\n // Watch for the $location\n scope.$watch(function() {\n\n return $location.path();\n\n }, function(newValue, oldValue) {\n\n var liElements = element[0].querySelectorAll('li[' + options.routeAttr + ']');\n\n angular.forEach(liElements, function(li) {\n\n var liElement = angular.element(li);\n var pattern = liElement.attr(options.routeAttr).replace('/', '\\\\/');\n if(options.strict) {\n pattern = '^' + pattern + '$';\n }\n var regexp = new RegExp(pattern, 'i');\n\n if(regexp.test(newValue)) {\n liElement.addClass(options.activeClass);\n } else {\n liElement.removeClass(options.activeClass);\n }\n\n });\n\n });\n\n }\n\n };\n\n });\n\n// Source: modal/modal.js\nangular.module('mgcrea.ngStrap.modal', ['mgcrea.ngStrap.core', 'mgcrea.ngStrap.helpers.dimensions'])\n\n .provider('$modal', function() {\n\n var defaults = this.defaults = {\n animation: 'am-fade',\n backdropAnimation: 'am-fade',\n prefixClass: 'modal',\n prefixEvent: 'modal',\n placement: 'top',\n templateUrl: 'modal/modal.tpl.html',\n template: '',\n contentTemplate: false,\n container: false,\n element: null,\n backdrop: true,\n keyboard: true,\n html: false,\n show: true\n };\n\n this.$get = function($window, $rootScope, $bsCompiler, $animate, $timeout, $sce, dimensions) {\n\n var forEach = angular.forEach;\n var trim = String.prototype.trim;\n var requestAnimationFrame = $window.requestAnimationFrame || $window.setTimeout;\n var bodyElement = angular.element($window.document.body);\n\n var backdropCount = 0;\n var dialogBaseZindex = 1050;\n var backdropBaseZindex = 1040;\n\n function ModalFactory(config) {\n\n var $modal = {};\n\n // Common vars\n var options = $modal.$options = angular.extend({}, defaults, config);\n var promise = $modal.$promise = $bsCompiler.compile(options);\n var scope = $modal.$scope = options.scope && options.scope.$new() || $rootScope.$new();\n\n if(!options.element && !options.container) {\n options.container = 'body';\n }\n\n // Store $id to identify the triggering element in events\n // give priority to options.id, otherwise, try to use\n // element id if defined\n $modal.$id = options.id || options.element && options.element.attr('id') || '';\n\n // Support scope as string options\n forEach(['title', 'content'], function(key) {\n if(options[key]) scope[key] = $sce.trustAsHtml(options[key]);\n });\n\n // Provide scope helpers\n scope.$hide = function() {\n scope.$$postDigest(function() {\n $modal.hide();\n });\n };\n scope.$show = function() {\n scope.$$postDigest(function() {\n $modal.show();\n });\n };\n scope.$toggle = function() {\n scope.$$postDigest(function() {\n $modal.toggle();\n });\n };\n // Publish isShown as a protected var on scope\n $modal.$isShown = scope.$isShown = false;\n\n // Fetch, compile then initialize modal\n var compileData, modalElement, modalScope;\n var backdropElement = angular.element('
');\n backdropElement.css({position:'fixed', top:'0px', left:'0px', bottom:'0px', right:'0px'});\n promise.then(function(data) {\n compileData = data;\n $modal.init();\n });\n\n $modal.init = function() {\n\n // Options: show\n if(options.show) {\n scope.$$postDigest(function() {\n $modal.show();\n });\n }\n\n };\n\n $modal.destroy = function() {\n\n // Remove element\n destroyModalElement();\n\n // remove backdrop element\n if(backdropElement) {\n backdropElement.remove();\n backdropElement = null;\n }\n\n // Destroy scope\n scope.$destroy();\n };\n\n $modal.show = function() {\n if($modal.$isShown) return;\n\n var parent, after;\n if(angular.isElement(options.container)) {\n parent = options.container;\n after = options.container[0].lastChild ? angular.element(options.container[0].lastChild) : null;\n } else {\n if (options.container) {\n parent = findElement(options.container);\n after = parent[0] && parent[0].lastChild ? angular.element(parent[0].lastChild) : null;\n } else {\n parent = null;\n after = options.element;\n }\n }\n\n // destroy any existing modal elements\n if(modalElement) destroyModalElement();\n\n // create a new scope, so we can destroy it and all child scopes\n // when destroying the modal element\n modalScope = $modal.$scope.$new();\n // Fetch a cloned element linked from template (noop callback is required)\n modalElement = $modal.$element = compileData.link(modalScope, function(clonedElement, scope) {});\n\n if(options.backdrop) {\n // set z-index\n modalElement.css({'z-index': dialogBaseZindex + (backdropCount * 20)});\n backdropElement.css({'z-index': backdropBaseZindex + (backdropCount * 20)});\n\n // increment number of backdrops\n backdropCount++;\n }\n\n if(scope.$emit(options.prefixEvent + '.show.before', $modal).defaultPrevented) {\n return;\n }\n\n // Set the initial positioning.\n modalElement.css({display: 'block'}).addClass(options.placement);\n\n // Options: animation\n if(options.animation) {\n if(options.backdrop) {\n backdropElement.addClass(options.backdropAnimation);\n }\n modalElement.addClass(options.animation);\n }\n\n if(options.backdrop) {\n $animate.enter(backdropElement, bodyElement, null);\n }\n\n // Support v1.2+ $animate\n // https://github.com/angular/angular.js/issues/11713\n if(angular.version.minor <= 2) {\n $animate.enter(modalElement, parent, after, enterAnimateCallback);\n } else {\n $animate.enter(modalElement, parent, after).then(enterAnimateCallback);\n }\n\n $modal.$isShown = scope.$isShown = true;\n safeDigest(scope);\n // Focus once the enter-animation has started\n // Weird PhantomJS bug hack\n var el = modalElement[0];\n requestAnimationFrame(function() {\n el.focus();\n });\n\n bodyElement.addClass(options.prefixClass + '-open');\n if(options.animation) {\n bodyElement.addClass(options.prefixClass + '-with-' + options.animation);\n }\n\n // Bind events\n bindBackdropEvents();\n bindKeyboardEvents();\n };\n\n function enterAnimateCallback() {\n scope.$emit(options.prefixEvent + '.show', $modal);\n }\n\n $modal.hide = function() {\n if(!$modal.$isShown) return;\n\n if(options.backdrop) {\n // decrement number of modals\n backdropCount--;\n }\n\n if(scope.$emit(options.prefixEvent + '.hide.before', $modal).defaultPrevented) {\n return;\n }\n\n // Support v1.2+ $animate\n // https://github.com/angular/angular.js/issues/11713\n if(angular.version.minor <= 2) {\n $animate.leave(modalElement, leaveAnimateCallback);\n } else {\n $animate.leave(modalElement).then(leaveAnimateCallback);\n }\n\n if(options.backdrop) {\n $animate.leave(backdropElement);\n }\n $modal.$isShown = scope.$isShown = false;\n safeDigest(scope);\n\n // Unbind events\n unbindBackdropEvents();\n unbindKeyboardEvents();\n };\n\n function leaveAnimateCallback() {\n scope.$emit(options.prefixEvent + '.hide', $modal);\n bodyElement.removeClass(options.prefixClass + '-open');\n if(options.animation) {\n bodyElement.removeClass(options.prefixClass + '-with-' + options.animation);\n }\n }\n\n $modal.toggle = function() {\n\n $modal.$isShown ? $modal.hide() : $modal.show();\n\n };\n\n $modal.focus = function() {\n modalElement[0].focus();\n };\n\n // Protected methods\n\n $modal.$onKeyUp = function(evt) {\n\n if (evt.which === 27 && $modal.$isShown) {\n $modal.hide();\n evt.stopPropagation();\n }\n\n };\n\n function bindBackdropEvents() {\n if(options.backdrop) {\n modalElement.on('click', hideOnBackdropClick);\n backdropElement.on('click', hideOnBackdropClick);\n backdropElement.on('wheel', preventEventDefault);\n }\n }\n\n function unbindBackdropEvents() {\n if(options.backdrop) {\n modalElement.off('click', hideOnBackdropClick);\n backdropElement.off('click', hideOnBackdropClick);\n backdropElement.off('wheel', preventEventDefault);\n }\n }\n\n function bindKeyboardEvents() {\n if(options.keyboard) {\n modalElement.on('keyup', $modal.$onKeyUp);\n }\n }\n\n function unbindKeyboardEvents() {\n if(options.keyboard) {\n modalElement.off('keyup', $modal.$onKeyUp);\n }\n }\n\n // Private helpers\n\n function hideOnBackdropClick(evt) {\n if(evt.target !== evt.currentTarget) return;\n options.backdrop === 'static' ? $modal.focus() : $modal.hide();\n }\n\n function preventEventDefault(evt) {\n evt.preventDefault();\n }\n\n function destroyModalElement() {\n if($modal.$isShown && modalElement !== null) {\n // un-bind events\n unbindBackdropEvents();\n unbindKeyboardEvents();\n }\n\n if(modalScope) {\n modalScope.$destroy();\n modalScope = null;\n }\n\n if(modalElement) {\n modalElement.remove();\n modalElement = $modal.$element = null;\n }\n }\n\n return $modal;\n\n }\n\n // Helper functions\n\n function safeDigest(scope) {\n scope.$$phase || (scope.$root && scope.$root.$$phase) || scope.$digest();\n }\n\n function findElement(query, element) {\n return angular.element((element || document).querySelectorAll(query));\n }\n\n return ModalFactory;\n\n };\n\n })\n\n .directive('bsModal', function($window, $sce, $modal) {\n\n return {\n restrict: 'EAC',\n scope: true,\n link: function postLink(scope, element, attr, transclusion) {\n\n // Directive options\n var options = {scope: scope, element: element, show: false};\n angular.forEach(['template', 'templateUrl', 'controller', 'controllerAs', 'contentTemplate', 'placement', 'backdrop', 'keyboard', 'html', 'container', 'animation', 'backdropAnimation', 'id', 'prefixEvent', 'prefixClass'], function(key) {\n if(angular.isDefined(attr[key])) options[key] = attr[key];\n });\n\n // use string regex match boolean attr falsy values, leave truthy values be\n var falseValueRegExp = /^(false|0|)$/i;\n angular.forEach(['backdrop', 'keyboard', 'html', 'container'], function(key) {\n if(angular.isDefined(attr[key]) && falseValueRegExp.test(attr[key]))\n options[key] = false;\n });\n\n // Support scope as data-attrs\n angular.forEach(['title', 'content'], function(key) {\n attr[key] && attr.$observe(key, function(newValue, oldValue) {\n scope[key] = $sce.trustAsHtml(newValue);\n });\n });\n\n // Support scope as an object\n attr.bsModal && scope.$watch(attr.bsModal, function(newValue, oldValue) {\n if(angular.isObject(newValue)) {\n angular.extend(scope, newValue);\n } else {\n scope.content = newValue;\n }\n }, true);\n\n // Initialize modal\n var modal = $modal(options);\n\n // Trigger\n element.on(attr.trigger || 'click', modal.toggle);\n\n // Garbage collection\n scope.$on('$destroy', function() {\n if (modal) modal.destroy();\n options = null;\n modal = null;\n });\n\n }\n };\n\n });\n\n// Source: dropdown/dropdown.js\nangular.module('mgcrea.ngStrap.dropdown', ['mgcrea.ngStrap.tooltip'])\n\n .provider('$dropdown', function() {\n\n var defaults = this.defaults = {\n animation: 'am-fade',\n prefixClass: 'dropdown',\n prefixEvent: 'dropdown',\n placement: 'bottom-left',\n templateUrl: 'dropdown/dropdown.tpl.html',\n trigger: 'click',\n container: false,\n keyboard: true,\n html: false,\n delay: 0\n };\n\n this.$get = function($window, $rootScope, $tooltip, $timeout) {\n\n var bodyEl = angular.element($window.document.body);\n var matchesSelector = Element.prototype.matchesSelector || Element.prototype.webkitMatchesSelector || Element.prototype.mozMatchesSelector || Element.prototype.msMatchesSelector || Element.prototype.oMatchesSelector;\n\n function DropdownFactory(element, config) {\n\n var $dropdown = {};\n\n // Common vars\n var options = angular.extend({}, defaults, config);\n var scope = $dropdown.$scope = options.scope && options.scope.$new() || $rootScope.$new();\n\n $dropdown = $tooltip(element, options);\n var parentEl = element.parent();\n\n // Protected methods\n\n $dropdown.$onKeyDown = function(evt) {\n if (!/(38|40)/.test(evt.keyCode)) return;\n evt.preventDefault();\n evt.stopPropagation();\n\n // Retrieve focused index\n var items = angular.element($dropdown.$element[0].querySelectorAll('li:not(.divider) a'));\n if (!items.length) return;\n var index;\n angular.forEach(items, function(el, i) {\n if (matchesSelector && matchesSelector.call(el, ':focus')) index = i;\n });\n\n // Navigate with keyboard\n if (evt.keyCode === 38 && index > 0) index--;\n else if (evt.keyCode === 40 && index < items.length - 1) index++;\n else if (angular.isUndefined(index)) index = 0;\n items.eq(index)[0].focus();\n\n };\n\n // Overrides\n\n var show = $dropdown.show;\n $dropdown.show = function() {\n show();\n // use timeout to hookup the events to prevent\n // event bubbling from being processed imediately.\n $timeout(function() {\n options.keyboard && $dropdown.$element && $dropdown.$element.on('keydown', $dropdown.$onKeyDown);\n bodyEl.on('click', onBodyClick);\n }, 0, false);\n parentEl.hasClass('dropdown') && parentEl.addClass('open');\n };\n\n var hide = $dropdown.hide;\n $dropdown.hide = function() {\n if (!$dropdown.$isShown) return;\n options.keyboard && $dropdown.$element && $dropdown.$element.off('keydown', $dropdown.$onKeyDown);\n bodyEl.off('click', onBodyClick);\n parentEl.hasClass('dropdown') && parentEl.removeClass('open');\n hide();\n };\n\n var destroy = $dropdown.destroy;\n $dropdown.destroy = function() {\n bodyEl.off('click', onBodyClick);\n destroy();\n };\n\n // Private functions\n\n function onBodyClick(evt) {\n if (evt.target === element[0]) return;\n return evt.target !== element[0] && $dropdown.hide();\n }\n\n return $dropdown;\n\n }\n\n return DropdownFactory;\n\n };\n\n })\n\n .directive('bsDropdown', function($window, $sce, $dropdown) {\n\n return {\n restrict: 'EAC',\n scope: true,\n compile: function(tElement, tAttrs) {\n\n // Support for inlined template (next sibling)\n // It must be fetched before compilation\n if (!tAttrs.bsDropdown) {\n var nextSibling = tElement[0].nextSibling;\n while (nextSibling && nextSibling.nodeType !== 1) {\n nextSibling = nextSibling.nextSibling;\n }\n if (nextSibling.classList.contains('dropdown-menu')) {\n tAttrs.template = nextSibling.outerHTML;\n tAttrs.templateUrl = undefined;\n nextSibling.parentNode.removeChild(nextSibling);\n }\n }\n\n return function postLink(scope, element, attr) {\n\n // Directive options\n var options = {scope: scope};\n angular.forEach(['template', 'templateUrl', 'controller', 'controllerAs', 'placement', 'container', 'delay', 'trigger', 'keyboard', 'html', 'animation', 'id', 'autoClose'], function(key) {\n if (angular.isDefined(tAttrs[key])) options[key] = tAttrs[key];\n });\n\n // use string regex match boolean attr falsy values, leave truthy values be\n var falseValueRegExp = /^(false|0|)$/i;\n angular.forEach(['html', 'container'], function(key) {\n if (angular.isDefined(attr[key]) && falseValueRegExp.test(attr[key]))\n options[key] = false;\n });\n\n // Support scope as an object\n attr.bsDropdown && scope.$watch(attr.bsDropdown, function(newValue, oldValue) {\n scope.content = newValue;\n }, true);\n\n // Visibility binding support\n attr.bsShow && scope.$watch(attr.bsShow, function(newValue, oldValue) {\n if (!dropdown || !angular.isDefined(newValue)) return;\n if (angular.isString(newValue)) newValue = !!newValue.match(/true|,?(dropdown),?/i);\n newValue === true ? dropdown.show() : dropdown.hide();\n });\n\n // Initialize dropdown\n var dropdown = $dropdown(element, options);\n\n // Garbage collection\n scope.$on('$destroy', function() {\n if (dropdown) dropdown.destroy();\n options = null;\n dropdown = null;\n });\n\n };\n }\n };\n\n });\n\n// Source: aside/aside.js\nangular.module('mgcrea.ngStrap.aside', ['mgcrea.ngStrap.modal'])\n\n .provider('$aside', function() {\n\n var defaults = this.defaults = {\n animation: 'am-fade-and-slide-right',\n prefixClass: 'aside',\n prefixEvent: 'aside',\n placement: 'right',\n templateUrl: 'aside/aside.tpl.html',\n contentTemplate: false,\n container: false,\n element: null,\n backdrop: true,\n keyboard: true,\n html: false,\n show: true\n };\n\n this.$get = function($modal) {\n\n function AsideFactory(config) {\n\n var $aside = {};\n\n // Common vars\n var options = angular.extend({}, defaults, config);\n\n $aside = $modal(options);\n\n return $aside;\n\n }\n\n return AsideFactory;\n\n };\n\n })\n\n .directive('bsAside', function($window, $sce, $aside) {\n\n var requestAnimationFrame = $window.requestAnimationFrame || $window.setTimeout;\n\n return {\n restrict: 'EAC',\n scope: true,\n link: function postLink(scope, element, attr, transclusion) {\n // Directive options\n var options = {scope: scope, element: element, show: false};\n angular.forEach(['template', 'templateUrl', 'controller', 'controllerAs', 'contentTemplate', 'placement', 'backdrop', 'keyboard', 'html', 'container', 'animation'], function(key) {\n if(angular.isDefined(attr[key])) options[key] = attr[key];\n });\n\n // use string regex match boolean attr falsy values, leave truthy values be\n var falseValueRegExp = /^(false|0|)$/i;\n angular.forEach(['backdrop', 'keyboard', 'html', 'container'], function(key) {\n if(angular.isDefined(attr[key]) && falseValueRegExp.test(attr[key]))\n options[key] = false;\n });\n\n // Support scope as data-attrs\n angular.forEach(['title', 'content'], function(key) {\n attr[key] && attr.$observe(key, function(newValue, oldValue) {\n scope[key] = $sce.trustAsHtml(newValue);\n });\n });\n\n // Support scope as an object\n attr.bsAside && scope.$watch(attr.bsAside, function(newValue, oldValue) {\n if(angular.isObject(newValue)) {\n angular.extend(scope, newValue);\n } else {\n scope.content = newValue;\n }\n }, true);\n\n // Initialize aside\n var aside = $aside(options);\n\n // Trigger\n element.on(attr.trigger || 'click', aside.toggle);\n\n // Garbage collection\n scope.$on('$destroy', function() {\n if (aside) aside.destroy();\n options = null;\n aside = null;\n });\n\n }\n };\n\n });\n\n// Source: datepicker/datepicker.js\nangular.module('mgcrea.ngStrap.datepicker', [\n 'mgcrea.ngStrap.helpers.dateParser',\n 'mgcrea.ngStrap.helpers.dateFormatter',\n 'mgcrea.ngStrap.tooltip'])\n\n .provider('$datepicker', function() {\n\n var defaults = this.defaults = {\n animation: 'am-fade',\n // Uncommenting the following line will break backwards compatability\n // prefixEvent: 'datepicker',\n prefixClass: 'datepicker',\n placement: 'bottom-left',\n templateUrl: 'datepicker/datepicker.tpl.html',\n trigger: 'focus',\n container: false,\n keyboard: true,\n html: false,\n delay: 0,\n // lang: $locale.id,\n useNative: false,\n dateType: 'date',\n dateFormat: 'shortDate',\n timezone: null,\n modelDateFormat: null,\n dayFormat: 'dd',\n monthFormat: 'MMM',\n yearFormat: 'yyyy',\n monthTitleFormat: 'MMMM yyyy',\n yearTitleFormat: 'yyyy',\n strictFormat: false,\n autoclose: false,\n minDate: -Infinity,\n maxDate: +Infinity,\n startView: 0,\n minView: 0,\n startWeek: 0,\n daysOfWeekDisabled: '',\n iconLeft: 'glyphicon glyphicon-chevron-left',\n iconRight: 'glyphicon glyphicon-chevron-right'\n };\n\n this.$get = function($window, $document, $rootScope, $sce, $dateFormatter, datepickerViews, $tooltip, $timeout) {\n\n var isNative = /(ip(a|o)d|iphone|android)/ig.test($window.navigator.userAgent);\n var isTouch = ('createTouch' in $window.document) && isNative;\n if (!defaults.lang) defaults.lang = $dateFormatter.getDefaultLocale();\n\n function DatepickerFactory(element, controller, config) {\n\n var $datepicker = $tooltip(element, angular.extend({}, defaults, config));\n var parentScope = config.scope;\n var options = $datepicker.$options;\n var scope = $datepicker.$scope;\n if (options.startView) options.startView -= options.minView;\n\n // View vars\n\n var pickerViews = datepickerViews($datepicker);\n $datepicker.$views = pickerViews.views;\n var viewDate = pickerViews.viewDate;\n scope.$mode = options.startView;\n scope.$iconLeft = options.iconLeft;\n scope.$iconRight = options.iconRight;\n var $picker = $datepicker.$views[scope.$mode];\n\n // Scope methods\n\n scope.$select = function(date) {\n $datepicker.select(date);\n };\n scope.$selectPane = function(value) {\n $datepicker.$selectPane(value);\n };\n scope.$toggleMode = function() {\n $datepicker.setMode((scope.$mode + 1) % $datepicker.$views.length);\n };\n\n // Public methods\n\n $datepicker.update = function(date) {\n // console.warn('$datepicker.update() newValue=%o', date);\n if (angular.isDate(date) && !isNaN(date.getTime())) {\n $datepicker.$date = date;\n $picker.update.call($picker, date);\n }\n // Build only if pristine\n $datepicker.$build(true);\n };\n\n $datepicker.updateDisabledDates = function(dateRanges) {\n options.disabledDateRanges = dateRanges;\n for (var i = 0, l = scope.rows.length; i < l; i++) {\n angular.forEach(scope.rows[i], $datepicker.$setDisabledEl);\n }\n };\n\n $datepicker.select = function(date, keep) {\n // console.warn('$datepicker.select', date, scope.$mode);\n if (!angular.isDate(controller.$dateValue)) controller.$dateValue = new Date(date);\n if (!scope.$mode || keep) {\n controller.$setViewValue(angular.copy(date));\n controller.$render();\n if (options.autoclose && !keep) {\n $timeout(function() { $datepicker.hide(true); });\n }\n } else {\n angular.extend(viewDate, {year: date.getFullYear(), month: date.getMonth(), date: date.getDate()});\n $datepicker.setMode(scope.$mode - 1);\n $datepicker.$build();\n }\n };\n\n $datepicker.setMode = function(mode) {\n // console.warn('$datepicker.setMode', mode);\n scope.$mode = mode;\n $picker = $datepicker.$views[scope.$mode];\n $datepicker.$build();\n };\n\n // Protected methods\n\n $datepicker.$build = function(pristine) {\n // console.warn('$datepicker.$build() viewDate=%o', viewDate);\n if (pristine === true && $picker.built) return;\n if (pristine === false && !$picker.built) return;\n $picker.build.call($picker);\n };\n\n $datepicker.$updateSelected = function() {\n for (var i = 0, l = scope.rows.length; i < l; i++) {\n angular.forEach(scope.rows[i], updateSelected);\n }\n };\n\n $datepicker.$isSelected = function(date) {\n return $picker.isSelected(date);\n };\n\n $datepicker.$setDisabledEl = function(el) {\n el.disabled = $picker.isDisabled(el.date);\n };\n\n $datepicker.$selectPane = function(value) {\n var steps = $picker.steps;\n // set targetDate to first day of month to avoid problems with\n // date values rollover. This assumes the viewDate does not\n // depend on the day of the month\n var targetDate = new Date(Date.UTC(viewDate.year + ((steps.year || 0) * value), viewDate.month + ((steps.month || 0) * value), 1));\n angular.extend(viewDate, {year: targetDate.getUTCFullYear(), month: targetDate.getUTCMonth(), date: targetDate.getUTCDate()});\n $datepicker.$build();\n };\n\n $datepicker.$onMouseDown = function(evt) {\n // Prevent blur on mousedown on .dropdown-menu\n evt.preventDefault();\n evt.stopPropagation();\n // Emulate click for mobile devices\n if (isTouch) {\n var targetEl = angular.element(evt.target);\n if (targetEl[0].nodeName.toLowerCase() !== 'button') {\n targetEl = targetEl.parent();\n }\n targetEl.triggerHandler('click');\n }\n };\n\n $datepicker.$onKeyDown = function(evt) {\n if (!/(38|37|39|40|13)/.test(evt.keyCode) || evt.shiftKey || evt.altKey) return;\n evt.preventDefault();\n evt.stopPropagation();\n\n if (evt.keyCode === 13) {\n if (!scope.$mode) {\n $datepicker.hide(true);\n } else {\n scope.$apply(function() { $datepicker.setMode(scope.$mode - 1); });\n }\n return;\n }\n\n // Navigate with keyboard\n $picker.onKeyDown(evt);\n parentScope.$digest();\n };\n\n // Private\n\n function updateSelected(el) {\n el.selected = $datepicker.$isSelected(el.date);\n }\n\n function focusElement() {\n element[0].focus();\n }\n\n // Overrides\n\n var _init = $datepicker.init;\n $datepicker.init = function() {\n if (isNative && options.useNative) {\n element.prop('type', 'date');\n element.css('-webkit-appearance', 'textfield');\n return;\n } else if (isTouch) {\n element.prop('type', 'text');\n element.attr('readonly', 'true');\n element.on('click', focusElement);\n }\n _init();\n };\n\n var _destroy = $datepicker.destroy;\n $datepicker.destroy = function() {\n if (isNative && options.useNative) {\n element.off('click', focusElement);\n }\n _destroy();\n };\n\n var _show = $datepicker.show;\n $datepicker.show = function() {\n if ((!isTouch && element.attr('readonly')) || element.attr('disabled')) return;\n _show();\n // use timeout to hookup the events to prevent\n // event bubbling from being processed imediately.\n $timeout(function() {\n // if $datepicker is no longer showing, don't setup events\n if (!$datepicker.$isShown) return;\n $datepicker.$element.on(isTouch ? 'touchstart' : 'mousedown', $datepicker.$onMouseDown);\n if (options.keyboard) {\n element.on('keydown', $datepicker.$onKeyDown);\n }\n }, 0, false);\n };\n\n var _hide = $datepicker.hide;\n $datepicker.hide = function(blur) {\n if (!$datepicker.$isShown) return;\n $datepicker.$element.off(isTouch ? 'touchstart' : 'mousedown', $datepicker.$onMouseDown);\n if (options.keyboard) {\n element.off('keydown', $datepicker.$onKeyDown);\n }\n _hide(blur);\n };\n\n return $datepicker;\n\n }\n\n DatepickerFactory.defaults = defaults;\n return DatepickerFactory;\n\n };\n\n })\n\n .directive('bsDatepicker', function($window, $parse, $q, $dateFormatter, $dateParser, $datepicker) {\n\n var defaults = $datepicker.defaults;\n var isNative = /(ip(a|o)d|iphone|android)/ig.test($window.navigator.userAgent);\n\n return {\n restrict: 'EAC',\n require: 'ngModel',\n link: function postLink(scope, element, attr, controller) {\n\n // Directive options\n var options = {scope: scope};\n 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'], function(key) {\n if (angular.isDefined(attr[key])) options[key] = attr[key];\n });\n\n // use string regex match boolean attr falsy values, leave truthy values be\n var falseValueRegExp = /^(false|0|)$/i;\n angular.forEach(['html', 'container', 'autoclose', 'useNative'], function(key) {\n if (angular.isDefined(attr[key]) && falseValueRegExp.test(attr[key])) {\n options[key] = false;\n }\n });\n\n // Initialize datepicker\n var datepicker = $datepicker(element, controller, options);\n options = datepicker.$options;\n // Set expected iOS format\n if (isNative && options.useNative) options.dateFormat = 'yyyy-MM-dd';\n\n var lang = options.lang;\n\n var formatDate = function(date, format) {\n return $dateFormatter.formatDate(date, format, lang);\n };\n\n var dateParser = $dateParser({format: options.dateFormat, lang: lang, strict: options.strictFormat});\n\n // Visibility binding support\n attr.bsShow && scope.$watch(attr.bsShow, function(newValue, oldValue) {\n if (!datepicker || !angular.isDefined(newValue)) return;\n if (angular.isString(newValue)) newValue = !!newValue.match(/true|,?(datepicker),?/i);\n newValue === true ? datepicker.show() : datepicker.hide();\n });\n\n // Observe attributes for changes\n angular.forEach(['minDate', 'maxDate'], function(key) {\n // console.warn('attr.$observe(%s)', key, attr[key]);\n angular.isDefined(attr[key]) && attr.$observe(key, function(newValue) {\n // console.warn('attr.$observe(%s)=%o', key, newValue);\n datepicker.$options[key] = dateParser.getDateForAttribute(key, newValue);\n // Build only if dirty\n !isNaN(datepicker.$options[key]) && datepicker.$build(false);\n validateAgainstMinMaxDate(controller.$dateValue);\n });\n });\n\n // Observe date format\n angular.isDefined(attr.dateFormat) && attr.$observe('dateFormat', function(newValue) {\n datepicker.$options.dateFormat = newValue;\n });\n\n // Watch model for changes\n scope.$watch(attr.ngModel, function(newValue, oldValue) {\n datepicker.update(controller.$dateValue);\n }, true);\n\n // Normalize undefined/null/empty array,\n // so that we don't treat changing from undefined->null as a change.\n function normalizeDateRanges(ranges) {\n if (!ranges || !ranges.length) return null;\n return ranges;\n }\n\n if (angular.isDefined(attr.disabledDates)) {\n scope.$watch(attr.disabledDates, function(disabledRanges, previousValue) {\n disabledRanges = normalizeDateRanges(disabledRanges);\n previousValue = normalizeDateRanges(previousValue);\n\n if (disabledRanges) {\n datepicker.updateDisabledDates(disabledRanges);\n }\n });\n }\n\n function validateAgainstMinMaxDate(parsedDate) {\n if (!angular.isDate(parsedDate)) return;\n var isMinValid = isNaN(datepicker.$options.minDate) || parsedDate.getTime() >= datepicker.$options.minDate;\n var isMaxValid = isNaN(datepicker.$options.maxDate) || parsedDate.getTime() <= datepicker.$options.maxDate;\n var isValid = isMinValid && isMaxValid;\n controller.$setValidity('date', isValid);\n controller.$setValidity('min', isMinValid);\n controller.$setValidity('max', isMaxValid);\n // Only update the model when we have a valid date\n if (isValid) controller.$dateValue = parsedDate;\n }\n\n // viewValue -> $parsers -> modelValue\n controller.$parsers.unshift(function(viewValue) {\n // console.warn('$parser(\"%s\"): viewValue=%o', element.attr('ng-model'), viewValue);\n var date;\n // Null values should correctly reset the model value & validity\n if (!viewValue) {\n controller.$setValidity('date', true);\n // BREAKING CHANGE:\n // return null (not undefined) when input value is empty, so angularjs 1.3\n // ngModelController can go ahead and run validators, like ngRequired\n return null;\n }\n var parsedDate = dateParser.parse(viewValue, controller.$dateValue);\n if (!parsedDate || isNaN(parsedDate.getTime())) {\n controller.$setValidity('date', false);\n // return undefined, causes ngModelController to\n // invalidate model value\n return;\n } else {\n validateAgainstMinMaxDate(parsedDate);\n }\n\n if (options.dateType === 'string') {\n date = dateParser.timezoneOffsetAdjust(parsedDate, options.timezone, true);\n return formatDate(date, options.modelDateFormat || options.dateFormat);\n }\n date = dateParser.timezoneOffsetAdjust(controller.$dateValue, options.timezone, true);\n if (options.dateType === 'number') {\n return date.getTime();\n } else if (options.dateType === 'unix') {\n return date.getTime() / 1000;\n } else if (options.dateType === 'iso') {\n return date.toISOString();\n } else {\n return new Date(date);\n }\n });\n\n // modelValue -> $formatters -> viewValue\n controller.$formatters.push(function(modelValue) {\n // console.warn('$formatter(\"%s\"): modelValue=%o (%o)', element.attr('ng-model'), modelValue, typeof modelValue);\n var date;\n if (angular.isUndefined(modelValue) || modelValue === null) {\n date = NaN;\n } else if (angular.isDate(modelValue)) {\n date = modelValue;\n } else if (options.dateType === 'string') {\n date = dateParser.parse(modelValue, null, options.modelDateFormat);\n } else if (options.dateType === 'unix') {\n date = new Date(modelValue * 1000);\n } else {\n date = new Date(modelValue);\n }\n // Setup default value?\n // if (isNaN(date.getTime())) {\n // var today = new Date();\n // date = new Date(today.getFullYear(), today.getMonth(), today.getDate(), 0, 0, 0, 0);\n // }\n controller.$dateValue = dateParser.timezoneOffsetAdjust(date, options.timezone);\n return getDateFormattedString();\n });\n\n // viewValue -> element\n controller.$render = function() {\n // console.warn('$render(\"%s\"): viewValue=%o', element.attr('ng-model'), controller.$viewValue);\n element.val(getDateFormattedString());\n };\n\n function getDateFormattedString() {\n return !controller.$dateValue || isNaN(controller.$dateValue.getTime()) ? '' : formatDate(controller.$dateValue, options.dateFormat);\n }\n\n // Garbage collection\n scope.$on('$destroy', function() {\n if (datepicker) datepicker.destroy();\n options = null;\n datepicker = null;\n });\n\n }\n };\n\n })\n\n .provider('datepickerViews', function() {\n\n var defaults = this.defaults = {\n dayFormat: 'dd',\n daySplit: 7\n };\n\n // Split array into smaller arrays\n function split(arr, size) {\n var arrays = [];\n while(arr.length > 0) {\n arrays.push(arr.splice(0, size));\n }\n return arrays;\n }\n\n // Modulus operator\n function mod(n, m) {\n return ((n % m) + m) % m;\n }\n\n this.$get = function($dateFormatter, $dateParser, $sce) {\n\n return function(picker) {\n\n var scope = picker.$scope;\n var options = picker.$options;\n\n var lang = options.lang;\n var formatDate = function(date, format) {\n return $dateFormatter.formatDate(date, format, lang);\n };\n var dateParser = $dateParser({format: options.dateFormat, lang: lang, strict: options.strictFormat});\n\n var weekDaysMin = $dateFormatter.weekdaysShort(lang);\n var weekDaysLabels = weekDaysMin.slice(options.startWeek).concat(weekDaysMin.slice(0, options.startWeek));\n var weekDaysLabelsHtml = $sce.trustAsHtml('
' + weekDaysLabels.join(' | ') + ' | ');\n\n var startDate = picker.$date || (options.startDate ? dateParser.getDateForAttribute('startDate', options.startDate) : new Date());\n var viewDate = {year: startDate.getFullYear(), month: startDate.getMonth(), date: startDate.getDate()};\n\n var views = [{\n format: options.dayFormat,\n split: 7,\n steps: { month: 1 },\n update: function(date, force) {\n if (!this.built || force || date.getFullYear() !== viewDate.year || date.getMonth() !== viewDate.month) {\n angular.extend(viewDate, {year: picker.$date.getFullYear(), month: picker.$date.getMonth(), date: picker.$date.getDate()});\n picker.$build();\n } else if (date.getDate() !== viewDate.date || date.getDate() === 1) {\n // chaging picker current month will cause viewDate.date to be set to first day of the month,\n // in $datepicker.$selectPane, so picker would not update selected day display if\n // user picks first day of the new month.\n // As a workaround, we are always forcing update when picked date is first day of month.\n viewDate.date = picker.$date.getDate();\n picker.$updateSelected();\n }\n },\n build: function() {\n var firstDayOfMonth = new Date(viewDate.year, viewDate.month, 1), firstDayOfMonthOffset = firstDayOfMonth.getTimezoneOffset();\n var firstDate = new Date(+firstDayOfMonth - mod(firstDayOfMonth.getDay() - options.startWeek, 7) * 864e5), firstDateOffset = firstDate.getTimezoneOffset();\n var today = dateParser.timezoneOffsetAdjust(new Date(), options.timezone).toDateString();\n // Handle daylight time switch\n if (firstDateOffset !== firstDayOfMonthOffset) firstDate = new Date(+firstDate + (firstDateOffset - firstDayOfMonthOffset) * 60e3);\n var days = [], day;\n for(var i = 0; i < 42; i++) { // < 7 * 6\n day = dateParser.daylightSavingAdjust(new Date(firstDate.getFullYear(), firstDate.getMonth(), firstDate.getDate() + i));\n days.push({date: day, isToday: day.toDateString() === today, label: formatDate(day, this.format), selected: picker.$date && this.isSelected(day), muted: day.getMonth() !== viewDate.month, disabled: this.isDisabled(day)});\n }\n scope.title = formatDate(firstDayOfMonth, options.monthTitleFormat);\n scope.showLabels = true;\n scope.labels = weekDaysLabelsHtml;\n scope.rows = split(days, this.split);\n this.built = true;\n },\n isSelected: function(date) {\n return picker.$date && date.getFullYear() === picker.$date.getFullYear() && date.getMonth() === picker.$date.getMonth() && date.getDate() === picker.$date.getDate();\n },\n isDisabled: function(date) {\n var time = date.getTime();\n\n // Disabled because of min/max date.\n if (time < options.minDate || time > options.maxDate) return true;\n\n // Disabled due to being a disabled day of the week\n if (options.daysOfWeekDisabled.indexOf(date.getDay()) !== -1) return true;\n\n // Disabled because of disabled date range.\n if (options.disabledDateRanges) {\n for (var i = 0; i < options.disabledDateRanges.length; i++) {\n if (time >= options.disabledDateRanges[i].start && time <= options.disabledDateRanges[i].end) {\n return true;\n }\n }\n }\n\n return false;\n },\n onKeyDown: function(evt) {\n if (!picker.$date) {\n return;\n }\n var actualTime = picker.$date.getTime();\n var newDate;\n\n if (evt.keyCode === 37) newDate = new Date(actualTime - 1 * 864e5);\n else if (evt.keyCode === 38) newDate = new Date(actualTime - 7 * 864e5);\n else if (evt.keyCode === 39) newDate = new Date(actualTime + 1 * 864e5);\n else if (evt.keyCode === 40) newDate = new Date(actualTime + 7 * 864e5);\n\n if (!this.isDisabled(newDate)) picker.select(newDate, true);\n }\n }, {\n name: 'month',\n format: options.monthFormat,\n split: 4,\n steps: { year: 1 },\n update: function(date, force) {\n if (!this.built || date.getFullYear() !== viewDate.year) {\n angular.extend(viewDate, {year: picker.$date.getFullYear(), month: picker.$date.getMonth(), date: picker.$date.getDate()});\n picker.$build();\n } else if (date.getMonth() !== viewDate.month) {\n angular.extend(viewDate, {month: picker.$date.getMonth(), date: picker.$date.getDate()});\n picker.$updateSelected();\n }\n },\n build: function() {\n var firstMonth = new Date(viewDate.year, 0, 1);\n var months = [], month;\n for (var i = 0; i < 12; i++) {\n month = new Date(viewDate.year, i, 1);\n months.push({date: month, label: formatDate(month, this.format), selected: picker.$isSelected(month), disabled: this.isDisabled(month)});\n }\n scope.title = formatDate(month, options.yearTitleFormat);\n scope.showLabels = false;\n scope.rows = split(months, this.split);\n this.built = true;\n },\n isSelected: function(date) {\n return picker.$date && date.getFullYear() === picker.$date.getFullYear() && date.getMonth() === picker.$date.getMonth();\n },\n isDisabled: function(date) {\n var lastDate = +new Date(date.getFullYear(), date.getMonth() + 1, 0);\n return lastDate < options.minDate || date.getTime() > options.maxDate;\n },\n onKeyDown: function(evt) {\n if (!picker.$date) {\n return;\n }\n var actualMonth = picker.$date.getMonth();\n var newDate = new Date(picker.$date);\n\n if (evt.keyCode === 37) newDate.setMonth(actualMonth - 1);\n else if (evt.keyCode === 38) newDate.setMonth(actualMonth - 4);\n else if (evt.keyCode === 39) newDate.setMonth(actualMonth + 1);\n else if (evt.keyCode === 40) newDate.setMonth(actualMonth + 4);\n\n if (!this.isDisabled(newDate)) picker.select(newDate, true);\n }\n }, {\n name: 'year',\n format: options.yearFormat,\n split: 4,\n steps: { year: 12 },\n update: function(date, force) {\n if (!this.built || force || parseInt(date.getFullYear()/20, 10) !== parseInt(viewDate.year/20, 10)) {\n angular.extend(viewDate, {year: picker.$date.getFullYear(), month: picker.$date.getMonth(), date: picker.$date.getDate()});\n picker.$build();\n } else if (date.getFullYear() !== viewDate.year) {\n angular.extend(viewDate, {year: picker.$date.getFullYear(), month: picker.$date.getMonth(), date: picker.$date.getDate()});\n picker.$updateSelected();\n }\n },\n build: function() {\n var firstYear = viewDate.year - viewDate.year % (this.split * 3);\n var years = [], year;\n for (var i = 0; i < 12; i++) {\n year = new Date(firstYear + i, 0, 1);\n years.push({date: year, label: formatDate(year, this.format), selected: picker.$isSelected(year), disabled: this.isDisabled(year)});\n }\n scope.title = years[0].label + '-' + years[years.length - 1].label;\n scope.showLabels = false;\n scope.rows = split(years, this.split);\n this.built = true;\n },\n isSelected: function(date) {\n return picker.$date && date.getFullYear() === picker.$date.getFullYear();\n },\n isDisabled: function(date) {\n var lastDate = +new Date(date.getFullYear() + 1, 0, 0);\n return lastDate < options.minDate || date.getTime() > options.maxDate;\n },\n onKeyDown: function(evt) {\n if (!picker.$date) {\n return;\n }\n var actualYear = picker.$date.getFullYear(),\n newDate = new Date(picker.$date);\n\n if (evt.keyCode === 37) newDate.setYear(actualYear - 1);\n else if (evt.keyCode === 38) newDate.setYear(actualYear - 4);\n else if (evt.keyCode === 39) newDate.setYear(actualYear + 1);\n else if (evt.keyCode === 40) newDate.setYear(actualYear + 4);\n\n if (!this.isDisabled(newDate)) picker.select(newDate, true);\n }\n }];\n\n return {\n views: options.minView ? Array.prototype.slice.call(views, options.minView) : views,\n viewDate: viewDate\n };\n\n };\n\n };\n\n });\n\n// Source: collapse/collapse.js\nangular.module('mgcrea.ngStrap.collapse', [])\n\n .provider('$collapse', function() {\n\n var defaults = this.defaults = {\n animation: 'am-collapse',\n disallowToggle: false,\n activeClass: 'in',\n startCollapsed: false,\n allowMultiple: false\n };\n\n var controller = this.controller = function($scope, $element, $attrs) {\n var self = this;\n\n // Attributes options\n self.$options = angular.copy(defaults);\n angular.forEach(['animation', 'disallowToggle', 'activeClass', 'startCollapsed', 'allowMultiple'], function (key) {\n if(angular.isDefined($attrs[key])) self.$options[key] = $attrs[key];\n });\n\n // use string regex match boolean attr falsy values, leave truthy values be\n var falseValueRegExp = /^(false|0|)$/i;\n angular.forEach(['disallowToggle', 'startCollapsed', 'allowMultiple'], function(key) {\n if(angular.isDefined($attrs[key]) && falseValueRegExp.test($attrs[key])) {\n self.$options[key] = false;\n }\n });\n\n self.$toggles = [];\n self.$targets = [];\n\n self.$viewChangeListeners = [];\n\n self.$registerToggle = function(element) {\n self.$toggles.push(element);\n };\n self.$registerTarget = function(element) {\n self.$targets.push(element);\n };\n\n self.$unregisterToggle = function(element) {\n var index = self.$toggles.indexOf(element);\n // remove toggle from $toggles array\n self.$toggles.splice(index, 1);\n };\n self.$unregisterTarget = function(element) {\n var index = self.$targets.indexOf(element);\n\n // remove element from $targets array\n self.$targets.splice(index, 1);\n\n if (self.$options.allowMultiple) {\n // remove target index from $active array values\n deactivateItem(element);\n }\n\n // fix active item indexes\n fixActiveItemIndexes(index);\n\n self.$viewChangeListeners.forEach(function(fn) {\n fn();\n });\n };\n\n // use array to store all the currently open panels\n self.$targets.$active = !self.$options.startCollapsed ? [0] : [];\n self.$setActive = $scope.$setActive = function(value) {\n if(angular.isArray(value)) {\n self.$targets.$active = value;\n }\n else if(!self.$options.disallowToggle) {\n // toogle element active status\n isActive(value) ? deactivateItem(value) : activateItem(value);\n } else {\n activateItem(value);\n }\n\n self.$viewChangeListeners.forEach(function(fn) {\n fn();\n });\n };\n\n self.$activeIndexes = function() {\n return self.$options.allowMultiple ? self.$targets.$active :\n self.$targets.$active.length === 1 ? self.$targets.$active[0] : -1;\n };\n\n function fixActiveItemIndexes(index) {\n // item with index was removed, so we\n // need to adjust other items index values\n var activeIndexes = self.$targets.$active;\n for(var i = 0; i < activeIndexes.length; i++) {\n if (index < activeIndexes[i]) {\n activeIndexes[i] = activeIndexes[i] - 1;\n }\n\n // the last item is active, so we need to\n // adjust its index\n if (activeIndexes[i] === self.$targets.length) {\n activeIndexes[i] = self.$targets.length - 1;\n }\n }\n }\n\n function isActive(value) {\n var activeItems = self.$targets.$active;\n return activeItems.indexOf(value) === -1 ? false : true;\n }\n\n function deactivateItem(value) {\n var index = self.$targets.$active.indexOf(value);\n if (index !== -1) {\n self.$targets.$active.splice(index, 1);\n }\n }\n\n function activateItem(value) {\n if (!self.$options.allowMultiple) {\n // remove current selected item\n self.$targets.$active.splice(0, 1);\n }\n\n if (self.$targets.$active.indexOf(value) === -1) {\n self.$targets.$active.push(value);\n }\n }\n\n };\n\n this.$get = function() {\n var $collapse = {};\n $collapse.defaults = defaults;\n $collapse.controller = controller;\n return $collapse;\n };\n\n })\n\n .directive('bsCollapse', function($window, $animate, $collapse) {\n\n var defaults = $collapse.defaults;\n\n return {\n require: ['?ngModel', 'bsCollapse'],\n controller: ['$scope', '$element', '$attrs', $collapse.controller],\n link: function postLink(scope, element, attrs, controllers) {\n\n var ngModelCtrl = controllers[0];\n var bsCollapseCtrl = controllers[1];\n\n if(ngModelCtrl) {\n\n // Update the modelValue following\n bsCollapseCtrl.$viewChangeListeners.push(function() {\n ngModelCtrl.$setViewValue(bsCollapseCtrl.$activeIndexes());\n });\n\n // modelValue -> $formatters -> viewValue\n ngModelCtrl.$formatters.push(function(modelValue) {\n // console.warn('$formatter(\"%s\"): modelValue=%o (%o)', element.attr('ng-model'), modelValue, typeof modelValue);\n if (angular.isArray(modelValue)) {\n // model value is an array, so just replace\n // the active items directly\n bsCollapseCtrl.$setActive(modelValue);\n }\n else {\n var activeIndexes = bsCollapseCtrl.$activeIndexes();\n\n if (angular.isArray(activeIndexes)) {\n // we have an array of selected indexes\n if (activeIndexes.indexOf(modelValue * 1) === -1) {\n // item with modelValue index is not active\n bsCollapseCtrl.$setActive(modelValue * 1);\n }\n }\n else if (activeIndexes !== modelValue * 1) {\n bsCollapseCtrl.$setActive(modelValue * 1);\n }\n }\n return modelValue;\n });\n\n }\n\n }\n };\n\n })\n\n .directive('bsCollapseToggle', function() {\n\n return {\n require: ['^?ngModel', '^bsCollapse'],\n link: function postLink(scope, element, attrs, controllers) {\n\n var ngModelCtrl = controllers[0];\n var bsCollapseCtrl = controllers[1];\n\n // Add base attr\n element.attr('data-toggle', 'collapse');\n\n // Push pane to parent bsCollapse controller\n bsCollapseCtrl.$registerToggle(element);\n\n // remove toggle from collapse controller when toggle is destroyed\n scope.$on('$destroy', function() {\n bsCollapseCtrl.$unregisterToggle(element);\n });\n\n element.on('click', function() {\n var index = attrs.bsCollapseToggle && attrs.bsCollapseToggle !== 'bs-collapse-toggle' ? attrs.bsCollapseToggle : bsCollapseCtrl.$toggles.indexOf(element);\n bsCollapseCtrl.$setActive(index * 1);\n scope.$apply();\n });\n\n }\n };\n\n })\n\n .directive('bsCollapseTarget', function($animate) {\n\n return {\n require: ['^?ngModel', '^bsCollapse'],\n // scope: true,\n link: function postLink(scope, element, attrs, controllers) {\n\n var ngModelCtrl = controllers[0];\n var bsCollapseCtrl = controllers[1];\n\n // Add base class\n element.addClass('collapse');\n\n // Add animation class\n if(bsCollapseCtrl.$options.animation) {\n element.addClass(bsCollapseCtrl.$options.animation);\n }\n\n // Push pane to parent bsCollapse controller\n bsCollapseCtrl.$registerTarget(element);\n\n // remove pane target from collapse controller when target is destroyed\n scope.$on('$destroy', function() {\n bsCollapseCtrl.$unregisterTarget(element);\n });\n\n function render() {\n var index = bsCollapseCtrl.$targets.indexOf(element);\n var active = bsCollapseCtrl.$activeIndexes();\n var action = 'removeClass';\n if (angular.isArray(active)) {\n if (active.indexOf(index) !== -1) {\n action = 'addClass';\n }\n }\n else if (index === active) {\n action = 'addClass';\n }\n\n $animate[action](element, bsCollapseCtrl.$options.activeClass);\n }\n\n bsCollapseCtrl.$viewChangeListeners.push(function() {\n render();\n });\n render();\n\n }\n };\n\n });\n\n// Source: button/button.js\nangular.module('mgcrea.ngStrap.button', [])\n\n .provider('$button', function() {\n\n var defaults = this.defaults = {\n activeClass:'active',\n toggleEvent:'click'\n };\n\n this.$get = function() {\n return {defaults: defaults};\n };\n\n })\n\n .directive('bsCheckboxGroup', function() {\n\n return {\n restrict: 'A',\n require: 'ngModel',\n compile: function postLink(element, attr) {\n element.attr('data-toggle', 'buttons');\n element.removeAttr('ng-model');\n var children = element[0].querySelectorAll('input[type=\"checkbox\"]');\n angular.forEach(children, function(child) {\n var childEl = angular.element(child);\n childEl.attr('bs-checkbox', '');\n childEl.attr('ng-model', attr.ngModel + '.' + childEl.attr('value'));\n });\n }\n\n };\n\n })\n\n .directive('bsCheckbox', function($button, $$rAF) {\n\n var defaults = $button.defaults;\n var constantValueRegExp = /^(true|false|\\d+)$/;\n\n return {\n restrict: 'A',\n require: 'ngModel',\n link: function postLink(scope, element, attr, controller) {\n\n var options = defaults;\n\n // Support label > input[type=\"checkbox\"]\n var isInput = element[0].nodeName === 'INPUT';\n var activeElement = isInput ? element.parent() : element;\n\n var trueValue = angular.isDefined(attr.trueValue) ? attr.trueValue : true;\n if(constantValueRegExp.test(attr.trueValue)) {\n trueValue = scope.$eval(attr.trueValue);\n }\n var falseValue = angular.isDefined(attr.falseValue) ? attr.falseValue : false;\n if(constantValueRegExp.test(attr.falseValue)) {\n falseValue = scope.$eval(attr.falseValue);\n }\n\n // Parse exotic values\n var hasExoticValues = typeof trueValue !== 'boolean' || typeof falseValue !== 'boolean';\n if(hasExoticValues) {\n controller.$parsers.push(function(viewValue) {\n // console.warn('$parser', element.attr('ng-model'), 'viewValue', viewValue);\n return viewValue ? trueValue : falseValue;\n });\n // modelValue -> $formatters -> viewValue\n controller.$formatters.push(function(modelValue) {\n // console.warn('$formatter(\"%s\"): modelValue=%o (%o)', element.attr('ng-model'), modelValue, typeof modelValue);\n return angular.equals(modelValue, trueValue);\n });\n // Fix rendering for exotic values\n scope.$watch(attr.ngModel, function(newValue, oldValue) {\n controller.$render();\n });\n }\n\n // model -> view\n controller.$render = function () {\n // console.warn('$render', element.attr('ng-model'), 'controller.$modelValue', typeof controller.$modelValue, controller.$modelValue, 'controller.$viewValue', typeof controller.$viewValue, controller.$viewValue);\n var isActive = angular.equals(controller.$modelValue, trueValue);\n $$rAF(function() {\n if(isInput) element[0].checked = isActive;\n activeElement.toggleClass(options.activeClass, isActive);\n });\n };\n\n // view -> model\n element.bind(options.toggleEvent, function() {\n scope.$apply(function () {\n // console.warn('!click', element.attr('ng-model'), 'controller.$viewValue', typeof controller.$viewValue, controller.$viewValue, 'controller.$modelValue', typeof controller.$modelValue, controller.$modelValue);\n if(!isInput) {\n controller.$setViewValue(!activeElement.hasClass('active'));\n }\n if(!hasExoticValues) {\n controller.$render();\n }\n });\n });\n\n }\n\n };\n\n })\n\n .directive('bsRadioGroup', function() {\n\n return {\n restrict: 'A',\n require: 'ngModel',\n compile: function postLink(element, attr) {\n element.attr('data-toggle', 'buttons');\n element.removeAttr('ng-model');\n var children = element[0].querySelectorAll('input[type=\"radio\"]');\n angular.forEach(children, function(child) {\n angular.element(child).attr('bs-radio', '');\n angular.element(child).attr('ng-model', attr.ngModel);\n });\n }\n\n };\n\n })\n\n .directive('bsRadio', function($button, $$rAF) {\n\n var defaults = $button.defaults;\n var constantValueRegExp = /^(true|false|\\d+)$/;\n\n return {\n restrict: 'A',\n require: 'ngModel',\n link: function postLink(scope, element, attr, controller) {\n\n var options = defaults;\n\n // Support `label > input[type=\"radio\"]` markup\n var isInput = element[0].nodeName === 'INPUT';\n var activeElement = isInput ? element.parent() : element;\n\n var value;\n attr.$observe('value', function(v) {\n value = constantValueRegExp.test(v) ? scope.$eval(v) : v;\n controller.$render();\n });\n\n // model -> view\n controller.$render = function () {\n // console.warn('$render', element.attr('value'), 'controller.$modelValue', typeof controller.$modelValue, controller.$modelValue, 'controller.$viewValue', typeof controller.$viewValue, controller.$viewValue);\n var isActive = angular.equals(controller.$modelValue, value);\n $$rAF(function() {\n if(isInput) element[0].checked = isActive;\n activeElement.toggleClass(options.activeClass, isActive);\n });\n };\n\n // view -> model\n element.bind(options.toggleEvent, function() {\n scope.$apply(function () {\n // console.warn('!click', element.attr('value'), 'controller.$viewValue', typeof controller.$viewValue, controller.$viewValue, 'controller.$modelValue', typeof controller.$modelValue, controller.$modelValue);\n controller.$setViewValue(value);\n controller.$render();\n });\n });\n\n }\n\n };\n\n });\n\n// Source: alert/alert.js\n// @BUG: following snippet won't compile correctly\n// @TODO: submit issue to core\n// '
' +\n\nangular.module('mgcrea.ngStrap.alert', ['mgcrea.ngStrap.modal'])\n\n .provider('$alert', function() {\n\n var defaults = this.defaults = {\n animation: 'am-fade',\n prefixClass: 'alert',\n prefixEvent: 'alert',\n placement: null,\n templateUrl: 'alert/alert.tpl.html',\n container: false,\n element: null,\n backdrop: false,\n keyboard: true,\n show: true,\n // Specific options\n duration: false,\n type: false,\n dismissable: true\n };\n\n this.$get = function($modal, $timeout) {\n\n function AlertFactory(config) {\n\n var $alert = {};\n\n // Common vars\n var options = angular.extend({}, defaults, config);\n\n $alert = $modal(options);\n\n // Support scope as string options [/*title, content, */ type, dismissable]\n $alert.$scope.dismissable = !!options.dismissable;\n if(options.type) {\n $alert.$scope.type = options.type;\n }\n\n // Support auto-close duration\n var show = $alert.show;\n if(options.duration) {\n $alert.show = function() {\n show();\n $timeout(function() {\n $alert.hide();\n }, options.duration * 1000);\n };\n }\n\n return $alert;\n\n }\n\n return AlertFactory;\n\n };\n\n })\n\n .directive('bsAlert', function($window, $sce, $alert) {\n\n var requestAnimationFrame = $window.requestAnimationFrame || $window.setTimeout;\n\n return {\n restrict: 'EAC',\n scope: true,\n link: function postLink(scope, element, attr, transclusion) {\n\n // Directive options\n var options = {scope: scope, element: element, show: false};\n angular.forEach(['template', 'templateUrl', 'controller', 'controllerAs', 'placement', 'keyboard', 'html', 'container', 'animation', 'duration', 'dismissable'], function(key) {\n if(angular.isDefined(attr[key])) options[key] = attr[key];\n });\n\n // use string regex match boolean attr falsy values, leave truthy values be\n var falseValueRegExp = /^(false|0|)$/i;\n angular.forEach(['keyboard', 'html', 'container', 'dismissable'], function(key) {\n if(angular.isDefined(attr[key]) && falseValueRegExp.test(attr[key]))\n options[key] = false;\n });\n\n // overwrite inherited title value when no value specified\n // fix for angular 1.3.1 531a8de72c439d8ddd064874bf364c00cedabb11\n if (!scope.hasOwnProperty('title')){\n scope.title = '';\n }\n\n // Support scope as data-attrs\n angular.forEach(['title', 'content', 'type'], function(key) {\n attr[key] && attr.$observe(key, function(newValue, oldValue) {\n scope[key] = $sce.trustAsHtml(newValue);\n });\n });\n\n // Support scope as an object\n attr.bsAlert && scope.$watch(attr.bsAlert, function(newValue, oldValue) {\n if(angular.isObject(newValue)) {\n angular.extend(scope, newValue);\n } else {\n scope.content = newValue;\n }\n }, true);\n\n // Initialize alert\n var alert = $alert(options);\n\n // Trigger\n element.on(attr.trigger || 'click', alert.toggle);\n\n // Garbage collection\n scope.$on('$destroy', function() {\n if (alert) alert.destroy();\n options = null;\n alert = null;\n });\n\n }\n };\n\n });\n\n// Source: affix/affix.js\nangular.module('mgcrea.ngStrap.affix', ['mgcrea.ngStrap.helpers.dimensions', 'mgcrea.ngStrap.helpers.debounce'])\n\n .provider('$affix', function() {\n\n var defaults = this.defaults = {\n offsetTop: 'auto',\n inlineStyles: true\n };\n\n this.$get = function($window, debounce, dimensions) {\n\n var bodyEl = angular.element($window.document.body);\n var windowEl = angular.element($window);\n\n function AffixFactory(element, config) {\n\n var $affix = {};\n\n // Common vars\n var options = angular.extend({}, defaults, config);\n var targetEl = options.target;\n\n // Initial private vars\n var reset = 'affix affix-top affix-bottom',\n setWidth = false,\n initialAffixTop = 0,\n initialOffsetTop = 0,\n offsetTop = 0,\n offsetBottom = 0,\n affixed = null,\n unpin = null;\n\n var parent = element.parent();\n // Options: custom parent\n if (options.offsetParent) {\n if (options.offsetParent.match(/^\\d+$/)) {\n for (var i = 0; i < (options.offsetParent * 1) - 1; i++) {\n parent = parent.parent();\n }\n }\n else {\n parent = angular.element(options.offsetParent);\n }\n }\n\n $affix.init = function() {\n\n this.$parseOffsets();\n initialOffsetTop = dimensions.offset(element[0]).top + initialAffixTop;\n setWidth = !element[0].style.width;\n\n // Bind events\n targetEl.on('scroll', this.checkPosition);\n targetEl.on('click', this.checkPositionWithEventLoop);\n windowEl.on('resize', this.$debouncedOnResize);\n\n // Both of these checkPosition() calls are necessary for the case where\n // the user hits refresh after scrolling to the bottom of the page.\n this.checkPosition();\n this.checkPositionWithEventLoop();\n\n };\n\n $affix.destroy = function() {\n\n // Unbind events\n targetEl.off('scroll', this.checkPosition);\n targetEl.off('click', this.checkPositionWithEventLoop);\n windowEl.off('resize', this.$debouncedOnResize);\n\n };\n\n $affix.checkPositionWithEventLoop = function() {\n\n // IE 9 throws an error if we use 'this' instead of '$affix'\n // in this setTimeout call\n setTimeout($affix.checkPosition, 1);\n\n };\n\n $affix.checkPosition = function() {\n // if (!this.$element.is(':visible')) return\n\n var scrollTop = getScrollTop();\n var position = dimensions.offset(element[0]);\n var elementHeight = dimensions.height(element[0]);\n\n // Get required affix class according to position\n var affix = getRequiredAffixClass(unpin, position, elementHeight);\n\n // Did affix status changed this last check?\n if(affixed === affix) return;\n affixed = affix;\n\n if(affix === 'top') {\n unpin = null;\n if(setWidth) {\n element.css('width', '');\n }\n if (options.inlineStyles) {\n element.css('position', (options.offsetParent) ? '' : 'relative');\n element.css('top', '');\n }\n } else if(affix === 'bottom') {\n if (options.offsetUnpin) {\n unpin = -(options.offsetUnpin * 1);\n }\n else {\n // Calculate unpin threshold when affixed to bottom.\n // Hopefully the browser scrolls pixel by pixel.\n unpin = position.top - scrollTop;\n }\n if(setWidth) {\n element.css('width', '');\n }\n if (options.inlineStyles) {\n element.css('position', (options.offsetParent) ? '' : 'relative');\n element.css('top', (options.offsetParent) ? '' : ((bodyEl[0].offsetHeight - offsetBottom - elementHeight - initialOffsetTop) + 'px'));\n }\n } else { // affix === 'middle'\n unpin = null;\n if(setWidth) {\n element.css('width', element[0].offsetWidth + 'px');\n }\n if (options.inlineStyles) {\n element.css('position', 'fixed');\n element.css('top', initialAffixTop + 'px');\n }\n }\n\n // Add proper affix class\n element.removeClass(reset).addClass('affix' + ((affix !== 'middle') ? '-' + affix : ''));\n\n };\n\n $affix.$onResize = function() {\n $affix.$parseOffsets();\n $affix.checkPosition();\n };\n $affix.$debouncedOnResize = debounce($affix.$onResize, 50);\n\n $affix.$parseOffsets = function() {\n var initialPosition = element.css('position');\n // Reset position to calculate correct offsetTop\n if (options.inlineStyles){\n element.css('position', (options.offsetParent) ? '' : 'relative');\n }\n\n if(options.offsetTop) {\n if(options.offsetTop === 'auto') {\n options.offsetTop = '+0';\n }\n if(options.offsetTop.match(/^[-+]\\d+$/)) {\n initialAffixTop = - options.offsetTop * 1;\n if(options.offsetParent) {\n offsetTop = dimensions.offset(parent[0]).top + (options.offsetTop * 1);\n }\n else {\n offsetTop = dimensions.offset(element[0]).top - dimensions.css(element[0], 'marginTop', true) + (options.offsetTop * 1);\n }\n }\n else {\n offsetTop = options.offsetTop * 1;\n }\n }\n\n if(options.offsetBottom) {\n if(options.offsetParent && options.offsetBottom.match(/^[-+]\\d+$/)) {\n // add 1 pixel due to rounding problems...\n offsetBottom = getScrollHeight() - (dimensions.offset(parent[0]).top + dimensions.height(parent[0])) + (options.offsetBottom * 1) + 1;\n }\n else {\n offsetBottom = options.offsetBottom * 1;\n }\n }\n\n // Bring back the element's position after calculations\n if (options.inlineStyles){\n element.css('position', initialPosition);\n }\n };\n\n // Private methods\n\n function getRequiredAffixClass(unpin, position, elementHeight) {\n\n var scrollTop = getScrollTop();\n var scrollHeight = getScrollHeight();\n\n if(scrollTop <= offsetTop) {\n return 'top';\n } else if(unpin !== null && (scrollTop + unpin <= position.top)) {\n return 'middle';\n } else if(offsetBottom !== null && (position.top + elementHeight + initialAffixTop >= scrollHeight - offsetBottom)) {\n return 'bottom';\n } else {\n return 'middle';\n }\n\n }\n\n function getScrollTop() {\n return targetEl[0] === $window ? $window.pageYOffset : targetEl[0].scrollTop;\n }\n\n function getScrollHeight() {\n return targetEl[0] === $window ? $window.document.body.scrollHeight : targetEl[0].scrollHeight;\n }\n\n $affix.init();\n return $affix;\n\n }\n\n return AffixFactory;\n\n };\n\n })\n\n .directive('bsAffix', function($affix, $window) {\n\n return {\n restrict: 'EAC',\n require: '^?bsAffixTarget',\n link: function postLink(scope, element, attr, affixTarget) {\n\n var options = {scope: scope, target: affixTarget ? affixTarget.$element : angular.element($window)};\n angular.forEach(['offsetTop', 'offsetBottom', 'offsetParent', 'offsetUnpin', 'inlineStyles'], function(key) {\n if(angular.isDefined(attr[key])) {\n var option = attr[key];\n if (/true/i.test(option)) option = true;\n if (/false/i.test(option)) option = false;\n options[key] = option;\n }\n });\n\n var affix = $affix(element, options);\n scope.$on('$destroy', function() {\n affix && affix.destroy();\n options = null;\n affix = null;\n });\n\n }\n };\n\n })\n\n .directive('bsAffixTarget', function() {\n return {\n controller: function($element) {\n this.$element = $element;\n }\n };\n });\n\n// Source: module.js\nangular.module('mgcrea.ngStrap', [\n 'mgcrea.ngStrap.modal',\n 'mgcrea.ngStrap.aside',\n 'mgcrea.ngStrap.alert',\n 'mgcrea.ngStrap.button',\n 'mgcrea.ngStrap.select',\n 'mgcrea.ngStrap.datepicker',\n 'mgcrea.ngStrap.timepicker',\n 'mgcrea.ngStrap.navbar',\n 'mgcrea.ngStrap.tooltip',\n 'mgcrea.ngStrap.popover',\n 'mgcrea.ngStrap.dropdown',\n 'mgcrea.ngStrap.typeahead',\n 'mgcrea.ngStrap.scrollspy',\n 'mgcrea.ngStrap.affix',\n 'mgcrea.ngStrap.tab',\n 'mgcrea.ngStrap.collapse'\n]);\n\n})(window, document);\n","'use strict';\n\nangular.module('mgcrea.ngStrap.tooltip', ['mgcrea.ngStrap.core', 'mgcrea.ngStrap.helpers.dimensions'])\n\n .provider('$tooltip', function() {\n\n var defaults = this.defaults = {\n animation: 'am-fade',\n customClass: '',\n prefixClass: 'tooltip',\n prefixEvent: 'tooltip',\n container: false,\n target: false,\n placement: 'top',\n templateUrl: 'tooltip/tooltip.tpl.html',\n template: '',\n contentTemplate: false,\n trigger: 'hover focus',\n keyboard: false,\n html: false,\n show: false,\n title: '',\n type: '',\n delay: 0,\n autoClose: false,\n bsEnabled: true,\n viewport: {\n selector: 'body',\n padding: 0\n }\n };\n\n this.$get = function($window, $rootScope, $bsCompiler, $q, $templateCache, $http, $animate, $sce, dimensions, $$rAF, $timeout) {\n\n var trim = String.prototype.trim;\n var isTouch = 'createTouch' in $window.document;\n var htmlReplaceRegExp = /ng-bind=\"/ig;\n var $body = angular.element($window.document);\n\n function TooltipFactory(element, config) {\n\n var $tooltip = {};\n\n // Common vars\n var options = $tooltip.$options = angular.extend({}, defaults, config);\n var promise = $tooltip.$promise = $bsCompiler.compile(options);\n var scope = $tooltip.$scope = options.scope && options.scope.$new() || $rootScope.$new();\n\n var nodeName = element[0].nodeName.toLowerCase();\n if(options.delay && angular.isString(options.delay)) {\n var split = options.delay.split(',').map(parseFloat);\n options.delay = split.length > 1 ? {show: split[0], hide: split[1]} : split[0];\n }\n\n // Store $id to identify the triggering element in events\n // give priority to options.id, otherwise, try to use\n // element id if defined\n $tooltip.$id = options.id || element.attr('id') || '';\n\n // Support scope as string options\n if(options.title) {\n scope.title = $sce.trustAsHtml(options.title);\n }\n\n // Provide scope helpers\n scope.$setEnabled = function(isEnabled) {\n scope.$$postDigest(function() {\n $tooltip.setEnabled(isEnabled);\n });\n };\n scope.$hide = function() {\n scope.$$postDigest(function() {\n $tooltip.hide();\n });\n };\n scope.$show = function() {\n scope.$$postDigest(function() {\n $tooltip.show();\n });\n };\n scope.$toggle = function() {\n scope.$$postDigest(function() {\n $tooltip.toggle();\n });\n };\n // Publish isShown as a protected var on scope\n $tooltip.$isShown = scope.$isShown = false;\n\n // Private vars\n var timeout, hoverState;\n\n // Fetch, compile then initialize tooltip\n var compileData, tipElement, tipContainer, tipScope;\n promise.then(function(data) {\n compileData = data;\n $tooltip.init();\n });\n\n $tooltip.init = function() {\n\n // Options: delay\n if (options.delay && angular.isNumber(options.delay)) {\n options.delay = {\n show: options.delay,\n hide: options.delay\n };\n }\n\n // Replace trigger on touch devices ?\n // if(isTouch && options.trigger === defaults.trigger) {\n // options.trigger.replace(/hover/g, 'click');\n // }\n\n // Options : container\n if(options.container === 'self') {\n tipContainer = element;\n } else if(angular.isElement(options.container)) {\n tipContainer = options.container;\n } else if(options.container) {\n tipContainer = findElement(options.container);\n }\n\n // Options: trigger\n bindTriggerEvents();\n\n // Options: target\n if(options.target) {\n options.target = angular.isElement(options.target) ? options.target : findElement(options.target);\n }\n\n // Options: show\n if(options.show) {\n scope.$$postDigest(function() {\n options.trigger === 'focus' ? element[0].focus() : $tooltip.show();\n });\n }\n\n };\n\n $tooltip.destroy = function() {\n\n // Unbind events\n unbindTriggerEvents();\n\n // Remove element\n destroyTipElement();\n\n // Destroy scope\n scope.$destroy();\n\n };\n\n $tooltip.enter = function() {\n\n clearTimeout(timeout);\n hoverState = 'in';\n if (!options.delay || !options.delay.show) {\n return $tooltip.show();\n }\n\n timeout = setTimeout(function() {\n if (hoverState ==='in') $tooltip.show();\n }, options.delay.show);\n\n };\n\n $tooltip.show = function() {\n if (!options.bsEnabled || $tooltip.$isShown) return;\n\n scope.$emit(options.prefixEvent + '.show.before', $tooltip);\n var parent, after;\n if (options.container) {\n parent = tipContainer;\n if (tipContainer[0].lastChild) {\n after = angular.element(tipContainer[0].lastChild);\n } else {\n after = null;\n }\n } else {\n parent = null;\n after = element;\n }\n\n\n // Hide any existing tipElement\n if(tipElement) destroyTipElement();\n // Fetch a cloned element linked from template\n tipScope = $tooltip.$scope.$new();\n tipElement = $tooltip.$element = compileData.link(tipScope, function(clonedElement, scope) {});\n\n // Set the initial positioning. Make the tooltip invisible\n // so IE doesn't try to focus on it off screen.\n tipElement.css({top: '-9999px', left: '-9999px', right: 'auto', display: 'block', visibility: 'hidden'});\n\n // Options: animation\n if(options.animation) tipElement.addClass(options.animation);\n // Options: type\n if(options.type) tipElement.addClass(options.prefixClass + '-' + options.type);\n // Options: custom classes\n if(options.customClass) tipElement.addClass(options.customClass);\n\n // Append the element, without any animations. If we append\n // using $animate.enter, some of the animations cause the placement\n // to be off due to the transforms.\n after ? after.after(tipElement) : parent.prepend(tipElement);\n\n $tooltip.$isShown = scope.$isShown = true;\n safeDigest(scope);\n\n // Now, apply placement\n $tooltip.$applyPlacement();\n\n // Once placed, animate it.\n // Support v1.2+ $animate\n // https://github.com/angular/angular.js/issues/11713\n if(angular.version.minor <= 2) {\n $animate.enter(tipElement, parent, after, enterAnimateCallback);\n } else {\n $animate.enter(tipElement, parent, after).then(enterAnimateCallback);\n }\n safeDigest(scope);\n\n $$rAF(function () {\n // Once the tooltip is placed and the animation starts, make the tooltip visible\n if(tipElement) tipElement.css({visibility: 'visible'});\n\n // Bind events\n if(options.keyboard) {\n if(options.trigger !== 'focus') {\n $tooltip.focus();\n }\n bindKeyboardEvents();\n }\n });\n\n if(options.autoClose) {\n bindAutoCloseEvents();\n }\n\n };\n\n function enterAnimateCallback() {\n scope.$emit(options.prefixEvent + '.show', $tooltip);\n }\n\n $tooltip.leave = function() {\n\n clearTimeout(timeout);\n hoverState = 'out';\n if (!options.delay || !options.delay.hide) {\n return $tooltip.hide();\n }\n timeout = setTimeout(function () {\n if (hoverState === 'out') {\n $tooltip.hide();\n }\n }, options.delay.hide);\n\n };\n\n var _blur;\n var _tipToHide;\n $tooltip.hide = function(blur) {\n\n if(!$tooltip.$isShown) return;\n scope.$emit(options.prefixEvent + '.hide.before', $tooltip);\n\n // store blur value for leaveAnimateCallback to use\n _blur = blur;\n\n // store current tipElement reference to use\n // in leaveAnimateCallback\n _tipToHide = tipElement;\n\n // Support v1.2+ $animate\n // https://github.com/angular/angular.js/issues/11713\n if(angular.version.minor <= 2) {\n $animate.leave(tipElement, leaveAnimateCallback);\n } else {\n $animate.leave(tipElement).then(leaveAnimateCallback);\n }\n\n $tooltip.$isShown = scope.$isShown = false;\n safeDigest(scope);\n\n // Unbind events\n if(options.keyboard && tipElement !== null) {\n unbindKeyboardEvents();\n }\n\n if(options.autoClose && tipElement !== null) {\n unbindAutoCloseEvents();\n }\n };\n\n function leaveAnimateCallback() {\n scope.$emit(options.prefixEvent + '.hide', $tooltip);\n\n // check if current tipElement still references\n // the same element when hide was called\n if (tipElement === _tipToHide) {\n // Allow to blur the input when hidden, like when pressing enter key\n if(_blur && options.trigger === 'focus') {\n return element[0].blur();\n }\n\n // clean up child scopes\n destroyTipElement();\n }\n }\n\n $tooltip.toggle = function() {\n $tooltip.$isShown ? $tooltip.leave() : $tooltip.enter();\n };\n\n $tooltip.focus = function() {\n tipElement[0].focus();\n };\n\n $tooltip.setEnabled = function(isEnabled) {\n options.bsEnabled = isEnabled;\n };\n\n $tooltip.setViewport = function(viewport) {\n options.viewport = viewport;\n };\n\n // Protected methods\n\n $tooltip.$applyPlacement = function() {\n if(!tipElement) return;\n\n // Determine if we're doing an auto or normal placement\n var placement = options.placement,\n autoToken = /\\s?auto?\\s?/i,\n autoPlace = autoToken.test(placement);\n\n if (autoPlace) {\n placement = placement.replace(autoToken, '') || defaults.placement;\n }\n\n // Need to add the position class before we get\n // the offsets\n tipElement.addClass(options.placement);\n\n // Get the position of the target element\n // and the height and width of the tooltip so we can center it.\n var elementPosition = getPosition(),\n tipWidth = tipElement.prop('offsetWidth'),\n tipHeight = tipElement.prop('offsetHeight');\n\n // Refresh viewport position\n $tooltip.$viewport = options.viewport && findElement(options.viewport.selector || options.viewport);\n\n // If we're auto placing, we need to check the positioning\n if (autoPlace) {\n var originalPlacement = placement;\n var viewportPosition = getPosition($tooltip.$viewport);\n\n if (/top/.test(originalPlacement) && elementPosition.bottom + tipHeight > viewportPosition.bottom) {\n placement = originalPlacement.replace('top', 'bottom');\n } else if (/bottom/.test(originalPlacement) && elementPosition.top - tipHeight < viewportPosition.top) {\n placement = originalPlacement.replace('bottom', 'top');\n }\n\n if (/left/.test(originalPlacement) && elementPosition.left - tipWidth < viewportPosition.left) {\n placement = placement.replace('left', 'right');\n } else if (/right/.test(originalPlacement) && elementPosition.right + tipWidth > viewportPosition.width) {\n placement = placement.replace('right', 'left');\n }\n\n tipElement.removeClass(originalPlacement).addClass(placement);\n }\n\n // Get the tooltip's top and left coordinates to center it with this directive.\n var tipPosition = getCalculatedOffset(placement, elementPosition, tipWidth, tipHeight);\n applyPlacement(tipPosition, placement);\n };\n\n $tooltip.$onKeyUp = function(evt) {\n if (evt.which === 27 && $tooltip.$isShown) {\n $tooltip.hide();\n evt.stopPropagation();\n }\n };\n\n $tooltip.$onFocusKeyUp = function(evt) {\n if (evt.which === 27) {\n element[0].blur();\n evt.stopPropagation();\n }\n };\n\n $tooltip.$onFocusElementMouseDown = function(evt) {\n evt.preventDefault();\n evt.stopPropagation();\n // Some browsers do not auto-focus buttons (eg. Safari)\n $tooltip.$isShown ? element[0].blur() : element[0].focus();\n };\n\n // bind/unbind events\n function bindTriggerEvents() {\n var triggers = options.trigger.split(' ');\n angular.forEach(triggers, function(trigger) {\n if(trigger === 'click') {\n element.on('click', $tooltip.toggle);\n } else if(trigger !== 'manual') {\n element.on(trigger === 'hover' ? 'mouseenter' : 'focus', $tooltip.enter);\n element.on(trigger === 'hover' ? 'mouseleave' : 'blur', $tooltip.leave);\n nodeName === 'button' && trigger !== 'hover' && element.on(isTouch ? 'touchstart' : 'mousedown', $tooltip.$onFocusElementMouseDown);\n }\n });\n }\n\n function unbindTriggerEvents() {\n var triggers = options.trigger.split(' ');\n for (var i = triggers.length; i--;) {\n var trigger = triggers[i];\n if(trigger === 'click') {\n element.off('click', $tooltip.toggle);\n } else if(trigger !== 'manual') {\n element.off(trigger === 'hover' ? 'mouseenter' : 'focus', $tooltip.enter);\n element.off(trigger === 'hover' ? 'mouseleave' : 'blur', $tooltip.leave);\n nodeName === 'button' && trigger !== 'hover' && element.off(isTouch ? 'touchstart' : 'mousedown', $tooltip.$onFocusElementMouseDown);\n }\n }\n }\n\n function bindKeyboardEvents() {\n if(options.trigger !== 'focus') {\n tipElement.on('keyup', $tooltip.$onKeyUp);\n } else {\n element.on('keyup', $tooltip.$onFocusKeyUp);\n }\n }\n\n function unbindKeyboardEvents() {\n if(options.trigger !== 'focus') {\n tipElement.off('keyup', $tooltip.$onKeyUp);\n } else {\n element.off('keyup', $tooltip.$onFocusKeyUp);\n }\n }\n\n var _autoCloseEventsBinded = false;\n function bindAutoCloseEvents() {\n // use timeout to hookup the events to prevent\n // event bubbling from being processed imediately.\n $timeout(function() {\n // Stop propagation when clicking inside tooltip\n tipElement.on('click', stopEventPropagation);\n\n // Hide when clicking outside tooltip\n $body.on('click', $tooltip.hide);\n\n _autoCloseEventsBinded = true;\n }, 0, false);\n }\n\n function unbindAutoCloseEvents() {\n if (_autoCloseEventsBinded) {\n tipElement.off('click', stopEventPropagation);\n $body.off('click', $tooltip.hide);\n _autoCloseEventsBinded = false;\n }\n }\n\n function stopEventPropagation(event) {\n event.stopPropagation();\n }\n\n // Private methods\n\n function getPosition($element) {\n $element = $element || (options.target || element);\n\n var el = $element[0],\n isBody = el.tagName === 'BODY';\n\n var elRect = el.getBoundingClientRect();\n var rect = {};\n\n // IE8 has issues with angular.extend and using elRect directly.\n // By coping the values of elRect into a new object, we can continue to use extend\n for (var p in elRect) {\n // DO NOT use hasOwnProperty when inspecting the return of getBoundingClientRect.\n rect[p] = elRect[p];\n }\n\n if (rect.width === null) {\n // width and height are missing in IE8, so compute them manually; see https://github.com/twbs/bootstrap/issues/14093\n rect = angular.extend({}, rect, { width: elRect.right - elRect.left, height: elRect.bottom - elRect.top });\n }\n var elOffset = isBody ? { top: 0, left: 0 } : dimensions.offset(el),\n scroll = { scroll: isBody ? document.documentElement.scrollTop || document.body.scrollTop : $element.prop('scrollTop') || 0 },\n outerDims = isBody ? { width: document.documentElement.clientWidth, height: $window.innerHeight } : null;\n\n return angular.extend({}, rect, scroll, outerDims, elOffset);\n }\n\n function getCalculatedOffset(placement, position, actualWidth, actualHeight) {\n var offset;\n var split = placement.split('-');\n\n switch (split[0]) {\n case 'right':\n offset = {\n top: position.top + position.height / 2 - actualHeight / 2,\n left: position.left + position.width\n };\n break;\n case 'bottom':\n offset = {\n top: position.top + position.height,\n left: position.left + position.width / 2 - actualWidth / 2\n };\n break;\n case 'left':\n offset = {\n top: position.top + position.height / 2 - actualHeight / 2,\n left: position.left - actualWidth\n };\n break;\n default:\n offset = {\n top: position.top - actualHeight,\n left: position.left + position.width / 2 - actualWidth / 2\n };\n break;\n }\n\n if(!split[1]) {\n return offset;\n }\n\n // Add support for corners @todo css\n if(split[0] === 'top' || split[0] === 'bottom') {\n switch (split[1]) {\n case 'left':\n offset.left = position.left;\n break;\n case 'right':\n offset.left = position.left + position.width - actualWidth;\n }\n } else if(split[0] === 'left' || split[0] === 'right') {\n switch (split[1]) {\n case 'top':\n offset.top = position.top - actualHeight + position.height;\n break;\n case 'bottom':\n offset.top = position.top;\n }\n }\n\n return offset;\n }\n\n function applyPlacement(offset, placement) {\n var tip = tipElement[0],\n width = tip.offsetWidth,\n height = tip.offsetHeight;\n\n // manually read margins because getBoundingClientRect includes difference\n var marginTop = parseInt(dimensions.css(tip, 'margin-top'), 10),\n marginLeft = parseInt(dimensions.css(tip, 'margin-left'), 10);\n\n // we must check for NaN for ie 8/9\n if (isNaN(marginTop)) marginTop = 0;\n if (isNaN(marginLeft)) marginLeft = 0;\n\n offset.top = offset.top + marginTop;\n offset.left = offset.left + marginLeft;\n\n // dimensions setOffset doesn't round pixel values\n // so we use setOffset directly with our own function\n dimensions.setOffset(tip, angular.extend({\n using: function (props) {\n tipElement.css({\n top: Math.round(props.top) + 'px',\n left: Math.round(props.left) + 'px',\n right: ''\n });\n }\n }, offset), 0);\n\n // check to see if placing tip in new offset caused the tip to resize itself\n var actualWidth = tip.offsetWidth,\n actualHeight = tip.offsetHeight;\n\n if (placement === 'top' && actualHeight !== height) {\n offset.top = offset.top + height - actualHeight;\n }\n\n // If it's an exotic placement, exit now instead of\n // applying a delta and changing the arrow\n if (/top-left|top-right|bottom-left|bottom-right/.test(placement)) return;\n\n var delta = getViewportAdjustedDelta(placement, offset, actualWidth, actualHeight);\n\n if (delta.left) {\n offset.left += delta.left;\n } else {\n offset.top += delta.top;\n }\n\n dimensions.setOffset(tip, offset);\n\n if (/top|right|bottom|left/.test(placement)) {\n var isVertical = /top|bottom/.test(placement),\n arrowDelta = isVertical ? delta.left * 2 - width + actualWidth : delta.top * 2 - height + actualHeight,\n arrowOffsetPosition = isVertical ? 'offsetWidth' : 'offsetHeight';\n\n replaceArrow(arrowDelta, tip[arrowOffsetPosition], isVertical);\n }\n }\n\n // @source https://github.com/twbs/bootstrap/blob/v3.3.5/js/tooltip.js#L380\n function getViewportAdjustedDelta(placement, position, actualWidth, actualHeight) {\n var delta = {top: 0, left: 0};\n if (!$tooltip.$viewport) return delta;\n\n var viewportPadding = options.viewport && options.viewport.padding || 0;\n var viewportDimensions = getPosition($tooltip.$viewport);\n\n if (/right|left/.test(placement)) {\n var topEdgeOffset = position.top - viewportPadding - viewportDimensions.scroll;\n var bottomEdgeOffset = position.top + viewportPadding - viewportDimensions.scroll + actualHeight;\n if (topEdgeOffset < viewportDimensions.top) { // top overflow\n delta.top = viewportDimensions.top - topEdgeOffset;\n } else if (bottomEdgeOffset > viewportDimensions.top + viewportDimensions.height) { // bottom overflow\n delta.top = viewportDimensions.top + viewportDimensions.height - bottomEdgeOffset;\n }\n } else {\n var leftEdgeOffset = position.left - viewportPadding;\n var rightEdgeOffset = position.left + viewportPadding + actualWidth;\n if (leftEdgeOffset < viewportDimensions.left) { // left overflow\n delta.left = viewportDimensions.left - leftEdgeOffset;\n } else if (rightEdgeOffset > viewportDimensions.right) { // right overflow\n delta.left = viewportDimensions.left + viewportDimensions.width - rightEdgeOffset;\n }\n }\n\n return delta;\n }\n\n function replaceArrow(delta, dimension, isHorizontal) {\n var $arrow = findElement('.tooltip-arrow, .arrow', tipElement[0]);\n\n $arrow.css(isHorizontal ? 'left' : 'top', 50 * (1 - delta / dimension) + '%')\n .css(isHorizontal ? 'top' : 'left', '');\n }\n\n function destroyTipElement() {\n // Cancel pending callbacks\n clearTimeout(timeout);\n\n if($tooltip.$isShown && tipElement !== null) {\n if(options.autoClose) {\n unbindAutoCloseEvents();\n }\n\n if(options.keyboard) {\n unbindKeyboardEvents();\n }\n }\n\n if(tipScope) {\n tipScope.$destroy();\n tipScope = null;\n }\n\n if(tipElement) {\n tipElement.remove();\n tipElement = $tooltip.$element = null;\n }\n }\n\n return $tooltip;\n\n }\n\n // Helper functions\n\n function safeDigest(scope) {\n scope.$$phase || (scope.$root && scope.$root.$$phase) || scope.$digest();\n }\n\n function findElement(query, element) {\n return angular.element((element || document).querySelectorAll(query));\n }\n\n var fetchPromises = {};\n function fetchTemplate(template) {\n if(fetchPromises[template]) return fetchPromises[template];\n return (fetchPromises[template] = $http.get(template, {cache: $templateCache}).then(function(res) {\n return res.data;\n }));\n }\n\n return TooltipFactory;\n\n };\n\n })\n\n .directive('bsTooltip', function($window, $location, $sce, $tooltip, $$rAF) {\n\n return {\n restrict: 'EAC',\n scope: true,\n link: function postLink(scope, element, attr, transclusion) {\n\n // Directive options\n var options = {scope: scope};\n angular.forEach(['template', 'templateUrl', 'controller', 'controllerAs', 'contentTemplate', 'placement', 'container', 'delay', 'trigger', 'html', 'animation', 'backdropAnimation', 'type', 'customClass', 'id'], function(key) {\n if(angular.isDefined(attr[key])) options[key] = attr[key];\n });\n\n // use string regex match boolean attr falsy values, leave truthy values be\n var falseValueRegExp = /^(false|0|)$/i;\n angular.forEach(['html', 'container'], function(key) {\n if(angular.isDefined(attr[key]) && falseValueRegExp.test(attr[key]))\n options[key] = false;\n });\n\n // should not parse target attribute (anchor tag), only data-target #1454\n var dataTarget = element.attr('data-target');\n if(angular.isDefined(dataTarget)) {\n if(falseValueRegExp.test(dataTarget))\n options.target = false;\n else\n options.target = dataTarget;\n }\n\n // overwrite inherited title value when no value specified\n // fix for angular 1.3.1 531a8de72c439d8ddd064874bf364c00cedabb11\n if (!scope.hasOwnProperty('title')){\n scope.title = '';\n }\n\n // Observe scope attributes for change\n attr.$observe('title', function(newValue) {\n if (angular.isDefined(newValue) || !scope.hasOwnProperty('title')) {\n var oldValue = scope.title;\n scope.title = $sce.trustAsHtml(newValue);\n angular.isDefined(oldValue) && $$rAF(function() {\n tooltip && tooltip.$applyPlacement();\n });\n }\n });\n\n // Support scope as an object\n attr.bsTooltip && scope.$watch(attr.bsTooltip, function(newValue, oldValue) {\n if(angular.isObject(newValue)) {\n angular.extend(scope, newValue);\n } else {\n scope.title = newValue;\n }\n angular.isDefined(oldValue) && $$rAF(function() {\n tooltip && tooltip.$applyPlacement();\n });\n }, true);\n\n // Visibility binding support\n attr.bsShow && scope.$watch(attr.bsShow, function(newValue, oldValue) {\n if(!tooltip || !angular.isDefined(newValue)) return;\n if(angular.isString(newValue)) newValue = !!newValue.match(/true|,?(tooltip),?/i);\n newValue === true ? tooltip.show() : tooltip.hide();\n });\n\n // Enabled binding support\n attr.bsEnabled && scope.$watch(attr.bsEnabled, function(newValue, oldValue) {\n // console.warn('scope.$watch(%s)', attr.bsEnabled, newValue, oldValue);\n if(!tooltip || !angular.isDefined(newValue)) return;\n if(angular.isString(newValue)) newValue = !!newValue.match(/true|1|,?(tooltip),?/i);\n newValue === false ? tooltip.setEnabled(false) : tooltip.setEnabled(true);\n });\n\n // Viewport support\n attr.viewport && scope.$watch(attr.viewport, function (newValue) {\n if(!tooltip || !angular.isDefined(newValue)) return;\n tooltip.setViewport(newValue);\n });\n\n // Initialize popover\n var tooltip = $tooltip(element, options);\n\n // Garbage collection\n scope.$on('$destroy', function() {\n if(tooltip) tooltip.destroy();\n options = null;\n tooltip = null;\n });\n\n }\n };\n\n });\n","'use strict';\n\n// NOTICE: This file was forked from the angular-material project (github.com/angular/material)\n// MIT Licensed - Copyright (c) 2014-2015 Google, Inc. http://angularjs.org\n\nangular.module('mgcrea.ngStrap.core', [])\n .service('$bsCompiler', bsCompilerService);\n\nfunction bsCompilerService($q, $http, $injector, $compile, $controller, $templateCache) {\n /* jshint validthis: true */\n\n /*\n * @ngdoc service\n * @name $bsCompiler\n * @module material.core\n * @description\n * The $bsCompiler service is an abstraction of angular's compiler, that allows the developer\n * to easily compile an element with a templateUrl, controller, and locals.\n *\n * @usage\n *
\n * $bsCompiler.compile({\n * templateUrl: 'modal.html',\n * controller: 'ModalCtrl',\n * locals: {\n * modal: myModalInstance;\n * }\n * }).then(function(compileData) {\n * compileData.element; // modal.html's template in an element\n * compileData.link(myScope); //attach controller & scope to element\n * });\n * \n */\n\n /*\n * @ngdoc method\n * @name $bsCompiler#compile\n * @description A helper to compile an HTML template/templateUrl with a given controller,\n * locals, and scope.\n * @param {object} options An options object, with the following properties:\n *\n * - `controller` - `{(string=|function()=}` Controller fn that should be associated with\n * newly created scope or the name of a registered controller if passed as a string.\n * - `controllerAs` - `{string=}` A controller alias name. If present the controller will be\n * published to scope under the `controllerAs` name.\n * - `template` - `{string=}` An html template as a string.\n * - `templateUrl` - `{string=}` A path to an html template.\n * - `transformTemplate` - `{function(template)=}` A function which transforms the template after\n * it is loaded. It will be given the template string as a parameter, and should\n * return a a new string representing the transformed template.\n * - `resolve` - `{Object.
=}` - An optional map of dependencies which should\n * be injected into the controller. If any of these dependencies are promises, the compiler\n * will wait for them all to be resolved, or if one is rejected before the controller is\n * instantiated `compile()` will fail..\n * * `key` - `{string}`: a name of a dependency to be injected into the controller.\n * * `factory` - `{string|function}`: If `string` then it is an alias for a service.\n * Otherwise if function, then it is injected and the return value is treated as the\n * dependency. If the result is a promise, it is resolved before its value is\n * injected into the controller.\n *\n * @returns {object=} promise A promise, which will be resolved with a `compileData` object.\n * `compileData` has the following properties:\n *\n * - `element` - `{element}`: an uncompiled element matching the provided template.\n * - `link` - `{function(scope)}`: A link function, which, when called, will compile\n * the element and instantiate the provided controller (if given).\n * - `locals` - `{object}`: The locals which will be passed into the controller once `link` is\n * called. If `bindToController` is true, they will be coppied to the ctrl instead\n * - `bindToController` - `bool`: bind the locals to the controller, instead of passing them in.\n */\n this.compile = function(options) {\n\n if(options.template && /\\.html$/.test(options.template)) {\n console.warn('Deprecated use of `template` option to pass a file. Please use the `templateUrl` option instead.');\n options.templateUrl = options.template;\n options.template = '';\n }\n\n var templateUrl = options.templateUrl;\n var template = options.template || '';\n var controller = options.controller;\n var controllerAs = options.controllerAs;\n var resolve = angular.copy(options.resolve || {});\n var locals = angular.copy(options.locals || {});\n var transformTemplate = options.transformTemplate || angular.identity;\n var bindToController = options.bindToController;\n\n // Take resolve values and invoke them.\n // Resolves can either be a string (value: 'MyRegisteredAngularConst'),\n // or an invokable 'factory' of sorts: (value: function ValueGetter($dependency) {})\n angular.forEach(resolve, function(value, key) {\n if (angular.isString(value)) {\n resolve[key] = $injector.get(value);\n } else {\n resolve[key] = $injector.invoke(value);\n }\n });\n // Add the locals, which are just straight values to inject\n // eg locals: { three: 3 }, will inject three into the controller\n angular.extend(resolve, locals);\n\n if (template) {\n resolve.$template = $q.when(template);\n } else if (templateUrl) {\n resolve.$template = fetchTemplate(templateUrl);\n } else {\n throw new Error('Missing `template` / `templateUrl` option.');\n }\n\n if (options.contentTemplate) {\n // TODO(mgcrea): deprecate?\n resolve.$template = $q.all([resolve.$template, fetchTemplate(options.contentTemplate)])\n .then(function(templates) {\n var templateEl = angular.element(templates[0]);\n var contentEl = findElement('[ng-bind=\"content\"]', templateEl[0]).removeAttr('ng-bind').html(templates[1]);\n // Drop the default footer as you probably don't want it if you use a custom contentTemplate\n if(!options.templateUrl) contentEl.next().remove();\n return templateEl[0].outerHTML;\n });\n }\n\n // Wait for all the resolves to finish if they are promises\n return $q.all(resolve).then(function(locals) {\n\n var template = transformTemplate(locals.$template);\n if (options.html) {\n template = template.replace(/ng-bind=\"/ig, 'ng-bind-html=\"');\n }\n // var element = options.element || angular.element('').html(template.trim()).contents();\n var element = angular.element('
').html(template.trim()).contents();\n var linkFn = $compile(element);\n\n // Return a linking function that can be used later when the element is ready\n return {\n locals: locals,\n element: element,\n link: function link(scope) {\n locals.$scope = scope;\n\n // Instantiate controller if it exists, because we have scope\n if (controller) {\n var invokeCtrl = $controller(controller, locals, true);\n if (bindToController) {\n angular.extend(invokeCtrl.instance, locals);\n }\n // Support angular@~1.2 invokeCtrl\n var ctrl = angular.isObject(invokeCtrl) ? invokeCtrl : invokeCtrl();\n // See angular-route source for this logic\n element.data('$ngControllerController', ctrl);\n element.children().data('$ngControllerController', ctrl);\n\n if (controllerAs) {\n scope[controllerAs] = ctrl;\n }\n }\n\n return linkFn.apply(null, arguments);\n }\n };\n });\n\n };\n\n function findElement(query, element) {\n return angular.element((element || document).querySelectorAll(query));\n }\n\n var fetchPromises = {};\n function fetchTemplate(template) {\n if(fetchPromises[template]) return fetchPromises[template];\n return (fetchPromises[template] = $http.get(template, {cache: $templateCache})\n .then(function(res) {\n return res.data;\n }));\n }\n\n}\n","'use strict';\n\nangular.module('mgcrea.ngStrap.navbar', [])\n\n .provider('$navbar', function() {\n\n var defaults = this.defaults = {\n activeClass: 'active',\n routeAttr: 'data-match-route',\n strict: false\n };\n\n this.$get = function() {\n return {defaults: defaults};\n };\n\n })\n\n .directive('bsNavbar', function($window, $location, $navbar) {\n\n var defaults = $navbar.defaults;\n\n return {\n restrict: 'A',\n link: function postLink(scope, element, attr, controller) {\n\n // Directive options\n var options = angular.copy(defaults);\n angular.forEach(Object.keys(defaults), function(key) {\n if(angular.isDefined(attr[key])) options[key] = attr[key];\n });\n\n // Watch for the $location\n scope.$watch(function() {\n\n return $location.path();\n\n }, function(newValue, oldValue) {\n\n var liElements = element[0].querySelectorAll('li[' + options.routeAttr + ']');\n\n angular.forEach(liElements, function(li) {\n\n var liElement = angular.element(li);\n var pattern = liElement.attr(options.routeAttr).replace('/', '\\\\/');\n if(options.strict) {\n pattern = '^' + pattern + '$';\n }\n var regexp = new RegExp(pattern, 'i');\n\n if(regexp.test(newValue)) {\n liElement.addClass(options.activeClass);\n } else {\n liElement.removeClass(options.activeClass);\n }\n\n });\n\n });\n\n }\n\n };\n\n });\n","'use strict';\n\nangular.module('mgcrea.ngStrap.typeahead', ['mgcrea.ngStrap.tooltip', 'mgcrea.ngStrap.helpers.parseOptions'])\n\n .provider('$typeahead', function() {\n\n var defaults = this.defaults = {\n animation: 'am-fade',\n prefixClass: 'typeahead',\n prefixEvent: '$typeahead',\n placement: 'bottom-left',\n templateUrl: 'typeahead/typeahead.tpl.html',\n trigger: 'focus',\n container: false,\n keyboard: true,\n html: false,\n delay: 0,\n minLength: 1,\n filter: 'bsAsyncFilter',\n limit: 6,\n autoSelect: false,\n comparator: '',\n trimValue: true\n };\n\n this.$get = function($window, $rootScope, $tooltip, $$rAF, $timeout) {\n\n var bodyEl = angular.element($window.document.body);\n\n function TypeaheadFactory(element, controller, config) {\n\n var $typeahead = {};\n\n // Common vars\n var options = angular.extend({}, defaults, config);\n\n $typeahead = $tooltip(element, options);\n var parentScope = config.scope;\n var scope = $typeahead.$scope;\n\n scope.$resetMatches = function() {\n scope.$matches = [];\n scope.$activeIndex = options.autoSelect ? 0 : -1; // If set to 0, the first match will be highlighted\n };\n scope.$resetMatches();\n\n scope.$activate = function(index) {\n scope.$$postDigest(function() {\n $typeahead.activate(index);\n });\n };\n\n scope.$select = function(index, evt) {\n scope.$$postDigest(function() {\n $typeahead.select(index);\n });\n };\n\n scope.$isVisible = function() {\n return $typeahead.$isVisible();\n };\n\n // Public methods\n\n $typeahead.update = function(matches) {\n scope.$matches = matches;\n if (scope.$activeIndex >= matches.length) {\n scope.$activeIndex = options.autoSelect ? 0 : -1;\n }\n\n // wrap in a $timeout so the results are updated\n // before repositioning\n safeDigest(scope);\n $$rAF($typeahead.$applyPlacement);\n };\n\n $typeahead.activate = function(index) {\n scope.$activeIndex = index;\n };\n\n $typeahead.select = function(index) {\n if (index === -1) return;\n var value = scope.$matches[index].value;\n // console.log('$setViewValue', value);\n controller.$setViewValue(value);\n controller.$render();\n scope.$resetMatches();\n if (parentScope) parentScope.$digest();\n // Emit event\n scope.$emit(options.prefixEvent + '.select', value, index, $typeahead);\n };\n\n // Protected methods\n\n $typeahead.$isVisible = function() {\n if (!options.minLength || !controller) {\n return !!scope.$matches.length;\n }\n // minLength support\n return scope.$matches.length && angular.isString(controller.$viewValue) && controller.$viewValue.length >= options.minLength;\n };\n\n $typeahead.$getIndex = function(value) {\n var l = scope.$matches.length,\n i = l;\n if (!l) return;\n for (i = l; i--;) {\n if (scope.$matches[i].value === value) break;\n }\n if (i < 0) return;\n return i;\n };\n\n $typeahead.$onMouseDown = function(evt) {\n // Prevent blur on mousedown\n evt.preventDefault();\n evt.stopPropagation();\n };\n\n $typeahead.$onKeyDown = function(evt) {\n if (!/(38|40|13)/.test(evt.keyCode)) return;\n\n // Let ngSubmit pass if the typeahead tip is hidden or no option is selected\n if ($typeahead.$isVisible() && !(evt.keyCode === 13 && scope.$activeIndex === -1)) {\n evt.preventDefault();\n evt.stopPropagation();\n }\n\n // Select with enter\n if (evt.keyCode === 13 && scope.$matches.length) {\n $typeahead.select(scope.$activeIndex);\n }\n\n // Navigate with keyboard\n else if (evt.keyCode === 38 && scope.$activeIndex > 0) scope.$activeIndex--;\n else if (evt.keyCode === 40 && scope.$activeIndex < scope.$matches.length - 1) scope.$activeIndex++;\n else if (angular.isUndefined(scope.$activeIndex)) scope.$activeIndex = 0;\n scope.$digest();\n };\n\n // Overrides\n\n var show = $typeahead.show;\n $typeahead.show = function() {\n show();\n // use timeout to hookup the events to prevent\n // event bubbling from being processed immediately.\n $timeout(function() {\n $typeahead.$element && $typeahead.$element.on('mousedown', $typeahead.$onMouseDown);\n if (options.keyboard) {\n element && element.on('keydown', $typeahead.$onKeyDown);\n }\n }, 0, false);\n };\n\n var hide = $typeahead.hide;\n $typeahead.hide = function() {\n $typeahead.$element && $typeahead.$element.off('mousedown', $typeahead.$onMouseDown);\n if (options.keyboard) {\n element && element.off('keydown', $typeahead.$onKeyDown);\n }\n if (!options.autoSelect)\n $typeahead.activate(-1);\n hide();\n };\n\n return $typeahead;\n\n }\n\n // Helper functions\n\n function safeDigest(scope) {\n scope.$$phase || (scope.$root && scope.$root.$$phase) || scope.$digest();\n }\n\n TypeaheadFactory.defaults = defaults;\n return TypeaheadFactory;\n\n };\n\n })\n\n .filter('bsAsyncFilter', function($filter) {\n return function(array, expression, comparator) {\n if (array && angular.isFunction(array.then)) {\n return array.then(function(results) {\n return $filter('filter')(results, expression, comparator);\n });\n } else {\n return $filter('filter')(array, expression, comparator);\n }\n };\n })\n\n .directive('bsTypeahead', function($window, $parse, $q, $typeahead, $parseOptions) {\n\n var defaults = $typeahead.defaults;\n\n return {\n restrict: 'EAC',\n require: 'ngModel',\n link: function postLink(scope, element, attr, controller) {\n\n // Directive options\n var options = {\n scope: scope\n };\n 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) {\n if (angular.isDefined(attr[key])) options[key] = attr[key];\n });\n\n // use string regex match boolean attr falsy values, leave truthy values be\n var falseValueRegExp = /^(false|0|)$/i;\n angular.forEach(['html', 'container', 'trimValue'], function(key) {\n if (angular.isDefined(attr[key]) && falseValueRegExp.test(attr[key])) options[key] = false;\n });\n\n // Disable browser autocompletion\n if (!element.attr('autocomplete')) element.attr('autocomplete', 'off');\n\n // Build proper bsOptions\n var filter = options.filter || defaults.filter;\n var limit = options.limit || defaults.limit;\n var comparator = options.comparator || defaults.comparator;\n\n var bsOptions = attr.bsOptions;\n if (filter) bsOptions += ' | ' + filter + ':$viewValue';\n if (comparator) bsOptions += ':' + comparator;\n if (limit) bsOptions += ' | limitTo:' + limit;\n var parsedOptions = $parseOptions(bsOptions);\n\n // Initialize typeahead\n var typeahead = $typeahead(element, controller, options);\n\n // Watch options on demand\n if (options.watchOptions) {\n // Watch bsOptions values before filtering for changes, drop function calls\n var watchedOptions = parsedOptions.$match[7].replace(/\\|.+/, '').replace(/\\(.*\\)/g, '').trim();\n scope.$watchCollection(watchedOptions, function(newValue, oldValue) {\n // console.warn('scope.$watch(%s)', watchedOptions, newValue, oldValue);\n parsedOptions.valuesFn(scope, controller).then(function(values) {\n typeahead.update(values);\n controller.$render();\n });\n });\n }\n\n // Watch model for changes\n scope.$watch(attr.ngModel, function(newValue, oldValue) {\n // console.warn('$watch', element.attr('ng-model'), newValue);\n scope.$modelValue = newValue; // Publish modelValue on scope for custom templates\n parsedOptions.valuesFn(scope, controller)\n .then(function(values) {\n // Prevent input with no future prospect if selectMode is truthy\n // @TODO test selectMode\n if (options.selectMode && !values.length && newValue.length > 0) {\n controller.$setViewValue(controller.$viewValue.substring(0, controller.$viewValue.length - 1));\n return;\n }\n if (values.length > limit) values = values.slice(0, limit);\n typeahead.update(values);\n // Queue a new rendering that will leverage collection loading\n controller.$render();\n });\n });\n\n // modelValue -> $formatters -> viewValue\n controller.$formatters.push(function(modelValue) {\n // console.warn('$formatter(\"%s\"): modelValue=%o (%o)', element.attr('ng-model'), modelValue, typeof modelValue);\n var displayValue = parsedOptions.displayValue(modelValue);\n\n // If we can determine the displayValue, use that\n if (displayValue) {\n return displayValue;\n }\n\n // If there's no display value, attempt to use the modelValue.\n // If the model is an object not much we can do\n if (modelValue && typeof modelValue !== 'object') {\n return modelValue;\n }\n return '';\n });\n\n // Model rendering in view\n controller.$render = function() {\n // console.warn('$render', element.attr('ng-model'), 'controller.$modelValue', typeof controller.$modelValue, controller.$modelValue, 'controller.$viewValue', typeof controller.$viewValue, controller.$viewValue);\n if (controller.$isEmpty(controller.$viewValue)) {\n return element.val('');\n }\n var index = typeahead.$getIndex(controller.$modelValue);\n var selected = angular.isDefined(index) ? typeahead.$scope.$matches[index].label : controller.$viewValue;\n selected = angular.isObject(selected) ? parsedOptions.displayValue(selected) : selected;\n var value = selected ? selected.toString().replace(/<(?:.|\\n)*?>/gm, '') : '';\n element.val(options.trimValue === false ? value : value.trim());\n };\n\n // Garbage collection\n scope.$on('$destroy', function() {\n if (typeahead) typeahead.destroy();\n options = null;\n typeahead = null;\n });\n\n }\n };\n\n });\n","'use strict';\n\nangular.module('mgcrea.ngStrap.tab', [])\n\n .provider('$tab', function() {\n\n var defaults = this.defaults = {\n animation: 'am-fade',\n template: 'tab/tab.tpl.html',\n navClass: 'nav-tabs',\n activeClass: 'active'\n };\n\n var controller = this.controller = function($scope, $element, $attrs) {\n var self = this;\n\n // Attributes options\n self.$options = angular.copy(defaults);\n angular.forEach(['animation', 'navClass', 'activeClass'], function(key) {\n if(angular.isDefined($attrs[key])) self.$options[key] = $attrs[key];\n });\n\n // Publish options on scope\n $scope.$navClass = self.$options.navClass;\n $scope.$activeClass = self.$options.activeClass;\n\n self.$panes = $scope.$panes = [];\n\n // Please use $activePaneChangeListeners if you use `bsActivePane`\n // Because we removed `ngModel` as default, we rename viewChangeListeners to\n // activePaneChangeListeners to make more sense.\n self.$activePaneChangeListeners = self.$viewChangeListeners = [];\n\n self.$push = function(pane) {\n if(angular.isUndefined(self.$panes.$active)) {\n $scope.$setActive(pane.name || 0);\n }\n self.$panes.push(pane);\n };\n\n self.$remove = function(pane) {\n var index = self.$panes.indexOf(pane);\n var active = self.$panes.$active;\n var activeIndex;\n if(angular.isString(active)) {\n activeIndex = self.$panes.map(function(pane) {\n return pane.name;\n }).indexOf(active);\n } else {\n activeIndex = self.$panes.$active;\n }\n\n // remove pane from $panes array\n self.$panes.splice(index, 1);\n\n if (index < activeIndex) {\n // we removed a pane before the active pane, so we need to\n // decrement the active pane index\n activeIndex--;\n }\n else if (index === activeIndex && activeIndex === self.$panes.length) {\n // we remove the active pane and it was the one at the end,\n // so select the previous one\n activeIndex--;\n }\n if(activeIndex >= 0 && activeIndex < self.$panes.length) {\n self.$setActive(self.$panes[activeIndex].name || activeIndex);\n } else {\n self.$setActive();\n }\n };\n\n self.$setActive = $scope.$setActive = function(value) {\n self.$panes.$active = value;\n self.$activePaneChangeListeners.forEach(function(fn) {\n fn();\n });\n };\n\n self.$isActive = $scope.$isActive = function($pane, $index) {\n return self.$panes.$active === $pane.name || self.$panes.$active === $index;\n };\n\n };\n\n this.$get = function() {\n var $tab = {};\n $tab.defaults = defaults;\n $tab.controller = controller;\n return $tab;\n };\n\n })\n\n .directive('bsTabs', function($window, $animate, $tab, $parse) {\n\n var defaults = $tab.defaults;\n\n return {\n require: ['?ngModel', 'bsTabs'],\n transclude: true,\n scope: true,\n controller: ['$scope', '$element', '$attrs', $tab.controller],\n templateUrl: function(element, attr) {\n return attr.template || defaults.template;\n },\n link: function postLink(scope, element, attrs, controllers) {\n\n var ngModelCtrl = controllers[0];\n var bsTabsCtrl = controllers[1];\n\n // 'ngModel' does interfere with form validation\n // and status, use `bsActivePane` instead to avoid it\n if(ngModelCtrl) {\n\n // Update the modelValue following\n bsTabsCtrl.$activePaneChangeListeners.push(function() {\n ngModelCtrl.$setViewValue(bsTabsCtrl.$panes.$active);\n });\n\n // modelValue -> $formatters -> viewValue\n ngModelCtrl.$formatters.push(function(modelValue) {\n // console.warn('$formatter(\"%s\"): modelValue=%o (%o)', element.attr('ng-model'), modelValue, typeof modelValue);\n bsTabsCtrl.$setActive(modelValue);\n return modelValue;\n });\n\n }\n\n if (attrs.bsActivePane) {\n // adapted from angularjs ngModelController bindings\n // https://github.com/angular/angular.js/blob/v1.3.1/src%2Fng%2Fdirective%2Finput.js#L1730\n var parsedBsActivePane = $parse(attrs.bsActivePane);\n\n // Update bsActivePane value with change\n bsTabsCtrl.$activePaneChangeListeners.push(function() {\n parsedBsActivePane.assign(scope, bsTabsCtrl.$panes.$active);\n });\n\n // watch bsActivePane for value changes\n scope.$watch(attrs.bsActivePane, function(newValue, oldValue) {\n bsTabsCtrl.$setActive(newValue);\n }, true);\n }\n }\n };\n\n })\n\n .directive('bsPane', function($window, $animate, $sce) {\n\n return {\n require: ['^?ngModel', '^bsTabs'],\n scope: true,\n link: function postLink(scope, element, attrs, controllers) {\n\n var ngModelCtrl = controllers[0];\n var bsTabsCtrl = controllers[1];\n\n // Add base class\n element.addClass('tab-pane');\n\n // Observe title attribute for change\n attrs.$observe('title', function(newValue, oldValue) {\n scope.title = $sce.trustAsHtml(newValue);\n });\n\n // Save tab name into scope\n scope.name = attrs.name;\n\n // Add animation class\n if(bsTabsCtrl.$options.animation) {\n element.addClass(bsTabsCtrl.$options.animation);\n }\n\n attrs.$observe('disabled', function(newValue, oldValue) {\n scope.disabled = scope.$eval(newValue);\n });\n\n // Push pane to parent bsTabs controller\n bsTabsCtrl.$push(scope);\n\n // remove pane from tab controller when pane is destroyed\n scope.$on('$destroy', function() {\n bsTabsCtrl.$remove(scope);\n });\n\n function render() {\n var index = bsTabsCtrl.$panes.indexOf(scope);\n $animate[bsTabsCtrl.$isActive(scope, index) ? 'addClass' : 'removeClass'](element, bsTabsCtrl.$options.activeClass);\n }\n\n bsTabsCtrl.$activePaneChangeListeners.push(function() {\n render();\n });\n render();\n\n }\n };\n\n });\n","'use strict';\n\nangular.module('mgcrea.ngStrap.timepicker', ['mgcrea.ngStrap.helpers.dateParser', 'mgcrea.ngStrap.helpers.dateFormatter', 'mgcrea.ngStrap.tooltip'])\n\n .provider('$timepicker', function() {\n\n var defaults = this.defaults = {\n animation: 'am-fade',\n //uncommenting the following line will break backwards compatability\n // prefixEvent: 'timepicker',\n prefixClass: 'timepicker',\n placement: 'bottom-left',\n templateUrl: 'timepicker/timepicker.tpl.html',\n trigger: 'focus',\n container: false,\n keyboard: true,\n html: false,\n delay: 0,\n // lang: $locale.id,\n useNative: true,\n timeType: 'date',\n timeFormat: 'shortTime',\n timezone: null,\n modelTimeFormat: null,\n autoclose: false,\n minTime: -Infinity,\n maxTime: +Infinity,\n length: 5,\n hourStep: 1,\n minuteStep: 5,\n secondStep: 5,\n roundDisplay: false,\n iconUp: 'glyphicon glyphicon-chevron-up',\n iconDown: 'glyphicon glyphicon-chevron-down',\n arrowBehavior: 'pager'\n };\n\n this.$get = function($window, $document, $rootScope, $sce, $dateFormatter, $tooltip, $timeout) {\n\n var isNative = /(ip(a|o)d|iphone|android)/ig.test($window.navigator.userAgent);\n var isTouch = ('createTouch' in $window.document) && isNative;\n if (!defaults.lang) {\n defaults.lang = $dateFormatter.getDefaultLocale();\n }\n\n function timepickerFactory(element, controller, config) {\n\n var $timepicker = $tooltip(element, angular.extend({}, defaults, config));\n var parentScope = config.scope;\n var options = $timepicker.$options;\n var scope = $timepicker.$scope;\n\n var lang = options.lang;\n var formatDate = function(date, format, timezone) {\n return $dateFormatter.formatDate(date, format, lang, timezone);\n };\n\n function floorMinutes(time) {\n // coeff used to floor current time to nearest minuteStep interval\n var coeff = 1000 * 60 * options.minuteStep;\n return new Date(Math.floor(time.getTime() / coeff) * coeff);\n }\n\n // View vars\n\n var selectedIndex = 0;\n var defaultDate = options.roundDisplay ? floorMinutes(new Date()) : new Date();\n var startDate = controller.$dateValue || defaultDate;\n var viewDate = {\n hour: startDate.getHours(),\n meridian: startDate.getHours() < 12,\n minute: startDate.getMinutes(),\n second: startDate.getSeconds(),\n millisecond: startDate.getMilliseconds()\n };\n\n var format = $dateFormatter.getDatetimeFormat(options.timeFormat, lang);\n\n var hoursFormat = $dateFormatter.hoursFormat(format),\n timeSeparator = $dateFormatter.timeSeparator(format),\n minutesFormat = $dateFormatter.minutesFormat(format),\n secondsFormat = $dateFormatter.secondsFormat(format),\n showSeconds = $dateFormatter.showSeconds(format),\n showAM = $dateFormatter.showAM(format);\n\n scope.$iconUp = options.iconUp;\n scope.$iconDown = options.iconDown;\n\n // Scope methods\n\n scope.$select = function(date, index) {\n $timepicker.select(date, index);\n };\n scope.$moveIndex = function(value, index) {\n $timepicker.$moveIndex(value, index);\n };\n scope.$switchMeridian = function(date) {\n $timepicker.switchMeridian(date);\n };\n\n // Public methods\n\n $timepicker.update = function(date) {\n // console.warn('$timepicker.update() newValue=%o', date);\n if (angular.isDate(date) && !isNaN(date.getTime())) {\n $timepicker.$date = date;\n angular.extend(viewDate, {\n hour: date.getHours(),\n minute: date.getMinutes(),\n second: date.getSeconds(),\n millisecond: date.getMilliseconds()\n });\n $timepicker.$build();\n } else if (!$timepicker.$isBuilt) {\n $timepicker.$build();\n }\n };\n\n $timepicker.select = function(date, index, keep) {\n // console.warn('$timepicker.select', date, scope.$mode);\n if (!controller.$dateValue || isNaN(controller.$dateValue.getTime())) controller.$dateValue = new Date(1970, 0, 1);\n if (!angular.isDate(date)) date = new Date(date);\n if (index === 0) controller.$dateValue.setHours(date.getHours());\n else if (index === 1) controller.$dateValue.setMinutes(date.getMinutes());\n else if (index === 2) controller.$dateValue.setSeconds(date.getSeconds());\n controller.$setViewValue(angular.copy(controller.$dateValue));\n controller.$render();\n if (options.autoclose && !keep) {\n $timeout(function() {\n $timepicker.hide(true);\n });\n }\n };\n\n $timepicker.switchMeridian = function(date) {\n if (!controller.$dateValue || isNaN(controller.$dateValue.getTime())) {\n return;\n }\n var hours = (date || controller.$dateValue).getHours();\n controller.$dateValue.setHours(hours < 12 ? hours + 12 : hours - 12);\n controller.$setViewValue(angular.copy(controller.$dateValue));\n controller.$render();\n };\n\n // Protected methods\n\n $timepicker.$build = function() {\n // console.warn('$timepicker.$build() viewDate=%o', viewDate);\n var i, midIndex = scope.midIndex = parseInt(options.length / 2, 10);\n var hours = [],\n hour;\n for (i = 0; i < options.length; i++) {\n hour = new Date(1970, 0, 1, viewDate.hour - (midIndex - i) * options.hourStep);\n hours.push({\n date: hour,\n label: formatDate(hour, hoursFormat),\n selected: $timepicker.$date && $timepicker.$isSelected(hour, 0),\n disabled: $timepicker.$isDisabled(hour, 0)\n });\n }\n var minutes = [],\n minute;\n for (i = 0; i < options.length; i++) {\n minute = new Date(1970, 0, 1, 0, viewDate.minute - (midIndex - i) * options.minuteStep);\n minutes.push({\n date: minute,\n label: formatDate(minute, minutesFormat),\n selected: $timepicker.$date && $timepicker.$isSelected(minute, 1),\n disabled: $timepicker.$isDisabled(minute, 1)\n });\n }\n var seconds = [],\n second;\n for (i = 0; i < options.length; i++) {\n second = new Date(1970, 0, 1, 0, 0, viewDate.second - (midIndex - i) * options.secondStep);\n seconds.push({\n date: second,\n label: formatDate(second, secondsFormat),\n selected: $timepicker.$date && $timepicker.$isSelected(second, 2),\n disabled: $timepicker.$isDisabled(second, 2)\n });\n }\n\n var rows = [];\n for (i = 0; i < options.length; i++) {\n if (showSeconds) {\n rows.push([hours[i], minutes[i], seconds[i]]);\n } else {\n rows.push([hours[i], minutes[i]]);\n }\n }\n scope.rows = rows;\n scope.showSeconds = showSeconds;\n scope.showAM = showAM;\n scope.isAM = ($timepicker.$date || hours[midIndex].date).getHours() < 12;\n scope.timeSeparator = timeSeparator;\n $timepicker.$isBuilt = true;\n };\n\n $timepicker.$isSelected = function(date, index) {\n if (!$timepicker.$date) return false;\n else if (index === 0) {\n return date.getHours() === $timepicker.$date.getHours();\n } else if (index === 1) {\n return date.getMinutes() === $timepicker.$date.getMinutes();\n } else if (index === 2) {\n return date.getSeconds() === $timepicker.$date.getSeconds();\n }\n };\n\n $timepicker.$isDisabled = function(date, index) {\n var selectedTime;\n if (index === 0) {\n selectedTime = date.getTime() + viewDate.minute * 6e4 + viewDate.second * 1e3;\n } else if (index === 1) {\n selectedTime = date.getTime() + viewDate.hour * 36e5 + viewDate.second * 1e3;\n } else if (index === 2) {\n selectedTime = date.getTime() + viewDate.hour * 36e5 + viewDate.minute * 6e4;\n }\n return selectedTime < options.minTime * 1 || selectedTime > options.maxTime * 1;\n };\n\n scope.$arrowAction = function(value, index) {\n if (options.arrowBehavior === 'picker') {\n $timepicker.$setTimeByStep(value, index);\n } else {\n $timepicker.$moveIndex(value, index);\n }\n };\n\n $timepicker.$setTimeByStep = function(value, index) {\n var newDate = new Date($timepicker.$date || startDate);\n var hours = newDate.getHours();\n var minutes = newDate.getMinutes();\n var seconds = newDate.getSeconds();\n if (index === 0) {\n newDate.setHours(hours - (parseInt(options.hourStep, 10) * value));\n } else if (index === 1) {\n newDate.setMinutes(minutes - (parseInt(options.minuteStep, 10) * value));\n } else if (index === 2) {\n newDate.setSeconds(seconds - (parseInt(options.secondStep, 10) * value));\n }\n $timepicker.select(newDate, index, true);\n };\n\n $timepicker.$moveIndex = function(value, index) {\n var targetDate;\n if (index === 0) {\n targetDate = new Date(1970, 0, 1, viewDate.hour + (value * options.length), viewDate.minute, viewDate.second);\n angular.extend(viewDate, {\n hour: targetDate.getHours()\n });\n } else if (index === 1) {\n targetDate = new Date(1970, 0, 1, viewDate.hour, viewDate.minute + (value * options.length * options.minuteStep), viewDate.second);\n angular.extend(viewDate, {\n minute: targetDate.getMinutes()\n });\n } else if (index === 2) {\n targetDate = new Date(1970, 0, 1, viewDate.hour, viewDate.minute, viewDate.second + (value * options.length * options.secondStep));\n angular.extend(viewDate, {\n second: targetDate.getSeconds()\n });\n }\n $timepicker.$build();\n };\n\n $timepicker.$onMouseDown = function(evt) {\n // Prevent blur on mousedown on .dropdown-menu\n if (evt.target.nodeName.toLowerCase() !== 'input') evt.preventDefault();\n evt.stopPropagation();\n // Emulate click for mobile devices\n if (isTouch) {\n var targetEl = angular.element(evt.target);\n if (targetEl[0].nodeName.toLowerCase() !== 'button') {\n targetEl = targetEl.parent();\n }\n targetEl.triggerHandler('click');\n }\n };\n\n $timepicker.$onKeyDown = function(evt) {\n if (!/(38|37|39|40|13)/.test(evt.keyCode) || evt.shiftKey || evt.altKey) return;\n evt.preventDefault();\n evt.stopPropagation();\n\n // Close on enter\n if (evt.keyCode === 13) {\n $timepicker.hide(true);\n return;\n }\n\n // Navigate with keyboard\n var newDate = new Date($timepicker.$date);\n var hours = newDate.getHours(),\n hoursLength = formatDate(newDate, hoursFormat).length;\n var minutes = newDate.getMinutes(),\n minutesLength = formatDate(newDate, minutesFormat).length;\n var seconds = newDate.getSeconds(),\n secondsLength = formatDate(newDate, secondsFormat).length;\n var sepLength = 1;\n var lateralMove = /(37|39)/.test(evt.keyCode);\n var count = 2 + showSeconds * 1 + showAM * 1;\n\n // Navigate indexes (left, right)\n if (lateralMove) {\n if (evt.keyCode === 37) selectedIndex = selectedIndex < 1 ? count - 1 : selectedIndex - 1;\n else if (evt.keyCode === 39) selectedIndex = selectedIndex < count - 1 ? selectedIndex + 1 : 0;\n }\n\n // Update values (up, down)\n var selectRange = [0, hoursLength];\n var incr = 0;\n if (evt.keyCode === 38) incr = -1;\n if (evt.keyCode === 40) incr = +1;\n var isSeconds = selectedIndex === 2 && showSeconds;\n var isMeridian = selectedIndex === 2 && !showSeconds || selectedIndex === 3 && showSeconds;\n if (selectedIndex === 0) {\n newDate.setHours(hours + incr * parseInt(options.hourStep, 10));\n // re-calculate hours length because we have changed hours value\n hoursLength = formatDate(newDate, hoursFormat).length;\n selectRange = [0, hoursLength];\n } else if (selectedIndex === 1) {\n newDate.setMinutes(minutes + incr * parseInt(options.minuteStep, 10));\n // re-calculate minutes length because we have changes minutes value\n minutesLength = formatDate(newDate, minutesFormat).length;\n selectRange = [hoursLength + sepLength, minutesLength];\n } else if (isSeconds) {\n newDate.setSeconds(seconds + incr * parseInt(options.secondStep, 10));\n // re-calculate seconds length because we have changes seconds value\n secondsLength = formatDate(newDate, secondsFormat).length;\n selectRange = [hoursLength + sepLength + minutesLength + sepLength, secondsLength];\n } else if (isMeridian) {\n if (!lateralMove) $timepicker.switchMeridian();\n selectRange = [hoursLength + sepLength + minutesLength + sepLength + (secondsLength + sepLength) * showSeconds, 2];\n }\n $timepicker.select(newDate, selectedIndex, true);\n createSelection(selectRange[0], selectRange[1]);\n parentScope.$digest();\n };\n\n // Private\n\n function createSelection(start, length) {\n var end = start + length;\n if (element[0].createTextRange) {\n var selRange = element[0].createTextRange();\n selRange.collapse(true);\n selRange.moveStart('character', start);\n selRange.moveEnd('character', end);\n selRange.select();\n } else if (element[0].setSelectionRange) {\n element[0].setSelectionRange(start, end);\n } else if (angular.isUndefined(element[0].selectionStart)) {\n element[0].selectionStart = start;\n element[0].selectionEnd = end;\n }\n }\n\n function focusElement() {\n element[0].focus();\n }\n\n // Overrides\n\n var _init = $timepicker.init;\n $timepicker.init = function() {\n if (isNative && options.useNative) {\n element.prop('type', 'time');\n element.css('-webkit-appearance', 'textfield');\n return;\n } else if (isTouch) {\n element.prop('type', 'text');\n element.attr('readonly', 'true');\n element.on('click', focusElement);\n }\n _init();\n };\n\n var _destroy = $timepicker.destroy;\n $timepicker.destroy = function() {\n if (isNative && options.useNative) {\n element.off('click', focusElement);\n }\n _destroy();\n };\n\n var _show = $timepicker.show;\n $timepicker.show = function() {\n if((!isTouch && element.attr('readonly')) || element.attr('disabled')) return;\n _show();\n // use timeout to hookup the events to prevent\n // event bubbling from being processed imediately.\n $timeout(function() {\n $timepicker.$element && $timepicker.$element.on(isTouch ? 'touchstart' : 'mousedown', $timepicker.$onMouseDown);\n if (options.keyboard) {\n element && element.on('keydown', $timepicker.$onKeyDown);\n }\n }, 0, false);\n };\n\n var _hide = $timepicker.hide;\n $timepicker.hide = function(blur) {\n if (!$timepicker.$isShown) return;\n $timepicker.$element && $timepicker.$element.off(isTouch ? 'touchstart' : 'mousedown', $timepicker.$onMouseDown);\n if (options.keyboard) {\n element && element.off('keydown', $timepicker.$onKeyDown);\n }\n _hide(blur);\n };\n\n return $timepicker;\n\n }\n\n timepickerFactory.defaults = defaults;\n return timepickerFactory;\n\n };\n\n })\n\n\n .directive('bsTimepicker', function($window, $parse, $q, $dateFormatter, $dateParser, $timepicker) {\n\n var defaults = $timepicker.defaults;\n var isNative = /(ip(a|o)d|iphone|android)/ig.test($window.navigator.userAgent);\n\n return {\n restrict: 'EAC',\n require: 'ngModel',\n link: function postLink(scope, element, attr, controller) {\n\n // Directive options\n var options = {\n scope: scope\n };\n 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'], function(key) {\n if (angular.isDefined(attr[key])) options[key] = attr[key];\n });\n\n // use string regex match boolean attr falsy values, leave truthy values be\n var falseValueRegExp = /^(false|0|)$/i;\n angular.forEach(['html', 'container', 'autoclose', 'useNative', 'roundDisplay'], function(key) {\n if (angular.isDefined(attr[key]) && falseValueRegExp.test(attr[key]))\n options[key] = false;\n });\n\n // Visibility binding support\n attr.bsShow && scope.$watch(attr.bsShow, function(newValue, oldValue) {\n if (!timepicker || !angular.isDefined(newValue)) return;\n if (angular.isString(newValue)) newValue = !!newValue.match(/true|,?(timepicker),?/i);\n newValue === true ? timepicker.show() : timepicker.hide();\n });\n\n // Initialize timepicker\n if (isNative && (options.useNative || defaults.useNative)) options.timeFormat = 'HH:mm';\n var timepicker = $timepicker(element, controller, options);\n options = timepicker.$options;\n\n var lang = options.lang;\n var formatDate = function(date, format, timezone) {\n return $dateFormatter.formatDate(date, format, lang, timezone);\n };\n\n // Initialize parser\n var dateParser = $dateParser({\n format: options.timeFormat,\n lang: lang\n });\n\n // Observe attributes for changes\n angular.forEach(['minTime', 'maxTime'], function(key) {\n // console.warn('attr.$observe(%s)', key, attr[key]);\n angular.isDefined(attr[key]) && attr.$observe(key, function(newValue) {\n timepicker.$options[key] = dateParser.getTimeForAttribute(key, newValue);\n !isNaN(timepicker.$options[key]) && timepicker.$build();\n validateAgainstMinMaxTime(controller.$dateValue);\n });\n });\n\n // Watch model for changes\n scope.$watch(attr.ngModel, function(newValue, oldValue) {\n // console.warn('scope.$watch(%s)', attr.ngModel, newValue, oldValue, controller.$dateValue);\n timepicker.update(controller.$dateValue);\n }, true);\n\n function validateAgainstMinMaxTime(parsedTime) {\n if (!angular.isDate(parsedTime)) return;\n var isMinValid = isNaN(options.minTime) || new Date(parsedTime.getTime()).setFullYear(1970, 0, 1) >= options.minTime;\n var isMaxValid = isNaN(options.maxTime) || new Date(parsedTime.getTime()).setFullYear(1970, 0, 1) <= options.maxTime;\n var isValid = isMinValid && isMaxValid;\n controller.$setValidity('date', isValid);\n controller.$setValidity('min', isMinValid);\n controller.$setValidity('max', isMaxValid);\n // Only update the model when we have a valid date\n if (!isValid) {\n return;\n }\n controller.$dateValue = parsedTime;\n }\n\n // viewValue -> $parsers -> modelValue\n controller.$parsers.unshift(function(viewValue) {\n // console.warn('$parser(\"%s\"): viewValue=%o', element.attr('ng-model'), viewValue);\n var date;\n // Null values should correctly reset the model value & validity\n if (!viewValue) {\n // BREAKING CHANGE:\n // return null (not undefined) when input value is empty, so angularjs 1.3\n // ngModelController can go ahead and run validators, like ngRequired\n controller.$setValidity('date', true);\n return null;\n }\n var parsedTime = angular.isDate(viewValue) ? viewValue : dateParser.parse(viewValue, controller.$dateValue);\n if (!parsedTime || isNaN(parsedTime.getTime())) {\n controller.$setValidity('date', false);\n // Return undefined, causes ngModelController to\n // invalidate model value\n return undefined;\n } else {\n validateAgainstMinMaxTime(parsedTime);\n }\n\n if (options.timeType === 'string') {\n date = dateParser.timezoneOffsetAdjust(parsedTime, options.timezone, true);\n return formatDate(date, options.modelTimeFormat || options.timeFormat);\n }\n date = dateParser.timezoneOffsetAdjust(controller.$dateValue, options.timezone, true);\n if (options.timeType === 'number') {\n return date.getTime();\n } else if (options.timeType === 'unix') {\n return date.getTime() / 1000;\n } else if (options.timeType === 'iso') {\n return date.toISOString();\n } else {\n return new Date(date);\n }\n });\n\n // modelValue -> $formatters -> viewValue\n controller.$formatters.push(function(modelValue) {\n // console.warn('$formatter(\"%s\"): modelValue=%o (%o)', element.attr('ng-model'), modelValue, typeof modelValue);\n var date;\n if (angular.isUndefined(modelValue) || modelValue === null) {\n date = NaN;\n } else if (angular.isDate(modelValue)) {\n date = modelValue;\n } else if (options.timeType === 'string') {\n date = dateParser.parse(modelValue, null, options.modelTimeFormat);\n } else if (options.timeType === 'unix') {\n date = new Date(modelValue * 1000);\n } else {\n date = new Date(modelValue);\n }\n // Setup default value?\n // if(isNaN(date.getTime())) date = new Date(new Date().setMinutes(0) + 36e5);\n controller.$dateValue = dateParser.timezoneOffsetAdjust(date, options.timezone);\n return getTimeFormattedString();\n });\n\n // viewValue -> element\n controller.$render = function() {\n // console.warn('$render(\"%s\"): viewValue=%o', element.attr('ng-model'), controller.$viewValue);\n element.val(getTimeFormattedString());\n };\n\n function getTimeFormattedString() {\n return !controller.$dateValue || isNaN(controller.$dateValue.getTime()) ? '' : formatDate(controller.$dateValue, options.timeFormat);\n }\n\n // Garbage collection\n scope.$on('$destroy', function() {\n if (timepicker) timepicker.destroy();\n options = null;\n timepicker = null;\n });\n\n }\n };\n\n });\n","'use strict';\n\nangular.module('mgcrea.ngStrap.scrollspy', ['mgcrea.ngStrap.helpers.debounce', 'mgcrea.ngStrap.helpers.dimensions'])\n\n .provider('$scrollspy', function() {\n\n // Pool of registered spies\n var spies = this.$$spies = {};\n\n var defaults = this.defaults = {\n debounce: 150,\n throttle: 100,\n offset: 100\n };\n\n this.$get = function($window, $document, $rootScope, dimensions, debounce, throttle) {\n\n var windowEl = angular.element($window);\n var docEl = angular.element($document.prop('documentElement'));\n var bodyEl = angular.element($window.document.body);\n\n // Helper functions\n\n function nodeName(element, name) {\n return element[0].nodeName && element[0].nodeName.toLowerCase() === name.toLowerCase();\n }\n\n function ScrollSpyFactory(config) {\n\n // Common vars\n var options = angular.extend({}, defaults, config);\n if(!options.element) options.element = bodyEl;\n var isWindowSpy = nodeName(options.element, 'body');\n var scrollEl = isWindowSpy ? windowEl : options.element;\n var scrollId = isWindowSpy ? 'window' : options.id;\n\n // Use existing spy\n if(spies[scrollId]) {\n spies[scrollId].$$count++;\n return spies[scrollId];\n }\n\n var $scrollspy = {};\n\n // Private vars\n var unbindViewContentLoaded, unbindIncludeContentLoaded;\n var trackedElements = $scrollspy.$trackedElements = [];\n var sortedElements = [];\n var activeTarget;\n var debouncedCheckPosition;\n var throttledCheckPosition;\n var debouncedCheckOffsets;\n var viewportHeight;\n var scrollTop;\n\n $scrollspy.init = function() {\n\n // Setup internal ref counter\n this.$$count = 1;\n\n // Bind events\n debouncedCheckPosition = debounce(this.checkPosition, options.debounce);\n throttledCheckPosition = throttle(this.checkPosition, options.throttle);\n scrollEl.on('click', this.checkPositionWithEventLoop);\n windowEl.on('resize', debouncedCheckPosition);\n scrollEl.on('scroll', throttledCheckPosition);\n\n debouncedCheckOffsets = debounce(this.checkOffsets, options.debounce);\n unbindViewContentLoaded = $rootScope.$on('$viewContentLoaded', debouncedCheckOffsets);\n unbindIncludeContentLoaded = $rootScope.$on('$includeContentLoaded', debouncedCheckOffsets);\n debouncedCheckOffsets();\n\n // Register spy for reuse\n if(scrollId) {\n spies[scrollId] = $scrollspy;\n }\n\n };\n\n $scrollspy.destroy = function() {\n\n // Check internal ref counter\n this.$$count--;\n if(this.$$count > 0) {\n return;\n }\n\n // Unbind events\n scrollEl.off('click', this.checkPositionWithEventLoop);\n windowEl.off('resize', debouncedCheckPosition);\n scrollEl.off('scroll', throttledCheckPosition);\n unbindViewContentLoaded();\n unbindIncludeContentLoaded();\n if (scrollId) {\n delete spies[scrollId];\n }\n };\n\n $scrollspy.checkPosition = function() {\n\n // Not ready yet\n if(!sortedElements.length) return;\n\n // Calculate the scroll position\n scrollTop = (isWindowSpy ? $window.pageYOffset : scrollEl.prop('scrollTop')) || 0;\n\n // Calculate the viewport height for use by the components\n viewportHeight = Math.max($window.innerHeight, docEl.prop('clientHeight'));\n\n // Activate first element if scroll is smaller\n if(scrollTop < sortedElements[0].offsetTop && activeTarget !== sortedElements[0].target) {\n return $scrollspy.$activateElement(sortedElements[0]);\n }\n\n // Activate proper element\n for (var i = sortedElements.length; i--;) {\n if(angular.isUndefined(sortedElements[i].offsetTop) || sortedElements[i].offsetTop === null) continue;\n if(activeTarget === sortedElements[i].target) continue;\n if(scrollTop < sortedElements[i].offsetTop) continue;\n if(sortedElements[i + 1] && scrollTop > sortedElements[i + 1].offsetTop) continue;\n return $scrollspy.$activateElement(sortedElements[i]);\n }\n\n };\n\n $scrollspy.checkPositionWithEventLoop = function() {\n // IE 9 throws an error if we use 'this' instead of '$scrollspy'\n // in this setTimeout call\n setTimeout($scrollspy.checkPosition, 1);\n };\n\n // Protected methods\n\n $scrollspy.$activateElement = function(element) {\n if(activeTarget) {\n var activeElement = $scrollspy.$getTrackedElement(activeTarget);\n if(activeElement) {\n activeElement.source.removeClass('active');\n if(nodeName(activeElement.source, 'li') && nodeName(activeElement.source.parent().parent(), 'li')) {\n activeElement.source.parent().parent().removeClass('active');\n }\n }\n }\n activeTarget = element.target;\n element.source.addClass('active');\n if(nodeName(element.source, 'li') && nodeName(element.source.parent().parent(), 'li')) {\n element.source.parent().parent().addClass('active');\n }\n };\n\n $scrollspy.$getTrackedElement = function(target) {\n return trackedElements.filter(function(obj) {\n return obj.target === target;\n })[0];\n };\n\n // Track offsets behavior\n\n $scrollspy.checkOffsets = function() {\n\n angular.forEach(trackedElements, function(trackedElement) {\n var targetElement = document.querySelector(trackedElement.target);\n trackedElement.offsetTop = targetElement ? dimensions.offset(targetElement).top : null;\n if(options.offset && trackedElement.offsetTop !== null) trackedElement.offsetTop -= options.offset * 1;\n });\n\n sortedElements = trackedElements\n .filter(function(el) {\n return el.offsetTop !== null;\n })\n .sort(function(a, b) {\n return a.offsetTop - b.offsetTop;\n });\n\n debouncedCheckPosition();\n\n };\n\n $scrollspy.trackElement = function(target, source) {\n trackedElements.push({target: target, source: source});\n };\n\n $scrollspy.untrackElement = function(target, source) {\n var toDelete;\n for (var i = trackedElements.length; i--;) {\n if(trackedElements[i].target === target && trackedElements[i].source === source) {\n toDelete = i;\n break;\n }\n }\n trackedElements = trackedElements.splice(toDelete, 1);\n };\n\n $scrollspy.activate = function(i) {\n trackedElements[i].addClass('active');\n };\n\n // Initialize plugin\n\n $scrollspy.init();\n return $scrollspy;\n\n }\n\n return ScrollSpyFactory;\n\n };\n\n })\n\n .directive('bsScrollspy', function($rootScope, debounce, dimensions, $scrollspy) {\n\n return {\n restrict: 'EAC',\n link: function postLink(scope, element, attr) {\n\n var options = {scope: scope};\n angular.forEach(['offset', 'target'], function(key) {\n if(angular.isDefined(attr[key])) options[key] = attr[key];\n });\n\n var scrollspy = $scrollspy(options);\n scrollspy.trackElement(options.target, element);\n\n scope.$on('$destroy', function() {\n if (scrollspy) {\n scrollspy.untrackElement(options.target, element);\n scrollspy.destroy();\n }\n options = null;\n scrollspy = null;\n });\n\n }\n };\n\n })\n\n\n .directive('bsScrollspyList', function($rootScope, debounce, dimensions, $scrollspy) {\n\n return {\n restrict: 'A',\n compile: function postLink(element, attr) {\n var children = element[0].querySelectorAll('li > a[href]');\n angular.forEach(children, function(child) {\n var childEl = angular.element(child);\n childEl.parent().attr('bs-scrollspy', '').attr('data-target', childEl.attr('href'));\n });\n }\n\n };\n\n });\n","'use strict';\n\nangular.module('mgcrea.ngStrap.select', ['mgcrea.ngStrap.tooltip', 'mgcrea.ngStrap.helpers.parseOptions'])\n\n .provider('$select', function() {\n\n var defaults = this.defaults = {\n animation: 'am-fade',\n prefixClass: 'select',\n prefixEvent: '$select',\n placement: 'bottom-left',\n templateUrl: 'select/select.tpl.html',\n trigger: 'focus',\n container: false,\n keyboard: true,\n html: false,\n delay: 0,\n multiple: false,\n allNoneButtons: false,\n sort: true,\n caretHtml: '
',\n placeholder: 'Choose among the following...',\n allText: 'All',\n noneText: 'None',\n maxLength: 3,\n maxLengthHtml: 'selected',\n iconCheckmark: 'glyphicon glyphicon-ok'\n };\n\n this.$get = function($window, $document, $rootScope, $tooltip, $timeout) {\n\n var bodyEl = angular.element($window.document.body);\n var isNative = /(ip(a|o)d|iphone|android)/ig.test($window.navigator.userAgent);\n var isTouch = ('createTouch' in $window.document) && isNative;\n\n function SelectFactory(element, controller, config) {\n\n var $select = {};\n\n // Common vars\n var options = angular.extend({}, defaults, config);\n\n $select = $tooltip(element, options);\n var scope = $select.$scope;\n\n scope.$matches = [];\n if (options.multiple) {\n scope.$activeIndex = [];\n }\n else {\n scope.$activeIndex = -1;\n }\n scope.$isMultiple = options.multiple;\n scope.$showAllNoneButtons = options.allNoneButtons && options.multiple;\n scope.$iconCheckmark = options.iconCheckmark;\n scope.$allText = options.allText;\n scope.$noneText = options.noneText;\n\n scope.$activate = function(index) {\n scope.$$postDigest(function() {\n $select.activate(index);\n });\n };\n\n scope.$select = function(index, evt) {\n scope.$$postDigest(function() {\n $select.select(index);\n });\n };\n\n scope.$isVisible = function() {\n return $select.$isVisible();\n };\n\n scope.$isActive = function(index) {\n return $select.$isActive(index);\n };\n\n scope.$selectAll = function () {\n for (var i = 0; i < scope.$matches.length; i++) {\n if (!scope.$isActive(i)) {\n scope.$select(i);\n }\n }\n };\n\n scope.$selectNone = function () {\n for (var i = 0; i < scope.$matches.length; i++) {\n if (scope.$isActive(i)) {\n scope.$select(i);\n }\n }\n };\n\n // Public methods\n\n $select.update = function(matches) {\n scope.$matches = matches;\n $select.$updateActiveIndex();\n };\n\n $select.activate = function(index) {\n if(options.multiple) {\n $select.$isActive(index) ? scope.$activeIndex.splice(scope.$activeIndex.indexOf(index), 1) : scope.$activeIndex.push(index);\n if(options.sort) scope.$activeIndex.sort(function(a, b) { return a - b; }); // use numeric sort instead of default sort\n } else {\n scope.$activeIndex = index;\n }\n return scope.$activeIndex;\n };\n\n $select.select = function(index) {\n var value = scope.$matches[index].value;\n scope.$apply(function() {\n $select.activate(index);\n if(options.multiple) {\n controller.$setViewValue(scope.$activeIndex.map(function(index) {\n if (angular.isUndefined(scope.$matches[index])) {\n return null;\n }\n return scope.$matches[index].value;\n }));\n } else {\n controller.$setViewValue(value);\n // Hide if single select\n $select.hide();\n }\n });\n // Emit event\n scope.$emit(options.prefixEvent + '.select', value, index, $select);\n };\n\n // Protected methods\n\n $select.$updateActiveIndex = function() {\n if(options.multiple) {\n if(angular.isArray(controller.$modelValue)) {\n scope.$activeIndex = controller.$modelValue.map(function(value) {\n return $select.$getIndex(value);\n });\n } else {\n scope.$activeIndex = [];\n }\n } else {\n if(angular.isDefined(controller.$modelValue) && scope.$matches.length) {\n scope.$activeIndex = $select.$getIndex(controller.$modelValue);\n } else {\n scope.$activeIndex = -1;\n }\n }\n };\n\n $select.$isVisible = function() {\n if(!options.minLength || !controller) {\n return scope.$matches.length;\n }\n // minLength support\n return scope.$matches.length && controller.$viewValue.length >= options.minLength;\n };\n\n $select.$isActive = function(index) {\n if(options.multiple) {\n return scope.$activeIndex.indexOf(index) !== -1;\n } else {\n return scope.$activeIndex === index;\n }\n };\n\n $select.$getIndex = function(value) {\n var l = scope.$matches.length, i = l;\n if(!l) return;\n for(i = l; i--;) {\n if(scope.$matches[i].value === value) break;\n }\n if(i < 0) return;\n return i;\n };\n\n $select.$onMouseDown = function(evt) {\n // Prevent blur on mousedown on .dropdown-menu\n evt.preventDefault();\n evt.stopPropagation();\n // Emulate click for mobile devices\n if(isTouch) {\n var targetEl = angular.element(evt.target);\n targetEl.triggerHandler('click');\n }\n };\n\n $select.$onKeyDown = function(evt) {\n if (!/(9|13|38|40)/.test(evt.keyCode)) return;\n // Let tab propagate\n if (evt.keyCode !== 9) {\n evt.preventDefault();\n evt.stopPropagation();\n }\n\n // release focus on tab\n if (options.multiple && evt.keyCode === 9) {\n return $select.hide();\n }\n\n // Select with enter\n if(!options.multiple && (evt.keyCode === 13 || evt.keyCode === 9)) {\n return $select.select(scope.$activeIndex);\n }\n\n if (!options.multiple) {\n // Navigate with keyboard\n if(evt.keyCode === 38 && scope.$activeIndex > 0) scope.$activeIndex--;\n else if(evt.keyCode === 38 && scope.$activeIndex < 0) scope.$activeIndex = scope.$matches.length - 1;\n else if(evt.keyCode === 40 && scope.$activeIndex < scope.$matches.length - 1) scope.$activeIndex++;\n else if(angular.isUndefined(scope.$activeIndex)) scope.$activeIndex = 0;\n scope.$digest();\n }\n };\n\n $select.$isIE = function() {\n var ua = $window.navigator.userAgent;\n return ua.indexOf('MSIE ') > 0 || ua.indexOf('Trident/') > 0 || ua.indexOf('Edge/') > 0;\n };\n\n $select.$selectScrollFix = function(e) {\n if ($document[0].activeElement.tagName === 'UL') {\n e.preventDefault();\n e.stopImmediatePropagation();\n e.target.focus();\n }\n };\n\n // Overrides\n\n var _show = $select.show;\n $select.show = function() {\n _show();\n if(options.multiple) {\n $select.$element.addClass('select-multiple');\n }\n // use timeout to hookup the events to prevent\n // event bubbling from being processed imediately.\n $timeout(function() {\n $select.$element.on(isTouch ? 'touchstart' : 'mousedown', $select.$onMouseDown);\n if(options.keyboard) {\n element.on('keydown', $select.$onKeyDown);\n }\n }, 0, false);\n };\n\n var _hide = $select.hide;\n $select.hide = function() {\n if(!options.multiple && angular.isUndefined(controller.$modelValue)) {\n scope.$activeIndex = -1;\n }\n $select.$element.off(isTouch ? 'touchstart' : 'mousedown', $select.$onMouseDown);\n if(options.keyboard) {\n element.off('keydown', $select.$onKeyDown);\n }\n _hide(true);\n };\n\n return $select;\n\n }\n\n SelectFactory.defaults = defaults;\n return SelectFactory;\n\n };\n\n })\n\n .directive('bsSelect', function($window, $parse, $q, $select, $parseOptions) {\n\n var defaults = $select.defaults;\n\n return {\n restrict: 'EAC',\n require: 'ngModel',\n link: function postLink(scope, element, attr, controller) {\n\n // Directive options\n var options = {scope: scope, placeholder: defaults.placeholder};\n 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'], function(key) {\n if(angular.isDefined(attr[key])) options[key] = attr[key];\n });\n\n // use string regex match boolean attr falsy values, leave truthy values be\n var falseValueRegExp = /^(false|0|)$/i;\n angular.forEach(['html', 'container', 'allNoneButtons', 'sort'], function(key) {\n if(angular.isDefined(attr[key]) && falseValueRegExp.test(attr[key]))\n options[key] = false;\n });\n\n // Only parse data-multiple. Angular sets existence attributes to true (multiple/required/etc), they apply this\n // to data-multiple as well for some reason, so we'll parse this ourselves and disregard multiple\n var dataMultiple = element.attr('data-multiple');\n if(angular.isDefined(dataMultiple)) {\n if(falseValueRegExp.test(dataMultiple))\n options.multiple = false;\n else\n options.multiple = dataMultiple;\n }\n\n // Add support for select markup\n if(element[0].nodeName.toLowerCase() === 'select') {\n var inputEl = element;\n inputEl.css('display', 'none');\n element = angular.element('
');\n inputEl.after(element);\n }\n\n // Build proper bsOptions\n var parsedOptions = $parseOptions(attr.bsOptions);\n\n // Initialize select\n var select = $select(element, controller, options);\n\n if (select.$isIE()) {\n element[0].addEventListener('blur', select.$selectScrollFix);\n }\n\n // Watch bsOptions values before filtering for changes\n var watchedOptions = parsedOptions.$match[7].replace(/\\|.+/, '').trim();\n scope.$watch(watchedOptions, function(newValue, oldValue) {\n // console.warn('scope.$watch(%s)', watchedOptions, newValue, oldValue);\n parsedOptions.valuesFn(scope, controller)\n .then(function(values) {\n select.update(values);\n controller.$render();\n });\n }, true);\n\n // Watch model for changes\n scope.$watch(attr.ngModel, function(newValue, oldValue) {\n // console.warn('scope.$watch(%s)', attr.ngModel, newValue, oldValue);\n select.$updateActiveIndex();\n controller.$render();\n }, true);\n\n // Model rendering in view\n controller.$render = function () {\n // console.warn('$render', element.attr('ng-model'), 'controller.$modelValue', typeof controller.$modelValue, controller.$modelValue, 'controller.$viewValue', typeof controller.$viewValue, controller.$viewValue);\n var selected, index;\n if(options.multiple && angular.isArray(controller.$modelValue)) {\n selected = controller.$modelValue.map(function(value) {\n index = select.$getIndex(value);\n return angular.isDefined(index) ? select.$scope.$matches[index].label : false;\n }).filter(angular.isDefined);\n if(selected.length > (options.maxLength || defaults.maxLength)) {\n selected = selected.length + ' ' + (options.maxLengthHtml || defaults.maxLengthHtml);\n } else {\n selected = selected.join(', ');\n }\n } else {\n index = select.$getIndex(controller.$modelValue);\n selected = angular.isDefined(index) ? select.$scope.$matches[index].label : false;\n }\n element.html((selected ? selected : options.placeholder) + (options.caretHtml ? options.caretHtml : defaults.caretHtml));\n };\n\n if(options.multiple){\n controller.$isEmpty = function(value){\n return !value || value.length === 0;\n };\n }\n\n // Garbage collection\n scope.$on('$destroy', function() {\n if (select) select.destroy();\n options = null;\n select = null;\n });\n\n }\n };\n\n });\n","'use strict';\n\nangular.module('mgcrea.ngStrap.popover', ['mgcrea.ngStrap.tooltip'])\n\n .provider('$popover', function() {\n\n var defaults = this.defaults = {\n animation: 'am-fade',\n customClass: '',\n // uncommenting the next two lines will break backwards compatability\n // prefixClass: 'popover',\n // prefixEvent: 'popover',\n container: false,\n target: false,\n placement: 'right',\n templateUrl: 'popover/popover.tpl.html',\n contentTemplate: false,\n trigger: 'click',\n keyboard: true,\n html: false,\n title: '',\n content: '',\n delay: 0,\n autoClose: false\n };\n\n this.$get = function($tooltip) {\n\n function PopoverFactory(element, config) {\n\n // Common vars\n var options = angular.extend({}, defaults, config);\n\n var $popover = $tooltip(element, options);\n\n // Support scope as string options [/*title, */content]\n if(options.content) {\n $popover.$scope.content = options.content;\n }\n\n return $popover;\n\n }\n\n return PopoverFactory;\n\n };\n\n })\n\n .directive('bsPopover', function($window, $sce, $popover) {\n\n var requestAnimationFrame = $window.requestAnimationFrame || $window.setTimeout;\n\n return {\n restrict: 'EAC',\n scope: true,\n link: function postLink(scope, element, attr) {\n\n // Directive options\n var options = {scope: scope};\n angular.forEach(['template', 'templateUrl', 'controller', 'controllerAs', 'contentTemplate', 'placement', 'container', 'delay', 'trigger', 'html', 'animation', 'customClass', 'autoClose', 'id', 'prefixClass', 'prefixEvent'], function(key) {\n if(angular.isDefined(attr[key])) options[key] = attr[key];\n });\n\n // use string regex match boolean attr falsy values, leave truthy values be\n var falseValueRegExp = /^(false|0|)$/i;\n angular.forEach(['html', 'container', 'autoClose'], function(key) {\n if(angular.isDefined(attr[key]) && falseValueRegExp.test(attr[key]))\n options[key] = false;\n });\n\n // should not parse target attribute (anchor tag), only data-target #1454\n var dataTarget = element.attr('data-target');\n if(angular.isDefined(dataTarget)) {\n if(falseValueRegExp.test(dataTarget))\n options.target = false;\n else\n options.target = dataTarget;\n }\n\n // Support scope as data-attrs\n angular.forEach(['title', 'content'], function(key) {\n attr[key] && attr.$observe(key, function(newValue, oldValue) {\n scope[key] = $sce.trustAsHtml(newValue);\n angular.isDefined(oldValue) && requestAnimationFrame(function() {\n popover && popover.$applyPlacement();\n });\n });\n });\n\n // Support scope as an object\n attr.bsPopover && scope.$watch(attr.bsPopover, function(newValue, oldValue) {\n if(angular.isObject(newValue)) {\n angular.extend(scope, newValue);\n } else {\n scope.content = newValue;\n }\n angular.isDefined(oldValue) && requestAnimationFrame(function() {\n popover && popover.$applyPlacement();\n });\n }, true);\n\n // Visibility binding support\n attr.bsShow && scope.$watch(attr.bsShow, function(newValue, oldValue) {\n if(!popover || !angular.isDefined(newValue)) return;\n if(angular.isString(newValue)) newValue = !!newValue.match(/true|,?(popover),?/i);\n newValue === true ? popover.show() : popover.hide();\n });\n\n // Viewport support\n attr.viewport && scope.$watch(attr.viewport, function (newValue) {\n if(!popover || !angular.isDefined(newValue)) return;\n popover.setViewport(newValue);\n });\n\n // Initialize popover\n var popover = $popover(element, options);\n\n // Garbage collection\n scope.$on('$destroy', function() {\n if (popover) popover.destroy();\n options = null;\n popover = null;\n });\n\n }\n };\n\n });\n","'use strict';\n\n(angular.version.minor < 3 && angular.version.dot < 14) && angular.module('ng')\n\n.factory('$$rAF', function($window, $timeout) {\n\n var requestAnimationFrame = $window.requestAnimationFrame ||\n $window.webkitRequestAnimationFrame ||\n $window.mozRequestAnimationFrame;\n\n var cancelAnimationFrame = $window.cancelAnimationFrame ||\n $window.webkitCancelAnimationFrame ||\n $window.mozCancelAnimationFrame ||\n $window.webkitCancelRequestAnimationFrame;\n\n var rafSupported = !!requestAnimationFrame;\n var raf = rafSupported ?\n function(fn) {\n var id = requestAnimationFrame(fn);\n return function() {\n cancelAnimationFrame(id);\n };\n } :\n function(fn) {\n var timer = $timeout(fn, 16.66, false); // 1000 / 60 = 16.666\n return function() {\n $timeout.cancel(timer);\n };\n };\n\n raf.supported = rafSupported;\n\n return raf;\n\n});\n\n// .factory('$$animateReflow', function($$rAF, $document) {\n\n// var bodyEl = $document[0].body;\n\n// return function(fn) {\n// //the returned function acts as the cancellation function\n// return $$rAF(function() {\n// //the line below will force the browser to perform a repaint\n// //so that all the animated elements within the animation frame\n// //will be properly updated and drawn on screen. This is\n// //required to perform multi-class CSS based animations with\n// //Firefox. DO NOT REMOVE THIS LINE.\n// var a = bodyEl.offsetWidth + 1;\n// fn();\n// });\n// };\n\n// });\n","'use strict';\n\nangular.module('mgcrea.ngStrap.helpers.parseOptions', [])\n\n .provider('$parseOptions', function() {\n\n var defaults = this.defaults = {\n 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+(.*?))?$/\n };\n\n this.$get = function($parse, $q) {\n\n function ParseOptionsFactory(attr, config) {\n\n var $parseOptions = {};\n\n // Common vars\n var options = angular.extend({}, defaults, config);\n $parseOptions.$values = [];\n\n // Private vars\n var match, displayFn, valueName, keyName, groupByFn, valueFn, valuesFn;\n\n $parseOptions.init = function() {\n $parseOptions.$match = match = attr.match(options.regexp);\n displayFn = $parse(match[2] || match[1]),\n valueName = match[4] || match[6],\n keyName = match[5],\n groupByFn = $parse(match[3] || ''),\n valueFn = $parse(match[2] ? match[1] : valueName),\n valuesFn = $parse(match[7]);\n };\n\n $parseOptions.valuesFn = function(scope, controller) {\n return $q.when(valuesFn(scope, controller))\n .then(function(values) {\n if(!angular.isArray(values)) {\n values = [];\n }\n $parseOptions.$values = values.length ? parseValues(values, scope) : [];\n return $parseOptions.$values;\n });\n };\n\n $parseOptions.displayValue = function(modelValue) {\n var scope = {};\n scope[valueName] = modelValue;\n return displayFn(scope);\n };\n\n // Private functions\n\n function parseValues(values, scope) {\n return values.map(function(match, index) {\n var locals = {}, label, value;\n locals[valueName] = match;\n label = displayFn(scope, locals);\n value = valueFn(scope, locals);\n return {label: label, value: value, index: index};\n });\n }\n\n $parseOptions.init();\n return $parseOptions;\n\n }\n\n return ParseOptionsFactory;\n\n };\n\n });\n","'use strict';\n\nangular.module('mgcrea.ngStrap.helpers.dimensions', [])\n\n .factory('dimensions', function($document, $window) {\n\n var jqLite = angular.element;\n var fn = {};\n\n /**\n * Test the element nodeName\n * @param element\n * @param name\n */\n var nodeName = fn.nodeName = function(element, name) {\n return element.nodeName && element.nodeName.toLowerCase() === name.toLowerCase();\n };\n\n /**\n * Returns the element computed style\n * @param element\n * @param prop\n * @param extra\n */\n fn.css = function(element, prop, extra) {\n var value;\n if (element.currentStyle) { //IE\n value = element.currentStyle[prop];\n } else if (window.getComputedStyle) {\n value = window.getComputedStyle(element)[prop];\n } else {\n value = element.style[prop];\n }\n return extra === true ? parseFloat(value) || 0 : value;\n };\n\n /**\n * Provides read-only equivalent of jQuery's offset function:\n * @required-by bootstrap-tooltip, bootstrap-affix\n * @url http://api.jquery.com/offset/\n * @param element\n */\n fn.offset = function(element) {\n var boxRect = element.getBoundingClientRect();\n var docElement = element.ownerDocument;\n return {\n width: boxRect.width || element.offsetWidth,\n height: boxRect.height || element.offsetHeight,\n top: boxRect.top + (window.pageYOffset || docElement.documentElement.scrollTop) - (docElement.documentElement.clientTop || 0),\n left: boxRect.left + (window.pageXOffset || docElement.documentElement.scrollLeft) - (docElement.documentElement.clientLeft || 0)\n };\n };\n \n /**\n * Provides set equivalent of jQuery's offset function:\n * @required-by bootstrap-tooltip\n * @url http://api.jquery.com/offset/\n * @param element\n * @param options\n * @param i\n */\n fn.setOffset = function (element, options, i) {\n var curPosition,\n curLeft,\n curCSSTop,\n curTop,\n curOffset,\n curCSSLeft,\n calculatePosition,\n position = fn.css(element, 'position'),\n curElem = angular.element(element),\n props = {};\n \n // Set position first, in-case top/left are set even on static elem\n if (position === 'static') {\n element.style.position = 'relative';\n }\n \n curOffset = fn.offset(element);\n curCSSTop = fn.css(element, 'top');\n curCSSLeft = fn.css(element, 'left');\n calculatePosition = (position === 'absolute' || position === 'fixed') && \n (curCSSTop + curCSSLeft).indexOf('auto') > -1;\n \n // Need to be able to calculate position if either\n // top or left is auto and position is either absolute or fixed\n if (calculatePosition) {\n curPosition = fn.position(element);\n curTop = curPosition.top;\n curLeft = curPosition.left;\n } else {\n curTop = parseFloat(curCSSTop) || 0;\n curLeft = parseFloat(curCSSLeft) || 0;\n }\n \n if (angular.isFunction(options)) {\n options = options.call(element, i, curOffset);\n }\n \n if (options.top !== null ) {\n props.top = (options.top - curOffset.top) + curTop;\n }\n if ( options.left !== null ) {\n props.left = (options.left - curOffset.left) + curLeft;\n }\n\n if ('using' in options) {\n options.using.call(curElem, props);\n } else {\n curElem.css({\n top: props.top + 'px',\n left: props.left + 'px'\n });\n }\n };\n\n /**\n * Provides read-only equivalent of jQuery's position function\n * @required-by bootstrap-tooltip, bootstrap-affix\n * @url http://api.jquery.com/offset/\n * @param element\n */\n fn.position = function(element) {\n\n var offsetParentRect = {top: 0, left: 0},\n offsetParentElement,\n offset;\n\n // Fixed elements are offset from window (parentOffset = {top:0, left: 0}, because it is it's only offset parent\n if (fn.css(element, 'position') === 'fixed') {\n\n // We assume that getBoundingClientRect is available when computed position is fixed\n offset = element.getBoundingClientRect();\n\n } else {\n\n // Get *real* offsetParentElement\n offsetParentElement = offsetParent(element);\n\n // Get correct offsets\n offset = fn.offset(element);\n if (!nodeName(offsetParentElement, 'html')) {\n offsetParentRect = fn.offset(offsetParentElement);\n }\n\n // Add offsetParent borders\n offsetParentRect.top += fn.css(offsetParentElement, 'borderTopWidth', true);\n offsetParentRect.left += fn.css(offsetParentElement, 'borderLeftWidth', true);\n }\n\n // Subtract parent offsets and element margins\n return {\n width: element.offsetWidth,\n height: element.offsetHeight,\n top: offset.top - offsetParentRect.top - fn.css(element, 'marginTop', true),\n left: offset.left - offsetParentRect.left - fn.css(element, 'marginLeft', true)\n };\n\n };\n\n /**\n * Returns the closest, non-statically positioned offsetParent of a given element\n * @required-by fn.position\n * @param element\n */\n var offsetParent = function offsetParentElement(element) {\n var docElement = element.ownerDocument;\n var offsetParent = element.offsetParent || docElement;\n if(nodeName(offsetParent, '#document')) return docElement.documentElement;\n while(offsetParent && !nodeName(offsetParent, 'html') && fn.css(offsetParent, 'position') === 'static') {\n offsetParent = offsetParent.offsetParent;\n }\n return offsetParent || docElement.documentElement;\n };\n\n /**\n * Provides equivalent of jQuery's height function\n * @required-by bootstrap-affix\n * @url http://api.jquery.com/height/\n * @param element\n * @param outer\n */\n fn.height = function(element, outer) {\n var value = element.offsetHeight;\n if(outer) {\n value += fn.css(element, 'marginTop', true) + fn.css(element, 'marginBottom', true);\n } else {\n value -= fn.css(element, 'paddingTop', true) + fn.css(element, 'paddingBottom', true) + fn.css(element, 'borderTopWidth', true) + fn.css(element, 'borderBottomWidth', true);\n }\n return value;\n };\n\n /**\n * Provides equivalent of jQuery's width function\n * @required-by bootstrap-affix\n * @url http://api.jquery.com/width/\n * @param element\n * @param outer\n */\n fn.width = function(element, outer) {\n var value = element.offsetWidth;\n if(outer) {\n value += fn.css(element, 'marginLeft', true) + fn.css(element, 'marginRight', true);\n } else {\n value -= fn.css(element, 'paddingLeft', true) + fn.css(element, 'paddingRight', true) + fn.css(element, 'borderLeftWidth', true) + fn.css(element, 'borderRightWidth', true);\n }\n return value;\n };\n\n return fn;\n\n });\n","'use strict';\n\nangular.module('mgcrea.ngStrap.helpers.debounce', [])\n\n// @source jashkenas/underscore\n// @url https://github.com/jashkenas/underscore/blob/1.5.2/underscore.js#L693\n.factory('debounce', function($timeout) {\n return function(func, wait, immediate) {\n var timeout = null;\n return function() {\n var context = this,\n args = arguments,\n callNow = immediate && !timeout;\n if(timeout) {\n $timeout.cancel(timeout);\n }\n timeout = $timeout(function later() {\n timeout = null;\n if(!immediate) {\n func.apply(context, args);\n }\n }, wait, false);\n if(callNow) {\n func.apply(context, args);\n }\n return timeout;\n };\n };\n})\n\n\n// @source jashkenas/underscore\n// @url https://github.com/jashkenas/underscore/blob/1.5.2/underscore.js#L661\n.factory('throttle', function($timeout) {\n return function(func, wait, options) {\n var timeout = null;\n options || (options = {});\n return function() {\n var context = this,\n args = arguments;\n if(!timeout) {\n if(options.leading !== false) {\n func.apply(context, args);\n }\n timeout = $timeout(function later() {\n timeout = null;\n if(options.trailing !== false) {\n func.apply(context, args);\n }\n }, wait, false);\n }\n };\n };\n});\n\n","'use strict';\n\nangular.module('mgcrea.ngStrap.helpers.dateParser', [])\n\n.provider('$dateParser', function($localeProvider) {\n\n // define a custom ParseDate object to use instead of native Date\n // to avoid date values wrapping when setting date component values\n function ParseDate() {\n this.year = 1970;\n this.month = 0;\n this.day = 1;\n this.hours = 0;\n this.minutes = 0;\n this.seconds = 0;\n this.milliseconds = 0;\n }\n\n ParseDate.prototype.setMilliseconds = function(value) { this.milliseconds = value; };\n ParseDate.prototype.setSeconds = function(value) { this.seconds = value; };\n ParseDate.prototype.setMinutes = function(value) { this.minutes = value; };\n ParseDate.prototype.setHours = function(value) { this.hours = value; };\n ParseDate.prototype.getHours = function() { return this.hours; };\n ParseDate.prototype.setDate = function(value) { this.day = value; };\n ParseDate.prototype.setMonth = function(value) { this.month = value; };\n ParseDate.prototype.setFullYear = function(value) { this.year = value; };\n ParseDate.prototype.fromDate = function(value) {\n this.year = value.getFullYear();\n this.month = value.getMonth();\n this.day = value.getDate();\n this.hours = value.getHours();\n this.minutes = value.getMinutes();\n this.seconds = value.getSeconds();\n this.milliseconds = value.getMilliseconds();\n return this;\n };\n\n ParseDate.prototype.toDate = function() {\n return new Date(this.year, this.month, this.day, this.hours, this.minutes, this.seconds, this.milliseconds);\n };\n\n var proto = ParseDate.prototype;\n\n function noop() {\n }\n\n function isNumeric(n) {\n return !isNaN(parseFloat(n)) && isFinite(n);\n }\n\n function indexOfCaseInsensitive(array, value) {\n var len = array.length, str=value.toString().toLowerCase();\n for (var i=0; i
12 when midnight changeover, but then cannot generate\n * midnight datetime, so jump to 1AM, otherwise reset.\n * @param date (Date) the date to check\n * @return (Date) the corrected date\n *\n * __ copied from jquery ui datepicker __\n */\n $dateParser.daylightSavingAdjust = function(date) {\n if (!date) {\n return null;\n }\n date.setHours(date.getHours() > 12 ? date.getHours() + 2 : 0);\n return date;\n };\n\n /* Correct the date for timezone offset.\n * @param date (Date) the date to adjust\n * @param timezone (string) the timezone to adjust for\n * @param undo (boolean) to add or subtract timezone offset\n * @return (Date) the corrected date\n */\n $dateParser.timezoneOffsetAdjust = function(date, timezone, undo) {\n if (!date) {\n return null;\n }\n // Right now, only 'UTC' is supported.\n if (timezone && timezone === 'UTC') {\n date = new Date(date.getTime());\n date.setMinutes(date.getMinutes() + (undo?-1:1)*date.getTimezoneOffset());\n }\n return date;\n };\n\n // Private functions\n\n function regExpForFormat(format) {\n // `format` string can contain literal values.\n // These need to be escaped by surrounding with\n // single quotes (e.g. `\"h 'in the morning'\"`).\n // In order to output a single quote, escape it - i.e.,\n // two single quotes in a sequence (e.g. `\"h 'o''clock'\"`).\n\n var re = buildDateAbstractRegex(format);\n return buildDateParseRegex(re);\n }\n\n function buildDateAbstractRegex(format) {\n var escapedFormat = escapeReservedSymbols(format);\n var escapedLiteralFormat = escapedFormat.replace(/''/g, '\\\\\\'');\n var literalRegex = /('(?:\\\\'|.)*?')/;\n var formatParts = escapedLiteralFormat.split(literalRegex);\n var dateElements = Object.keys(regExpMap);\n var dateRegexParts = [];\n\n angular.forEach(formatParts, function (part) {\n if (isFormatStringLiteral(part)) {\n part = trimLiteralEscapeChars(part);\n }\n else {\n // Abstract replaces to avoid collisions\n for(var i = 0; i < dateElements.length; i++) {\n part = part.split(dateElements[i]).join('${' + i + '}');\n }\n }\n dateRegexParts.push(part);\n });\n\n return dateRegexParts.join('');\n }\n\n function escapeReservedSymbols(text) {\n return text.replace(/\\\\/g, '[\\\\\\\\]')\n .replace(/-/g, '[-]')\n .replace(/\\./g, '[.]')\n .replace(/\\*/g, '[*]')\n .replace(/\\+/g, '[+]')\n .replace(/\\?/g, '[?]')\n .replace(/\\$/g, '[$]')\n .replace(/\\^/g, '[^]')\n .replace(/\\//g, '[/]')\n .replace(/\\\\s/g, '[\\\\s]');\n }\n\n function isFormatStringLiteral(text) {\n return /^'.*'$/.test(text);\n }\n\n function trimLiteralEscapeChars(text) {\n return text.replace(/^'(.*)'$/, '$1');\n }\n\n function buildDateParseRegex(abstractRegex) {\n var dateElements = Object.keys(regExpMap);\n var re = abstractRegex;\n\n // Replace abstracted values\n for(var i = 0; i < dateElements.length; i++) {\n re = re.split('${' + i + '}').join('(' + regExpMap[dateElements[i]] + ')');\n }\n\n return new RegExp('^' + re + '$', ['i']);\n }\n\n function setMapForFormat(format) {\n var re = buildDateAbstractRegex(format);\n return buildDateParseValuesMap(re);\n }\n\n function buildDateParseValuesMap(abstractRegex) {\n var dateElements = Object.keys(regExpMap);\n var valuesRegex = new RegExp('\\\\${(\\\\d+)}', 'g');\n var valuesMatch, keyIndex, valueKey, valueFunction;\n var valuesFunctionMap = [];\n\n while((valuesMatch = valuesRegex.exec(abstractRegex)) !== null) {\n keyIndex = valuesMatch[1];\n valueKey = dateElements[keyIndex];\n valueFunction = setFnMap[valueKey];\n\n valuesFunctionMap.push(valueFunction);\n }\n\n return valuesFunctionMap;\n }\n\n $dateParser.init();\n return $dateParser;\n\n };\n\n return DateParserFactory;\n\n };\n\n});\n","'use strict';\n\nangular.module('mgcrea.ngStrap.helpers.dateFormatter', [])\n\n .service('$dateFormatter', function($locale, dateFilter) {\n\n // The unused `lang` arguments are on purpose. The default implementation does not\n // use them and it always uses the locale loaded into the `$locale` service.\n // Custom implementations might use it, thus allowing different directives to\n // have different languages.\n\n this.getDefaultLocale = function() {\n return $locale.id;\n };\n\n // Format is either a data format name, e.g. \"shortTime\" or \"fullDate\", or a date format\n // Return either the corresponding date format or the given date format.\n this.getDatetimeFormat = function(format, lang) {\n return $locale.DATETIME_FORMATS[format] || format;\n };\n\n this.weekdaysShort = function(lang) {\n return $locale.DATETIME_FORMATS.SHORTDAY;\n };\n\n function splitTimeFormat(format) {\n return /(h+)([:\\.])?(m+)([:\\.])?(s*)[ ]?(a?)/i.exec(format).slice(1);\n }\n\n // h:mm a => h\n this.hoursFormat = function(timeFormat) {\n return splitTimeFormat(timeFormat)[0];\n };\n\n // h:mm a => mm\n this.minutesFormat = function(timeFormat) {\n return splitTimeFormat(timeFormat)[2];\n };\n\n // h:mm:ss a => ss\n this.secondsFormat = function(timeFormat) {\n return splitTimeFormat(timeFormat)[4];\n };\n\n // h:mm a => :\n this.timeSeparator = function(timeFormat) {\n return splitTimeFormat(timeFormat)[1];\n };\n\n // h:mm:ss a => true, h:mm a => false\n this.showSeconds = function(timeFormat) {\n return !!splitTimeFormat(timeFormat)[4];\n };\n\n // h:mm a => true, H.mm => false\n this.showAM = function(timeFormat) {\n return !!splitTimeFormat(timeFormat)[5];\n };\n\n this.formatDate = function(date, format, lang, timezone){\n return dateFilter(date, format, timezone);\n };\n\n });\n","'use strict';\n\nangular.module('mgcrea.ngStrap.modal', ['mgcrea.ngStrap.core', 'mgcrea.ngStrap.helpers.dimensions'])\n\n .provider('$modal', function() {\n\n var defaults = this.defaults = {\n animation: 'am-fade',\n backdropAnimation: 'am-fade',\n prefixClass: 'modal',\n prefixEvent: 'modal',\n placement: 'top',\n templateUrl: 'modal/modal.tpl.html',\n template: '',\n contentTemplate: false,\n container: false,\n element: null,\n backdrop: true,\n keyboard: true,\n html: false,\n show: true\n };\n\n this.$get = function($window, $rootScope, $bsCompiler, $animate, $timeout, $sce, dimensions) {\n\n var forEach = angular.forEach;\n var trim = String.prototype.trim;\n var requestAnimationFrame = $window.requestAnimationFrame || $window.setTimeout;\n var bodyElement = angular.element($window.document.body);\n\n var backdropCount = 0;\n var dialogBaseZindex = 1050;\n var backdropBaseZindex = 1040;\n\n function ModalFactory(config) {\n\n var $modal = {};\n\n // Common vars\n var options = $modal.$options = angular.extend({}, defaults, config);\n var promise = $modal.$promise = $bsCompiler.compile(options);\n var scope = $modal.$scope = options.scope && options.scope.$new() || $rootScope.$new();\n\n if(!options.element && !options.container) {\n options.container = 'body';\n }\n\n // Store $id to identify the triggering element in events\n // give priority to options.id, otherwise, try to use\n // element id if defined\n $modal.$id = options.id || options.element && options.element.attr('id') || '';\n\n // Support scope as string options\n forEach(['title', 'content'], function(key) {\n if(options[key]) scope[key] = $sce.trustAsHtml(options[key]);\n });\n\n // Provide scope helpers\n scope.$hide = function() {\n scope.$$postDigest(function() {\n $modal.hide();\n });\n };\n scope.$show = function() {\n scope.$$postDigest(function() {\n $modal.show();\n });\n };\n scope.$toggle = function() {\n scope.$$postDigest(function() {\n $modal.toggle();\n });\n };\n // Publish isShown as a protected var on scope\n $modal.$isShown = scope.$isShown = false;\n\n // Fetch, compile then initialize modal\n var compileData, modalElement, modalScope;\n var backdropElement = angular.element('');\n backdropElement.css({position:'fixed', top:'0px', left:'0px', bottom:'0px', right:'0px'});\n promise.then(function(data) {\n compileData = data;\n $modal.init();\n });\n\n $modal.init = function() {\n\n // Options: show\n if(options.show) {\n scope.$$postDigest(function() {\n $modal.show();\n });\n }\n\n };\n\n $modal.destroy = function() {\n\n // Remove element\n destroyModalElement();\n\n // remove backdrop element\n if(backdropElement) {\n backdropElement.remove();\n backdropElement = null;\n }\n\n // Destroy scope\n scope.$destroy();\n };\n\n $modal.show = function() {\n if($modal.$isShown) return;\n\n var parent, after;\n if(angular.isElement(options.container)) {\n parent = options.container;\n after = options.container[0].lastChild ? angular.element(options.container[0].lastChild) : null;\n } else {\n if (options.container) {\n parent = findElement(options.container);\n after = parent[0] && parent[0].lastChild ? angular.element(parent[0].lastChild) : null;\n } else {\n parent = null;\n after = options.element;\n }\n }\n\n // destroy any existing modal elements\n if(modalElement) destroyModalElement();\n\n // create a new scope, so we can destroy it and all child scopes\n // when destroying the modal element\n modalScope = $modal.$scope.$new();\n // Fetch a cloned element linked from template (noop callback is required)\n modalElement = $modal.$element = compileData.link(modalScope, function(clonedElement, scope) {});\n\n if(options.backdrop) {\n // set z-index\n modalElement.css({'z-index': dialogBaseZindex + (backdropCount * 20)});\n backdropElement.css({'z-index': backdropBaseZindex + (backdropCount * 20)});\n\n // increment number of backdrops\n backdropCount++;\n }\n\n if(scope.$emit(options.prefixEvent + '.show.before', $modal).defaultPrevented) {\n return;\n }\n\n // Set the initial positioning.\n modalElement.css({display: 'block'}).addClass(options.placement);\n\n // Options: animation\n if(options.animation) {\n if(options.backdrop) {\n backdropElement.addClass(options.backdropAnimation);\n }\n modalElement.addClass(options.animation);\n }\n\n if(options.backdrop) {\n $animate.enter(backdropElement, bodyElement, null);\n }\n\n // Support v1.2+ $animate\n // https://github.com/angular/angular.js/issues/11713\n if(angular.version.minor <= 2) {\n $animate.enter(modalElement, parent, after, enterAnimateCallback);\n } else {\n $animate.enter(modalElement, parent, after).then(enterAnimateCallback);\n }\n\n $modal.$isShown = scope.$isShown = true;\n safeDigest(scope);\n // Focus once the enter-animation has started\n // Weird PhantomJS bug hack\n var el = modalElement[0];\n requestAnimationFrame(function() {\n el.focus();\n });\n\n bodyElement.addClass(options.prefixClass + '-open');\n if(options.animation) {\n bodyElement.addClass(options.prefixClass + '-with-' + options.animation);\n }\n\n // Bind events\n bindBackdropEvents();\n bindKeyboardEvents();\n };\n\n function enterAnimateCallback() {\n scope.$emit(options.prefixEvent + '.show', $modal);\n }\n\n $modal.hide = function() {\n if(!$modal.$isShown) return;\n\n if(options.backdrop) {\n // decrement number of modals\n backdropCount--;\n }\n\n if(scope.$emit(options.prefixEvent + '.hide.before', $modal).defaultPrevented) {\n return;\n }\n\n // Support v1.2+ $animate\n // https://github.com/angular/angular.js/issues/11713\n if(angular.version.minor <= 2) {\n $animate.leave(modalElement, leaveAnimateCallback);\n } else {\n $animate.leave(modalElement).then(leaveAnimateCallback);\n }\n\n if(options.backdrop) {\n $animate.leave(backdropElement);\n }\n $modal.$isShown = scope.$isShown = false;\n safeDigest(scope);\n\n // Unbind events\n unbindBackdropEvents();\n unbindKeyboardEvents();\n };\n\n function leaveAnimateCallback() {\n scope.$emit(options.prefixEvent + '.hide', $modal);\n bodyElement.removeClass(options.prefixClass + '-open');\n if(options.animation) {\n bodyElement.removeClass(options.prefixClass + '-with-' + options.animation);\n }\n }\n\n $modal.toggle = function() {\n\n $modal.$isShown ? $modal.hide() : $modal.show();\n\n };\n\n $modal.focus = function() {\n modalElement[0].focus();\n };\n\n // Protected methods\n\n $modal.$onKeyUp = function(evt) {\n\n if (evt.which === 27 && $modal.$isShown) {\n $modal.hide();\n evt.stopPropagation();\n }\n\n };\n\n function bindBackdropEvents() {\n if(options.backdrop) {\n modalElement.on('click', hideOnBackdropClick);\n backdropElement.on('click', hideOnBackdropClick);\n backdropElement.on('wheel', preventEventDefault);\n }\n }\n\n function unbindBackdropEvents() {\n if(options.backdrop) {\n modalElement.off('click', hideOnBackdropClick);\n backdropElement.off('click', hideOnBackdropClick);\n backdropElement.off('wheel', preventEventDefault);\n }\n }\n\n function bindKeyboardEvents() {\n if(options.keyboard) {\n modalElement.on('keyup', $modal.$onKeyUp);\n }\n }\n\n function unbindKeyboardEvents() {\n if(options.keyboard) {\n modalElement.off('keyup', $modal.$onKeyUp);\n }\n }\n\n // Private helpers\n\n function hideOnBackdropClick(evt) {\n if(evt.target !== evt.currentTarget) return;\n options.backdrop === 'static' ? $modal.focus() : $modal.hide();\n }\n\n function preventEventDefault(evt) {\n evt.preventDefault();\n }\n\n function destroyModalElement() {\n if($modal.$isShown && modalElement !== null) {\n // un-bind events\n unbindBackdropEvents();\n unbindKeyboardEvents();\n }\n\n if(modalScope) {\n modalScope.$destroy();\n modalScope = null;\n }\n\n if(modalElement) {\n modalElement.remove();\n modalElement = $modal.$element = null;\n }\n }\n\n return $modal;\n\n }\n\n // Helper functions\n\n function safeDigest(scope) {\n scope.$$phase || (scope.$root && scope.$root.$$phase) || scope.$digest();\n }\n\n function findElement(query, element) {\n return angular.element((element || document).querySelectorAll(query));\n }\n\n return ModalFactory;\n\n };\n\n })\n\n .directive('bsModal', function($window, $sce, $modal) {\n\n return {\n restrict: 'EAC',\n scope: true,\n link: function postLink(scope, element, attr, transclusion) {\n\n // Directive options\n var options = {scope: scope, element: element, show: false};\n angular.forEach(['template', 'templateUrl', 'controller', 'controllerAs', 'contentTemplate', 'placement', 'backdrop', 'keyboard', 'html', 'container', 'animation', 'backdropAnimation', 'id', 'prefixEvent', 'prefixClass'], function(key) {\n if(angular.isDefined(attr[key])) options[key] = attr[key];\n });\n\n // use string regex match boolean attr falsy values, leave truthy values be\n var falseValueRegExp = /^(false|0|)$/i;\n angular.forEach(['backdrop', 'keyboard', 'html', 'container'], function(key) {\n if(angular.isDefined(attr[key]) && falseValueRegExp.test(attr[key]))\n options[key] = false;\n });\n\n // Support scope as data-attrs\n angular.forEach(['title', 'content'], function(key) {\n attr[key] && attr.$observe(key, function(newValue, oldValue) {\n scope[key] = $sce.trustAsHtml(newValue);\n });\n });\n\n // Support scope as an object\n attr.bsModal && scope.$watch(attr.bsModal, function(newValue, oldValue) {\n if(angular.isObject(newValue)) {\n angular.extend(scope, newValue);\n } else {\n scope.content = newValue;\n }\n }, true);\n\n // Initialize modal\n var modal = $modal(options);\n\n // Trigger\n element.on(attr.trigger || 'click', modal.toggle);\n\n // Garbage collection\n scope.$on('$destroy', function() {\n if (modal) modal.destroy();\n options = null;\n modal = null;\n });\n\n }\n };\n\n });\n","'use strict';\n\nangular.module('mgcrea.ngStrap.dropdown', ['mgcrea.ngStrap.tooltip'])\n\n .provider('$dropdown', function() {\n\n var defaults = this.defaults = {\n animation: 'am-fade',\n prefixClass: 'dropdown',\n prefixEvent: 'dropdown',\n placement: 'bottom-left',\n templateUrl: 'dropdown/dropdown.tpl.html',\n trigger: 'click',\n container: false,\n keyboard: true,\n html: false,\n delay: 0\n };\n\n this.$get = function($window, $rootScope, $tooltip, $timeout) {\n\n var bodyEl = angular.element($window.document.body);\n var matchesSelector = Element.prototype.matchesSelector || Element.prototype.webkitMatchesSelector || Element.prototype.mozMatchesSelector || Element.prototype.msMatchesSelector || Element.prototype.oMatchesSelector;\n\n function DropdownFactory(element, config) {\n\n var $dropdown = {};\n\n // Common vars\n var options = angular.extend({}, defaults, config);\n var scope = $dropdown.$scope = options.scope && options.scope.$new() || $rootScope.$new();\n\n $dropdown = $tooltip(element, options);\n var parentEl = element.parent();\n\n // Protected methods\n\n $dropdown.$onKeyDown = function(evt) {\n if (!/(38|40)/.test(evt.keyCode)) return;\n evt.preventDefault();\n evt.stopPropagation();\n\n // Retrieve focused index\n var items = angular.element($dropdown.$element[0].querySelectorAll('li:not(.divider) a'));\n if (!items.length) return;\n var index;\n angular.forEach(items, function(el, i) {\n if (matchesSelector && matchesSelector.call(el, ':focus')) index = i;\n });\n\n // Navigate with keyboard\n if (evt.keyCode === 38 && index > 0) index--;\n else if (evt.keyCode === 40 && index < items.length - 1) index++;\n else if (angular.isUndefined(index)) index = 0;\n items.eq(index)[0].focus();\n\n };\n\n // Overrides\n\n var show = $dropdown.show;\n $dropdown.show = function() {\n show();\n // use timeout to hookup the events to prevent\n // event bubbling from being processed imediately.\n $timeout(function() {\n options.keyboard && $dropdown.$element && $dropdown.$element.on('keydown', $dropdown.$onKeyDown);\n bodyEl.on('click', onBodyClick);\n }, 0, false);\n parentEl.hasClass('dropdown') && parentEl.addClass('open');\n };\n\n var hide = $dropdown.hide;\n $dropdown.hide = function() {\n if (!$dropdown.$isShown) return;\n options.keyboard && $dropdown.$element && $dropdown.$element.off('keydown', $dropdown.$onKeyDown);\n bodyEl.off('click', onBodyClick);\n parentEl.hasClass('dropdown') && parentEl.removeClass('open');\n hide();\n };\n\n var destroy = $dropdown.destroy;\n $dropdown.destroy = function() {\n bodyEl.off('click', onBodyClick);\n destroy();\n };\n\n // Private functions\n\n function onBodyClick(evt) {\n if (evt.target === element[0]) return;\n return evt.target !== element[0] && $dropdown.hide();\n }\n\n return $dropdown;\n\n }\n\n return DropdownFactory;\n\n };\n\n })\n\n .directive('bsDropdown', function($window, $sce, $dropdown) {\n\n return {\n restrict: 'EAC',\n scope: true,\n compile: function(tElement, tAttrs) {\n\n // Support for inlined template (next sibling)\n // It must be fetched before compilation\n if (!tAttrs.bsDropdown) {\n var nextSibling = tElement[0].nextSibling;\n while (nextSibling && nextSibling.nodeType !== 1) {\n nextSibling = nextSibling.nextSibling;\n }\n if (nextSibling.classList.contains('dropdown-menu')) {\n tAttrs.template = nextSibling.outerHTML;\n tAttrs.templateUrl = undefined;\n nextSibling.parentNode.removeChild(nextSibling);\n }\n }\n\n return function postLink(scope, element, attr) {\n\n // Directive options\n var options = {scope: scope};\n angular.forEach(['template', 'templateUrl', 'controller', 'controllerAs', 'placement', 'container', 'delay', 'trigger', 'keyboard', 'html', 'animation', 'id', 'autoClose'], function(key) {\n if (angular.isDefined(tAttrs[key])) options[key] = tAttrs[key];\n });\n\n // use string regex match boolean attr falsy values, leave truthy values be\n var falseValueRegExp = /^(false|0|)$/i;\n angular.forEach(['html', 'container'], function(key) {\n if (angular.isDefined(attr[key]) && falseValueRegExp.test(attr[key]))\n options[key] = false;\n });\n\n // Support scope as an object\n attr.bsDropdown && scope.$watch(attr.bsDropdown, function(newValue, oldValue) {\n scope.content = newValue;\n }, true);\n\n // Visibility binding support\n attr.bsShow && scope.$watch(attr.bsShow, function(newValue, oldValue) {\n if (!dropdown || !angular.isDefined(newValue)) return;\n if (angular.isString(newValue)) newValue = !!newValue.match(/true|,?(dropdown),?/i);\n newValue === true ? dropdown.show() : dropdown.hide();\n });\n\n // Initialize dropdown\n var dropdown = $dropdown(element, options);\n\n // Garbage collection\n scope.$on('$destroy', function() {\n if (dropdown) dropdown.destroy();\n options = null;\n dropdown = null;\n });\n\n };\n }\n };\n\n });\n","'use strict';\n\nangular.module('mgcrea.ngStrap.aside', ['mgcrea.ngStrap.modal'])\n\n .provider('$aside', function() {\n\n var defaults = this.defaults = {\n animation: 'am-fade-and-slide-right',\n prefixClass: 'aside',\n prefixEvent: 'aside',\n placement: 'right',\n templateUrl: 'aside/aside.tpl.html',\n contentTemplate: false,\n container: false,\n element: null,\n backdrop: true,\n keyboard: true,\n html: false,\n show: true\n };\n\n this.$get = function($modal) {\n\n function AsideFactory(config) {\n\n var $aside = {};\n\n // Common vars\n var options = angular.extend({}, defaults, config);\n\n $aside = $modal(options);\n\n return $aside;\n\n }\n\n return AsideFactory;\n\n };\n\n })\n\n .directive('bsAside', function($window, $sce, $aside) {\n\n var requestAnimationFrame = $window.requestAnimationFrame || $window.setTimeout;\n\n return {\n restrict: 'EAC',\n scope: true,\n link: function postLink(scope, element, attr, transclusion) {\n // Directive options\n var options = {scope: scope, element: element, show: false};\n angular.forEach(['template', 'templateUrl', 'controller', 'controllerAs', 'contentTemplate', 'placement', 'backdrop', 'keyboard', 'html', 'container', 'animation'], function(key) {\n if(angular.isDefined(attr[key])) options[key] = attr[key];\n });\n\n // use string regex match boolean attr falsy values, leave truthy values be\n var falseValueRegExp = /^(false|0|)$/i;\n angular.forEach(['backdrop', 'keyboard', 'html', 'container'], function(key) {\n if(angular.isDefined(attr[key]) && falseValueRegExp.test(attr[key]))\n options[key] = false;\n });\n\n // Support scope as data-attrs\n angular.forEach(['title', 'content'], function(key) {\n attr[key] && attr.$observe(key, function(newValue, oldValue) {\n scope[key] = $sce.trustAsHtml(newValue);\n });\n });\n\n // Support scope as an object\n attr.bsAside && scope.$watch(attr.bsAside, function(newValue, oldValue) {\n if(angular.isObject(newValue)) {\n angular.extend(scope, newValue);\n } else {\n scope.content = newValue;\n }\n }, true);\n\n // Initialize aside\n var aside = $aside(options);\n\n // Trigger\n element.on(attr.trigger || 'click', aside.toggle);\n\n // Garbage collection\n scope.$on('$destroy', function() {\n if (aside) aside.destroy();\n options = null;\n aside = null;\n });\n\n }\n };\n\n });\n","'use strict';\n\nangular.module('mgcrea.ngStrap.datepicker', [\n 'mgcrea.ngStrap.helpers.dateParser',\n 'mgcrea.ngStrap.helpers.dateFormatter',\n 'mgcrea.ngStrap.tooltip'])\n\n .provider('$datepicker', function() {\n\n var defaults = this.defaults = {\n animation: 'am-fade',\n // Uncommenting the following line will break backwards compatability\n // prefixEvent: 'datepicker',\n prefixClass: 'datepicker',\n placement: 'bottom-left',\n templateUrl: 'datepicker/datepicker.tpl.html',\n trigger: 'focus',\n container: false,\n keyboard: true,\n html: false,\n delay: 0,\n // lang: $locale.id,\n useNative: false,\n dateType: 'date',\n dateFormat: 'shortDate',\n timezone: null,\n modelDateFormat: null,\n dayFormat: 'dd',\n monthFormat: 'MMM',\n yearFormat: 'yyyy',\n monthTitleFormat: 'MMMM yyyy',\n yearTitleFormat: 'yyyy',\n strictFormat: false,\n autoclose: false,\n minDate: -Infinity,\n maxDate: +Infinity,\n startView: 0,\n minView: 0,\n startWeek: 0,\n daysOfWeekDisabled: '',\n iconLeft: 'glyphicon glyphicon-chevron-left',\n iconRight: 'glyphicon glyphicon-chevron-right'\n };\n\n this.$get = function($window, $document, $rootScope, $sce, $dateFormatter, datepickerViews, $tooltip, $timeout) {\n\n var isNative = /(ip(a|o)d|iphone|android)/ig.test($window.navigator.userAgent);\n var isTouch = ('createTouch' in $window.document) && isNative;\n if (!defaults.lang) defaults.lang = $dateFormatter.getDefaultLocale();\n\n function DatepickerFactory(element, controller, config) {\n\n var $datepicker = $tooltip(element, angular.extend({}, defaults, config));\n var parentScope = config.scope;\n var options = $datepicker.$options;\n var scope = $datepicker.$scope;\n if (options.startView) options.startView -= options.minView;\n\n // View vars\n\n var pickerViews = datepickerViews($datepicker);\n $datepicker.$views = pickerViews.views;\n var viewDate = pickerViews.viewDate;\n scope.$mode = options.startView;\n scope.$iconLeft = options.iconLeft;\n scope.$iconRight = options.iconRight;\n var $picker = $datepicker.$views[scope.$mode];\n\n // Scope methods\n\n scope.$select = function(date) {\n $datepicker.select(date);\n };\n scope.$selectPane = function(value) {\n $datepicker.$selectPane(value);\n };\n scope.$toggleMode = function() {\n $datepicker.setMode((scope.$mode + 1) % $datepicker.$views.length);\n };\n\n // Public methods\n\n $datepicker.update = function(date) {\n // console.warn('$datepicker.update() newValue=%o', date);\n if (angular.isDate(date) && !isNaN(date.getTime())) {\n $datepicker.$date = date;\n $picker.update.call($picker, date);\n }\n // Build only if pristine\n $datepicker.$build(true);\n };\n\n $datepicker.updateDisabledDates = function(dateRanges) {\n options.disabledDateRanges = dateRanges;\n for (var i = 0, l = scope.rows.length; i < l; i++) {\n angular.forEach(scope.rows[i], $datepicker.$setDisabledEl);\n }\n };\n\n $datepicker.select = function(date, keep) {\n // console.warn('$datepicker.select', date, scope.$mode);\n if (!angular.isDate(controller.$dateValue)) controller.$dateValue = new Date(date);\n if (!scope.$mode || keep) {\n controller.$setViewValue(angular.copy(date));\n controller.$render();\n if (options.autoclose && !keep) {\n $timeout(function() { $datepicker.hide(true); });\n }\n } else {\n angular.extend(viewDate, {year: date.getFullYear(), month: date.getMonth(), date: date.getDate()});\n $datepicker.setMode(scope.$mode - 1);\n $datepicker.$build();\n }\n };\n\n $datepicker.setMode = function(mode) {\n // console.warn('$datepicker.setMode', mode);\n scope.$mode = mode;\n $picker = $datepicker.$views[scope.$mode];\n $datepicker.$build();\n };\n\n // Protected methods\n\n $datepicker.$build = function(pristine) {\n // console.warn('$datepicker.$build() viewDate=%o', viewDate);\n if (pristine === true && $picker.built) return;\n if (pristine === false && !$picker.built) return;\n $picker.build.call($picker);\n };\n\n $datepicker.$updateSelected = function() {\n for (var i = 0, l = scope.rows.length; i < l; i++) {\n angular.forEach(scope.rows[i], updateSelected);\n }\n };\n\n $datepicker.$isSelected = function(date) {\n return $picker.isSelected(date);\n };\n\n $datepicker.$setDisabledEl = function(el) {\n el.disabled = $picker.isDisabled(el.date);\n };\n\n $datepicker.$selectPane = function(value) {\n var steps = $picker.steps;\n // set targetDate to first day of month to avoid problems with\n // date values rollover. This assumes the viewDate does not\n // depend on the day of the month\n var targetDate = new Date(Date.UTC(viewDate.year + ((steps.year || 0) * value), viewDate.month + ((steps.month || 0) * value), 1));\n angular.extend(viewDate, {year: targetDate.getUTCFullYear(), month: targetDate.getUTCMonth(), date: targetDate.getUTCDate()});\n $datepicker.$build();\n };\n\n $datepicker.$onMouseDown = function(evt) {\n // Prevent blur on mousedown on .dropdown-menu\n evt.preventDefault();\n evt.stopPropagation();\n // Emulate click for mobile devices\n if (isTouch) {\n var targetEl = angular.element(evt.target);\n if (targetEl[0].nodeName.toLowerCase() !== 'button') {\n targetEl = targetEl.parent();\n }\n targetEl.triggerHandler('click');\n }\n };\n\n $datepicker.$onKeyDown = function(evt) {\n if (!/(38|37|39|40|13)/.test(evt.keyCode) || evt.shiftKey || evt.altKey) return;\n evt.preventDefault();\n evt.stopPropagation();\n\n if (evt.keyCode === 13) {\n if (!scope.$mode) {\n $datepicker.hide(true);\n } else {\n scope.$apply(function() { $datepicker.setMode(scope.$mode - 1); });\n }\n return;\n }\n\n // Navigate with keyboard\n $picker.onKeyDown(evt);\n parentScope.$digest();\n };\n\n // Private\n\n function updateSelected(el) {\n el.selected = $datepicker.$isSelected(el.date);\n }\n\n function focusElement() {\n element[0].focus();\n }\n\n // Overrides\n\n var _init = $datepicker.init;\n $datepicker.init = function() {\n if (isNative && options.useNative) {\n element.prop('type', 'date');\n element.css('-webkit-appearance', 'textfield');\n return;\n } else if (isTouch) {\n element.prop('type', 'text');\n element.attr('readonly', 'true');\n element.on('click', focusElement);\n }\n _init();\n };\n\n var _destroy = $datepicker.destroy;\n $datepicker.destroy = function() {\n if (isNative && options.useNative) {\n element.off('click', focusElement);\n }\n _destroy();\n };\n\n var _show = $datepicker.show;\n $datepicker.show = function() {\n if ((!isTouch && element.attr('readonly')) || element.attr('disabled')) return;\n _show();\n // use timeout to hookup the events to prevent\n // event bubbling from being processed imediately.\n $timeout(function() {\n // if $datepicker is no longer showing, don't setup events\n if (!$datepicker.$isShown) return;\n $datepicker.$element.on(isTouch ? 'touchstart' : 'mousedown', $datepicker.$onMouseDown);\n if (options.keyboard) {\n element.on('keydown', $datepicker.$onKeyDown);\n }\n }, 0, false);\n };\n\n var _hide = $datepicker.hide;\n $datepicker.hide = function(blur) {\n if (!$datepicker.$isShown) return;\n $datepicker.$element.off(isTouch ? 'touchstart' : 'mousedown', $datepicker.$onMouseDown);\n if (options.keyboard) {\n element.off('keydown', $datepicker.$onKeyDown);\n }\n _hide(blur);\n };\n\n return $datepicker;\n\n }\n\n DatepickerFactory.defaults = defaults;\n return DatepickerFactory;\n\n };\n\n })\n\n .directive('bsDatepicker', function($window, $parse, $q, $dateFormatter, $dateParser, $datepicker) {\n\n var defaults = $datepicker.defaults;\n var isNative = /(ip(a|o)d|iphone|android)/ig.test($window.navigator.userAgent);\n\n return {\n restrict: 'EAC',\n require: 'ngModel',\n link: function postLink(scope, element, attr, controller) {\n\n // Directive options\n var options = {scope: scope};\n 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'], function(key) {\n if (angular.isDefined(attr[key])) options[key] = attr[key];\n });\n\n // use string regex match boolean attr falsy values, leave truthy values be\n var falseValueRegExp = /^(false|0|)$/i;\n angular.forEach(['html', 'container', 'autoclose', 'useNative'], function(key) {\n if (angular.isDefined(attr[key]) && falseValueRegExp.test(attr[key])) {\n options[key] = false;\n }\n });\n\n // Initialize datepicker\n var datepicker = $datepicker(element, controller, options);\n options = datepicker.$options;\n // Set expected iOS format\n if (isNative && options.useNative) options.dateFormat = 'yyyy-MM-dd';\n\n var lang = options.lang;\n\n var formatDate = function(date, format) {\n return $dateFormatter.formatDate(date, format, lang);\n };\n\n var dateParser = $dateParser({format: options.dateFormat, lang: lang, strict: options.strictFormat});\n\n // Visibility binding support\n attr.bsShow && scope.$watch(attr.bsShow, function(newValue, oldValue) {\n if (!datepicker || !angular.isDefined(newValue)) return;\n if (angular.isString(newValue)) newValue = !!newValue.match(/true|,?(datepicker),?/i);\n newValue === true ? datepicker.show() : datepicker.hide();\n });\n\n // Observe attributes for changes\n angular.forEach(['minDate', 'maxDate'], function(key) {\n // console.warn('attr.$observe(%s)', key, attr[key]);\n angular.isDefined(attr[key]) && attr.$observe(key, function(newValue) {\n // console.warn('attr.$observe(%s)=%o', key, newValue);\n datepicker.$options[key] = dateParser.getDateForAttribute(key, newValue);\n // Build only if dirty\n !isNaN(datepicker.$options[key]) && datepicker.$build(false);\n validateAgainstMinMaxDate(controller.$dateValue);\n });\n });\n\n // Observe date format\n angular.isDefined(attr.dateFormat) && attr.$observe('dateFormat', function(newValue) {\n datepicker.$options.dateFormat = newValue;\n });\n\n // Watch model for changes\n scope.$watch(attr.ngModel, function(newValue, oldValue) {\n datepicker.update(controller.$dateValue);\n }, true);\n\n // Normalize undefined/null/empty array,\n // so that we don't treat changing from undefined->null as a change.\n function normalizeDateRanges(ranges) {\n if (!ranges || !ranges.length) return null;\n return ranges;\n }\n\n if (angular.isDefined(attr.disabledDates)) {\n scope.$watch(attr.disabledDates, function(disabledRanges, previousValue) {\n disabledRanges = normalizeDateRanges(disabledRanges);\n previousValue = normalizeDateRanges(previousValue);\n\n if (disabledRanges) {\n datepicker.updateDisabledDates(disabledRanges);\n }\n });\n }\n\n function validateAgainstMinMaxDate(parsedDate) {\n if (!angular.isDate(parsedDate)) return;\n var isMinValid = isNaN(datepicker.$options.minDate) || parsedDate.getTime() >= datepicker.$options.minDate;\n var isMaxValid = isNaN(datepicker.$options.maxDate) || parsedDate.getTime() <= datepicker.$options.maxDate;\n var isValid = isMinValid && isMaxValid;\n controller.$setValidity('date', isValid);\n controller.$setValidity('min', isMinValid);\n controller.$setValidity('max', isMaxValid);\n // Only update the model when we have a valid date\n if (isValid) controller.$dateValue = parsedDate;\n }\n\n // viewValue -> $parsers -> modelValue\n controller.$parsers.unshift(function(viewValue) {\n // console.warn('$parser(\"%s\"): viewValue=%o', element.attr('ng-model'), viewValue);\n var date;\n // Null values should correctly reset the model value & validity\n if (!viewValue) {\n controller.$setValidity('date', true);\n // BREAKING CHANGE:\n // return null (not undefined) when input value is empty, so angularjs 1.3\n // ngModelController can go ahead and run validators, like ngRequired\n return null;\n }\n var parsedDate = dateParser.parse(viewValue, controller.$dateValue);\n if (!parsedDate || isNaN(parsedDate.getTime())) {\n controller.$setValidity('date', false);\n // return undefined, causes ngModelController to\n // invalidate model value\n return;\n } else {\n validateAgainstMinMaxDate(parsedDate);\n }\n\n if (options.dateType === 'string') {\n date = dateParser.timezoneOffsetAdjust(parsedDate, options.timezone, true);\n return formatDate(date, options.modelDateFormat || options.dateFormat);\n }\n date = dateParser.timezoneOffsetAdjust(controller.$dateValue, options.timezone, true);\n if (options.dateType === 'number') {\n return date.getTime();\n } else if (options.dateType === 'unix') {\n return date.getTime() / 1000;\n } else if (options.dateType === 'iso') {\n return date.toISOString();\n } else {\n return new Date(date);\n }\n });\n\n // modelValue -> $formatters -> viewValue\n controller.$formatters.push(function(modelValue) {\n // console.warn('$formatter(\"%s\"): modelValue=%o (%o)', element.attr('ng-model'), modelValue, typeof modelValue);\n var date;\n if (angular.isUndefined(modelValue) || modelValue === null) {\n date = NaN;\n } else if (angular.isDate(modelValue)) {\n date = modelValue;\n } else if (options.dateType === 'string') {\n date = dateParser.parse(modelValue, null, options.modelDateFormat);\n } else if (options.dateType === 'unix') {\n date = new Date(modelValue * 1000);\n } else {\n date = new Date(modelValue);\n }\n // Setup default value?\n // if (isNaN(date.getTime())) {\n // var today = new Date();\n // date = new Date(today.getFullYear(), today.getMonth(), today.getDate(), 0, 0, 0, 0);\n // }\n controller.$dateValue = dateParser.timezoneOffsetAdjust(date, options.timezone);\n return getDateFormattedString();\n });\n\n // viewValue -> element\n controller.$render = function() {\n // console.warn('$render(\"%s\"): viewValue=%o', element.attr('ng-model'), controller.$viewValue);\n element.val(getDateFormattedString());\n };\n\n function getDateFormattedString() {\n return !controller.$dateValue || isNaN(controller.$dateValue.getTime()) ? '' : formatDate(controller.$dateValue, options.dateFormat);\n }\n\n // Garbage collection\n scope.$on('$destroy', function() {\n if (datepicker) datepicker.destroy();\n options = null;\n datepicker = null;\n });\n\n }\n };\n\n })\n\n .provider('datepickerViews', function() {\n\n var defaults = this.defaults = {\n dayFormat: 'dd',\n daySplit: 7\n };\n\n // Split array into smaller arrays\n function split(arr, size) {\n var arrays = [];\n while(arr.length > 0) {\n arrays.push(arr.splice(0, size));\n }\n return arrays;\n }\n\n // Modulus operator\n function mod(n, m) {\n return ((n % m) + m) % m;\n }\n\n this.$get = function($dateFormatter, $dateParser, $sce) {\n\n return function(picker) {\n\n var scope = picker.$scope;\n var options = picker.$options;\n\n var lang = options.lang;\n var formatDate = function(date, format) {\n return $dateFormatter.formatDate(date, format, lang);\n };\n var dateParser = $dateParser({format: options.dateFormat, lang: lang, strict: options.strictFormat});\n\n var weekDaysMin = $dateFormatter.weekdaysShort(lang);\n var weekDaysLabels = weekDaysMin.slice(options.startWeek).concat(weekDaysMin.slice(0, options.startWeek));\n var weekDaysLabelsHtml = $sce.trustAsHtml('' + weekDaysLabels.join(' | ') + ' | ');\n\n var startDate = picker.$date || (options.startDate ? dateParser.getDateForAttribute('startDate', options.startDate) : new Date());\n var viewDate = {year: startDate.getFullYear(), month: startDate.getMonth(), date: startDate.getDate()};\n\n var views = [{\n format: options.dayFormat,\n split: 7,\n steps: { month: 1 },\n update: function(date, force) {\n if (!this.built || force || date.getFullYear() !== viewDate.year || date.getMonth() !== viewDate.month) {\n angular.extend(viewDate, {year: picker.$date.getFullYear(), month: picker.$date.getMonth(), date: picker.$date.getDate()});\n picker.$build();\n } else if (date.getDate() !== viewDate.date || date.getDate() === 1) {\n // chaging picker current month will cause viewDate.date to be set to first day of the month,\n // in $datepicker.$selectPane, so picker would not update selected day display if\n // user picks first day of the new month.\n // As a workaround, we are always forcing update when picked date is first day of month.\n viewDate.date = picker.$date.getDate();\n picker.$updateSelected();\n }\n },\n build: function() {\n var firstDayOfMonth = new Date(viewDate.year, viewDate.month, 1), firstDayOfMonthOffset = firstDayOfMonth.getTimezoneOffset();\n var firstDate = new Date(+firstDayOfMonth - mod(firstDayOfMonth.getDay() - options.startWeek, 7) * 864e5), firstDateOffset = firstDate.getTimezoneOffset();\n var today = dateParser.timezoneOffsetAdjust(new Date(), options.timezone).toDateString();\n // Handle daylight time switch\n if (firstDateOffset !== firstDayOfMonthOffset) firstDate = new Date(+firstDate + (firstDateOffset - firstDayOfMonthOffset) * 60e3);\n var days = [], day;\n for(var i = 0; i < 42; i++) { // < 7 * 6\n day = dateParser.daylightSavingAdjust(new Date(firstDate.getFullYear(), firstDate.getMonth(), firstDate.getDate() + i));\n days.push({date: day, isToday: day.toDateString() === today, label: formatDate(day, this.format), selected: picker.$date && this.isSelected(day), muted: day.getMonth() !== viewDate.month, disabled: this.isDisabled(day)});\n }\n scope.title = formatDate(firstDayOfMonth, options.monthTitleFormat);\n scope.showLabels = true;\n scope.labels = weekDaysLabelsHtml;\n scope.rows = split(days, this.split);\n this.built = true;\n },\n isSelected: function(date) {\n return picker.$date && date.getFullYear() === picker.$date.getFullYear() && date.getMonth() === picker.$date.getMonth() && date.getDate() === picker.$date.getDate();\n },\n isDisabled: function(date) {\n var time = date.getTime();\n\n // Disabled because of min/max date.\n if (time < options.minDate || time > options.maxDate) return true;\n\n // Disabled due to being a disabled day of the week\n if (options.daysOfWeekDisabled.indexOf(date.getDay()) !== -1) return true;\n\n // Disabled because of disabled date range.\n if (options.disabledDateRanges) {\n for (var i = 0; i < options.disabledDateRanges.length; i++) {\n if (time >= options.disabledDateRanges[i].start && time <= options.disabledDateRanges[i].end) {\n return true;\n }\n }\n }\n\n return false;\n },\n onKeyDown: function(evt) {\n if (!picker.$date) {\n return;\n }\n var actualTime = picker.$date.getTime();\n var newDate;\n\n if (evt.keyCode === 37) newDate = new Date(actualTime - 1 * 864e5);\n else if (evt.keyCode === 38) newDate = new Date(actualTime - 7 * 864e5);\n else if (evt.keyCode === 39) newDate = new Date(actualTime + 1 * 864e5);\n else if (evt.keyCode === 40) newDate = new Date(actualTime + 7 * 864e5);\n\n if (!this.isDisabled(newDate)) picker.select(newDate, true);\n }\n }, {\n name: 'month',\n format: options.monthFormat,\n split: 4,\n steps: { year: 1 },\n update: function(date, force) {\n if (!this.built || date.getFullYear() !== viewDate.year) {\n angular.extend(viewDate, {year: picker.$date.getFullYear(), month: picker.$date.getMonth(), date: picker.$date.getDate()});\n picker.$build();\n } else if (date.getMonth() !== viewDate.month) {\n angular.extend(viewDate, {month: picker.$date.getMonth(), date: picker.$date.getDate()});\n picker.$updateSelected();\n }\n },\n build: function() {\n var firstMonth = new Date(viewDate.year, 0, 1);\n var months = [], month;\n for (var i = 0; i < 12; i++) {\n month = new Date(viewDate.year, i, 1);\n months.push({date: month, label: formatDate(month, this.format), selected: picker.$isSelected(month), disabled: this.isDisabled(month)});\n }\n scope.title = formatDate(month, options.yearTitleFormat);\n scope.showLabels = false;\n scope.rows = split(months, this.split);\n this.built = true;\n },\n isSelected: function(date) {\n return picker.$date && date.getFullYear() === picker.$date.getFullYear() && date.getMonth() === picker.$date.getMonth();\n },\n isDisabled: function(date) {\n var lastDate = +new Date(date.getFullYear(), date.getMonth() + 1, 0);\n return lastDate < options.minDate || date.getTime() > options.maxDate;\n },\n onKeyDown: function(evt) {\n if (!picker.$date) {\n return;\n }\n var actualMonth = picker.$date.getMonth();\n var newDate = new Date(picker.$date);\n\n if (evt.keyCode === 37) newDate.setMonth(actualMonth - 1);\n else if (evt.keyCode === 38) newDate.setMonth(actualMonth - 4);\n else if (evt.keyCode === 39) newDate.setMonth(actualMonth + 1);\n else if (evt.keyCode === 40) newDate.setMonth(actualMonth + 4);\n\n if (!this.isDisabled(newDate)) picker.select(newDate, true);\n }\n }, {\n name: 'year',\n format: options.yearFormat,\n split: 4,\n steps: { year: 12 },\n update: function(date, force) {\n if (!this.built || force || parseInt(date.getFullYear()/20, 10) !== parseInt(viewDate.year/20, 10)) {\n angular.extend(viewDate, {year: picker.$date.getFullYear(), month: picker.$date.getMonth(), date: picker.$date.getDate()});\n picker.$build();\n } else if (date.getFullYear() !== viewDate.year) {\n angular.extend(viewDate, {year: picker.$date.getFullYear(), month: picker.$date.getMonth(), date: picker.$date.getDate()});\n picker.$updateSelected();\n }\n },\n build: function() {\n var firstYear = viewDate.year - viewDate.year % (this.split * 3);\n var years = [], year;\n for (var i = 0; i < 12; i++) {\n year = new Date(firstYear + i, 0, 1);\n years.push({date: year, label: formatDate(year, this.format), selected: picker.$isSelected(year), disabled: this.isDisabled(year)});\n }\n scope.title = years[0].label + '-' + years[years.length - 1].label;\n scope.showLabels = false;\n scope.rows = split(years, this.split);\n this.built = true;\n },\n isSelected: function(date) {\n return picker.$date && date.getFullYear() === picker.$date.getFullYear();\n },\n isDisabled: function(date) {\n var lastDate = +new Date(date.getFullYear() + 1, 0, 0);\n return lastDate < options.minDate || date.getTime() > options.maxDate;\n },\n onKeyDown: function(evt) {\n if (!picker.$date) {\n return;\n }\n var actualYear = picker.$date.getFullYear(),\n newDate = new Date(picker.$date);\n\n if (evt.keyCode === 37) newDate.setYear(actualYear - 1);\n else if (evt.keyCode === 38) newDate.setYear(actualYear - 4);\n else if (evt.keyCode === 39) newDate.setYear(actualYear + 1);\n else if (evt.keyCode === 40) newDate.setYear(actualYear + 4);\n\n if (!this.isDisabled(newDate)) picker.select(newDate, true);\n }\n }];\n\n return {\n views: options.minView ? Array.prototype.slice.call(views, options.minView) : views,\n viewDate: viewDate\n };\n\n };\n\n };\n\n });\n","'use strict';\n\nangular.module('mgcrea.ngStrap.collapse', [])\n\n .provider('$collapse', function() {\n\n var defaults = this.defaults = {\n animation: 'am-collapse',\n disallowToggle: false,\n activeClass: 'in',\n startCollapsed: false,\n allowMultiple: false\n };\n\n var controller = this.controller = function($scope, $element, $attrs) {\n var self = this;\n\n // Attributes options\n self.$options = angular.copy(defaults);\n angular.forEach(['animation', 'disallowToggle', 'activeClass', 'startCollapsed', 'allowMultiple'], function (key) {\n if(angular.isDefined($attrs[key])) self.$options[key] = $attrs[key];\n });\n\n // use string regex match boolean attr falsy values, leave truthy values be\n var falseValueRegExp = /^(false|0|)$/i;\n angular.forEach(['disallowToggle', 'startCollapsed', 'allowMultiple'], function(key) {\n if(angular.isDefined($attrs[key]) && falseValueRegExp.test($attrs[key])) {\n self.$options[key] = false;\n }\n });\n\n self.$toggles = [];\n self.$targets = [];\n\n self.$viewChangeListeners = [];\n\n self.$registerToggle = function(element) {\n self.$toggles.push(element);\n };\n self.$registerTarget = function(element) {\n self.$targets.push(element);\n };\n\n self.$unregisterToggle = function(element) {\n var index = self.$toggles.indexOf(element);\n // remove toggle from $toggles array\n self.$toggles.splice(index, 1);\n };\n self.$unregisterTarget = function(element) {\n var index = self.$targets.indexOf(element);\n\n // remove element from $targets array\n self.$targets.splice(index, 1);\n\n if (self.$options.allowMultiple) {\n // remove target index from $active array values\n deactivateItem(element);\n }\n\n // fix active item indexes\n fixActiveItemIndexes(index);\n\n self.$viewChangeListeners.forEach(function(fn) {\n fn();\n });\n };\n\n // use array to store all the currently open panels\n self.$targets.$active = !self.$options.startCollapsed ? [0] : [];\n self.$setActive = $scope.$setActive = function(value) {\n if(angular.isArray(value)) {\n self.$targets.$active = value;\n }\n else if(!self.$options.disallowToggle) {\n // toogle element active status\n isActive(value) ? deactivateItem(value) : activateItem(value);\n } else {\n activateItem(value);\n }\n\n self.$viewChangeListeners.forEach(function(fn) {\n fn();\n });\n };\n\n self.$activeIndexes = function() {\n return self.$options.allowMultiple ? self.$targets.$active :\n self.$targets.$active.length === 1 ? self.$targets.$active[0] : -1;\n };\n\n function fixActiveItemIndexes(index) {\n // item with index was removed, so we\n // need to adjust other items index values\n var activeIndexes = self.$targets.$active;\n for(var i = 0; i < activeIndexes.length; i++) {\n if (index < activeIndexes[i]) {\n activeIndexes[i] = activeIndexes[i] - 1;\n }\n\n // the last item is active, so we need to\n // adjust its index\n if (activeIndexes[i] === self.$targets.length) {\n activeIndexes[i] = self.$targets.length - 1;\n }\n }\n }\n\n function isActive(value) {\n var activeItems = self.$targets.$active;\n return activeItems.indexOf(value) === -1 ? false : true;\n }\n\n function deactivateItem(value) {\n var index = self.$targets.$active.indexOf(value);\n if (index !== -1) {\n self.$targets.$active.splice(index, 1);\n }\n }\n\n function activateItem(value) {\n if (!self.$options.allowMultiple) {\n // remove current selected item\n self.$targets.$active.splice(0, 1);\n }\n\n if (self.$targets.$active.indexOf(value) === -1) {\n self.$targets.$active.push(value);\n }\n }\n\n };\n\n this.$get = function() {\n var $collapse = {};\n $collapse.defaults = defaults;\n $collapse.controller = controller;\n return $collapse;\n };\n\n })\n\n .directive('bsCollapse', function($window, $animate, $collapse) {\n\n var defaults = $collapse.defaults;\n\n return {\n require: ['?ngModel', 'bsCollapse'],\n controller: ['$scope', '$element', '$attrs', $collapse.controller],\n link: function postLink(scope, element, attrs, controllers) {\n\n var ngModelCtrl = controllers[0];\n var bsCollapseCtrl = controllers[1];\n\n if(ngModelCtrl) {\n\n // Update the modelValue following\n bsCollapseCtrl.$viewChangeListeners.push(function() {\n ngModelCtrl.$setViewValue(bsCollapseCtrl.$activeIndexes());\n });\n\n // modelValue -> $formatters -> viewValue\n ngModelCtrl.$formatters.push(function(modelValue) {\n // console.warn('$formatter(\"%s\"): modelValue=%o (%o)', element.attr('ng-model'), modelValue, typeof modelValue);\n if (angular.isArray(modelValue)) {\n // model value is an array, so just replace\n // the active items directly\n bsCollapseCtrl.$setActive(modelValue);\n }\n else {\n var activeIndexes = bsCollapseCtrl.$activeIndexes();\n\n if (angular.isArray(activeIndexes)) {\n // we have an array of selected indexes\n if (activeIndexes.indexOf(modelValue * 1) === -1) {\n // item with modelValue index is not active\n bsCollapseCtrl.$setActive(modelValue * 1);\n }\n }\n else if (activeIndexes !== modelValue * 1) {\n bsCollapseCtrl.$setActive(modelValue * 1);\n }\n }\n return modelValue;\n });\n\n }\n\n }\n };\n\n })\n\n .directive('bsCollapseToggle', function() {\n\n return {\n require: ['^?ngModel', '^bsCollapse'],\n link: function postLink(scope, element, attrs, controllers) {\n\n var ngModelCtrl = controllers[0];\n var bsCollapseCtrl = controllers[1];\n\n // Add base attr\n element.attr('data-toggle', 'collapse');\n\n // Push pane to parent bsCollapse controller\n bsCollapseCtrl.$registerToggle(element);\n\n // remove toggle from collapse controller when toggle is destroyed\n scope.$on('$destroy', function() {\n bsCollapseCtrl.$unregisterToggle(element);\n });\n\n element.on('click', function() {\n var index = attrs.bsCollapseToggle && attrs.bsCollapseToggle !== 'bs-collapse-toggle' ? attrs.bsCollapseToggle : bsCollapseCtrl.$toggles.indexOf(element);\n bsCollapseCtrl.$setActive(index * 1);\n scope.$apply();\n });\n\n }\n };\n\n })\n\n .directive('bsCollapseTarget', function($animate) {\n\n return {\n require: ['^?ngModel', '^bsCollapse'],\n // scope: true,\n link: function postLink(scope, element, attrs, controllers) {\n\n var ngModelCtrl = controllers[0];\n var bsCollapseCtrl = controllers[1];\n\n // Add base class\n element.addClass('collapse');\n\n // Add animation class\n if(bsCollapseCtrl.$options.animation) {\n element.addClass(bsCollapseCtrl.$options.animation);\n }\n\n // Push pane to parent bsCollapse controller\n bsCollapseCtrl.$registerTarget(element);\n\n // remove pane target from collapse controller when target is destroyed\n scope.$on('$destroy', function() {\n bsCollapseCtrl.$unregisterTarget(element);\n });\n\n function render() {\n var index = bsCollapseCtrl.$targets.indexOf(element);\n var active = bsCollapseCtrl.$activeIndexes();\n var action = 'removeClass';\n if (angular.isArray(active)) {\n if (active.indexOf(index) !== -1) {\n action = 'addClass';\n }\n }\n else if (index === active) {\n action = 'addClass';\n }\n\n $animate[action](element, bsCollapseCtrl.$options.activeClass);\n }\n\n bsCollapseCtrl.$viewChangeListeners.push(function() {\n render();\n });\n render();\n\n }\n };\n\n });\n","'use strict';\n\nangular.module('mgcrea.ngStrap.button', [])\n\n .provider('$button', function() {\n\n var defaults = this.defaults = {\n activeClass:'active',\n toggleEvent:'click'\n };\n\n this.$get = function() {\n return {defaults: defaults};\n };\n\n })\n\n .directive('bsCheckboxGroup', function() {\n\n return {\n restrict: 'A',\n require: 'ngModel',\n compile: function postLink(element, attr) {\n element.attr('data-toggle', 'buttons');\n element.removeAttr('ng-model');\n var children = element[0].querySelectorAll('input[type=\"checkbox\"]');\n angular.forEach(children, function(child) {\n var childEl = angular.element(child);\n childEl.attr('bs-checkbox', '');\n childEl.attr('ng-model', attr.ngModel + '.' + childEl.attr('value'));\n });\n }\n\n };\n\n })\n\n .directive('bsCheckbox', function($button, $$rAF) {\n\n var defaults = $button.defaults;\n var constantValueRegExp = /^(true|false|\\d+)$/;\n\n return {\n restrict: 'A',\n require: 'ngModel',\n link: function postLink(scope, element, attr, controller) {\n\n var options = defaults;\n\n // Support label > input[type=\"checkbox\"]\n var isInput = element[0].nodeName === 'INPUT';\n var activeElement = isInput ? element.parent() : element;\n\n var trueValue = angular.isDefined(attr.trueValue) ? attr.trueValue : true;\n if(constantValueRegExp.test(attr.trueValue)) {\n trueValue = scope.$eval(attr.trueValue);\n }\n var falseValue = angular.isDefined(attr.falseValue) ? attr.falseValue : false;\n if(constantValueRegExp.test(attr.falseValue)) {\n falseValue = scope.$eval(attr.falseValue);\n }\n\n // Parse exotic values\n var hasExoticValues = typeof trueValue !== 'boolean' || typeof falseValue !== 'boolean';\n if(hasExoticValues) {\n controller.$parsers.push(function(viewValue) {\n // console.warn('$parser', element.attr('ng-model'), 'viewValue', viewValue);\n return viewValue ? trueValue : falseValue;\n });\n // modelValue -> $formatters -> viewValue\n controller.$formatters.push(function(modelValue) {\n // console.warn('$formatter(\"%s\"): modelValue=%o (%o)', element.attr('ng-model'), modelValue, typeof modelValue);\n return angular.equals(modelValue, trueValue);\n });\n // Fix rendering for exotic values\n scope.$watch(attr.ngModel, function(newValue, oldValue) {\n controller.$render();\n });\n }\n\n // model -> view\n controller.$render = function () {\n // console.warn('$render', element.attr('ng-model'), 'controller.$modelValue', typeof controller.$modelValue, controller.$modelValue, 'controller.$viewValue', typeof controller.$viewValue, controller.$viewValue);\n var isActive = angular.equals(controller.$modelValue, trueValue);\n $$rAF(function() {\n if(isInput) element[0].checked = isActive;\n activeElement.toggleClass(options.activeClass, isActive);\n });\n };\n\n // view -> model\n element.bind(options.toggleEvent, function() {\n scope.$apply(function () {\n // console.warn('!click', element.attr('ng-model'), 'controller.$viewValue', typeof controller.$viewValue, controller.$viewValue, 'controller.$modelValue', typeof controller.$modelValue, controller.$modelValue);\n if(!isInput) {\n controller.$setViewValue(!activeElement.hasClass('active'));\n }\n if(!hasExoticValues) {\n controller.$render();\n }\n });\n });\n\n }\n\n };\n\n })\n\n .directive('bsRadioGroup', function() {\n\n return {\n restrict: 'A',\n require: 'ngModel',\n compile: function postLink(element, attr) {\n element.attr('data-toggle', 'buttons');\n element.removeAttr('ng-model');\n var children = element[0].querySelectorAll('input[type=\"radio\"]');\n angular.forEach(children, function(child) {\n angular.element(child).attr('bs-radio', '');\n angular.element(child).attr('ng-model', attr.ngModel);\n });\n }\n\n };\n\n })\n\n .directive('bsRadio', function($button, $$rAF) {\n\n var defaults = $button.defaults;\n var constantValueRegExp = /^(true|false|\\d+)$/;\n\n return {\n restrict: 'A',\n require: 'ngModel',\n link: function postLink(scope, element, attr, controller) {\n\n var options = defaults;\n\n // Support `label > input[type=\"radio\"]` markup\n var isInput = element[0].nodeName === 'INPUT';\n var activeElement = isInput ? element.parent() : element;\n\n var value;\n attr.$observe('value', function(v) {\n value = constantValueRegExp.test(v) ? scope.$eval(v) : v;\n controller.$render();\n });\n\n // model -> view\n controller.$render = function () {\n // console.warn('$render', element.attr('value'), 'controller.$modelValue', typeof controller.$modelValue, controller.$modelValue, 'controller.$viewValue', typeof controller.$viewValue, controller.$viewValue);\n var isActive = angular.equals(controller.$modelValue, value);\n $$rAF(function() {\n if(isInput) element[0].checked = isActive;\n activeElement.toggleClass(options.activeClass, isActive);\n });\n };\n\n // view -> model\n element.bind(options.toggleEvent, function() {\n scope.$apply(function () {\n // console.warn('!click', element.attr('value'), 'controller.$viewValue', typeof controller.$viewValue, controller.$viewValue, 'controller.$modelValue', typeof controller.$modelValue, controller.$modelValue);\n controller.$setViewValue(value);\n controller.$render();\n });\n });\n\n }\n\n };\n\n });\n","'use strict';\n\n// @BUG: following snippet won't compile correctly\n// @TODO: submit issue to core\n// ' ' +\n\nangular.module('mgcrea.ngStrap.alert', ['mgcrea.ngStrap.modal'])\n\n .provider('$alert', function() {\n\n var defaults = this.defaults = {\n animation: 'am-fade',\n prefixClass: 'alert',\n prefixEvent: 'alert',\n placement: null,\n templateUrl: 'alert/alert.tpl.html',\n container: false,\n element: null,\n backdrop: false,\n keyboard: true,\n show: true,\n // Specific options\n duration: false,\n type: false,\n dismissable: true\n };\n\n this.$get = function($modal, $timeout) {\n\n function AlertFactory(config) {\n\n var $alert = {};\n\n // Common vars\n var options = angular.extend({}, defaults, config);\n\n $alert = $modal(options);\n\n // Support scope as string options [/*title, content, */ type, dismissable]\n $alert.$scope.dismissable = !!options.dismissable;\n if(options.type) {\n $alert.$scope.type = options.type;\n }\n\n // Support auto-close duration\n var show = $alert.show;\n if(options.duration) {\n $alert.show = function() {\n show();\n $timeout(function() {\n $alert.hide();\n }, options.duration * 1000);\n };\n }\n\n return $alert;\n\n }\n\n return AlertFactory;\n\n };\n\n })\n\n .directive('bsAlert', function($window, $sce, $alert) {\n\n var requestAnimationFrame = $window.requestAnimationFrame || $window.setTimeout;\n\n return {\n restrict: 'EAC',\n scope: true,\n link: function postLink(scope, element, attr, transclusion) {\n\n // Directive options\n var options = {scope: scope, element: element, show: false};\n angular.forEach(['template', 'templateUrl', 'controller', 'controllerAs', 'placement', 'keyboard', 'html', 'container', 'animation', 'duration', 'dismissable'], function(key) {\n if(angular.isDefined(attr[key])) options[key] = attr[key];\n });\n\n // use string regex match boolean attr falsy values, leave truthy values be\n var falseValueRegExp = /^(false|0|)$/i;\n angular.forEach(['keyboard', 'html', 'container', 'dismissable'], function(key) {\n if(angular.isDefined(attr[key]) && falseValueRegExp.test(attr[key]))\n options[key] = false;\n });\n\n // overwrite inherited title value when no value specified\n // fix for angular 1.3.1 531a8de72c439d8ddd064874bf364c00cedabb11\n if (!scope.hasOwnProperty('title')){\n scope.title = '';\n }\n\n // Support scope as data-attrs\n angular.forEach(['title', 'content', 'type'], function(key) {\n attr[key] && attr.$observe(key, function(newValue, oldValue) {\n scope[key] = $sce.trustAsHtml(newValue);\n });\n });\n\n // Support scope as an object\n attr.bsAlert && scope.$watch(attr.bsAlert, function(newValue, oldValue) {\n if(angular.isObject(newValue)) {\n angular.extend(scope, newValue);\n } else {\n scope.content = newValue;\n }\n }, true);\n\n // Initialize alert\n var alert = $alert(options);\n\n // Trigger\n element.on(attr.trigger || 'click', alert.toggle);\n\n // Garbage collection\n scope.$on('$destroy', function() {\n if (alert) alert.destroy();\n options = null;\n alert = null;\n });\n\n }\n };\n\n });\n","'use strict';\n\nangular.module('mgcrea.ngStrap.affix', ['mgcrea.ngStrap.helpers.dimensions', 'mgcrea.ngStrap.helpers.debounce'])\n\n .provider('$affix', function() {\n\n var defaults = this.defaults = {\n offsetTop: 'auto',\n inlineStyles: true\n };\n\n this.$get = function($window, debounce, dimensions) {\n\n var bodyEl = angular.element($window.document.body);\n var windowEl = angular.element($window);\n\n function AffixFactory(element, config) {\n\n var $affix = {};\n\n // Common vars\n var options = angular.extend({}, defaults, config);\n var targetEl = options.target;\n\n // Initial private vars\n var reset = 'affix affix-top affix-bottom',\n setWidth = false,\n initialAffixTop = 0,\n initialOffsetTop = 0,\n offsetTop = 0,\n offsetBottom = 0,\n affixed = null,\n unpin = null;\n\n var parent = element.parent();\n // Options: custom parent\n if (options.offsetParent) {\n if (options.offsetParent.match(/^\\d+$/)) {\n for (var i = 0; i < (options.offsetParent * 1) - 1; i++) {\n parent = parent.parent();\n }\n }\n else {\n parent = angular.element(options.offsetParent);\n }\n }\n\n $affix.init = function() {\n\n this.$parseOffsets();\n initialOffsetTop = dimensions.offset(element[0]).top + initialAffixTop;\n setWidth = !element[0].style.width;\n\n // Bind events\n targetEl.on('scroll', this.checkPosition);\n targetEl.on('click', this.checkPositionWithEventLoop);\n windowEl.on('resize', this.$debouncedOnResize);\n\n // Both of these checkPosition() calls are necessary for the case where\n // the user hits refresh after scrolling to the bottom of the page.\n this.checkPosition();\n this.checkPositionWithEventLoop();\n\n };\n\n $affix.destroy = function() {\n\n // Unbind events\n targetEl.off('scroll', this.checkPosition);\n targetEl.off('click', this.checkPositionWithEventLoop);\n windowEl.off('resize', this.$debouncedOnResize);\n\n };\n\n $affix.checkPositionWithEventLoop = function() {\n\n // IE 9 throws an error if we use 'this' instead of '$affix'\n // in this setTimeout call\n setTimeout($affix.checkPosition, 1);\n\n };\n\n $affix.checkPosition = function() {\n // if (!this.$element.is(':visible')) return\n\n var scrollTop = getScrollTop();\n var position = dimensions.offset(element[0]);\n var elementHeight = dimensions.height(element[0]);\n\n // Get required affix class according to position\n var affix = getRequiredAffixClass(unpin, position, elementHeight);\n\n // Did affix status changed this last check?\n if(affixed === affix) return;\n affixed = affix;\n\n if(affix === 'top') {\n unpin = null;\n if(setWidth) {\n element.css('width', '');\n }\n if (options.inlineStyles) {\n element.css('position', (options.offsetParent) ? '' : 'relative');\n element.css('top', '');\n }\n } else if(affix === 'bottom') {\n if (options.offsetUnpin) {\n unpin = -(options.offsetUnpin * 1);\n }\n else {\n // Calculate unpin threshold when affixed to bottom.\n // Hopefully the browser scrolls pixel by pixel.\n unpin = position.top - scrollTop;\n }\n if(setWidth) {\n element.css('width', '');\n }\n if (options.inlineStyles) {\n element.css('position', (options.offsetParent) ? '' : 'relative');\n element.css('top', (options.offsetParent) ? '' : ((bodyEl[0].offsetHeight - offsetBottom - elementHeight - initialOffsetTop) + 'px'));\n }\n } else { // affix === 'middle'\n unpin = null;\n if(setWidth) {\n element.css('width', element[0].offsetWidth + 'px');\n }\n if (options.inlineStyles) {\n element.css('position', 'fixed');\n element.css('top', initialAffixTop + 'px');\n }\n }\n\n // Add proper affix class\n element.removeClass(reset).addClass('affix' + ((affix !== 'middle') ? '-' + affix : ''));\n\n };\n\n $affix.$onResize = function() {\n $affix.$parseOffsets();\n $affix.checkPosition();\n };\n $affix.$debouncedOnResize = debounce($affix.$onResize, 50);\n\n $affix.$parseOffsets = function() {\n var initialPosition = element.css('position');\n // Reset position to calculate correct offsetTop\n if (options.inlineStyles){\n element.css('position', (options.offsetParent) ? '' : 'relative');\n }\n\n if(options.offsetTop) {\n if(options.offsetTop === 'auto') {\n options.offsetTop = '+0';\n }\n if(options.offsetTop.match(/^[-+]\\d+$/)) {\n initialAffixTop = - options.offsetTop * 1;\n if(options.offsetParent) {\n offsetTop = dimensions.offset(parent[0]).top + (options.offsetTop * 1);\n }\n else {\n offsetTop = dimensions.offset(element[0]).top - dimensions.css(element[0], 'marginTop', true) + (options.offsetTop * 1);\n }\n }\n else {\n offsetTop = options.offsetTop * 1;\n }\n }\n\n if(options.offsetBottom) {\n if(options.offsetParent && options.offsetBottom.match(/^[-+]\\d+$/)) {\n // add 1 pixel due to rounding problems...\n offsetBottom = getScrollHeight() - (dimensions.offset(parent[0]).top + dimensions.height(parent[0])) + (options.offsetBottom * 1) + 1;\n }\n else {\n offsetBottom = options.offsetBottom * 1;\n }\n }\n\n // Bring back the element's position after calculations\n if (options.inlineStyles){\n element.css('position', initialPosition);\n }\n };\n\n // Private methods\n\n function getRequiredAffixClass(unpin, position, elementHeight) {\n\n var scrollTop = getScrollTop();\n var scrollHeight = getScrollHeight();\n\n if(scrollTop <= offsetTop) {\n return 'top';\n } else if(unpin !== null && (scrollTop + unpin <= position.top)) {\n return 'middle';\n } else if(offsetBottom !== null && (position.top + elementHeight + initialAffixTop >= scrollHeight - offsetBottom)) {\n return 'bottom';\n } else {\n return 'middle';\n }\n\n }\n\n function getScrollTop() {\n return targetEl[0] === $window ? $window.pageYOffset : targetEl[0].scrollTop;\n }\n\n function getScrollHeight() {\n return targetEl[0] === $window ? $window.document.body.scrollHeight : targetEl[0].scrollHeight;\n }\n\n $affix.init();\n return $affix;\n\n }\n\n return AffixFactory;\n\n };\n\n })\n\n .directive('bsAffix', function($affix, $window) {\n\n return {\n restrict: 'EAC',\n require: '^?bsAffixTarget',\n link: function postLink(scope, element, attr, affixTarget) {\n\n var options = {scope: scope, target: affixTarget ? affixTarget.$element : angular.element($window)};\n angular.forEach(['offsetTop', 'offsetBottom', 'offsetParent', 'offsetUnpin', 'inlineStyles'], function(key) {\n if(angular.isDefined(attr[key])) {\n var option = attr[key];\n if (/true/i.test(option)) option = true;\n if (/false/i.test(option)) option = false;\n options[key] = option;\n }\n });\n\n var affix = $affix(element, options);\n scope.$on('$destroy', function() {\n affix && affix.destroy();\n options = null;\n affix = null;\n });\n\n }\n };\n\n })\n\n .directive('bsAffixTarget', function() {\n return {\n controller: function($element) {\n this.$element = $element;\n }\n };\n });\n","\nangular.module('mgcrea.ngStrap', [\n 'mgcrea.ngStrap.modal',\n 'mgcrea.ngStrap.aside',\n 'mgcrea.ngStrap.alert',\n 'mgcrea.ngStrap.button',\n 'mgcrea.ngStrap.select',\n 'mgcrea.ngStrap.datepicker',\n 'mgcrea.ngStrap.timepicker',\n 'mgcrea.ngStrap.navbar',\n 'mgcrea.ngStrap.tooltip',\n 'mgcrea.ngStrap.popover',\n 'mgcrea.ngStrap.dropdown',\n 'mgcrea.ngStrap.typeahead',\n 'mgcrea.ngStrap.scrollspy',\n 'mgcrea.ngStrap.affix',\n 'mgcrea.ngStrap.tab',\n 'mgcrea.ngStrap.collapse'\n]);\n"],"sourceRoot":"/source/"}