bastion 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +15 -0
- data/.jshintrc +35 -0
- data/Gruntfile.js +24 -0
- data/LICENSE +339 -0
- data/README.md +8 -0
- data/Rakefile +53 -0
- data/app/assets/javascripts/bastion/auth/auth.module.js +87 -0
- data/app/assets/javascripts/bastion/auth/authorization.service.js +48 -0
- data/app/assets/javascripts/bastion/bastion-bootstrap.js +32 -0
- data/app/assets/javascripts/bastion/bastion-resource.factory.js +39 -0
- data/app/assets/javascripts/bastion/bastion.js +52 -0
- data/app/assets/javascripts/bastion/bastion.module.js +213 -0
- data/app/assets/javascripts/bastion/i18n/README +9 -0
- data/app/assets/javascripts/bastion/i18n/i18n.module.js +24 -0
- data/app/assets/javascripts/bastion/i18n/katello.pot +968 -0
- data/app/assets/javascripts/bastion/i18n/locale/README +1 -0
- data/app/assets/javascripts/bastion/i18n/translate.service.js +14 -0
- data/app/assets/javascripts/bastion/i18n/translations.js +3 -0
- data/app/assets/javascripts/bastion/i18n/zanata.xml +28 -0
- data/app/assets/javascripts/bastion/incubator/alch-alert.directive.js +67 -0
- data/app/assets/javascripts/bastion/incubator/alch-container-scroll.directive.js +44 -0
- data/app/assets/javascripts/bastion/incubator/alch-dropdown.directive.js +28 -0
- data/app/assets/javascripts/bastion/incubator/alch-edit.directive.js +361 -0
- data/app/assets/javascripts/bastion/incubator/alch-flyout.directive.js +19 -0
- data/app/assets/javascripts/bastion/incubator/alch-form-buttons.directive.js +60 -0
- data/app/assets/javascripts/bastion/incubator/alch-form-group.directive.js +87 -0
- data/app/assets/javascripts/bastion/incubator/alch-infinite-scroll.directive.js +84 -0
- data/app/assets/javascripts/bastion/incubator/alch-menu.directive.js +56 -0
- data/app/assets/javascripts/bastion/incubator/alch-modal.directive.js +80 -0
- data/app/assets/javascripts/bastion/incubator/alch-save-control.directive.js +42 -0
- data/app/assets/javascripts/bastion/incubator/alch-table.directive.js +295 -0
- data/app/assets/javascripts/bastion/incubator/format/alch-format.module.js +21 -0
- data/app/assets/javascripts/bastion/incubator/format/array-to-string.filter.js +30 -0
- data/app/assets/javascripts/bastion/incubator/format/boolean-to-yes-no.filter.js +34 -0
- data/app/assets/javascripts/bastion/incubator/format/capitalize.filter.js +32 -0
- data/app/assets/javascripts/bastion/incubator/format/key-value-to-string.filter.js +40 -0
- data/app/assets/javascripts/bastion/incubator/format/unlimitedFilter.filter.js +35 -0
- data/app/assets/javascripts/bastion/incubator/stylesheets/alch-edit.scss +34 -0
- data/app/assets/javascripts/bastion/incubator/stylesheets/header.scss +207 -0
- data/app/assets/javascripts/bastion/incubator/views/alch-alert.html +6 -0
- data/app/assets/javascripts/bastion/incubator/views/alch-dropdown.html +19 -0
- data/app/assets/javascripts/bastion/incubator/views/alch-edit-add-item.html +21 -0
- data/app/assets/javascripts/bastion/incubator/views/alch-edit-add-remove-cancel.html +28 -0
- data/app/assets/javascripts/bastion/incubator/views/alch-edit-checkbox.html +6 -0
- data/app/assets/javascripts/bastion/incubator/views/alch-edit-custom.html +8 -0
- data/app/assets/javascripts/bastion/incubator/views/alch-edit-multiselect.html +17 -0
- data/app/assets/javascripts/bastion/incubator/views/alch-edit-save-cancel.html +20 -0
- data/app/assets/javascripts/bastion/incubator/views/alch-edit-select.html +10 -0
- data/app/assets/javascripts/bastion/incubator/views/alch-edit-text.html +6 -0
- data/app/assets/javascripts/bastion/incubator/views/alch-edit-textarea.html +6 -0
- data/app/assets/javascripts/bastion/incubator/views/alch-edit.html +18 -0
- data/app/assets/javascripts/bastion/incubator/views/alch-flyout.html +10 -0
- data/app/assets/javascripts/bastion/incubator/views/alch-form-buttons.html +10 -0
- data/app/assets/javascripts/bastion/incubator/views/alch-form-group.html +9 -0
- data/app/assets/javascripts/bastion/incubator/views/alch-menu.html +18 -0
- data/app/assets/javascripts/bastion/incubator/views/alch-modal-remove.html +13 -0
- data/app/assets/javascripts/bastion/incubator/views/alch-save-control.html +18 -0
- data/app/assets/javascripts/bastion/layouts/details-nutupane.html +73 -0
- data/app/assets/javascripts/bastion/layouts/nutupane.html +63 -0
- data/app/assets/javascripts/bastion/layouts/select-all-results.html +9 -0
- data/app/assets/javascripts/bastion/menu/menu-expander.service.js +51 -0
- data/app/assets/javascripts/bastion/menu/menu.module.js +26 -0
- data/app/assets/javascripts/bastion/utils/as.filter.js +33 -0
- data/app/assets/javascripts/bastion/utils/form-utils.service.js +53 -0
- data/app/assets/javascripts/bastion/utils/utils.module.js +21 -0
- data/app/assets/javascripts/bastion/widgets/current-tasks.directive.js +67 -0
- data/app/assets/javascripts/bastion/widgets/nutupane-table.directive.js +59 -0
- data/app/assets/javascripts/bastion/widgets/nutupane.factory.js +316 -0
- data/app/assets/javascripts/bastion/widgets/page-title.directive.js +61 -0
- data/app/assets/javascripts/bastion/widgets/page-title.service.js +42 -0
- data/app/assets/javascripts/bastion/widgets/path-selector.directive.js +109 -0
- data/app/assets/javascripts/bastion/widgets/views/current-tasks.html +23 -0
- data/app/assets/javascripts/bastion/widgets/views/path-selector.html +11 -0
- data/app/assets/javascripts/bastion/widgets/widgets.module.js +24 -0
- data/app/assets/stylesheets/bastion/animations.less +15 -0
- data/app/assets/stylesheets/bastion/bastion.less +160 -0
- data/app/assets/stylesheets/bastion/forms.less +41 -0
- data/app/assets/stylesheets/bastion/gpg-keys.less +36 -0
- data/app/assets/stylesheets/bastion/helpers.less +6 -0
- data/app/assets/stylesheets/bastion/mixins.less +15 -0
- data/app/assets/stylesheets/bastion/nutupane.less +382 -0
- data/app/assets/stylesheets/bastion/overrides.less +54 -0
- data/app/assets/stylesheets/bastion/path-selector.less +123 -0
- data/app/assets/stylesheets/bastion/systems.less +35 -0
- data/app/assets/stylesheets/bastion/tasks.less +23 -0
- data/app/assets/stylesheets/bastion/typography.less +31 -0
- data/app/assets/stylesheets/bastion/variables.less +3 -0
- data/app/controllers/bastion/bastion_controller.rb +24 -0
- data/app/views/bastion/layouts/application.html.erb +32 -0
- data/app/views/bastion/layouts/application_ie.html.erb +5 -0
- data/bastion.js +26 -0
- data/bower.json +94 -0
- data/config/routes.rb +25 -0
- data/config/routes/mount_engine.rb +3 -0
- data/grunt/bower.js +20 -0
- data/grunt/htmlhint.js +15 -0
- data/grunt/jshint.js +9 -0
- data/grunt/karma.js +83 -0
- data/lib/bastion.rb +17 -0
- data/lib/bastion/engine.rb +37 -0
- data/lib/bastion/version.rb +3 -0
- data/package.json +29 -0
- data/test/auth/authorization.service.test.js +63 -0
- data/test/bastion/bastion-resource.factory.test.js +31 -0
- data/test/bastion/test-constants.js +30 -0
- data/test/i18n/translate.service.test.js +31 -0
- data/test/incubator/alch-alert.directive.test.js +84 -0
- data/test/incubator/alch-container-scroll.directive.test.js +68 -0
- data/test/incubator/alch-dropdown.directive.test.js +113 -0
- data/test/incubator/alch-edit.directive.test.js +320 -0
- data/test/incubator/alch-flyout.directive.test.js +73 -0
- data/test/incubator/alch-form-buttons.directive.test.js +55 -0
- data/test/incubator/alch-form-group.directive.test.js +76 -0
- data/test/incubator/alch-infinite-scroll.directive.test.js +123 -0
- data/test/incubator/alch-menu.directive.test.js +94 -0
- data/test/incubator/alch-modal.directive.test.js +81 -0
- data/test/incubator/alch-table.directive.test.js +270 -0
- data/test/incubator/format/array-to-string.filter.test.js +38 -0
- data/test/incubator/format/boolean-to-yes-no.filter.test.js +41 -0
- data/test/incubator/format/capitalize.filter.test.js +35 -0
- data/test/incubator/format/key-value-to-string.filter.test.js +60 -0
- data/test/incubator/format/unlimited-filter.filter.test.js +31 -0
- data/test/incubator/nutupane-table.directive.test.js +69 -0
- data/test/incubator/nutupane.factory.test.js +307 -0
- data/test/menu/menu-expander.service.test.js +74 -0
- data/test/test-mocks.module.js +268 -0
- data/test/utils/as.filter.test.js +33 -0
- data/test/utils/form-utils.service.test.js +52 -0
- data/test/widgets/page-title.directive.test.js +54 -0
- data/test/widgets/page-title.service.test.js +51 -0
- data/test/widgets/path-selector.directive.test.js +136 -0
- data/vendor/assets/fonts/bastion/bootstrap/glyphicons-halflings-regular.eot +0 -0
- data/vendor/assets/fonts/bastion/bootstrap/glyphicons-halflings-regular.svg +229 -0
- data/vendor/assets/fonts/bastion/bootstrap/glyphicons-halflings-regular.ttf +0 -0
- data/vendor/assets/fonts/bastion/bootstrap/glyphicons-halflings-regular.woff +0 -0
- data/vendor/assets/fonts/bastion/font-awesome/FontAwesome.otf +0 -0
- data/vendor/assets/fonts/bastion/font-awesome/fontawesome-webfont.eot +0 -0
- data/vendor/assets/fonts/bastion/font-awesome/fontawesome-webfont.svg +399 -0
- data/vendor/assets/fonts/bastion/font-awesome/fontawesome-webfont.ttf +0 -0
- data/vendor/assets/fonts/bastion/font-awesome/fontawesome-webfont.woff +0 -0
- data/vendor/assets/fonts/bastion/rcue/OpenSans-Bold-webfont.eot +0 -0
- data/vendor/assets/fonts/bastion/rcue/OpenSans-Bold-webfont.svg +146 -0
- data/vendor/assets/fonts/bastion/rcue/OpenSans-Bold-webfont.ttf +0 -0
- data/vendor/assets/fonts/bastion/rcue/OpenSans-Bold-webfont.woff +0 -0
- data/vendor/assets/fonts/bastion/rcue/OpenSans-BoldItalic-webfont.eot +0 -0
- data/vendor/assets/fonts/bastion/rcue/OpenSans-BoldItalic-webfont.svg +146 -0
- data/vendor/assets/fonts/bastion/rcue/OpenSans-BoldItalic-webfont.ttf +0 -0
- data/vendor/assets/fonts/bastion/rcue/OpenSans-BoldItalic-webfont.woff +0 -0
- data/vendor/assets/fonts/bastion/rcue/OpenSans-ExtraBold-webfont.eot +0 -0
- data/vendor/assets/fonts/bastion/rcue/OpenSans-ExtraBold-webfont.svg +146 -0
- data/vendor/assets/fonts/bastion/rcue/OpenSans-ExtraBold-webfont.ttf +0 -0
- data/vendor/assets/fonts/bastion/rcue/OpenSans-ExtraBold-webfont.woff +0 -0
- data/vendor/assets/fonts/bastion/rcue/OpenSans-ExtraBoldItalic-webfont.eot +0 -0
- data/vendor/assets/fonts/bastion/rcue/OpenSans-ExtraBoldItalic-webfont.svg +146 -0
- data/vendor/assets/fonts/bastion/rcue/OpenSans-ExtraBoldItalic-webfont.ttf +0 -0
- data/vendor/assets/fonts/bastion/rcue/OpenSans-ExtraBoldItalic-webfont.woff +0 -0
- data/vendor/assets/fonts/bastion/rcue/OpenSans-Italic-webfont.eot +0 -0
- data/vendor/assets/fonts/bastion/rcue/OpenSans-Italic-webfont.svg +146 -0
- data/vendor/assets/fonts/bastion/rcue/OpenSans-Italic-webfont.ttf +0 -0
- data/vendor/assets/fonts/bastion/rcue/OpenSans-Italic-webfont.woff +0 -0
- data/vendor/assets/fonts/bastion/rcue/OpenSans-Light-webfont.eot +0 -0
- data/vendor/assets/fonts/bastion/rcue/OpenSans-Light-webfont.svg +146 -0
- data/vendor/assets/fonts/bastion/rcue/OpenSans-Light-webfont.ttf +0 -0
- data/vendor/assets/fonts/bastion/rcue/OpenSans-Light-webfont.woff +0 -0
- data/vendor/assets/fonts/bastion/rcue/OpenSans-LightItalic-webfont.eot +0 -0
- data/vendor/assets/fonts/bastion/rcue/OpenSans-LightItalic-webfont.svg +146 -0
- data/vendor/assets/fonts/bastion/rcue/OpenSans-LightItalic-webfont.ttf +0 -0
- data/vendor/assets/fonts/bastion/rcue/OpenSans-LightItalic-webfont.woff +0 -0
- data/vendor/assets/fonts/bastion/rcue/OpenSans-Regular-webfont.eot +0 -0
- data/vendor/assets/fonts/bastion/rcue/OpenSans-Regular-webfont.svg +146 -0
- data/vendor/assets/fonts/bastion/rcue/OpenSans-Regular-webfont.ttf +0 -0
- data/vendor/assets/fonts/bastion/rcue/OpenSans-Regular-webfont.woff +0 -0
- data/vendor/assets/fonts/bastion/rcue/OpenSans-Semibold-webfont.eot +0 -0
- data/vendor/assets/fonts/bastion/rcue/OpenSans-Semibold-webfont.svg +146 -0
- data/vendor/assets/fonts/bastion/rcue/OpenSans-Semibold-webfont.ttf +0 -0
- data/vendor/assets/fonts/bastion/rcue/OpenSans-Semibold-webfont.woff +0 -0
- data/vendor/assets/fonts/bastion/rcue/OpenSans-SemiboldItalic-webfont.eot +0 -0
- data/vendor/assets/fonts/bastion/rcue/OpenSans-SemiboldItalic-webfont.svg +146 -0
- data/vendor/assets/fonts/bastion/rcue/OpenSans-SemiboldItalic-webfont.ttf +0 -0
- data/vendor/assets/fonts/bastion/rcue/OpenSans-SemiboldItalic-webfont.woff +0 -0
- data/vendor/assets/fonts/bastion/rcue/Overpass-Bold-webfont.eot +0 -0
- data/vendor/assets/fonts/bastion/rcue/Overpass-Bold-webfont.svg +454 -0
- data/vendor/assets/fonts/bastion/rcue/Overpass-Bold-webfont.ttf +0 -0
- data/vendor/assets/fonts/bastion/rcue/Overpass-Bold-webfont.woff +0 -0
- data/vendor/assets/fonts/bastion/rcue/Overpass-Regular-webfont.eot +0 -0
- data/vendor/assets/fonts/bastion/rcue/Overpass-Regular-webfont.svg +454 -0
- data/vendor/assets/fonts/bastion/rcue/Overpass-Regular-webfont.ttf +0 -0
- data/vendor/assets/fonts/bastion/rcue/Overpass-Regular-webfont.woff +0 -0
- data/vendor/assets/javascripts/bastion/alchemy/alchemy.js +20 -0
- data/vendor/assets/javascripts/bastion/angular-animate/angular-animate.js +1226 -0
- data/vendor/assets/javascripts/bastion/angular-blocks/angular-blocks.js +79 -0
- data/vendor/assets/javascripts/bastion/angular-bootstrap/ui-bootstrap-tpls.js +3677 -0
- data/vendor/assets/javascripts/bastion/angular-bootstrap/ui-bootstrap.js +3427 -0
- data/vendor/assets/javascripts/bastion/angular-gettext/angular-gettext.js +345 -0
- data/vendor/assets/javascripts/bastion/angular-resource/angular-resource.js +594 -0
- data/vendor/assets/javascripts/bastion/angular-route/angular-route.js +920 -0
- data/vendor/assets/javascripts/bastion/angular-sanitize/angular-sanitize.js +622 -0
- data/vendor/assets/javascripts/bastion/angular-ui-bootstrap/datepicker.js +447 -0
- data/vendor/assets/javascripts/bastion/angular-ui-bootstrap/timepicker.js +232 -0
- data/vendor/assets/javascripts/bastion/angular-ui-router/angular-ui-router.js +3223 -0
- data/vendor/assets/javascripts/bastion/angular-uuid4/angular-uuid4.js +21 -0
- data/vendor/assets/javascripts/bastion/angular/angular.js +20560 -0
- data/vendor/assets/javascripts/bastion/es5-shim/es5-shim.js +1216 -0
- data/vendor/assets/javascripts/bastion/json3/json3.js +861 -0
- data/vendor/assets/javascripts/bastion/ngUpload/ng-upload.js +205 -0
- data/vendor/assets/javascripts/bastion/underscore/underscore.js +1276 -0
- data/vendor/assets/stylesheets/bastion/alchemy/_colors.scss +99 -0
- data/vendor/assets/stylesheets/bastion/alchemy/_media_object.scss +22 -0
- data/vendor/assets/stylesheets/bastion/alchemy/_mixins.scss +24 -0
- data/vendor/assets/stylesheets/bastion/alchemy/_normalize.scss +396 -0
- data/vendor/assets/stylesheets/bastion/alchemy/_vars.scss +31 -0
- data/vendor/assets/stylesheets/bastion/alchemy/alchemy.scss +8 -0
- data/vendor/assets/stylesheets/bastion/bootstrap/alerts.less +67 -0
- data/vendor/assets/stylesheets/bastion/bootstrap/badges.less +51 -0
- data/vendor/assets/stylesheets/bastion/bootstrap/bootstrap.less +49 -0
- data/vendor/assets/stylesheets/bastion/bootstrap/breadcrumbs.less +23 -0
- data/vendor/assets/stylesheets/bastion/bootstrap/button-groups.less +227 -0
- data/vendor/assets/stylesheets/bastion/bootstrap/buttons.less +155 -0
- data/vendor/assets/stylesheets/bastion/bootstrap/carousel.less +232 -0
- data/vendor/assets/stylesheets/bastion/bootstrap/close.less +33 -0
- data/vendor/assets/stylesheets/bastion/bootstrap/code.less +53 -0
- data/vendor/assets/stylesheets/bastion/bootstrap/component-animations.less +29 -0
- data/vendor/assets/stylesheets/bastion/bootstrap/dropdowns.less +187 -0
- data/vendor/assets/stylesheets/bastion/bootstrap/forms.less +375 -0
- data/vendor/assets/stylesheets/bastion/bootstrap/glyphicons.less +237 -0
- data/vendor/assets/stylesheets/bastion/bootstrap/grid.less +79 -0
- data/vendor/assets/stylesheets/bastion/bootstrap/input-groups.less +136 -0
- data/vendor/assets/stylesheets/bastion/bootstrap/jumbotron.less +46 -0
- data/vendor/assets/stylesheets/bastion/bootstrap/labels.less +64 -0
- data/vendor/assets/stylesheets/bastion/bootstrap/list-group.less +88 -0
- data/vendor/assets/stylesheets/bastion/bootstrap/media.less +56 -0
- data/vendor/assets/stylesheets/bastion/bootstrap/mixins.less +845 -0
- data/vendor/assets/stylesheets/bastion/bootstrap/modals.less +129 -0
- data/vendor/assets/stylesheets/bastion/bootstrap/navbar.less +612 -0
- data/vendor/assets/stylesheets/bastion/bootstrap/navs.less +242 -0
- data/vendor/assets/stylesheets/bastion/bootstrap/normalize.less +406 -0
- data/vendor/assets/stylesheets/bastion/bootstrap/pager.less +55 -0
- data/vendor/assets/stylesheets/bastion/bootstrap/pagination.less +85 -0
- data/vendor/assets/stylesheets/bastion/bootstrap/panels.less +182 -0
- data/vendor/assets/stylesheets/bastion/bootstrap/popovers.less +133 -0
- data/vendor/assets/stylesheets/bastion/bootstrap/print.less +105 -0
- data/vendor/assets/stylesheets/bastion/bootstrap/progress-bars.less +80 -0
- data/vendor/assets/stylesheets/bastion/bootstrap/responsive-utilities.less +209 -0
- data/vendor/assets/stylesheets/bastion/bootstrap/scaffolding.less +119 -0
- data/vendor/assets/stylesheets/bastion/bootstrap/tables.less +231 -0
- data/vendor/assets/stylesheets/bastion/bootstrap/theme.less +247 -0
- data/vendor/assets/stylesheets/bastion/bootstrap/thumbnails.less +36 -0
- data/vendor/assets/stylesheets/bastion/bootstrap/tooltip.less +95 -0
- data/vendor/assets/stylesheets/bastion/bootstrap/type.less +281 -0
- data/vendor/assets/stylesheets/bastion/bootstrap/utilities.less +56 -0
- data/vendor/assets/stylesheets/bastion/bootstrap/variables.less +642 -0
- data/vendor/assets/stylesheets/bastion/bootstrap/wells.less +29 -0
- data/vendor/assets/stylesheets/bastion/font-awesome/less/bootstrap.less +84 -0
- data/vendor/assets/stylesheets/bastion/font-awesome/less/core.less +129 -0
- data/vendor/assets/stylesheets/bastion/font-awesome/less/extras.less +93 -0
- data/vendor/assets/stylesheets/bastion/font-awesome/less/font-awesome-ie7.less +1953 -0
- data/vendor/assets/stylesheets/bastion/font-awesome/less/font-awesome.less +33 -0
- data/vendor/assets/stylesheets/bastion/font-awesome/less/icons.less +381 -0
- data/vendor/assets/stylesheets/bastion/font-awesome/less/mixins.less +48 -0
- data/vendor/assets/stylesheets/bastion/font-awesome/less/path.less +14 -0
- data/vendor/assets/stylesheets/bastion/font-awesome/less/variables.less +735 -0
- data/vendor/assets/stylesheets/bastion/font-awesome/scss/_bootstrap.scss +84 -0
- data/vendor/assets/stylesheets/bastion/font-awesome/scss/_core.scss +129 -0
- data/vendor/assets/stylesheets/bastion/font-awesome/scss/_extras.scss +93 -0
- data/vendor/assets/stylesheets/bastion/font-awesome/scss/_icons.scss +381 -0
- data/vendor/assets/stylesheets/bastion/font-awesome/scss/_mixins.scss +48 -0
- data/vendor/assets/stylesheets/bastion/font-awesome/scss/_path.scss +14 -0
- data/vendor/assets/stylesheets/bastion/font-awesome/scss/_variables.scss +734 -0
- data/vendor/assets/stylesheets/bastion/font-awesome/scss/font-awesome-ie7.scss +1953 -0
- data/vendor/assets/stylesheets/bastion/font-awesome/scss/font-awesome.scss +33 -0
- data/vendor/assets/stylesheets/bastion/rcue/buttons.less +44 -0
- data/vendor/assets/stylesheets/bastion/rcue/fonts.less +52 -0
- data/vendor/assets/stylesheets/bastion/rcue/forms.less +19 -0
- data/vendor/assets/stylesheets/bastion/rcue/mixins.less +50 -0
- data/vendor/assets/stylesheets/bastion/rcue/navbar.less +481 -0
- data/vendor/assets/stylesheets/bastion/rcue/rcue.less +15 -0
- data/vendor/assets/stylesheets/bastion/rcue/variables.less +47 -0
- metadata +376 -0
@@ -0,0 +1,447 @@
|
|
1
|
+
angular.module('ui.bootstrap.datepicker', ['ui.bootstrap.position'])
|
2
|
+
|
3
|
+
.constant('datepickerConfig', {
|
4
|
+
dayFormat: 'dd',
|
5
|
+
monthFormat: 'MMMM',
|
6
|
+
yearFormat: 'yyyy',
|
7
|
+
dayHeaderFormat: 'EEE',
|
8
|
+
dayTitleFormat: 'MMMM yyyy',
|
9
|
+
monthTitleFormat: 'yyyy',
|
10
|
+
showWeeks: true,
|
11
|
+
startingDay: 0,
|
12
|
+
yearRange: 20,
|
13
|
+
minDate: null,
|
14
|
+
maxDate: null
|
15
|
+
})
|
16
|
+
|
17
|
+
.controller('DatepickerController', ['$scope', '$attrs', 'dateFilter', 'datepickerConfig', function($scope, $attrs, dateFilter, dtConfig) {
|
18
|
+
var format = {
|
19
|
+
day: getValue($attrs.dayFormat, dtConfig.dayFormat),
|
20
|
+
month: getValue($attrs.monthFormat, dtConfig.monthFormat),
|
21
|
+
year: getValue($attrs.yearFormat, dtConfig.yearFormat),
|
22
|
+
dayHeader: getValue($attrs.dayHeaderFormat, dtConfig.dayHeaderFormat),
|
23
|
+
dayTitle: getValue($attrs.dayTitleFormat, dtConfig.dayTitleFormat),
|
24
|
+
monthTitle: getValue($attrs.monthTitleFormat, dtConfig.monthTitleFormat)
|
25
|
+
},
|
26
|
+
startingDay = getValue($attrs.startingDay, dtConfig.startingDay),
|
27
|
+
yearRange = getValue($attrs.yearRange, dtConfig.yearRange);
|
28
|
+
|
29
|
+
this.minDate = dtConfig.minDate ? new Date(dtConfig.minDate) : null;
|
30
|
+
this.maxDate = dtConfig.maxDate ? new Date(dtConfig.maxDate) : null;
|
31
|
+
|
32
|
+
function getValue(value, defaultValue) {
|
33
|
+
return angular.isDefined(value) ? $scope.$parent.$eval(value) : defaultValue;
|
34
|
+
}
|
35
|
+
|
36
|
+
function getDaysInMonth( year, month ) {
|
37
|
+
return new Date(year, month, 0).getDate();
|
38
|
+
}
|
39
|
+
|
40
|
+
function getDates(startDate, n) {
|
41
|
+
var dates = new Array(n);
|
42
|
+
var current = startDate, i = 0;
|
43
|
+
while (i < n) {
|
44
|
+
dates[i++] = new Date(current);
|
45
|
+
current.setDate( current.getDate() + 1 );
|
46
|
+
}
|
47
|
+
return dates;
|
48
|
+
}
|
49
|
+
|
50
|
+
function makeDate(date, format, isSelected, isSecondary) {
|
51
|
+
return { date: date, label: dateFilter(date, format), selected: !!isSelected, secondary: !!isSecondary };
|
52
|
+
}
|
53
|
+
|
54
|
+
this.modes = [
|
55
|
+
{
|
56
|
+
name: 'day',
|
57
|
+
getVisibleDates: function(date, selected) {
|
58
|
+
var year = date.getFullYear(), month = date.getMonth(), firstDayOfMonth = new Date(year, month, 1);
|
59
|
+
var difference = startingDay - firstDayOfMonth.getDay(),
|
60
|
+
numDisplayedFromPreviousMonth = (difference > 0) ? 7 - difference : - difference,
|
61
|
+
firstDate = new Date(firstDayOfMonth), numDates = 0;
|
62
|
+
|
63
|
+
if ( numDisplayedFromPreviousMonth > 0 ) {
|
64
|
+
firstDate.setDate( - numDisplayedFromPreviousMonth + 1 );
|
65
|
+
numDates += numDisplayedFromPreviousMonth; // Previous
|
66
|
+
}
|
67
|
+
numDates += getDaysInMonth(year, month + 1); // Current
|
68
|
+
numDates += (7 - numDates % 7) % 7; // Next
|
69
|
+
|
70
|
+
var days = getDates(firstDate, numDates), labels = new Array(7);
|
71
|
+
for (var i = 0; i < numDates; i ++) {
|
72
|
+
var dt = new Date(days[i]);
|
73
|
+
days[i] = makeDate(dt, format.day, (selected && selected.getDate() === dt.getDate() && selected.getMonth() === dt.getMonth() && selected.getFullYear() === dt.getFullYear()), dt.getMonth() !== month);
|
74
|
+
}
|
75
|
+
for (var j = 0; j < 7; j++) {
|
76
|
+
labels[j] = dateFilter(days[j].date, format.dayHeader);
|
77
|
+
}
|
78
|
+
return { objects: days, title: dateFilter(date, format.dayTitle), labels: labels };
|
79
|
+
},
|
80
|
+
compare: function(date1, date2) {
|
81
|
+
return (new Date( date1.getFullYear(), date1.getMonth(), date1.getDate() ) - new Date( date2.getFullYear(), date2.getMonth(), date2.getDate() ) );
|
82
|
+
},
|
83
|
+
split: 7,
|
84
|
+
step: { months: 1 }
|
85
|
+
},
|
86
|
+
{
|
87
|
+
name: 'month',
|
88
|
+
getVisibleDates: function(date, selected) {
|
89
|
+
var months = new Array(12), year = date.getFullYear();
|
90
|
+
for ( var i = 0; i < 12; i++ ) {
|
91
|
+
var dt = new Date(year, i, 1);
|
92
|
+
months[i] = makeDate(dt, format.month, (selected && selected.getMonth() === i && selected.getFullYear() === year));
|
93
|
+
}
|
94
|
+
return { objects: months, title: dateFilter(date, format.monthTitle) };
|
95
|
+
},
|
96
|
+
compare: function(date1, date2) {
|
97
|
+
return new Date( date1.getFullYear(), date1.getMonth() ) - new Date( date2.getFullYear(), date2.getMonth() );
|
98
|
+
},
|
99
|
+
split: 3,
|
100
|
+
step: { years: 1 }
|
101
|
+
},
|
102
|
+
{
|
103
|
+
name: 'year',
|
104
|
+
getVisibleDates: function(date, selected) {
|
105
|
+
var years = new Array(yearRange), year = date.getFullYear(), startYear = parseInt((year - 1) / yearRange, 10) * yearRange + 1;
|
106
|
+
for ( var i = 0; i < yearRange; i++ ) {
|
107
|
+
var dt = new Date(startYear + i, 0, 1);
|
108
|
+
years[i] = makeDate(dt, format.year, (selected && selected.getFullYear() === dt.getFullYear()));
|
109
|
+
}
|
110
|
+
return { objects: years, title: [years[0].label, years[yearRange - 1].label].join(' - ') };
|
111
|
+
},
|
112
|
+
compare: function(date1, date2) {
|
113
|
+
return date1.getFullYear() - date2.getFullYear();
|
114
|
+
},
|
115
|
+
split: 5,
|
116
|
+
step: { years: yearRange }
|
117
|
+
}
|
118
|
+
];
|
119
|
+
|
120
|
+
this.isDisabled = function(date, mode) {
|
121
|
+
var currentMode = this.modes[mode || 0];
|
122
|
+
return ((this.minDate && currentMode.compare(date, this.minDate) < 0) || (this.maxDate && currentMode.compare(date, this.maxDate) > 0) || ($scope.dateDisabled && $scope.dateDisabled({date: date, mode: currentMode.name})));
|
123
|
+
};
|
124
|
+
}])
|
125
|
+
|
126
|
+
.directive( 'datepicker', ['dateFilter', '$parse', 'datepickerConfig', '$log', function (dateFilter, $parse, datepickerConfig, $log) {
|
127
|
+
return {
|
128
|
+
restrict: 'EA',
|
129
|
+
replace: true,
|
130
|
+
templateUrl: 'template/datepicker/datepicker.html',
|
131
|
+
scope: {
|
132
|
+
dateDisabled: '&'
|
133
|
+
},
|
134
|
+
require: ['datepicker', '?^ngModel'],
|
135
|
+
controller: 'DatepickerController',
|
136
|
+
link: function(scope, element, attrs, ctrls) {
|
137
|
+
var datepickerCtrl = ctrls[0], ngModel = ctrls[1];
|
138
|
+
|
139
|
+
if (!ngModel) {
|
140
|
+
return; // do nothing if no ng-model
|
141
|
+
}
|
142
|
+
|
143
|
+
// Configuration parameters
|
144
|
+
var mode = 0, selected = new Date(), showWeeks = datepickerConfig.showWeeks;
|
145
|
+
|
146
|
+
if (attrs.showWeeks) {
|
147
|
+
scope.$parent.$watch($parse(attrs.showWeeks), function(value) {
|
148
|
+
showWeeks = !! value;
|
149
|
+
updateShowWeekNumbers();
|
150
|
+
});
|
151
|
+
} else {
|
152
|
+
updateShowWeekNumbers();
|
153
|
+
}
|
154
|
+
|
155
|
+
if (attrs.min) {
|
156
|
+
scope.$parent.$watch($parse(attrs.min), function(value) {
|
157
|
+
datepickerCtrl.minDate = value ? new Date(value) : null;
|
158
|
+
refill();
|
159
|
+
});
|
160
|
+
}
|
161
|
+
if (attrs.max) {
|
162
|
+
scope.$parent.$watch($parse(attrs.max), function(value) {
|
163
|
+
datepickerCtrl.maxDate = value ? new Date(value) : null;
|
164
|
+
refill();
|
165
|
+
});
|
166
|
+
}
|
167
|
+
|
168
|
+
function updateShowWeekNumbers() {
|
169
|
+
scope.showWeekNumbers = mode === 0 && showWeeks;
|
170
|
+
}
|
171
|
+
|
172
|
+
// Split array into smaller arrays
|
173
|
+
function split(arr, size) {
|
174
|
+
var arrays = [];
|
175
|
+
while (arr.length > 0) {
|
176
|
+
arrays.push(arr.splice(0, size));
|
177
|
+
}
|
178
|
+
return arrays;
|
179
|
+
}
|
180
|
+
|
181
|
+
function refill( updateSelected ) {
|
182
|
+
var date = null, valid = true;
|
183
|
+
|
184
|
+
if ( ngModel.$modelValue ) {
|
185
|
+
date = new Date( ngModel.$modelValue );
|
186
|
+
|
187
|
+
if ( isNaN(date) ) {
|
188
|
+
valid = false;
|
189
|
+
$log.error('Datepicker directive: "ng-model" value must be a Date object, a number of milliseconds since 01.01.1970 or a string representing an RFC2822 or ISO 8601 date.');
|
190
|
+
} else if ( updateSelected ) {
|
191
|
+
selected = date;
|
192
|
+
}
|
193
|
+
}
|
194
|
+
ngModel.$setValidity('date', valid);
|
195
|
+
|
196
|
+
var currentMode = datepickerCtrl.modes[mode], data = currentMode.getVisibleDates(selected, date);
|
197
|
+
angular.forEach(data.objects, function(obj) {
|
198
|
+
obj.disabled = datepickerCtrl.isDisabled(obj.date, mode);
|
199
|
+
});
|
200
|
+
|
201
|
+
ngModel.$setValidity('date-disabled', (!date || !datepickerCtrl.isDisabled(date)));
|
202
|
+
|
203
|
+
scope.rows = split(data.objects, currentMode.split);
|
204
|
+
scope.labels = data.labels || [];
|
205
|
+
scope.title = data.title;
|
206
|
+
}
|
207
|
+
|
208
|
+
function setMode(value) {
|
209
|
+
mode = value;
|
210
|
+
updateShowWeekNumbers();
|
211
|
+
refill();
|
212
|
+
}
|
213
|
+
|
214
|
+
ngModel.$render = function() {
|
215
|
+
refill( true );
|
216
|
+
};
|
217
|
+
|
218
|
+
scope.select = function( date ) {
|
219
|
+
if ( mode === 0 ) {
|
220
|
+
var dt = new Date( ngModel.$modelValue );
|
221
|
+
dt.setFullYear( date.getFullYear(), date.getMonth(), date.getDate() );
|
222
|
+
ngModel.$setViewValue( dt );
|
223
|
+
refill( true );
|
224
|
+
} else {
|
225
|
+
selected = date;
|
226
|
+
setMode( mode - 1 );
|
227
|
+
}
|
228
|
+
};
|
229
|
+
scope.move = function(direction) {
|
230
|
+
var step = datepickerCtrl.modes[mode].step;
|
231
|
+
selected.setMonth( selected.getMonth() + direction * (step.months || 0) );
|
232
|
+
selected.setFullYear( selected.getFullYear() + direction * (step.years || 0) );
|
233
|
+
refill();
|
234
|
+
};
|
235
|
+
scope.toggleMode = function() {
|
236
|
+
setMode( (mode + 1) % datepickerCtrl.modes.length );
|
237
|
+
};
|
238
|
+
scope.getWeekNumber = function(row) {
|
239
|
+
return ( mode === 0 && scope.showWeekNumbers && row.length === 7 ) ? getISO8601WeekNumber(row[0].date) : null;
|
240
|
+
};
|
241
|
+
|
242
|
+
function getISO8601WeekNumber(date) {
|
243
|
+
var checkDate = new Date(date);
|
244
|
+
checkDate.setDate(checkDate.getDate() + 4 - (checkDate.getDay() || 7)); // Thursday
|
245
|
+
var time = checkDate.getTime();
|
246
|
+
checkDate.setMonth(0); // Compare with Jan 1
|
247
|
+
checkDate.setDate(1);
|
248
|
+
return Math.floor(Math.round((time - checkDate) / 86400000) / 7) + 1;
|
249
|
+
}
|
250
|
+
}
|
251
|
+
};
|
252
|
+
}])
|
253
|
+
|
254
|
+
.constant('datepickerPopupConfig', {
|
255
|
+
dateFormat: 'yyyy-MM-dd',
|
256
|
+
closeOnDateSelection: true
|
257
|
+
})
|
258
|
+
|
259
|
+
.directive('datepickerPopup', ['$compile', '$parse', '$document', '$position', 'dateFilter', 'datepickerPopupConfig',
|
260
|
+
function ($compile, $parse, $document, $position, dateFilter, datepickerPopupConfig) {
|
261
|
+
return {
|
262
|
+
restrict: 'EA',
|
263
|
+
require: 'ngModel',
|
264
|
+
link: function(originalScope, element, attrs, ngModel) {
|
265
|
+
|
266
|
+
var closeOnDateSelection = angular.isDefined(attrs.closeOnDateSelection) ? scope.$eval(attrs.closeOnDateSelection) : datepickerPopupConfig.closeOnDateSelection;
|
267
|
+
var dateFormat = attrs.datepickerPopup || datepickerPopupConfig.dateFormat;
|
268
|
+
|
269
|
+
// create a child scope for the datepicker directive so we are not polluting original scope
|
270
|
+
var scope = originalScope.$new();
|
271
|
+
originalScope.$on('$destroy', function() {
|
272
|
+
scope.$destroy();
|
273
|
+
});
|
274
|
+
|
275
|
+
var getIsOpen, setIsOpen;
|
276
|
+
if ( attrs.isOpen ) {
|
277
|
+
getIsOpen = $parse(attrs.isOpen);
|
278
|
+
setIsOpen = getIsOpen.assign;
|
279
|
+
|
280
|
+
originalScope.$watch(getIsOpen, function updateOpen(value) {
|
281
|
+
scope.isOpen = !! value;
|
282
|
+
});
|
283
|
+
}
|
284
|
+
scope.isOpen = getIsOpen ? getIsOpen(originalScope) : false; // Initial state
|
285
|
+
|
286
|
+
function setOpen( value ) {
|
287
|
+
if (setIsOpen) {
|
288
|
+
setIsOpen(originalScope, !!value);
|
289
|
+
} else {
|
290
|
+
scope.isOpen = !!value;
|
291
|
+
}
|
292
|
+
}
|
293
|
+
|
294
|
+
var documentClickBind = function(event) {
|
295
|
+
if (scope.isOpen && event.target !== element[0]) {
|
296
|
+
scope.$apply(function() {
|
297
|
+
setOpen(false);
|
298
|
+
});
|
299
|
+
}
|
300
|
+
};
|
301
|
+
|
302
|
+
var elementFocusBind = function() {
|
303
|
+
scope.$apply(function() {
|
304
|
+
setOpen( true );
|
305
|
+
});
|
306
|
+
};
|
307
|
+
|
308
|
+
// popup element used to display calendar
|
309
|
+
var popupEl = angular.element('<datepicker-popup-wrap><datepicker></datepicker></datepicker-popup-wrap>');
|
310
|
+
popupEl.attr({
|
311
|
+
'ng-model': 'date',
|
312
|
+
'ng-change': 'dateSelection()'
|
313
|
+
});
|
314
|
+
var datepickerEl = popupEl.find('datepicker');
|
315
|
+
if (attrs.datepickerOptions) {
|
316
|
+
datepickerEl.attr(angular.extend({}, originalScope.$eval(attrs.datepickerOptions)));
|
317
|
+
}
|
318
|
+
|
319
|
+
// TODO: reverse from dateFilter string to Date object
|
320
|
+
function parseDate(viewValue) {
|
321
|
+
if (!viewValue) {
|
322
|
+
ngModel.$setValidity('date', true);
|
323
|
+
return null;
|
324
|
+
} else if (angular.isDate(viewValue)) {
|
325
|
+
ngModel.$setValidity('date', true);
|
326
|
+
return viewValue;
|
327
|
+
} else if (angular.isString(viewValue)) {
|
328
|
+
var date = new Date(viewValue);
|
329
|
+
if (isNaN(date)) {
|
330
|
+
ngModel.$setValidity('date', false);
|
331
|
+
return undefined;
|
332
|
+
} else {
|
333
|
+
ngModel.$setValidity('date', true);
|
334
|
+
return date;
|
335
|
+
}
|
336
|
+
} else {
|
337
|
+
ngModel.$setValidity('date', false);
|
338
|
+
return undefined;
|
339
|
+
}
|
340
|
+
}
|
341
|
+
ngModel.$parsers.unshift(parseDate);
|
342
|
+
|
343
|
+
// Inner change
|
344
|
+
scope.dateSelection = function() {
|
345
|
+
ngModel.$setViewValue(scope.date);
|
346
|
+
ngModel.$render();
|
347
|
+
|
348
|
+
if (closeOnDateSelection) {
|
349
|
+
setOpen( false );
|
350
|
+
}
|
351
|
+
};
|
352
|
+
|
353
|
+
element.bind('input change keyup', function() {
|
354
|
+
scope.$apply(function() {
|
355
|
+
updateCalendar();
|
356
|
+
});
|
357
|
+
});
|
358
|
+
|
359
|
+
// Outter change
|
360
|
+
ngModel.$render = function() {
|
361
|
+
var date = ngModel.$viewValue ? dateFilter(ngModel.$viewValue, dateFormat) : '';
|
362
|
+
element.val(date);
|
363
|
+
|
364
|
+
updateCalendar();
|
365
|
+
};
|
366
|
+
|
367
|
+
function updateCalendar() {
|
368
|
+
scope.date = ngModel.$modelValue;
|
369
|
+
updatePosition();
|
370
|
+
}
|
371
|
+
|
372
|
+
function addWatchableAttribute(attribute, scopeProperty, datepickerAttribute) {
|
373
|
+
if (attribute) {
|
374
|
+
originalScope.$watch($parse(attribute), function(value){
|
375
|
+
scope[scopeProperty] = value;
|
376
|
+
});
|
377
|
+
datepickerEl.attr(datepickerAttribute || scopeProperty, scopeProperty);
|
378
|
+
}
|
379
|
+
}
|
380
|
+
addWatchableAttribute(attrs.min, 'min');
|
381
|
+
addWatchableAttribute(attrs.max, 'max');
|
382
|
+
if (attrs.showWeeks) {
|
383
|
+
addWatchableAttribute(attrs.showWeeks, 'showWeeks', 'show-weeks');
|
384
|
+
} else {
|
385
|
+
scope.showWeeks = true;
|
386
|
+
datepickerEl.attr('show-weeks', 'showWeeks');
|
387
|
+
}
|
388
|
+
if (attrs.dateDisabled) {
|
389
|
+
datepickerEl.attr('date-disabled', attrs.dateDisabled);
|
390
|
+
}
|
391
|
+
|
392
|
+
function updatePosition() {
|
393
|
+
scope.position = $position.position(element);
|
394
|
+
scope.position.top = scope.position.top + element.prop('offsetHeight');
|
395
|
+
}
|
396
|
+
|
397
|
+
var documentBindingInitialized = false, elementFocusInitialized = false;
|
398
|
+
scope.$watch('isOpen', function(value) {
|
399
|
+
if (value) {
|
400
|
+
updatePosition();
|
401
|
+
$document.bind('click', documentClickBind);
|
402
|
+
if(elementFocusInitialized) {
|
403
|
+
element.unbind('focus', elementFocusBind);
|
404
|
+
}
|
405
|
+
element[0].focus();
|
406
|
+
documentBindingInitialized = true;
|
407
|
+
} else {
|
408
|
+
if(documentBindingInitialized) {
|
409
|
+
$document.unbind('click', documentClickBind);
|
410
|
+
}
|
411
|
+
element.bind('focus', elementFocusBind);
|
412
|
+
elementFocusInitialized = true;
|
413
|
+
}
|
414
|
+
|
415
|
+
if ( setIsOpen ) {
|
416
|
+
setIsOpen(originalScope, value);
|
417
|
+
}
|
418
|
+
});
|
419
|
+
|
420
|
+
var $setModelValue = $parse(attrs.ngModel).assign;
|
421
|
+
|
422
|
+
scope.today = function() {
|
423
|
+
$setModelValue(originalScope, new Date());
|
424
|
+
};
|
425
|
+
scope.clear = function() {
|
426
|
+
$setModelValue(originalScope, null);
|
427
|
+
};
|
428
|
+
|
429
|
+
element.after($compile(popupEl)(scope));
|
430
|
+
}
|
431
|
+
};
|
432
|
+
}])
|
433
|
+
|
434
|
+
.directive('datepickerPopupWrap', [function() {
|
435
|
+
return {
|
436
|
+
restrict:'E',
|
437
|
+
replace: true,
|
438
|
+
transclude: true,
|
439
|
+
templateUrl: 'template/datepicker/popup.html',
|
440
|
+
link:function (scope, element, attrs) {
|
441
|
+
element.bind('click', function(event) {
|
442
|
+
event.preventDefault();
|
443
|
+
event.stopPropagation();
|
444
|
+
});
|
445
|
+
}
|
446
|
+
};
|
447
|
+
}]);
|
@@ -0,0 +1,232 @@
|
|
1
|
+
angular.module('ui.bootstrap.timepicker', [])
|
2
|
+
|
3
|
+
.constant('timepickerConfig', {
|
4
|
+
hourStep: 1,
|
5
|
+
minuteStep: 1,
|
6
|
+
showMeridian: true,
|
7
|
+
meridians: ['AM', 'PM'],
|
8
|
+
readonlyInput: false,
|
9
|
+
mousewheel: true
|
10
|
+
})
|
11
|
+
|
12
|
+
.directive('timepicker', ['$parse', '$log', 'timepickerConfig', function ($parse, $log, timepickerConfig) {
|
13
|
+
return {
|
14
|
+
restrict: 'EA',
|
15
|
+
require:'?^ngModel',
|
16
|
+
replace: true,
|
17
|
+
scope: {},
|
18
|
+
templateUrl: 'template/timepicker/timepicker.html',
|
19
|
+
link: function(scope, element, attrs, ngModel) {
|
20
|
+
if ( !ngModel ) {
|
21
|
+
return; // do nothing if no ng-model
|
22
|
+
}
|
23
|
+
|
24
|
+
var selected = new Date(), meridians = timepickerConfig.meridians;
|
25
|
+
|
26
|
+
var hourStep = timepickerConfig.hourStep;
|
27
|
+
if (attrs.hourStep) {
|
28
|
+
scope.$parent.$watch($parse(attrs.hourStep), function(value) {
|
29
|
+
hourStep = parseInt(value, 10);
|
30
|
+
});
|
31
|
+
}
|
32
|
+
|
33
|
+
var minuteStep = timepickerConfig.minuteStep;
|
34
|
+
if (attrs.minuteStep) {
|
35
|
+
scope.$parent.$watch($parse(attrs.minuteStep), function(value) {
|
36
|
+
minuteStep = parseInt(value, 10);
|
37
|
+
});
|
38
|
+
}
|
39
|
+
|
40
|
+
// 12H / 24H mode
|
41
|
+
scope.showMeridian = timepickerConfig.showMeridian;
|
42
|
+
if (attrs.showMeridian) {
|
43
|
+
scope.$parent.$watch($parse(attrs.showMeridian), function(value) {
|
44
|
+
scope.showMeridian = !!value;
|
45
|
+
|
46
|
+
if ( ngModel.$error.time ) {
|
47
|
+
// Evaluate from template
|
48
|
+
var hours = getHoursFromTemplate(), minutes = getMinutesFromTemplate();
|
49
|
+
if (angular.isDefined( hours ) && angular.isDefined( minutes )) {
|
50
|
+
selected.setHours( hours );
|
51
|
+
refresh();
|
52
|
+
}
|
53
|
+
} else {
|
54
|
+
updateTemplate();
|
55
|
+
}
|
56
|
+
});
|
57
|
+
}
|
58
|
+
|
59
|
+
// Get scope.hours in 24H mode if valid
|
60
|
+
function getHoursFromTemplate ( ) {
|
61
|
+
var hours = parseInt( scope.hours, 10 );
|
62
|
+
var valid = ( scope.showMeridian ) ? (hours > 0 && hours < 13) : (hours >= 0 && hours < 24);
|
63
|
+
if ( !valid ) {
|
64
|
+
return undefined;
|
65
|
+
}
|
66
|
+
|
67
|
+
if ( scope.showMeridian ) {
|
68
|
+
if ( hours === 12 ) {
|
69
|
+
hours = 0;
|
70
|
+
}
|
71
|
+
if ( scope.meridian === meridians[1] ) {
|
72
|
+
hours = hours + 12;
|
73
|
+
}
|
74
|
+
}
|
75
|
+
return hours;
|
76
|
+
}
|
77
|
+
|
78
|
+
function getMinutesFromTemplate() {
|
79
|
+
var minutes = parseInt(scope.minutes, 10);
|
80
|
+
return ( minutes >= 0 && minutes < 60 ) ? minutes : undefined;
|
81
|
+
}
|
82
|
+
|
83
|
+
function pad( value ) {
|
84
|
+
return ( angular.isDefined(value) && value.toString().length < 2 ) ? '0' + value : value;
|
85
|
+
}
|
86
|
+
|
87
|
+
// Input elements
|
88
|
+
var inputs = element.find('input'), hoursInputEl = inputs.eq(0), minutesInputEl = inputs.eq(1);
|
89
|
+
|
90
|
+
// Respond on mousewheel spin
|
91
|
+
var mousewheel = (angular.isDefined(attrs.mousewheel)) ? scope.$eval(attrs.mousewheel) : timepickerConfig.mousewheel;
|
92
|
+
if ( mousewheel ) {
|
93
|
+
|
94
|
+
var isScrollingUp = function(e) {
|
95
|
+
if (e.originalEvent) {
|
96
|
+
e = e.originalEvent;
|
97
|
+
}
|
98
|
+
//pick correct delta variable depending on event
|
99
|
+
var delta = (e.wheelDelta) ? e.wheelDelta : -e.deltaY;
|
100
|
+
return (e.detail || delta > 0);
|
101
|
+
};
|
102
|
+
|
103
|
+
hoursInputEl.bind('mousewheel wheel', function(e) {
|
104
|
+
scope.$apply( (isScrollingUp(e)) ? scope.incrementHours() : scope.decrementHours() );
|
105
|
+
e.preventDefault();
|
106
|
+
});
|
107
|
+
|
108
|
+
minutesInputEl.bind('mousewheel wheel', function(e) {
|
109
|
+
scope.$apply( (isScrollingUp(e)) ? scope.incrementMinutes() : scope.decrementMinutes() );
|
110
|
+
e.preventDefault();
|
111
|
+
});
|
112
|
+
}
|
113
|
+
|
114
|
+
scope.readonlyInput = (angular.isDefined(attrs.readonlyInput)) ? scope.$eval(attrs.readonlyInput) : timepickerConfig.readonlyInput;
|
115
|
+
if ( ! scope.readonlyInput ) {
|
116
|
+
|
117
|
+
var invalidate = function(invalidHours, invalidMinutes) {
|
118
|
+
ngModel.$setViewValue( null );
|
119
|
+
ngModel.$setValidity('time', false);
|
120
|
+
if (angular.isDefined(invalidHours)) {
|
121
|
+
scope.invalidHours = invalidHours;
|
122
|
+
}
|
123
|
+
if (angular.isDefined(invalidMinutes)) {
|
124
|
+
scope.invalidMinutes = invalidMinutes;
|
125
|
+
}
|
126
|
+
};
|
127
|
+
|
128
|
+
scope.updateHours = function() {
|
129
|
+
var hours = getHoursFromTemplate();
|
130
|
+
|
131
|
+
if ( angular.isDefined(hours) ) {
|
132
|
+
selected.setHours( hours );
|
133
|
+
refresh( 'h' );
|
134
|
+
} else {
|
135
|
+
invalidate(true);
|
136
|
+
}
|
137
|
+
};
|
138
|
+
|
139
|
+
hoursInputEl.bind('blur', function(e) {
|
140
|
+
if ( !scope.validHours && scope.hours < 10) {
|
141
|
+
scope.$apply( function() {
|
142
|
+
scope.hours = pad( scope.hours );
|
143
|
+
});
|
144
|
+
}
|
145
|
+
});
|
146
|
+
|
147
|
+
scope.updateMinutes = function() {
|
148
|
+
var minutes = getMinutesFromTemplate();
|
149
|
+
|
150
|
+
if ( angular.isDefined(minutes) ) {
|
151
|
+
selected.setMinutes( minutes );
|
152
|
+
refresh( 'm' );
|
153
|
+
} else {
|
154
|
+
invalidate(undefined, true);
|
155
|
+
}
|
156
|
+
};
|
157
|
+
|
158
|
+
minutesInputEl.bind('blur', function(e) {
|
159
|
+
if ( !scope.invalidMinutes && scope.minutes < 10 ) {
|
160
|
+
scope.$apply( function() {
|
161
|
+
scope.minutes = pad( scope.minutes );
|
162
|
+
});
|
163
|
+
}
|
164
|
+
});
|
165
|
+
} else {
|
166
|
+
scope.updateHours = angular.noop;
|
167
|
+
scope.updateMinutes = angular.noop;
|
168
|
+
}
|
169
|
+
|
170
|
+
ngModel.$render = function() {
|
171
|
+
var date = ngModel.$modelValue ? new Date( ngModel.$modelValue ) : null;
|
172
|
+
|
173
|
+
if ( isNaN(date) ) {
|
174
|
+
ngModel.$setValidity('time', false);
|
175
|
+
$log.error('Timepicker directive: "ng-model" value must be a Date object, a number of milliseconds since 01.01.1970 or a string representing an RFC2822 or ISO 8601 date.');
|
176
|
+
} else {
|
177
|
+
if ( date ) {
|
178
|
+
selected = date;
|
179
|
+
}
|
180
|
+
makeValid();
|
181
|
+
updateTemplate();
|
182
|
+
}
|
183
|
+
};
|
184
|
+
|
185
|
+
// Call internally when we know that model is valid.
|
186
|
+
function refresh( keyboardChange ) {
|
187
|
+
makeValid();
|
188
|
+
ngModel.$setViewValue( new Date(selected) );
|
189
|
+
updateTemplate( keyboardChange );
|
190
|
+
}
|
191
|
+
|
192
|
+
function makeValid() {
|
193
|
+
ngModel.$setValidity('time', true);
|
194
|
+
scope.invalidHours = false;
|
195
|
+
scope.invalidMinutes = false;
|
196
|
+
}
|
197
|
+
|
198
|
+
function updateTemplate( keyboardChange ) {
|
199
|
+
var hours = selected.getHours(), minutes = selected.getMinutes();
|
200
|
+
|
201
|
+
if ( scope.showMeridian ) {
|
202
|
+
hours = ( hours === 0 || hours === 12 ) ? 12 : hours % 12; // Convert 24 to 12 hour system
|
203
|
+
}
|
204
|
+
scope.hours = keyboardChange === 'h' ? hours : pad(hours);
|
205
|
+
scope.minutes = keyboardChange === 'm' ? minutes : pad(minutes);
|
206
|
+
scope.meridian = selected.getHours() < 12 ? meridians[0] : meridians[1];
|
207
|
+
}
|
208
|
+
|
209
|
+
function addMinutes( minutes ) {
|
210
|
+
var dt = new Date( selected.getTime() + minutes * 60000 );
|
211
|
+
selected.setHours( dt.getHours(), dt.getMinutes() );
|
212
|
+
refresh();
|
213
|
+
}
|
214
|
+
|
215
|
+
scope.incrementHours = function() {
|
216
|
+
addMinutes( hourStep * 60 );
|
217
|
+
};
|
218
|
+
scope.decrementHours = function() {
|
219
|
+
addMinutes( - hourStep * 60 );
|
220
|
+
};
|
221
|
+
scope.incrementMinutes = function() {
|
222
|
+
addMinutes( minuteStep );
|
223
|
+
};
|
224
|
+
scope.decrementMinutes = function() {
|
225
|
+
addMinutes( - minuteStep );
|
226
|
+
};
|
227
|
+
scope.toggleMeridian = function() {
|
228
|
+
addMinutes( 12 * 60 * (( selected.getHours() < 12 ) ? 1 : -1) );
|
229
|
+
};
|
230
|
+
}
|
231
|
+
};
|
232
|
+
}]);
|