rails-angular-strap 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (101) hide show
  1. checksums.yaml +7 -0
  2. data/MIT-LICENSE +20 -0
  3. data/README.md +21 -0
  4. data/lib/rails-angular-strap.rb +11 -0
  5. data/lib/rails-angular-strap/engine.rb +4 -0
  6. data/lib/rails-angular-strap/version.rb +4 -0
  7. data/vendor/assets/javascripts/angular-strap.js +2 -0
  8. data/vendor/assets/javascripts/angular-strap.min.js +2 -0
  9. data/vendor/assets/javascripts/angular-strap/angular-strap.js +5014 -0
  10. data/vendor/assets/javascripts/angular-strap/angular-strap.min.js +11 -0
  11. data/vendor/assets/javascripts/angular-strap/angular-strap.tpl.js +89 -0
  12. data/vendor/assets/javascripts/angular-strap/angular-strap.tpl.min.js +8 -0
  13. data/vendor/assets/javascripts/angular-strap/modules/affix.js +249 -0
  14. data/vendor/assets/javascripts/angular-strap/modules/affix.min.js +9 -0
  15. data/vendor/assets/javascripts/angular-strap/modules/affix.min.js.map +1 -0
  16. data/vendor/assets/javascripts/angular-strap/modules/alert.js +120 -0
  17. data/vendor/assets/javascripts/angular-strap/modules/alert.min.js +9 -0
  18. data/vendor/assets/javascripts/angular-strap/modules/alert.min.js.map +1 -0
  19. data/vendor/assets/javascripts/angular-strap/modules/alert.tpl.js +14 -0
  20. data/vendor/assets/javascripts/angular-strap/modules/alert.tpl.min.js +8 -0
  21. data/vendor/assets/javascripts/angular-strap/modules/aside.js +96 -0
  22. data/vendor/assets/javascripts/angular-strap/modules/aside.min.js +9 -0
  23. data/vendor/assets/javascripts/angular-strap/modules/aside.min.js.map +1 -0
  24. data/vendor/assets/javascripts/angular-strap/modules/aside.tpl.js +14 -0
  25. data/vendor/assets/javascripts/angular-strap/modules/aside.tpl.min.js +8 -0
  26. data/vendor/assets/javascripts/angular-strap/modules/button.js +177 -0
  27. data/vendor/assets/javascripts/angular-strap/modules/button.min.js +9 -0
  28. data/vendor/assets/javascripts/angular-strap/modules/button.min.js.map +1 -0
  29. data/vendor/assets/javascripts/angular-strap/modules/collapse.js +273 -0
  30. data/vendor/assets/javascripts/angular-strap/modules/collapse.min.js +9 -0
  31. data/vendor/assets/javascripts/angular-strap/modules/collapse.min.js.map +1 -0
  32. data/vendor/assets/javascripts/angular-strap/modules/date-formatter.js +61 -0
  33. data/vendor/assets/javascripts/angular-strap/modules/date-formatter.min.js +9 -0
  34. data/vendor/assets/javascripts/angular-strap/modules/date-formatter.min.js.map +1 -0
  35. data/vendor/assets/javascripts/angular-strap/modules/date-parser.js +273 -0
  36. data/vendor/assets/javascripts/angular-strap/modules/date-parser.min.js +9 -0
  37. data/vendor/assets/javascripts/angular-strap/modules/date-parser.min.js.map +1 -0
  38. data/vendor/assets/javascripts/angular-strap/modules/datepicker.js +640 -0
  39. data/vendor/assets/javascripts/angular-strap/modules/datepicker.min.js +9 -0
  40. data/vendor/assets/javascripts/angular-strap/modules/datepicker.min.js.map +1 -0
  41. data/vendor/assets/javascripts/angular-strap/modules/datepicker.tpl.js +14 -0
  42. data/vendor/assets/javascripts/angular-strap/modules/datepicker.tpl.min.js +8 -0
  43. data/vendor/assets/javascripts/angular-strap/modules/debounce.js +62 -0
  44. data/vendor/assets/javascripts/angular-strap/modules/debounce.min.js +9 -0
  45. data/vendor/assets/javascripts/angular-strap/modules/debounce.min.js.map +1 -0
  46. data/vendor/assets/javascripts/angular-strap/modules/dimensions.js +156 -0
  47. data/vendor/assets/javascripts/angular-strap/modules/dimensions.min.js +9 -0
  48. data/vendor/assets/javascripts/angular-strap/modules/dimensions.min.js.map +1 -0
  49. data/vendor/assets/javascripts/angular-strap/modules/dropdown.js +149 -0
  50. data/vendor/assets/javascripts/angular-strap/modules/dropdown.min.js +9 -0
  51. data/vendor/assets/javascripts/angular-strap/modules/dropdown.min.js.map +1 -0
  52. data/vendor/assets/javascripts/angular-strap/modules/dropdown.tpl.js +14 -0
  53. data/vendor/assets/javascripts/angular-strap/modules/dropdown.tpl.min.js +8 -0
  54. data/vendor/assets/javascripts/angular-strap/modules/modal.js +349 -0
  55. data/vendor/assets/javascripts/angular-strap/modules/modal.min.js +9 -0
  56. data/vendor/assets/javascripts/angular-strap/modules/modal.min.js.map +1 -0
  57. data/vendor/assets/javascripts/angular-strap/modules/modal.tpl.js +14 -0
  58. data/vendor/assets/javascripts/angular-strap/modules/modal.tpl.min.js +8 -0
  59. data/vendor/assets/javascripts/angular-strap/modules/navbar.js +72 -0
  60. data/vendor/assets/javascripts/angular-strap/modules/navbar.min.js +9 -0
  61. data/vendor/assets/javascripts/angular-strap/modules/navbar.min.js.map +1 -0
  62. data/vendor/assets/javascripts/angular-strap/modules/parse-options.js +76 -0
  63. data/vendor/assets/javascripts/angular-strap/modules/parse-options.min.js +9 -0
  64. data/vendor/assets/javascripts/angular-strap/modules/parse-options.min.js.map +1 -0
  65. data/vendor/assets/javascripts/angular-strap/modules/popover.js +112 -0
  66. data/vendor/assets/javascripts/angular-strap/modules/popover.min.js +9 -0
  67. data/vendor/assets/javascripts/angular-strap/modules/popover.min.js.map +1 -0
  68. data/vendor/assets/javascripts/angular-strap/modules/popover.tpl.js +14 -0
  69. data/vendor/assets/javascripts/angular-strap/modules/popover.tpl.min.js +8 -0
  70. data/vendor/assets/javascripts/angular-strap/modules/raf.js +61 -0
  71. data/vendor/assets/javascripts/angular-strap/modules/raf.min.js +9 -0
  72. data/vendor/assets/javascripts/angular-strap/modules/raf.min.js.map +1 -0
  73. data/vendor/assets/javascripts/angular-strap/modules/scrollspy.js +261 -0
  74. data/vendor/assets/javascripts/angular-strap/modules/scrollspy.min.js +9 -0
  75. data/vendor/assets/javascripts/angular-strap/modules/scrollspy.min.js.map +1 -0
  76. data/vendor/assets/javascripts/angular-strap/modules/select.js +325 -0
  77. data/vendor/assets/javascripts/angular-strap/modules/select.min.js +9 -0
  78. data/vendor/assets/javascripts/angular-strap/modules/select.min.js.map +1 -0
  79. data/vendor/assets/javascripts/angular-strap/modules/select.tpl.js +14 -0
  80. data/vendor/assets/javascripts/angular-strap/modules/select.tpl.min.js +8 -0
  81. data/vendor/assets/javascripts/angular-strap/modules/tab.js +186 -0
  82. data/vendor/assets/javascripts/angular-strap/modules/tab.min.js +9 -0
  83. data/vendor/assets/javascripts/angular-strap/modules/tab.min.js.map +1 -0
  84. data/vendor/assets/javascripts/angular-strap/modules/tab.tpl.js +14 -0
  85. data/vendor/assets/javascripts/angular-strap/modules/tab.tpl.min.js +8 -0
  86. data/vendor/assets/javascripts/angular-strap/modules/timepicker.js +485 -0
  87. data/vendor/assets/javascripts/angular-strap/modules/timepicker.min.js +9 -0
  88. data/vendor/assets/javascripts/angular-strap/modules/timepicker.min.js.map +1 -0
  89. data/vendor/assets/javascripts/angular-strap/modules/timepicker.tpl.js +14 -0
  90. data/vendor/assets/javascripts/angular-strap/modules/timepicker.tpl.min.js +8 -0
  91. data/vendor/assets/javascripts/angular-strap/modules/tooltip.js +690 -0
  92. data/vendor/assets/javascripts/angular-strap/modules/tooltip.min.js +9 -0
  93. data/vendor/assets/javascripts/angular-strap/modules/tooltip.min.js.map +1 -0
  94. data/vendor/assets/javascripts/angular-strap/modules/tooltip.tpl.js +14 -0
  95. data/vendor/assets/javascripts/angular-strap/modules/tooltip.tpl.min.js +8 -0
  96. data/vendor/assets/javascripts/angular-strap/modules/typeahead.js +266 -0
  97. data/vendor/assets/javascripts/angular-strap/modules/typeahead.min.js +9 -0
  98. data/vendor/assets/javascripts/angular-strap/modules/typeahead.min.js.map +1 -0
  99. data/vendor/assets/javascripts/angular-strap/modules/typeahead.tpl.js +14 -0
  100. data/vendor/assets/javascripts/angular-strap/modules/typeahead.tpl.min.js +8 -0
  101. metadata +143 -0
@@ -0,0 +1,9 @@
1
+ /**
2
+ * angular-strap
3
+ * @version v2.1.6 - 2015-01-11
4
+ * @link http://mgcrea.github.io/angular-strap
5
+ * @author Olivier Louvignes (olivier@mg-crea.com)
6
+ * @license MIT License, http://www.opensource.org/licenses/MIT
7
+ */
8
+ "use strict";angular.module("mgcrea.ngStrap.tab",[]).provider("$tab",function(){var e=this.defaults={animation:"am-fade",template:"tab/tab.tpl.html",navClass:"nav-tabs",activeClass:"active"},a=this.controller=function(a,n,t){var s=this;s.$options=angular.copy(e),angular.forEach(["animation","navClass","activeClass"],function(e){angular.isDefined(t[e])&&(s.$options[e]=t[e])}),a.$navClass=s.$options.navClass,a.$activeClass=s.$options.activeClass,s.$panes=a.$panes=[],s.$activePaneChangeListeners=s.$viewChangeListeners=[],s.$push=function(e){s.$panes.push(e)},s.$remove=function(e){var a=s.$panes.indexOf(e),n=s.$panes.$active;s.$panes.splice(a,1),n>a?n--:a===n&&n===s.$panes.length&&n--,s.$setActive(n)},s.$panes.$active=0,s.$setActive=a.$setActive=function(e){s.$panes.$active=e,s.$activePaneChangeListeners.forEach(function(e){e()})}};this.$get=function(){var n={};return n.defaults=e,n.controller=a,n}}).directive("bsTabs",["$window","$animate","$tab","$parse",function(e,a,n,t){var s=n.defaults;return{require:["?ngModel","bsTabs"],transclude:!0,scope:!0,controller:["$scope","$element","$attrs",n.controller],templateUrl:function(e,a){return a.template||s.template},link:function(e,a,n,s){var i=s[0],o=s[1];if(i&&(console.warn("Usage of ngModel is deprecated, please use bsActivePane instead!"),o.$activePaneChangeListeners.push(function(){i.$setViewValue(o.$panes.$active)}),i.$formatters.push(function(e){return o.$setActive(1*e),e})),n.bsActivePane){var c=t(n.bsActivePane);o.$activePaneChangeListeners.push(function(){c.assign(e,o.$panes.$active)}),e.$watch(n.bsActivePane,function(e){o.$setActive(1*e)},!0)}}}}]).directive("bsPane",["$window","$animate","$sce",function(e,a,n){return{require:["^?ngModel","^bsTabs"],scope:!0,link:function(e,t,s,i){function o(){var n=c.$panes.indexOf(e),s=c.$panes.$active;a[n===s?"addClass":"removeClass"](t,c.$options.activeClass)}var c=(i[0],i[1]);t.addClass("tab-pane"),s.$observe("title",function(a){e.title=n.trustAsHtml(a)}),c.$options.animation&&t.addClass(c.$options.animation),c.$push(e),e.$on("$destroy",function(){c.$remove(e)}),c.$activePaneChangeListeners.push(function(){o()}),o()}}}]);
9
+ //# sourceMappingURL=tab.min.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["tab/tab.js"],"names":[],"mappings":"qBASM,OAAA,oDAIF,GAAI,GAAA,KAAa,UACf,UAAW,0DAGX,YAAK,sDAOL,GAAA,SAAO,QAAe,KAAK,sEAEtB,QAAA,UAAgB,EAAS,MAAA,EAAA,SAAA,GAAA,EAAA,sCAK9B,EAAK,aAAA,EAAA,SAA6B,YAElC,EAAK,OAAQ,EAAA,YAKX,2BAAgC,EAAA,gEAMhC,QAAY,SAAA,kDAKP,OAAI,OAAU,EAAA,OAKnB,gCAKA,MAEE,WAAA,gFAMN,EAAK,2BAAkB,QAAA,SAAA,GACjB,qCAUN,OAFD,GAAA,SAAU,iBAEL,eAKK,UAAA,UAAA,WAAA,OAAA,SAAA,SAAA,EAAA,EAAA,EAAA,MAEP,GAAa,EAAA,gBAGb,SAAM,WAAS,+BAET,cACA,SAAa,WAAY,SAAA,EAAA,+FAM3B,GAAa,EAAA,oGAUX,2BAAmC,KAAA,WACnC,EAAO,cAAA,EAAA,OAAA,wEAYP,EAAA,aAAmB,2BAMlB,2BAAA,KAAA,kGAOA,mBAOD,UAAA,UAAc,WAAY,OAAA,SAAA,EAAA,EAAA,iDAI9B,iCA2BE,+BAEF,EAAA,EAAA,OAAA,oEA1BA,GACE,IADa,EAAS,GACR,EAAK,2GAYrB,EAAU,SAAA,EAAY,SAAW,WAIjC,EAAS,MAAA,KAGP,IAAA,WAAmB,uEAWxB","file":"tab.min.js","sourcesContent":["'use strict';\n\nangular.module('mgcrea.ngStrap.tab', [])\n\n .provider('$tab', function() {\n\n var defaults = this.defaults = {\n animation: 'am-fade',\n template: 'tab/tab.tpl.html',\n navClass: 'nav-tabs',\n activeClass: 'active'\n };\n\n var controller = this.controller = function($scope, $element, $attrs) {\n var self = this;\n\n // Attributes options\n self.$options = angular.copy(defaults);\n angular.forEach(['animation', 'navClass', 'activeClass'], function(key) {\n if(angular.isDefined($attrs[key])) self.$options[key] = $attrs[key];\n });\n\n // Publish options on scope\n $scope.$navClass = self.$options.navClass;\n $scope.$activeClass = self.$options.activeClass;\n\n self.$panes = $scope.$panes = [];\n\n // DEPRECATED: $viewChangeListeners, please use $activePaneChangeListeners\n // Because we deprecated ngModel usage, we rename viewChangeListeners to \n // activePaneChangeListeners to make more sense.\n self.$activePaneChangeListeners = self.$viewChangeListeners = [];\n\n self.$push = function(pane) {\n self.$panes.push(pane);\n };\n\n self.$remove = function(pane) {\n var index = self.$panes.indexOf(pane);\n var activeIndex = self.$panes.$active;\n\n // remove pane from $panes array\n self.$panes.splice(index, 1);\n\n if (index < activeIndex) {\n // we removed a pane before the active pane, so we need to \n // decrement the active pane index\n activeIndex--;\n }\n else if (index === activeIndex && activeIndex === self.$panes.length) {\n // we remove the active pane and it was the one at the end,\n // so select the previous one\n activeIndex--;\n }\n self.$setActive(activeIndex);\n };\n\n self.$panes.$active = 0;\n self.$setActive = $scope.$setActive = function(value) {\n self.$panes.$active = value;\n self.$activePaneChangeListeners.forEach(function(fn) {\n fn();\n });\n };\n\n };\n\n this.$get = function() {\n var $tab = {};\n $tab.defaults = defaults;\n $tab.controller = controller;\n return $tab;\n };\n\n })\n\n .directive('bsTabs', function($window, $animate, $tab, $parse) {\n\n var defaults = $tab.defaults;\n\n return {\n require: ['?ngModel', 'bsTabs'],\n transclude: true,\n scope: true,\n controller: ['$scope', '$element', '$attrs', $tab.controller],\n templateUrl: function(element, attr) {\n return attr.template || defaults.template;\n },\n link: function postLink(scope, element, attrs, controllers) {\n\n var ngModelCtrl = controllers[0];\n var bsTabsCtrl = controllers[1];\n\n // DEPRECATED: ngModel, please use bsActivePane\n // 'ngModel' is deprecated bacause if interferes with form validation\n // and status, so avoid using it here.\n if(ngModelCtrl) {\n console.warn('Usage of ngModel is deprecated, please use bsActivePane instead!');\n\n // Update the modelValue following\n bsTabsCtrl.$activePaneChangeListeners.push(function() {\n ngModelCtrl.$setViewValue(bsTabsCtrl.$panes.$active);\n });\n\n // modelValue -> $formatters -> viewValue\n ngModelCtrl.$formatters.push(function(modelValue) {\n // console.warn('$formatter(\"%s\"): modelValue=%o (%o)', element.attr('ng-model'), modelValue, typeof modelValue);\n bsTabsCtrl.$setActive(modelValue * 1);\n return modelValue;\n });\n\n }\n\n if (attrs.bsActivePane) {\n // adapted from angularjs ngModelController bindings\n // https://github.com/angular/angular.js/blob/v1.3.1/src%2Fng%2Fdirective%2Finput.js#L1730\n var parsedBsActivePane = $parse(attrs.bsActivePane);\n\n // Update bsActivePane value with change\n bsTabsCtrl.$activePaneChangeListeners.push(function() {\n parsedBsActivePane.assign(scope, bsTabsCtrl.$panes.$active);\n });\n\n // watch bsActivePane for value changes\n scope.$watch(attrs.bsActivePane, function(newValue, oldValue) {\n bsTabsCtrl.$setActive(newValue * 1);\n }, true);\n }\n }\n };\n\n })\n\n .directive('bsPane', function($window, $animate, $sce) {\n\n return {\n require: ['^?ngModel', '^bsTabs'],\n scope: true,\n link: function postLink(scope, element, attrs, controllers) {\n\n var ngModelCtrl = controllers[0];\n var bsTabsCtrl = controllers[1];\n\n // Add base class\n element.addClass('tab-pane');\n\n // Observe title attribute for change\n attrs.$observe('title', function(newValue, oldValue) {\n scope.title = $sce.trustAsHtml(newValue);\n });\n\n // Add animation class\n if(bsTabsCtrl.$options.animation) {\n element.addClass(bsTabsCtrl.$options.animation);\n }\n\n // Push pane to parent bsTabs controller\n bsTabsCtrl.$push(scope);\n\n // remove pane from tab controller when pane is destroyed\n scope.$on('$destroy', function() {\n bsTabsCtrl.$remove(scope);\n });\n\n function render() {\n var index = bsTabsCtrl.$panes.indexOf(scope);\n var active = bsTabsCtrl.$panes.$active;\n $animate[index === active ? 'addClass' : 'removeClass'](element, bsTabsCtrl.$options.activeClass);\n }\n\n bsTabsCtrl.$activePaneChangeListeners.push(function() {\n render();\n });\n render();\n\n }\n };\n\n });\n"],"sourceRoot":"/source/"}
@@ -0,0 +1,14 @@
1
+ /**
2
+ * angular-strap
3
+ * @version v2.1.6 - 2015-01-11
4
+ * @link http://mgcrea.github.io/angular-strap
5
+ * @author Olivier Louvignes (olivier@mg-crea.com)
6
+ * @license MIT License, http://www.opensource.org/licenses/MIT
7
+ */
8
+ 'use strict';
9
+
10
+ angular.module('mgcrea.ngStrap.tab').run(['$templateCache', function($templateCache) {
11
+
12
+ $templateCache.put('tab/tab.tpl.html', '<ul class="nav" ng-class="$navClass" role="tablist"><li ng-repeat="$pane in $panes track by $index" ng-class="$index == $panes.$active ? $activeClass : \'\'"><a role="tab" data-toggle="tab" ng-click="$setActive($index)" data-index="{{ $index }}" ng-bind-html="$pane.title"></a></li></ul><div ng-transclude class="tab-content"></div>');
13
+
14
+ }]);
@@ -0,0 +1,8 @@
1
+ /**
2
+ * angular-strap
3
+ * @version v2.1.6 - 2015-01-11
4
+ * @link http://mgcrea.github.io/angular-strap
5
+ * @author Olivier Louvignes (olivier@mg-crea.com)
6
+ * @license MIT License, http://www.opensource.org/licenses/MIT
7
+ */
8
+ "use strict";angular.module("mgcrea.ngStrap.tab").run(["$templateCache",function(a){a.put("tab/tab.tpl.html",'<ul class="nav" ng-class="$navClass" role="tablist"><li ng-repeat="$pane in $panes track by $index" ng-class="$index == $panes.$active ? $activeClass : \'\'"><a role="tab" data-toggle="tab" ng-click="$setActive($index)" data-index="{{ $index }}" ng-bind-html="$pane.title"></a></li></ul><div ng-transclude class="tab-content"></div>')}]);
@@ -0,0 +1,485 @@
1
+ /**
2
+ * angular-strap
3
+ * @version v2.1.6 - 2015-01-11
4
+ * @link http://mgcrea.github.io/angular-strap
5
+ * @author Olivier Louvignes (olivier@mg-crea.com)
6
+ * @license MIT License, http://www.opensource.org/licenses/MIT
7
+ */
8
+ 'use strict';
9
+
10
+ angular.module('mgcrea.ngStrap.timepicker', [
11
+ 'mgcrea.ngStrap.helpers.dateParser',
12
+ 'mgcrea.ngStrap.helpers.dateFormatter',
13
+ 'mgcrea.ngStrap.tooltip'])
14
+
15
+ .provider('$timepicker', function() {
16
+
17
+ var defaults = this.defaults = {
18
+ animation: 'am-fade',
19
+ prefixClass: 'timepicker',
20
+ placement: 'bottom-left',
21
+ template: 'timepicker/timepicker.tpl.html',
22
+ trigger: 'focus',
23
+ container: false,
24
+ keyboard: true,
25
+ html: false,
26
+ delay: 0,
27
+ // lang: $locale.id,
28
+ useNative: true,
29
+ timeType: 'date',
30
+ timeFormat: 'shortTime',
31
+ modelTimeFormat: null,
32
+ autoclose: false,
33
+ minTime: -Infinity,
34
+ maxTime: +Infinity,
35
+ length: 5,
36
+ hourStep: 1,
37
+ minuteStep: 5,
38
+ iconUp: 'glyphicon glyphicon-chevron-up',
39
+ iconDown: 'glyphicon glyphicon-chevron-down',
40
+ arrowBehavior: 'pager'
41
+ };
42
+
43
+ this.$get = ["$window", "$document", "$rootScope", "$sce", "$dateFormatter", "$tooltip", "$timeout", function($window, $document, $rootScope, $sce, $dateFormatter, $tooltip, $timeout) {
44
+
45
+ var bodyEl = angular.element($window.document.body);
46
+ var isNative = /(ip(a|o)d|iphone|android)/ig.test($window.navigator.userAgent);
47
+ var isTouch = ('createTouch' in $window.document) && isNative;
48
+ if(!defaults.lang) defaults.lang = $dateFormatter.getDefaultLocale();
49
+
50
+ function timepickerFactory(element, controller, config) {
51
+
52
+ var $timepicker = $tooltip(element, angular.extend({}, defaults, config));
53
+ var parentScope = config.scope;
54
+ var options = $timepicker.$options;
55
+ var scope = $timepicker.$scope;
56
+
57
+ var lang = options.lang;
58
+ var formatDate = function(date, format) {
59
+ return $dateFormatter.formatDate(date, format, lang);
60
+ };
61
+
62
+ // View vars
63
+
64
+ var selectedIndex = 0;
65
+ var startDate = controller.$dateValue || new Date();
66
+ var viewDate = {hour: startDate.getHours(), meridian: startDate.getHours() < 12, minute: startDate.getMinutes(), second: startDate.getSeconds(), millisecond: startDate.getMilliseconds()};
67
+
68
+ var format = $dateFormatter.getDatetimeFormat(options.timeFormat, lang);
69
+
70
+ var hoursFormat = $dateFormatter.hoursFormat(format),
71
+ timeSeparator = $dateFormatter.timeSeparator(format),
72
+ minutesFormat = $dateFormatter.minutesFormat(format),
73
+ showAM = $dateFormatter.showAM(format);
74
+
75
+ scope.$iconUp = options.iconUp;
76
+ scope.$iconDown = options.iconDown;
77
+
78
+ // Scope methods
79
+
80
+ scope.$select = function(date, index) {
81
+ $timepicker.select(date, index);
82
+ };
83
+ scope.$moveIndex = function(value, index) {
84
+ $timepicker.$moveIndex(value, index);
85
+ };
86
+ scope.$switchMeridian = function(date) {
87
+ $timepicker.switchMeridian(date);
88
+ };
89
+
90
+ // Public methods
91
+
92
+ $timepicker.update = function(date) {
93
+ // console.warn('$timepicker.update() newValue=%o', date);
94
+ if(angular.isDate(date) && !isNaN(date.getTime())) {
95
+ $timepicker.$date = date;
96
+ angular.extend(viewDate, {hour: date.getHours(), minute: date.getMinutes(), second: date.getSeconds(), millisecond: date.getMilliseconds()});
97
+ $timepicker.$build();
98
+ } else if(!$timepicker.$isBuilt) {
99
+ $timepicker.$build();
100
+ }
101
+ };
102
+
103
+ $timepicker.select = function(date, index, keep) {
104
+ // console.warn('$timepicker.select', date, scope.$mode);
105
+ if(!controller.$dateValue || isNaN(controller.$dateValue.getTime())) controller.$dateValue = new Date(1970, 0, 1);
106
+ if(!angular.isDate(date)) date = new Date(date);
107
+ if(index === 0) controller.$dateValue.setHours(date.getHours());
108
+ else if(index === 1) controller.$dateValue.setMinutes(date.getMinutes());
109
+ controller.$setViewValue(angular.copy(controller.$dateValue));
110
+ controller.$render();
111
+ if(options.autoclose && !keep) {
112
+ $timeout(function() { $timepicker.hide(true); });
113
+ }
114
+ };
115
+
116
+ $timepicker.switchMeridian = function(date) {
117
+ if (!controller.$dateValue || isNaN(controller.$dateValue.getTime())) {
118
+ return;
119
+ }
120
+ var hours = (date || controller.$dateValue).getHours();
121
+ controller.$dateValue.setHours(hours < 12 ? hours + 12 : hours - 12);
122
+ controller.$setViewValue(angular.copy(controller.$dateValue));
123
+ controller.$render();
124
+ };
125
+
126
+ // Protected methods
127
+
128
+ $timepicker.$build = function() {
129
+ // console.warn('$timepicker.$build() viewDate=%o', viewDate);
130
+ var i, midIndex = scope.midIndex = parseInt(options.length / 2, 10);
131
+ var hours = [], hour;
132
+ for(i = 0; i < options.length; i++) {
133
+ hour = new Date(1970, 0, 1, viewDate.hour - (midIndex - i) * options.hourStep);
134
+ hours.push({date: hour, label: formatDate(hour, hoursFormat), selected: $timepicker.$date && $timepicker.$isSelected(hour, 0), disabled: $timepicker.$isDisabled(hour, 0)});
135
+ }
136
+ var minutes = [], minute;
137
+ for(i = 0; i < options.length; i++) {
138
+ minute = new Date(1970, 0, 1, 0, viewDate.minute - (midIndex - i) * options.minuteStep);
139
+ minutes.push({date: minute, label: formatDate(minute, minutesFormat), selected: $timepicker.$date && $timepicker.$isSelected(minute, 1), disabled: $timepicker.$isDisabled(minute, 1)});
140
+ }
141
+
142
+ var rows = [];
143
+ for(i = 0; i < options.length; i++) {
144
+ rows.push([hours[i], minutes[i]]);
145
+ }
146
+ scope.rows = rows;
147
+ scope.showAM = showAM;
148
+ scope.isAM = ($timepicker.$date || hours[midIndex].date).getHours() < 12;
149
+ scope.timeSeparator = timeSeparator;
150
+ $timepicker.$isBuilt = true;
151
+ };
152
+
153
+ $timepicker.$isSelected = function(date, index) {
154
+ if(!$timepicker.$date) return false;
155
+ else if(index === 0) {
156
+ return date.getHours() === $timepicker.$date.getHours();
157
+ } else if(index === 1) {
158
+ return date.getMinutes() === $timepicker.$date.getMinutes();
159
+ }
160
+ };
161
+
162
+ $timepicker.$isDisabled = function(date, index) {
163
+ var selectedTime;
164
+ if(index === 0) {
165
+ selectedTime = date.getTime() + viewDate.minute * 6e4;
166
+ } else if(index === 1) {
167
+ selectedTime = date.getTime() + viewDate.hour * 36e5;
168
+ }
169
+ return selectedTime < options.minTime * 1 || selectedTime > options.maxTime * 1;
170
+ };
171
+
172
+ scope.$arrowAction = function (value, index) {
173
+ if (options.arrowBehavior === 'picker') {
174
+ $timepicker.$setTimeByStep(value,index);
175
+ } else {
176
+ $timepicker.$moveIndex(value,index);
177
+ }
178
+ };
179
+
180
+ $timepicker.$setTimeByStep = function(value, index) {
181
+ var newDate = new Date($timepicker.$date);
182
+ var hours = newDate.getHours(), hoursLength = formatDate(newDate, hoursFormat).length;
183
+ var minutes = newDate.getMinutes(), minutesLength = formatDate(newDate, minutesFormat).length;
184
+ if (index === 0) {
185
+ newDate.setHours(hours - (parseInt(options.hourStep, 10) * value));
186
+ }
187
+ else {
188
+ newDate.setMinutes(minutes - (parseInt(options.minuteStep, 10) * value));
189
+ }
190
+ $timepicker.select(newDate, index, true);
191
+ };
192
+
193
+ $timepicker.$moveIndex = function(value, index) {
194
+ var targetDate;
195
+ if(index === 0) {
196
+ targetDate = new Date(1970, 0, 1, viewDate.hour + (value * options.length), viewDate.minute);
197
+ angular.extend(viewDate, {hour: targetDate.getHours()});
198
+ } else if(index === 1) {
199
+ targetDate = new Date(1970, 0, 1, viewDate.hour, viewDate.minute + (value * options.length * options.minuteStep));
200
+ angular.extend(viewDate, {minute: targetDate.getMinutes()});
201
+ }
202
+ $timepicker.$build();
203
+ };
204
+
205
+ $timepicker.$onMouseDown = function(evt) {
206
+ // Prevent blur on mousedown on .dropdown-menu
207
+ if(evt.target.nodeName.toLowerCase() !== 'input') evt.preventDefault();
208
+ evt.stopPropagation();
209
+ // Emulate click for mobile devices
210
+ if(isTouch) {
211
+ var targetEl = angular.element(evt.target);
212
+ if(targetEl[0].nodeName.toLowerCase() !== 'button') {
213
+ targetEl = targetEl.parent();
214
+ }
215
+ targetEl.triggerHandler('click');
216
+ }
217
+ };
218
+
219
+ $timepicker.$onKeyDown = function(evt) {
220
+ if (!/(38|37|39|40|13)/.test(evt.keyCode) || evt.shiftKey || evt.altKey) return;
221
+ evt.preventDefault();
222
+ evt.stopPropagation();
223
+
224
+ // Close on enter
225
+ if(evt.keyCode === 13) return $timepicker.hide(true);
226
+
227
+ // Navigate with keyboard
228
+ var newDate = new Date($timepicker.$date);
229
+ var hours = newDate.getHours(), hoursLength = formatDate(newDate, hoursFormat).length;
230
+ var minutes = newDate.getMinutes(), minutesLength = formatDate(newDate, minutesFormat).length;
231
+ var lateralMove = /(37|39)/.test(evt.keyCode);
232
+ var count = 2 + showAM * 1;
233
+
234
+ // Navigate indexes (left, right)
235
+ if (lateralMove) {
236
+ if(evt.keyCode === 37) selectedIndex = selectedIndex < 1 ? count - 1 : selectedIndex - 1;
237
+ else if(evt.keyCode === 39) selectedIndex = selectedIndex < count - 1 ? selectedIndex + 1 : 0;
238
+ }
239
+
240
+ // Update values (up, down)
241
+ var selectRange = [0, hoursLength];
242
+ if(selectedIndex === 0) {
243
+ if(evt.keyCode === 38) newDate.setHours(hours - parseInt(options.hourStep, 10));
244
+ else if(evt.keyCode === 40) newDate.setHours(hours + parseInt(options.hourStep, 10));
245
+ // re-calculate hours length because we have changed hours value
246
+ hoursLength = formatDate(newDate, hoursFormat).length;
247
+ selectRange = [0, hoursLength];
248
+ } else if(selectedIndex === 1) {
249
+ if(evt.keyCode === 38) newDate.setMinutes(minutes - parseInt(options.minuteStep, 10));
250
+ else if(evt.keyCode === 40) newDate.setMinutes(minutes + parseInt(options.minuteStep, 10));
251
+ // re-calculate minutes length because we have changes minutes value
252
+ minutesLength = formatDate(newDate, minutesFormat).length;
253
+ selectRange = [hoursLength + 1, hoursLength + 1 + minutesLength];
254
+ } else if(selectedIndex === 2) {
255
+ if(!lateralMove) $timepicker.switchMeridian();
256
+ selectRange = [hoursLength + 1 + minutesLength + 1, hoursLength + 1 + minutesLength + 3];
257
+ }
258
+ $timepicker.select(newDate, selectedIndex, true);
259
+ createSelection(selectRange[0], selectRange[1]);
260
+ parentScope.$digest();
261
+ };
262
+
263
+ // Private
264
+
265
+ function createSelection(start, end) {
266
+ if(element[0].createTextRange) {
267
+ var selRange = element[0].createTextRange();
268
+ selRange.collapse(true);
269
+ selRange.moveStart('character', start);
270
+ selRange.moveEnd('character', end);
271
+ selRange.select();
272
+ } else if(element[0].setSelectionRange) {
273
+ element[0].setSelectionRange(start, end);
274
+ } else if(angular.isUndefined(element[0].selectionStart)) {
275
+ element[0].selectionStart = start;
276
+ element[0].selectionEnd = end;
277
+ }
278
+ }
279
+
280
+ function focusElement() {
281
+ element[0].focus();
282
+ }
283
+
284
+ // Overrides
285
+
286
+ var _init = $timepicker.init;
287
+ $timepicker.init = function() {
288
+ if(isNative && options.useNative) {
289
+ element.prop('type', 'time');
290
+ element.css('-webkit-appearance', 'textfield');
291
+ return;
292
+ } else if(isTouch) {
293
+ element.prop('type', 'text');
294
+ element.attr('readonly', 'true');
295
+ element.on('click', focusElement);
296
+ }
297
+ _init();
298
+ };
299
+
300
+ var _destroy = $timepicker.destroy;
301
+ $timepicker.destroy = function() {
302
+ if(isNative && options.useNative) {
303
+ element.off('click', focusElement);
304
+ }
305
+ _destroy();
306
+ };
307
+
308
+ var _show = $timepicker.show;
309
+ $timepicker.show = function() {
310
+ _show();
311
+ // use timeout to hookup the events to prevent
312
+ // event bubbling from being processed imediately.
313
+ $timeout(function() {
314
+ $timepicker.$element.on(isTouch ? 'touchstart' : 'mousedown', $timepicker.$onMouseDown);
315
+ if(options.keyboard) {
316
+ element.on('keydown', $timepicker.$onKeyDown);
317
+ }
318
+ }, 0, false);
319
+ };
320
+
321
+ var _hide = $timepicker.hide;
322
+ $timepicker.hide = function(blur) {
323
+ if(!$timepicker.$isShown) return;
324
+ $timepicker.$element.off(isTouch ? 'touchstart' : 'mousedown', $timepicker.$onMouseDown);
325
+ if(options.keyboard) {
326
+ element.off('keydown', $timepicker.$onKeyDown);
327
+ }
328
+ _hide(blur);
329
+ };
330
+
331
+ return $timepicker;
332
+
333
+ }
334
+
335
+ timepickerFactory.defaults = defaults;
336
+ return timepickerFactory;
337
+
338
+ }];
339
+
340
+ })
341
+
342
+
343
+ .directive('bsTimepicker', ["$window", "$parse", "$q", "$dateFormatter", "$dateParser", "$timepicker", function($window, $parse, $q, $dateFormatter, $dateParser, $timepicker) {
344
+
345
+ var defaults = $timepicker.defaults;
346
+ var isNative = /(ip(a|o)d|iphone|android)/ig.test($window.navigator.userAgent);
347
+ var requestAnimationFrame = $window.requestAnimationFrame || $window.setTimeout;
348
+
349
+ return {
350
+ restrict: 'EAC',
351
+ require: 'ngModel',
352
+ link: function postLink(scope, element, attr, controller) {
353
+
354
+ // Directive options
355
+ var options = {scope: scope, controller: controller};
356
+ angular.forEach(['placement', 'container', 'delay', 'trigger', 'keyboard', 'html', 'animation', 'template', 'autoclose', 'timeType', 'timeFormat', 'modelTimeFormat', 'useNative', 'hourStep', 'minuteStep', 'length', 'arrowBehavior', 'iconUp', 'iconDown', 'id'], function(key) {
357
+ if(angular.isDefined(attr[key])) options[key] = attr[key];
358
+ });
359
+
360
+ // Visibility binding support
361
+ attr.bsShow && scope.$watch(attr.bsShow, function(newValue, oldValue) {
362
+ if(!timepicker || !angular.isDefined(newValue)) return;
363
+ if(angular.isString(newValue)) newValue = !!newValue.match(/true|,?(timepicker),?/i);
364
+ newValue === true ? timepicker.show() : timepicker.hide();
365
+ });
366
+
367
+ // Initialize timepicker
368
+ if(isNative && (options.useNative || defaults.useNative)) options.timeFormat = 'HH:mm';
369
+ var timepicker = $timepicker(element, controller, options);
370
+ options = timepicker.$options;
371
+
372
+ var lang = options.lang;
373
+ var formatDate = function(date, format) {
374
+ return $dateFormatter.formatDate(date, format, lang);
375
+ };
376
+
377
+ // Initialize parser
378
+ var dateParser = $dateParser({format: options.timeFormat, lang: lang});
379
+
380
+ // Observe attributes for changes
381
+ angular.forEach(['minTime', 'maxTime'], function(key) {
382
+ // console.warn('attr.$observe(%s)', key, attr[key]);
383
+ angular.isDefined(attr[key]) && attr.$observe(key, function(newValue) {
384
+ timepicker.$options[key] = dateParser.getTimeForAttribute(key, newValue);
385
+ !isNaN(timepicker.$options[key]) && timepicker.$build();
386
+ validateAgainstMinMaxTime(controller.$dateValue);
387
+ });
388
+ });
389
+
390
+ // Watch model for changes
391
+ scope.$watch(attr.ngModel, function(newValue, oldValue) {
392
+ // console.warn('scope.$watch(%s)', attr.ngModel, newValue, oldValue, controller.$dateValue);
393
+ timepicker.update(controller.$dateValue);
394
+ }, true);
395
+
396
+ function validateAgainstMinMaxTime(parsedTime) {
397
+ if (!angular.isDate(parsedTime)) return;
398
+ var isMinValid = isNaN(options.minTime) || new Date(parsedTime.getTime()).setFullYear(1970, 0, 1) >= options.minTime;
399
+ var isMaxValid = isNaN(options.maxTime) || new Date(parsedTime.getTime()).setFullYear(1970, 0, 1) <= options.maxTime;
400
+ var isValid = isMinValid && isMaxValid;
401
+ controller.$setValidity('date', isValid);
402
+ controller.$setValidity('min', isMinValid);
403
+ controller.$setValidity('max', isMaxValid);
404
+ // Only update the model when we have a valid date
405
+ if(!isValid) {
406
+ return;
407
+ }
408
+ controller.$dateValue = parsedTime;
409
+ }
410
+
411
+ // viewValue -> $parsers -> modelValue
412
+ controller.$parsers.unshift(function(viewValue) {
413
+ // console.warn('$parser("%s"): viewValue=%o', element.attr('ng-model'), viewValue);
414
+ // Null values should correctly reset the model value & validity
415
+ if(!viewValue) {
416
+ // BREAKING CHANGE:
417
+ // return null (not undefined) when input value is empty, so angularjs 1.3
418
+ // ngModelController can go ahead and run validators, like ngRequired
419
+ controller.$setValidity('date', true);
420
+ return null;
421
+ }
422
+ var parsedTime = angular.isDate(viewValue) ? viewValue : dateParser.parse(viewValue, controller.$dateValue);
423
+ if(!parsedTime || isNaN(parsedTime.getTime())) {
424
+ controller.$setValidity('date', false);
425
+ // return undefined, causes ngModelController to
426
+ // invalidate model value
427
+ return;
428
+ } else {
429
+ validateAgainstMinMaxTime(parsedTime);
430
+ }
431
+ if(options.timeType === 'string') {
432
+ return formatDate(parsedTime, options.modelTimeFormat || options.timeFormat);
433
+ } else if(options.timeType === 'number') {
434
+ return controller.$dateValue.getTime();
435
+ } else if(options.timeType === 'unix') {
436
+ return controller.$dateValue.getTime() / 1000;
437
+ } else if(options.timeType === 'iso') {
438
+ return controller.$dateValue.toISOString();
439
+ } else {
440
+ return new Date(controller.$dateValue);
441
+ }
442
+ });
443
+
444
+ // modelValue -> $formatters -> viewValue
445
+ controller.$formatters.push(function(modelValue) {
446
+ // console.warn('$formatter("%s"): modelValue=%o (%o)', element.attr('ng-model'), modelValue, typeof modelValue);
447
+ var date;
448
+ if(angular.isUndefined(modelValue) || modelValue === null) {
449
+ date = NaN;
450
+ } else if(angular.isDate(modelValue)) {
451
+ date = modelValue;
452
+ } else if(options.timeType === 'string') {
453
+ date = dateParser.parse(modelValue, null, options.modelTimeFormat);
454
+ } else if(options.timeType === 'unix') {
455
+ date = new Date(modelValue * 1000);
456
+ } else {
457
+ date = new Date(modelValue);
458
+ }
459
+ // Setup default value?
460
+ // if(isNaN(date.getTime())) date = new Date(new Date().setMinutes(0) + 36e5);
461
+ controller.$dateValue = date;
462
+ return getTimeFormattedString();
463
+ });
464
+
465
+ // viewValue -> element
466
+ controller.$render = function() {
467
+ // console.warn('$render("%s"): viewValue=%o', element.attr('ng-model'), controller.$viewValue);
468
+ element.val(getTimeFormattedString());
469
+ };
470
+
471
+ function getTimeFormattedString() {
472
+ return !controller.$dateValue || isNaN(controller.$dateValue.getTime()) ? '' : formatDate(controller.$dateValue, options.timeFormat);
473
+ }
474
+
475
+ // Garbage collection
476
+ scope.$on('$destroy', function() {
477
+ if (timepicker) timepicker.destroy();
478
+ options = null;
479
+ timepicker = null;
480
+ });
481
+
482
+ }
483
+ };
484
+
485
+ }]);