/* globals define, jQuery, module, require, angular, moment */ /* jslint vars:true */ /** * @license angular-bootstrap-datetimepicker * Copyright 2016 Knight Rider Consulting, Inc. http://www.knightrider.com * License: MIT * * @author Dale "Ducky" Lotts * @since 2013-Jul-8 */ ;(function (root, factory) { 'use strict' /* istanbul ignore if */ if (typeof module !== 'undefined' && module.exports) { var ng = typeof angular === 'undefined' ? require('angular') : angular var mt = typeof moment === 'undefined' ? require('moment') : moment factory(ng, mt) module.exports = 'ui.bootstrap.datetimepicker' /* istanbul ignore next */ } else if (typeof define === 'function' && /* istanbul ignore next */ define.amd) { define(['angular', 'moment'], factory) } else { factory(root.angular, root.moment) } }(this, function (angular, moment) { 'use strict' angular.module('ui.bootstrap.datetimepicker', []) .service('dateTimePickerConfig', DateTimePickerConfigProvider) .service('dateTimePickerValidator', DateTimePickerValidatorService) .directive('datetimepicker', DatetimepickerDirective) DatetimepickerDirective.$inject = ['dateTimePickerConfig', 'dateTimePickerValidator'] function DatetimepickerDirective (defaultConfig, configurationValidator) { var directiveDefinition = { bindToController: false, controller: DirectiveController, controllerAs: 'dateTimePickerController', replace: true, require: 'ngModel', restrict: 'E', scope: { beforeRender: '&', onSetTime: '&' }, templateUrl: 'templates/datetimepicker.html' } DirectiveController.$inject = ['$scope', '$element', '$attrs'] function DirectiveController ($scope, $element, $attrs) { // Configuration var ngModelController = $element.controller('ngModel') var configuration = createConfiguration() $scope.screenReader = configuration.screenReader // Behavior $scope.changeView = changeView ngModelController.$render = $render if (configuration.configureOn) { $scope.$on(configuration.configureOn, function () { configuration = createConfiguration() $scope.screenReader = configuration.screenReader ngModelController.$render() }) } if (configuration.renderOn) { $scope.$on(configuration.renderOn, ngModelController.$render) } // Implementation var viewToModelFactory = { year: yearModelFactory, month: monthModelFactory, day: dayModelFactory, hour: hourModelFactory, minute: minuteModelFactory, setTime: setTime } function changeView (viewName, dateObject, event) { if (event) { event.stopPropagation() event.preventDefault() } if (viewName && (dateObject.utcDateValue > -Infinity) && dateObject.selectable && viewToModelFactory[viewName]) { var result = viewToModelFactory[viewName](dateObject.utcDateValue) var weekDates = [] if (result.weeks) { for (var i = 0; i < result.weeks.length; i += 1) { var week = result.weeks[i] for (var j = 0; j < week.dates.length; j += 1) { var weekDate = week.dates[j] weekDates.push(weekDate) } } } $scope.beforeRender({ $view: result.currentView, $dates: result.dates || weekDates, $leftDate: result.leftDate, $upDate: result.previousViewDate, $rightDate: result.rightDate }) $scope.data = result } } function yearModelFactory (milliseconds) { var selectedDate = moment.utc(milliseconds).startOf('year') // View starts one year before the decade starts and ends one year after the decade ends // i.e. passing in a date of 1/1/2013 will give a range of 2009 to 2020 // Truncate the last digit from the current year and subtract 1 to get the start of the decade var startDecade = (parseInt(selectedDate.year() / 10, 10) * 10) var startDate = moment.utc(startOfDecade(milliseconds)).subtract(1, 'year').startOf('year') var yearFormat = 'YYYY' var activeFormat = formatValue(ngModelController.$modelValue, yearFormat) var currentFormat = moment().format(yearFormat) var result = { 'currentView': 'year', 'nextView': configuration.minView === 'year' ? 'setTime' : 'month', 'previousViewDate': new DateObject({ utcDateValue: null, display: startDecade + '-' + (startDecade + 9) }), 'leftDate': new DateObject({utcDateValue: moment.utc(startDate).subtract(9, 'year').valueOf()}), 'rightDate': new DateObject({utcDateValue: moment.utc(startDate).add(11, 'year').valueOf()}), 'dates': [] } for (var i = 0; i < 12; i += 1) { var yearMoment = moment.utc(startDate).add(i, 'years') var dateValue = { 'active': yearMoment.format(yearFormat) === activeFormat, 'current': yearMoment.format(yearFormat) === currentFormat, 'display': yearMoment.format(yearFormat), 'future': yearMoment.year() > startDecade + 9, 'past': yearMoment.year() < startDecade, 'utcDateValue': yearMoment.valueOf() } result.dates.push(new DateObject(dateValue)) } return result } function monthModelFactory (milliseconds) { var startDate = moment.utc(milliseconds).startOf('year') var previousViewDate = startOfDecade(milliseconds) var monthFormat = 'YYYY-MMM' var activeFormat = formatValue(ngModelController.$modelValue, monthFormat) var currentFormat = moment().format(monthFormat) var result = { 'previousView': 'year', 'currentView': 'month', 'nextView': configuration.minView === 'month' ? 'setTime' : 'day', 'previousViewDate': new DateObject({ utcDateValue: previousViewDate.valueOf(), display: startDate.format('YYYY') }), 'leftDate': new DateObject({utcDateValue: moment.utc(startDate).subtract(1, 'year').valueOf()}), 'rightDate': new DateObject({utcDateValue: moment.utc(startDate).add(1, 'year').valueOf()}), 'dates': [] } for (var i = 0; i < 12; i += 1) { var monthMoment = moment.utc(startDate).add(i, 'months') var dateValue = { 'active': monthMoment.format(monthFormat) === activeFormat, 'current': monthMoment.format(monthFormat) === currentFormat, 'display': monthMoment.format('MMM'), 'utcDateValue': monthMoment.valueOf() } result.dates.push(new DateObject(dateValue)) } return result } function dayModelFactory (milliseconds) { var selectedDate = moment.utc(milliseconds) var startOfMonth = moment.utc(selectedDate).startOf('month') var previousViewDate = moment.utc(selectedDate).startOf('year') var endOfMonth = moment.utc(selectedDate).endOf('month') var startDate = moment.utc(startOfMonth).subtract(Math.abs(startOfMonth.weekday()), 'days') var dayFormat = 'YYYY-MMM-DD' var activeFormat = formatValue(ngModelController.$modelValue, dayFormat) var currentFormat = moment().format(dayFormat) var result = { 'previousView': 'month', 'currentView': 'day', 'nextView': configuration.minView === 'day' ? 'setTime' : 'hour', 'previousViewDate': new DateObject({ utcDateValue: previousViewDate.valueOf(), display: startOfMonth.format('YYYY-MMM') }), 'leftDate': new DateObject({utcDateValue: moment.utc(startOfMonth).subtract(1, 'months').valueOf()}), 'rightDate': new DateObject({utcDateValue: moment.utc(startOfMonth).add(1, 'months').valueOf()}), 'dayNames': [], 'weeks': [] } for (var dayNumber = 0; dayNumber < 7; dayNumber += 1) { result.dayNames.push(moment.utc().weekday(dayNumber).format('dd')) } for (var i = 0; i < 6; i += 1) { var week = {dates: []} for (var j = 0; j < 7; j += 1) { var dayMoment = moment.utc(startDate).add((i * 7) + j, 'days') var dateValue = { 'active': dayMoment.format(dayFormat) === activeFormat, 'current': dayMoment.format(dayFormat) === currentFormat, 'display': dayMoment.format('D'), 'future': dayMoment.isAfter(endOfMonth), 'past': dayMoment.isBefore(startOfMonth), 'utcDateValue': dayMoment.valueOf() } week.dates.push(new DateObject(dateValue)) } result.weeks.push(week) } return result } function hourModelFactory (milliseconds) { var selectedDate = moment.utc(milliseconds).startOf('day') var previousViewDate = moment.utc(selectedDate).startOf('month') var hourFormat = 'YYYY-MM-DD H' var activeFormat = formatValue(ngModelController.$modelValue, hourFormat) var currentFormat = moment().format(hourFormat) var result = { 'previousView': 'day', 'currentView': 'hour', 'nextView': configuration.minView === 'hour' ? 'setTime' : 'minute', 'previousViewDate': new DateObject({ utcDateValue: previousViewDate.valueOf(), display: selectedDate.format('ll') }), 'leftDate': new DateObject({utcDateValue: moment.utc(selectedDate).subtract(1, 'days').valueOf()}), 'rightDate': new DateObject({utcDateValue: moment.utc(selectedDate).add(1, 'days').valueOf()}), 'dates': [] } for (var i = 0; i < 24; i += 1) { var hourMoment = moment.utc(selectedDate).add(i, 'hours') var dateValue = { 'active': hourMoment.format(hourFormat) === activeFormat, 'current': hourMoment.format(hourFormat) === currentFormat, 'display': hourMoment.format('LT'), 'utcDateValue': hourMoment.valueOf() } result.dates.push(new DateObject(dateValue)) } return result } function minuteModelFactory (milliseconds) { var selectedDate = moment.utc(milliseconds).startOf('hour') var previousViewDate = moment.utc(selectedDate).startOf('day') var minuteFormat = 'YYYY-MM-DD H:mm' var activeFormat = formatValue(ngModelController.$modelValue, minuteFormat) var currentFormat = moment().format(minuteFormat) var result = { 'previousView': 'hour', 'currentView': 'minute', 'nextView': 'setTime', 'previousViewDate': new DateObject({ utcDateValue: previousViewDate.valueOf(), display: selectedDate.format('lll') }), 'leftDate': new DateObject({utcDateValue: moment.utc(selectedDate).subtract(1, 'hours').valueOf()}), 'rightDate': new DateObject({utcDateValue: moment.utc(selectedDate).add(1, 'hours').valueOf()}), 'dates': [] } var limit = 60 / configuration.minuteStep for (var i = 0; i < limit; i += 1) { var hourMoment = moment.utc(selectedDate).add(i * configuration.minuteStep, 'minute') var dateValue = { 'active': hourMoment.format(minuteFormat) === activeFormat, 'current': hourMoment.format(minuteFormat) === currentFormat, 'display': hourMoment.format('LT'), 'utcDateValue': hourMoment.valueOf() } result.dates.push(new DateObject(dateValue)) } return result } function setTime (milliseconds) { var tempDate = new Date(milliseconds) var newDate = new Date(tempDate.getUTCFullYear(), tempDate.getUTCMonth(), tempDate.getUTCDate(), tempDate.getUTCHours(), tempDate.getUTCMinutes(), tempDate.getUTCSeconds(), tempDate.getUTCMilliseconds()) switch (configuration.modelType) { case 'Date': // No additional work needed break case 'moment': newDate = moment([tempDate.getUTCFullYear(), tempDate.getUTCMonth(), tempDate.getUTCDate(), tempDate.getUTCHours(), tempDate.getUTCMinutes(), tempDate.getUTCSeconds(), tempDate.getUTCMilliseconds()]) break case 'milliseconds': newDate = milliseconds break default: // It is assumed that the modelType is a formatting string. newDate = moment([tempDate.getUTCFullYear(), tempDate.getUTCMonth(), tempDate.getUTCDate(), tempDate.getUTCHours(), tempDate.getUTCMinutes(), tempDate.getUTCSeconds(), tempDate.getUTCMilliseconds()]).format(configuration.modelType) } var oldDate = ngModelController.$modelValue ngModelController.$setViewValue(newDate) if (configuration.dropdownSelector) { jQuery(configuration.dropdownSelector).dropdown('toggle') } $scope.onSetTime({newDate: newDate, oldDate: oldDate}) return viewToModelFactory[configuration.startView](milliseconds) } function $render () { $scope.changeView(configuration.startView, new DateObject({utcDateValue: getUTCTime(ngModelController.$viewValue)})) } function startOfDecade (milliseconds) { var startYear = (parseInt(moment.utc(milliseconds).year() / 10, 10) * 10) return moment.utc(milliseconds).year(startYear).startOf('year') } function formatValue (timeValue, formatString) { if (timeValue) { return getMoment(timeValue).format(formatString) } else { return '' } } /** * Converts a time value into a moment. * * This function is now necessary because moment logs a warning when parsing a string without a format. * @param modelValue * a time value in any of the supported formats (Date, moment, milliseconds, and string) * @returns {moment} * representing the specified time value. */ function getMoment (modelValue) { return moment(modelValue, angular.isString(modelValue) ? configuration.parseFormat : undefined) } /** * Converts a time value to UCT/GMT time. * @param modelValue * a time value in any of the supported formats (Date, moment, milliseconds, and string) * @returns {number} * number of milliseconds since 1/1/1970 */ function getUTCTime (modelValue) { var tempDate = new Date() if (modelValue) { var tempMoment = getMoment(modelValue) if (tempMoment.isValid()) { tempDate = tempMoment.toDate() } else { throw new Error('Invalid date: ' + modelValue) } } return tempDate.getTime() - (tempDate.getTimezoneOffset() * 60000) } function createConfiguration () { var directiveConfig = {} if ($attrs.datetimepickerConfig) { directiveConfig = $scope.$parent.$eval($attrs.datetimepickerConfig) } var configuration = angular.extend({}, defaultConfig, directiveConfig) configurationValidator.validate(configuration) return configuration } } function DateObject () { var tempDate = new Date(arguments[0].utcDateValue) var localOffset = tempDate.getTimezoneOffset() * 60000 this.utcDateValue = tempDate.getTime() this.selectable = true this.localDateValue = function localDateValue () { return this.utcDateValue + localOffset } var validProperties = ['active', 'current', 'display', 'future', 'past', 'selectable', 'utcDateValue'] var constructorObject = arguments[0] Object.keys(constructorObject).filter(function (key) { return validProperties.indexOf(key) >= 0 }).forEach(function (key) { this[key] = constructorObject[key] }, this) } return directiveDefinition } function DateTimePickerConfigProvider () { var defaultConfiguration = { configureOn: null, dropdownSelector: null, minuteStep: 5, minView: 'minute', modelType: 'Date', parseFormat: 'YYYY-MM-DDTHH:mm:ss.SSSZZ', renderOn: null, startView: 'day' } var defaultLocalization = { 'bg': {previous: 'предишна', next: 'следваща'}, 'ca': {previous: 'anterior', next: 'següent'}, 'da': {previous: 'forrige', next: 'næste'}, 'de': {previous: 'vorige', next: 'weiter'}, 'en-au': {previous: 'previous', next: 'next'}, 'en-gb': {previous: 'previous', next: 'next'}, 'en': {previous: 'previous', next: 'next'}, 'es-us': {previous: 'atrás', next: 'siguiente'}, 'es': {previous: 'atrás', next: 'siguiente'}, 'fi': {previous: 'edellinen', next: 'seuraava'}, 'fr': {previous: 'précédent', next: 'suivant'}, 'hu': {previous: 'előző', next: 'következő'}, 'it': {previous: 'precedente', next: 'successivo'}, 'ja': {previous: '前へ', next: '次へ'}, 'ml': {previous: 'മുൻപുള്ളത്', next: 'അടുത്തത്'}, 'nl': {previous: 'vorige', next: 'volgende'}, 'pl': {previous: 'poprzednia', next: 'następna'}, 'pt-br': {previous: 'anteriores', next: 'próximos'}, 'pt': {previous: 'anterior', next: 'próximo'}, 'ro': {previous: 'anterior', next: 'următor'}, 'ru': {previous: 'предыдущая', next: 'следующая'}, 'sk': {previous: 'predošlá', next: 'ďalšia'}, 'sv': {previous: 'föregående', next: 'nästa'}, 'tr': {previous: 'önceki', next: 'sonraki'}, 'uk': {previous: 'назад', next: 'далі'}, 'zh-cn': {previous: '上一页', next: '下一页'}, 'zh-tw': {previous: '上一頁', next: '下一頁'} } var screenReader = defaultLocalization[moment.locale().toLowerCase()] return angular.extend({}, defaultConfiguration, {screenReader: screenReader}) } DateTimePickerValidatorService.$inject = ['$log'] function DateTimePickerValidatorService ($log) { return { validate: validator } function validator (configuration) { var validOptions = [ 'configureOn', 'dropdownSelector', 'minuteStep', 'minView', 'modelType', 'parseFormat', 'renderOn', 'startView', 'screenReader' ] var invalidOptions = Object.keys(configuration).filter(function (key) { return (validOptions.indexOf(key) < 0) }) if (invalidOptions.length) { throw new Error('Invalid options: ' + invalidOptions.join(', ')) } // Order of the elements in the validViews array is significant. var validViews = ['minute', 'hour', 'day', 'month', 'year'] if (validViews.indexOf(configuration.startView) < 0) { throw new Error('invalid startView value: ' + configuration.startView) } if (validViews.indexOf(configuration.minView) < 0) { throw new Error('invalid minView value: ' + configuration.minView) } if (validViews.indexOf(configuration.minView) > validViews.indexOf(configuration.startView)) { throw new Error('startView must be greater than minView') } if (!angular.isNumber(configuration.minuteStep)) { throw new Error('minuteStep must be numeric') } if (configuration.minuteStep <= 0 || configuration.minuteStep >= 60) { throw new Error('minuteStep must be greater than zero and less than 60') } if (configuration.configureOn !== null && !angular.isString(configuration.configureOn)) { throw new Error('configureOn must be a string') } if (configuration.configureOn !== null && configuration.configureOn.length < 1) { throw new Error('configureOn must not be an empty string') } if (configuration.renderOn !== null && !angular.isString(configuration.renderOn)) { throw new Error('renderOn must be a string') } if (configuration.renderOn !== null && configuration.renderOn.length < 1) { throw new Error('renderOn must not be an empty string') } if (configuration.modelType !== null && !angular.isString(configuration.modelType)) { throw new Error('modelType must be a string') } if (configuration.modelType !== null && configuration.modelType.length < 1) { throw new Error('modelType must not be an empty string') } if (configuration.modelType !== 'Date' && configuration.modelType !== 'moment' && configuration.modelType !== 'milliseconds') { // modelType contains string format, overriding parseFormat with modelType configuration.parseFormat = configuration.modelType } if (configuration.dropdownSelector !== null && !angular.isString(configuration.dropdownSelector)) { throw new Error('dropdownSelector must be a string') } /* istanbul ignore next */ if (configuration.dropdownSelector !== null && ((typeof jQuery === 'undefined') || (typeof jQuery().dropdown !== 'function'))) { $log.error('Please DO NOT specify the dropdownSelector option unless you are using jQuery AND Bootstrap.js. ' + 'Please include jQuery AND Bootstrap.js, or write code to close the dropdown in the on-set-time callback. \n\n' + 'The dropdownSelector configuration option is being removed because it will not function properly.') delete configuration.dropdownSelector } } } })); // eslint-disable-line semi //todo datetimepicker.templates.js 在异步加载首次运行有问题,只能将其直接合并。。。 'use strict' angular.module('ui.bootstrap.datetimepicker').run(['$templateCache', function ($templateCache) { $templateCache.put('templates/datetimepicker.html', '
\n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n
{{ screenReader.previous }}\n {{ data.previousViewDate.display }}{{ screenReader.next }}\n
{{ day }}
\n {{ dateObject.display }}
{{ dateObject.display }}
\n
\n') }])