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