diff --git a/app/assets/javascripts/bootstrap-datetimepicker.js b/app/assets/javascripts/bootstrap-datetimepicker.js index 8838cbcd2..f66d69c13 100755 --- a/app/assets/javascripts/bootstrap-datetimepicker.js +++ b/app/assets/javascripts/bootstrap-datetimepicker.js @@ -1,2636 +1,1967 @@ -/*! version : 4.17.47 - ========================================================= - bootstrap-datetimejs - https://github.com/Eonasdan/bootstrap-datetimepicker - Copyright (c) 2015 Jonathan Peterson - ========================================================= - */ -/* - The MIT License (MIT) - - Copyright (c) 2015 Jonathan Peterson - - Permission is hereby granted, free of charge, to any person obtaining a copy - of this software and associated documentation files (the "Software"), to deal - in the Software without restriction, including without limitation the rights - to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - copies of the Software, and to permit persons to whom the Software is - furnished to do so, subject to the following conditions: - - The above copyright notice and this permission notice shall be included in - all copies or substantial portions of the Software. - - THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - THE SOFTWARE. - */ -/*global define:false */ -/*global exports:false */ -/*global require:false */ -/*global jQuery:false */ -/*global moment:false */ -(function (factory) { - 'use strict'; - if (typeof define === 'function' && define.amd) { - // AMD is used - Register as an anonymous module. - define(['jquery', 'moment'], factory); - } else if (typeof exports === 'object') { - module.exports = factory(require('jquery'), require('moment')); - } else { - // Neither AMD nor CommonJS used. Use global variables. - if (typeof jQuery === 'undefined') { - throw 'bootstrap-datetimepicker requires jQuery to be loaded first'; +/* ========================================================= + * bootstrap-datetimepicker.js + * ========================================================= + * Copyright 2012 Stefan Petre + * + * Improvements by Andrew Rowls + * Improvements by Sébastien Malot + * Improvements by Yun Lai + * Improvements by Kenneth Henderick + * Improvements by CuGBabyBeaR + * Improvements by Christian Vaas + * + * Project URL : http://www.malot.fr/bootstrap-datetimepicker + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ========================================================= */ + +(function(factory){ + if (typeof define === 'function' && define.amd) + define(['jquery'], factory); + else if (typeof exports === 'object') + factory(require('jquery')); + else + factory(jQuery); + +}(function($, undefined){ + + // Add ECMA262-5 Array methods if not supported natively (IE8) + if (!('indexOf' in Array.prototype)) { + Array.prototype.indexOf = function (find, i) { + if (i === undefined) i = 0; + if (i < 0) i += this.length; + if (i < 0) i = 0; + for (var n = this.length; i < n; i++) { + if (i in this && this[i] === find) { + return i; } - if (typeof moment === 'undefined') { - throw 'bootstrap-datetimepicker requires Moment.js to be loaded first'; + } + return -1; + } + } + + // Add timezone abbreviation support for ie6+, Chrome, Firefox + function timeZoneAbbreviation() { + var abbreviation, date, formattedStr, i, len, matchedStrings, ref, str; + date = (new Date()).toString(); + formattedStr = ((ref = date.split('(')[1]) != null ? ref.slice(0, -1) : 0) || date.split(' '); + if (formattedStr instanceof Array) { + matchedStrings = []; + for (var i = 0, len = formattedStr.length; i < len; i++) { + str = formattedStr[i]; + if ((abbreviation = (ref = str.match(/\b[A-Z]+\b/)) !== null) ? ref[0] : 0) { + matchedStrings.push(abbreviation); } - factory(jQuery, moment); + } + formattedStr = matchedStrings.pop(); } -}(function ($, moment) { - 'use strict'; - if (!moment) { - throw new Error('bootstrap-datetimepicker requires Moment.js to be loaded first'); + return formattedStr; + } + + function UTCDate() { + return new Date(Date.UTC.apply(Date, arguments)); + } + + // Picker object + var Datetimepicker = function (element, options) { + var that = this; + + this.element = $(element); + + // add container for single page application + // when page switch the datetimepicker div will be removed also. + this.container = options.container || 'body'; + + this.language = options.language || this.element.data('date-language') || 'en'; + this.language = this.language in dates ? this.language : this.language.split('-')[0]; // fr-CA fallback to fr + this.language = this.language in dates ? this.language : 'en'; + this.isRTL = dates[this.language].rtl || false; + this.formatType = options.formatType || this.element.data('format-type') || 'standard'; + this.format = DPGlobal.parseFormat(options.format || this.element.data('date-format') || dates[this.language].format || DPGlobal.getDefaultFormat(this.formatType, 'input'), this.formatType); + this.isInline = false; + this.isVisible = false; + this.isInput = this.element.is('input'); + this.fontAwesome = options.fontAwesome || this.element.data('font-awesome') || false; + + this.bootcssVer = options.bootcssVer || (this.isInput ? (this.element.is('.form-control') ? 3 : 2) : ( this.bootcssVer = this.element.is('.input-group') ? 3 : 2 )); + + this.component = this.element.is('.date') ? ( this.bootcssVer === 3 ? this.element.find('.input-group-addon .glyphicon-th, .input-group-addon .glyphicon-time, .input-group-addon .glyphicon-remove, .input-group-addon .glyphicon-calendar, .input-group-addon .fa-calendar, .input-group-addon .fa-clock-o').parent() : this.element.find('.add-on .icon-th, .add-on .icon-time, .add-on .icon-calendar, .add-on .fa-calendar, .add-on .fa-clock-o').parent()) : false; + this.componentReset = this.element.is('.date') ? ( this.bootcssVer === 3 ? this.element.find('.input-group-addon .glyphicon-remove, .input-group-addon .fa-times').parent():this.element.find('.add-on .icon-remove, .add-on .fa-times').parent()) : false; + this.hasInput = this.component && this.element.find('input').length; + if (this.component && this.component.length === 0) { + this.component = false; } + this.linkField = options.linkField || this.element.data('link-field') || false; + this.linkFormat = DPGlobal.parseFormat(options.linkFormat || this.element.data('link-format') || DPGlobal.getDefaultFormat(this.formatType, 'link'), this.formatType); + this.minuteStep = options.minuteStep || this.element.data('minute-step') || 5; + this.pickerPosition = options.pickerPosition || this.element.data('picker-position') || 'bottom-right'; + this.showMeridian = options.showMeridian || this.element.data('show-meridian') || false; + this.initialDate = options.initialDate || new Date(); + this.zIndex = options.zIndex || this.element.data('z-index') || undefined; + this.title = typeof options.title === 'undefined' ? false : options.title; + this.timezone = options.timezone || timeZoneAbbreviation(); + + this.icons = { + leftArrow: this.fontAwesome ? 'fa-arrow-left' : (this.bootcssVer === 3 ? 'glyphicon-arrow-left' : 'icon-arrow-left'), + rightArrow: this.fontAwesome ? 'fa-arrow-right' : (this.bootcssVer === 3 ? 'glyphicon-arrow-right' : 'icon-arrow-right') + } + this.icontype = this.fontAwesome ? 'fa' : 'glyphicon'; - var dateTimePicker = function (element, options) { - var picker = {}, - date, - viewDate, - unset = true, - input, - component = false, - widget = false, - use24Hours, - minViewModeNumber = 0, - actualFormat, - parseFormats, - currentViewMode, - datePickerModes = [ - { - clsName: 'days', - navFnc: 'M', - navStep: 1 - }, - { - clsName: 'months', - navFnc: 'y', - navStep: 1 - }, - { - clsName: 'years', - navFnc: 'y', - navStep: 10 - }, - { - clsName: 'decades', - navFnc: 'y', - navStep: 100 - } - ], - viewModes = ['days', 'months', 'years', 'decades'], - verticalModes = ['top', 'bottom', 'auto'], - horizontalModes = ['left', 'right', 'auto'], - toolbarPlacements = ['default', 'top', 'bottom'], - keyMap = { - 'up': 38, - 38: 'up', - 'down': 40, - 40: 'down', - 'left': 37, - 37: 'left', - 'right': 39, - 39: 'right', - 'tab': 9, - 9: 'tab', - 'escape': 27, - 27: 'escape', - 'enter': 13, - 13: 'enter', - 'pageUp': 33, - 33: 'pageUp', - 'pageDown': 34, - 34: 'pageDown', - 'shift': 16, - 16: 'shift', - 'control': 17, - 17: 'control', - 'space': 32, - 32: 'space', - 't': 84, - 84: 't', - 'delete': 46, - 46: 'delete' - }, - keyState = {}, - - /******************************************************************************** - * - * Private functions - * - ********************************************************************************/ - - hasTimeZone = function () { - return moment.tz !== undefined && options.timeZone !== undefined && options.timeZone !== null && options.timeZone !== ''; - }, - - getMoment = function (d) { - var returnMoment; - - if (d === undefined || d === null) { - returnMoment = moment(); //TODO should this use format? and locale? - } else if (moment.isDate(d) || moment.isMoment(d)) { - // If the date that is passed in is already a Date() or moment() object, - // pass it directly to moment. - returnMoment = moment(d); - } else if (hasTimeZone()) { // There is a string to parse and a default time zone - // parse with the tz function which takes a default time zone if it is not in the format string - returnMoment = moment.tz(d, parseFormats, options.useStrict, options.timeZone); - } else { - returnMoment = moment(d, parseFormats, options.useStrict); - } - - if (hasTimeZone()) { - returnMoment.tz(options.timeZone); - } - - return returnMoment; - }, - - isEnabled = function (granularity) { - if (typeof granularity !== 'string' || granularity.length > 1) { - throw new TypeError('isEnabled expects a single character string parameter'); - } - switch (granularity) { - case 'y': - return actualFormat.indexOf('Y') !== -1; - case 'M': - return actualFormat.indexOf('M') !== -1; - case 'd': - return actualFormat.toLowerCase().indexOf('d') !== -1; - case 'h': - case 'H': - return actualFormat.toLowerCase().indexOf('h') !== -1; - case 'm': - return actualFormat.indexOf('m') !== -1; - case 's': - return actualFormat.indexOf('s') !== -1; - default: - return false; - } - }, - - hasTime = function () { - return (isEnabled('h') || isEnabled('m') || isEnabled('s')); - }, - - hasDate = function () { - return (isEnabled('y') || isEnabled('M') || isEnabled('d')); - }, - - getDatePickerTemplate = function () { - var headTemplate = $('') - .append($('') - .append($('').addClass('prev').attr('data-action', 'previous') - .append($('').addClass(options.icons.previous)) - ) - .append($('').addClass('picker-switch').attr('data-action', 'pickerSwitch').attr('colspan', (options.calendarWeeks ? '6' : '5'))) - .append($('').addClass('next').attr('data-action', 'next') - .append($('').addClass(options.icons.next)) - ) - ), - contTemplate = $('') - .append($('') - .append($('').attr('colspan', (options.calendarWeeks ? '8' : '7'))) - ); - - return [ - $('
').addClass('datepicker-days') - .append($('').addClass('table-condensed') - .append(headTemplate) - .append($('')) - ), - $('
').addClass('datepicker-months') - .append($('
').addClass('table-condensed') - .append(headTemplate.clone()) - .append(contTemplate.clone()) - ), - $('
').addClass('datepicker-years') - .append($('
').addClass('table-condensed') - .append(headTemplate.clone()) - .append(contTemplate.clone()) - ), - $('
').addClass('datepicker-decades') - .append($('
').addClass('table-condensed') - .append(headTemplate.clone()) - .append(contTemplate.clone()) - ) - ]; - }, - - getTimePickerMainTemplate = function () { - var topRow = $(''), - middleRow = $(''), - bottomRow = $(''); - - if (isEnabled('h')) { - topRow.append($('
') - .append($('').attr({ href: '#', tabindex: '-1', 'title': options.tooltips.incrementHour }).addClass('btn').attr('data-action', 'incrementHours').append($('').addClass(options.icons.up)))); - middleRow.append($('') - .append($('').addClass('timepicker-hour').attr({ 'data-time-component': 'hours', 'title': options.tooltips.pickHour }).attr('data-action', 'showHours'))); - bottomRow.append($('') - .append($('').attr({ href: '#', tabindex: '-1', 'title': options.tooltips.decrementHour }).addClass('btn').attr('data-action', 'decrementHours').append($('').addClass(options.icons.down)))); - } - if (isEnabled('m')) { - if (isEnabled('h')) { - topRow.append($('').addClass('separator')); - middleRow.append($('').addClass('separator').html(':')); - bottomRow.append($('').addClass('separator')); - } - topRow.append($('') - .append($('').attr({ href: '#', tabindex: '-1', 'title': options.tooltips.incrementMinute }).addClass('btn').attr('data-action', 'incrementMinutes') - .append($('').addClass(options.icons.up)))); - middleRow.append($('') - .append($('').addClass('timepicker-minute').attr({ 'data-time-component': 'minutes', 'title': options.tooltips.pickMinute }).attr('data-action', 'showMinutes'))); - bottomRow.append($('') - .append($('').attr({ href: '#', tabindex: '-1', 'title': options.tooltips.decrementMinute }).addClass('btn').attr('data-action', 'decrementMinutes') - .append($('').addClass(options.icons.down)))); - } - if (isEnabled('s')) { - if (isEnabled('m')) { - topRow.append($('').addClass('separator')); - middleRow.append($('').addClass('separator').html(':')); - bottomRow.append($('').addClass('separator')); - } - topRow.append($('') - .append($('').attr({ href: '#', tabindex: '-1', 'title': options.tooltips.incrementSecond }).addClass('btn').attr('data-action', 'incrementSeconds') - .append($('').addClass(options.icons.up)))); - middleRow.append($('') - .append($('').addClass('timepicker-second').attr({ 'data-time-component': 'seconds', 'title': options.tooltips.pickSecond }).attr('data-action', 'showSeconds'))); - bottomRow.append($('') - .append($('').attr({ href: '#', tabindex: '-1', 'title': options.tooltips.decrementSecond }).addClass('btn').attr('data-action', 'decrementSeconds') - .append($('').addClass(options.icons.down)))); - } - - if (!use24Hours) { - topRow.append($('').addClass('separator')); - middleRow.append($('') - .append($('').addClass('separator')); - } - - return $('
').addClass('timepicker-picker') - .append($('').addClass('table-condensed') - .append([topRow, middleRow, bottomRow])); - }, - - getTimePickerTemplate = function () { - var hoursView = $('
').addClass('timepicker-hours') - .append($('
').addClass('table-condensed')), - minutesView = $('
').addClass('timepicker-minutes') - .append($('
').addClass('table-condensed')), - secondsView = $('
').addClass('timepicker-seconds') - .append($('
').addClass('table-condensed')), - ret = [getTimePickerMainTemplate()]; - - if (isEnabled('h')) { - ret.push(hoursView); - } - if (isEnabled('m')) { - ret.push(minutesView); - } - if (isEnabled('s')) { - ret.push(secondsView); - } + this._attachEvents(); - return ret; - }, + this.clickedOutside = function (e) { + // Clicked outside the datetimepicker, hide it + if ($(e.target).closest('.datetimepicker').length === 0) { + that.hide(); + } + } - getToolbar = function () { - var row = []; - if (options.showTodayButton) { - row.push($('
').append($('').attr({ 'data-action': 'today', 'title': options.tooltips.today }).append($('').addClass(options.icons.today)))); - } - if (!options.sideBySide && hasDate() && hasTime()) { - row.push($('').append($('').attr({ 'data-action': 'togglePicker', 'title': options.tooltips.selectTime }).append($('').addClass(options.icons.time)))); - } - if (options.showClear) { - row.push($('').append($('').attr({ 'data-action': 'clear', 'title': options.tooltips.clear }).append($('').addClass(options.icons.clear)))); - } - if (options.showClose) { - row.push($('').append($('').attr({ 'data-action': 'close', 'title': options.tooltips.close }).append($('').addClass(options.icons.close)))); - } - return $('').addClass('table-condensed').append($('').append($('').append(row))); - }, - - getTemplate = function () { - var template = $('
').addClass('bootstrap-datetimepicker-widget dropdown-menu'), - dateView = $('
').addClass('datepicker').append(getDatePickerTemplate()), - timeView = $('
').addClass('timepicker').append(getTimePickerTemplate()), - content = $('
    ').addClass('list-unstyled'), - toolbar = $('
  • ').addClass('picker-switch' + (options.collapse ? ' accordion-toggle' : '')).append(getToolbar()); - - if (options.inline) { - template.removeClass('dropdown-menu'); - } + this.formatViewType = 'datetime'; + if ('formatViewType' in options) { + this.formatViewType = options.formatViewType; + } else if ('formatViewType' in this.element.data()) { + this.formatViewType = this.element.data('formatViewType'); + } - if (use24Hours) { - template.addClass('usetwentyfour'); - } + this.minView = 0; + if ('minView' in options) { + this.minView = options.minView; + } else if ('minView' in this.element.data()) { + this.minView = this.element.data('min-view'); + } + this.minView = DPGlobal.convertViewMode(this.minView); - if (isEnabled('s') && !use24Hours) { - template.addClass('wider'); - } + this.maxView = DPGlobal.modes.length - 1; + if ('maxView' in options) { + this.maxView = options.maxView; + } else if ('maxView' in this.element.data()) { + this.maxView = this.element.data('max-view'); + } + this.maxView = DPGlobal.convertViewMode(this.maxView); - if (options.sideBySide && hasDate() && hasTime()) { - template.addClass('timepicker-sbs'); - if (options.toolbarPlacement === 'top') { - template.append(toolbar); - } - template.append( - $('
    ').addClass('row') - .append(dateView.addClass('col-md-6')) - .append(timeView.addClass('col-md-6')) - ); - if (options.toolbarPlacement === 'bottom') { - template.append(toolbar); - } - return template; - } + this.wheelViewModeNavigation = false; + if ('wheelViewModeNavigation' in options) { + this.wheelViewModeNavigation = options.wheelViewModeNavigation; + } else if ('wheelViewModeNavigation' in this.element.data()) { + this.wheelViewModeNavigation = this.element.data('view-mode-wheel-navigation'); + } - if (options.toolbarPlacement === 'top') { - content.append(toolbar); - } - if (hasDate()) { - content.append($('
  • ').addClass((options.collapse && hasTime() ? 'collapse in' : '')).append(dateView)); - } - if (options.toolbarPlacement === 'default') { - content.append(toolbar); - } - if (hasTime()) { - content.append($('
  • ').addClass((options.collapse && hasDate() ? 'collapse' : '')).append(timeView)); - } - if (options.toolbarPlacement === 'bottom') { - content.append(toolbar); - } - return template.append(content); - }, + this.wheelViewModeNavigationInverseDirection = false; - dataToOptions = function () { - var eData, - dataOptions = {}; + if ('wheelViewModeNavigationInverseDirection' in options) { + this.wheelViewModeNavigationInverseDirection = options.wheelViewModeNavigationInverseDirection; + } else if ('wheelViewModeNavigationInverseDirection' in this.element.data()) { + this.wheelViewModeNavigationInverseDirection = this.element.data('view-mode-wheel-navigation-inverse-dir'); + } - if (element.is('input') || options.inline) { - eData = element.data(); - } else { - eData = element.find('input').data(); - } + this.wheelViewModeNavigationDelay = 100; + if ('wheelViewModeNavigationDelay' in options) { + this.wheelViewModeNavigationDelay = options.wheelViewModeNavigationDelay; + } else if ('wheelViewModeNavigationDelay' in this.element.data()) { + this.wheelViewModeNavigationDelay = this.element.data('view-mode-wheel-navigation-delay'); + } - if (eData.dateOptions && eData.dateOptions instanceof Object) { - dataOptions = $.extend(true, dataOptions, eData.dateOptions); - } + this.startViewMode = 2; + if ('startView' in options) { + this.startViewMode = options.startView; + } else if ('startView' in this.element.data()) { + this.startViewMode = this.element.data('start-view'); + } + this.startViewMode = DPGlobal.convertViewMode(this.startViewMode); + this.viewMode = this.startViewMode; + + this.viewSelect = this.minView; + if ('viewSelect' in options) { + this.viewSelect = options.viewSelect; + } else if ('viewSelect' in this.element.data()) { + this.viewSelect = this.element.data('view-select'); + } + this.viewSelect = DPGlobal.convertViewMode(this.viewSelect); - $.each(options, function (key) { - var attributeName = 'date' + key.charAt(0).toUpperCase() + key.slice(1); - if (eData[attributeName] !== undefined) { - dataOptions[key] = eData[attributeName]; - } - }); - return dataOptions; - }, - - place = function () { - var position = (component || element).position(), - offset = (component || element).offset(), - vertical = options.widgetPositioning.vertical, - horizontal = options.widgetPositioning.horizontal, - parent; - - if (options.widgetParent) { - parent = options.widgetParent.append(widget); - } else if (element.is('input')) { - parent = element.after(widget).parent(); - } else if (options.inline) { - parent = element.append(widget); - return; - } else { - parent = element; - element.children().first().after(widget); - } + this.forceParse = true; + if ('forceParse' in options) { + this.forceParse = options.forceParse; + } else if ('dateForceParse' in this.element.data()) { + this.forceParse = this.element.data('date-force-parse'); + } + var template = this.bootcssVer === 3 ? DPGlobal.templateV3 : DPGlobal.template; + while (template.indexOf('{iconType}') !== -1) { + template = template.replace('{iconType}', this.icontype); + } + while (template.indexOf('{leftArrow}') !== -1) { + template = template.replace('{leftArrow}', this.icons.leftArrow); + } + while (template.indexOf('{rightArrow}') !== -1) { + template = template.replace('{rightArrow}', this.icons.rightArrow); + } + this.picker = $(template) + .appendTo(this.isInline ? this.element : this.container) // 'body') + .on({ + click: $.proxy(this.click, this), + mousedown: $.proxy(this.mousedown, this) + }); + + if (this.wheelViewModeNavigation) { + if ($.fn.mousewheel) { + this.picker.on({mousewheel: $.proxy(this.mousewheel, this)}); + } else { + console.log('Mouse Wheel event is not supported. Please include the jQuery Mouse Wheel plugin before enabling this option'); + } + } - // Top and bottom logic - if (vertical === 'auto') { - if (offset.top + widget.height() * 1.5 >= $(window).height() + $(window).scrollTop() && - widget.height() + element.outerHeight() < offset.top) { - vertical = 'top'; - } else { - vertical = 'bottom'; - } - } + if (this.isInline) { + this.picker.addClass('datetimepicker-inline'); + } else { + this.picker.addClass('datetimepicker-dropdown-' + this.pickerPosition + ' dropdown-menu'); + } + if (this.isRTL) { + this.picker.addClass('datetimepicker-rtl'); + var selector = this.bootcssVer === 3 ? '.prev span, .next span' : '.prev i, .next i'; + this.picker.find(selector).toggleClass(this.icons.leftArrow + ' ' + this.icons.rightArrow); + } - // Left and right logic - if (horizontal === 'auto') { - if (parent.width() < offset.left + widget.outerWidth() / 2 && - offset.left + widget.outerWidth() > $(window).width()) { - horizontal = 'right'; - } else { - horizontal = 'left'; - } - } + $(document).on('mousedown touchend', this.clickedOutside); - if (vertical === 'top') { - widget.addClass('top').removeClass('bottom'); - } else { - widget.addClass('bottom').removeClass('top'); - } + this.autoclose = false; + if ('autoclose' in options) { + this.autoclose = options.autoclose; + } else if ('dateAutoclose' in this.element.data()) { + this.autoclose = this.element.data('date-autoclose'); + } - if (horizontal === 'right') { - widget.addClass('pull-right'); - } else { - widget.removeClass('pull-right'); - } + this.keyboardNavigation = true; + if ('keyboardNavigation' in options) { + this.keyboardNavigation = options.keyboardNavigation; + } else if ('dateKeyboardNavigation' in this.element.data()) { + this.keyboardNavigation = this.element.data('date-keyboard-navigation'); + } - // find the first parent element that has a non-static css positioning - if (parent.css('position') === 'static') { - parent = parent.parents().filter(function () { - return $(this).css('position') !== 'static'; - }).first(); - } + this.todayBtn = (options.todayBtn || this.element.data('date-today-btn') || false); + this.clearBtn = (options.clearBtn || this.element.data('date-clear-btn') || false); + this.todayHighlight = (options.todayHighlight || this.element.data('date-today-highlight') || false); + + this.weekStart = 0; + if (typeof options.weekStart !== 'undefined') { + this.weekStart = options.weekStart; + } else if (typeof this.element.data('date-weekstart') !== 'undefined') { + this.weekStart = this.element.data('date-weekstart'); + } else if (typeof dates[this.language].weekStart !== 'undefined') { + this.weekStart = dates[this.language].weekStart; + } + this.weekStart = this.weekStart % 7; + this.weekEnd = ((this.weekStart + 6) % 7); + this.onRenderDay = function (date) { + var render = (options.onRenderDay || function () { return []; })(date); + if (typeof render === 'string') { + render = [render]; + } + var res = ['day']; + return res.concat((render ? render : [])); + }; + this.onRenderHour = function (date) { + var render = (options.onRenderHour || function () { return []; })(date); + var res = ['hour']; + if (typeof render === 'string') { + render = [render]; + } + return res.concat((render ? render : [])); + }; + this.onRenderMinute = function (date) { + var render = (options.onRenderMinute || function () { return []; })(date); + var res = ['minute']; + if (typeof render === 'string') { + render = [render]; + } + if (date < this.startDate || date > this.endDate) { + res.push('disabled'); + } else if (Math.floor(this.date.getUTCMinutes() / this.minuteStep) === Math.floor(date.getUTCMinutes() / this.minuteStep)) { + res.push('active'); + } + return res.concat((render ? render : [])); + }; + this.onRenderYear = function (date) { + var render = (options.onRenderYear || function () { return []; })(date); + var res = ['year']; + if (typeof render === 'string') { + render = [render]; + } + if (this.date.getUTCFullYear() === date.getUTCFullYear()) { + res.push('active'); + } + var currentYear = date.getUTCFullYear(); + var endYear = this.endDate.getUTCFullYear(); + if (date < this.startDate || currentYear > endYear) { + res.push('disabled'); + } + return res.concat((render ? render : [])); + } + this.onRenderMonth = function (date) { + var render = (options.onRenderMonth || function () { return []; })(date); + var res = ['month']; + if (typeof render === 'string') { + render = [render]; + } + return res.concat((render ? render : [])); + } + this.startDate = new Date(-8639968443048000); + this.endDate = new Date(8639968443048000); + this.datesDisabled = []; + this.daysOfWeekDisabled = []; + this.setStartDate(options.startDate || this.element.data('date-startdate')); + this.setEndDate(options.endDate || this.element.data('date-enddate')); + this.setDatesDisabled(options.datesDisabled || this.element.data('date-dates-disabled')); + this.setDaysOfWeekDisabled(options.daysOfWeekDisabled || this.element.data('date-days-of-week-disabled')); + this.setMinutesDisabled(options.minutesDisabled || this.element.data('date-minute-disabled')); + this.setHoursDisabled(options.hoursDisabled || this.element.data('date-hour-disabled')); + this.fillDow(); + this.fillMonths(); + this.update(); + this.showMode(); + + if (this.isInline) { + this.show(); + } + }; + + Datetimepicker.prototype = { + constructor: Datetimepicker, + + _events: [], + _attachEvents: function () { + this._detachEvents(); + if (this.isInput) { // single input + this._events = [ + [this.element, { + focus: $.proxy(this.show, this), + keyup: $.proxy(this.update, this), + keydown: $.proxy(this.keydown, this) + }] + ]; + } + else if (this.component && this.hasInput) { // component: input + button + this._events = [ + // For components that are not readonly, allow keyboard nav + [this.element.find('input'), { + focus: $.proxy(this.show, this), + keyup: $.proxy(this.update, this), + keydown: $.proxy(this.keydown, this) + }], + [this.component, { + click: $.proxy(this.show, this) + }] + ]; + if (this.componentReset) { + this._events.push([ + this.componentReset, + {click: $.proxy(this.reset, this)} + ]); + } + } + else if (this.element.is('div')) { // inline datetimepicker + this.isInline = true; + } + else { + this._events = [ + [this.element, { + click: $.proxy(this.show, this) + }] + ]; + } + for (var i = 0, el, ev; i < this._events.length; i++) { + el = this._events[i][0]; + ev = this._events[i][1]; + el.on(ev); + } + }, + + _detachEvents: function () { + for (var i = 0, el, ev; i < this._events.length; i++) { + el = this._events[i][0]; + ev = this._events[i][1]; + el.off(ev); + } + this._events = []; + }, + + show: function (e) { + this.picker.show(); + this.height = this.component ? this.component.outerHeight() : this.element.outerHeight(); + if (this.forceParse) { + this.update(); + } + this.place(); + $(window).on('resize', $.proxy(this.place, this)); + if (e) { + e.stopPropagation(); + e.preventDefault(); + } + this.isVisible = true; + this.element.trigger({ + type: 'show', + date: this.date + }); + }, + + hide: function () { + if (!this.isVisible) return; + if (this.isInline) return; + this.picker.hide(); + $(window).off('resize', this.place); + this.viewMode = this.startViewMode; + this.showMode(); + if (!this.isInput) { + $(document).off('mousedown', this.hide); + } + + if ( + this.forceParse && + ( + this.isInput && this.element.val() || + this.hasInput && this.element.find('input').val() + ) + ) + this.setValue(); + this.isVisible = false; + this.element.trigger({ + type: 'hide', + date: this.date + }); + }, + + remove: function () { + this._detachEvents(); + $(document).off('mousedown', this.clickedOutside); + this.picker.remove(); + delete this.picker; + delete this.element.data().datetimepicker; + }, + + getDate: function () { + var d = this.getUTCDate(); + if (d === null) { + return null; + } + return new Date(d.getTime() + (d.getTimezoneOffset() * 60000)); + }, + + getUTCDate: function () { + return this.date; + }, + + getInitialDate: function () { + return this.initialDate + }, + + setInitialDate: function (initialDate) { + this.initialDate = initialDate; + }, + + setDate: function (d) { + this.setUTCDate(new Date(d.getTime() - (d.getTimezoneOffset() * 60000))); + }, + + setUTCDate: function (d) { + if (d >= this.startDate && d <= this.endDate) { + this.date = d; + this.setValue(); + this.viewDate = this.date; + this.fill(); + } else { + this.element.trigger({ + type: 'outOfRange', + date: d, + startDate: this.startDate, + endDate: this.endDate + }); + } + }, + + setFormat: function (format) { + this.format = DPGlobal.parseFormat(format, this.formatType); + var element; + if (this.isInput) { + element = this.element; + } else if (this.component) { + element = this.element.find('input'); + } + if (element && element.val()) { + this.setValue(); + } + }, + + setValue: function () { + var formatted = this.getFormattedDate(); + if (!this.isInput) { + if (this.component) { + this.element.find('input').val(formatted); + } + this.element.data('date', formatted); + } else { + this.element.val(formatted); + } + if (this.linkField) { + $('#' + this.linkField).val(this.getFormattedDate(this.linkFormat)); + } + }, + + getFormattedDate: function (format) { + format = format || this.format; + return DPGlobal.formatDate(this.date, format, this.language, this.formatType, this.timezone); + }, + + setStartDate: function (startDate) { + this.startDate = startDate || this.startDate; + if (this.startDate.valueOf() !== 8639968443048000) { + this.startDate = DPGlobal.parseDate(this.startDate, this.format, this.language, this.formatType, this.timezone); + } + this.update(); + this.updateNavArrows(); + }, + + setEndDate: function (endDate) { + this.endDate = endDate || this.endDate; + if (this.endDate.valueOf() !== 8639968443048000) { + this.endDate = DPGlobal.parseDate(this.endDate, this.format, this.language, this.formatType, this.timezone); + } + this.update(); + this.updateNavArrows(); + }, + + setDatesDisabled: function (datesDisabled) { + this.datesDisabled = datesDisabled || []; + if (!$.isArray(this.datesDisabled)) { + this.datesDisabled = this.datesDisabled.split(/,\s*/); + } + var mThis = this; + this.datesDisabled = $.map(this.datesDisabled, function (d) { + return DPGlobal.parseDate(d, mThis.format, mThis.language, mThis.formatType, mThis.timezone).toDateString(); + }); + this.update(); + this.updateNavArrows(); + }, + + setTitle: function (selector, value) { + return this.picker.find(selector) + .find('th:eq(1)') + .text(this.title === false ? value : this.title); + }, + + setDaysOfWeekDisabled: function (daysOfWeekDisabled) { + this.daysOfWeekDisabled = daysOfWeekDisabled || []; + if (!$.isArray(this.daysOfWeekDisabled)) { + this.daysOfWeekDisabled = this.daysOfWeekDisabled.split(/,\s*/); + } + this.daysOfWeekDisabled = $.map(this.daysOfWeekDisabled, function (d) { + return parseInt(d, 10); + }); + this.update(); + this.updateNavArrows(); + }, + + setMinutesDisabled: function (minutesDisabled) { + this.minutesDisabled = minutesDisabled || []; + if (!$.isArray(this.minutesDisabled)) { + this.minutesDisabled = this.minutesDisabled.split(/,\s*/); + } + this.minutesDisabled = $.map(this.minutesDisabled, function (d) { + return parseInt(d, 10); + }); + this.update(); + this.updateNavArrows(); + }, + + setHoursDisabled: function (hoursDisabled) { + this.hoursDisabled = hoursDisabled || []; + if (!$.isArray(this.hoursDisabled)) { + this.hoursDisabled = this.hoursDisabled.split(/,\s*/); + } + this.hoursDisabled = $.map(this.hoursDisabled, function (d) { + return parseInt(d, 10); + }); + this.update(); + this.updateNavArrows(); + }, + + place: function () { + if (this.isInline) return; + + if (!this.zIndex) { + var index_highest = 0; + $('div').each(function () { + var index_current = parseInt($(this).css('zIndex'), 10); + if (index_current > index_highest) { + index_highest = index_current; + } + }); + this.zIndex = index_highest + 10; + } + + var offset, top, left, containerOffset; + if (this.container instanceof $) { + containerOffset = this.container.offset(); + } else { + containerOffset = $(this.container).offset(); + } + + if (this.component) { + offset = this.component.offset(); + left = offset.left; + if (this.pickerPosition === 'bottom-left' || this.pickerPosition === 'top-left') { + left += this.component.outerWidth() - this.picker.outerWidth(); + } + } else { + offset = this.element.offset(); + left = offset.left; + if (this.pickerPosition === 'bottom-left' || this.pickerPosition === 'top-left') { + left += this.element.outerWidth() - this.picker.outerWidth(); + } + } + + var bodyWidth = document.body.clientWidth || window.innerWidth; + if (left + 220 > bodyWidth) { + left = bodyWidth - 220; + } + + if (this.pickerPosition === 'top-left' || this.pickerPosition === 'top-right') { + top = offset.top - this.picker.outerHeight(); + } else { + top = offset.top + this.height; + } + + top = top - containerOffset.top; + left = left - containerOffset.left; + + this.picker.css({ + top: top, + left: left, + zIndex: this.zIndex + }); + }, + + hour_minute: "^([0-9]|0[0-9]|1[0-9]|2[0-3]):[0-5][0-9]", + + update: function () { + var date, fromArgs = false; + if (arguments && arguments.length && (typeof arguments[0] === 'string' || arguments[0] instanceof Date)) { + date = arguments[0]; + fromArgs = true; + } else { + date = (this.isInput ? this.element.val() : this.element.find('input').val()) || this.element.data('date') || this.initialDate; + if (typeof date === 'string') { + date = date.replace(/^\s+|\s+$/g,''); + } + } - if (parent.length === 0) { - throw new Error('datetimepicker component should be placed within a non-static positioned container'); - } + if (!date) { + date = new Date(); + fromArgs = false; + } - widget.css({ - top: vertical === 'top' ? 'auto' : position.top + element.outerHeight(), - bottom: vertical === 'top' ? parent.outerHeight() - (parent === element ? 0 : position.top) : 'auto', - left: horizontal === 'left' ? (parent === element ? 0 : position.left) : 'auto', - right: horizontal === 'left' ? 'auto' : parent.outerWidth() - element.outerWidth() - (parent === element ? 0 : position.left) + if (typeof date === "string") { + if (new RegExp(this.hour_minute).test(date) || new RegExp(this.hour_minute + ":[0-5][0-9]").test(date)) { + date = this.getDate() + } + } + + this.date = DPGlobal.parseDate(date, this.format, this.language, this.formatType, this.timezone); + + if (fromArgs) this.setValue(); + + if (this.date < this.startDate) { + this.viewDate = new Date(this.startDate); + } else if (this.date > this.endDate) { + this.viewDate = new Date(this.endDate); + } else { + this.viewDate = new Date(this.date); + } + this.fill(); + }, + + fillDow: function () { + var dowCnt = this.weekStart, + html = '
'; + while (dowCnt < this.weekStart + 7) { + html += ''; + } + html += ''; + this.picker.find('.datetimepicker-days thead').append(html); + }, + + fillMonths: function () { + var html = ''; + var d = new Date(this.viewDate); + for (var i = 0; i < 12; i++) { + d.setUTCMonth(i); + var classes = this.onRenderMonth(d); + html += '' + dates[this.language].monthsShort[i] + ''; + } + this.picker.find('.datetimepicker-months td').html(html); + }, + + fill: function () { + if (!this.date || !this.viewDate) { + return; + } + var d = new Date(this.viewDate), + year = d.getUTCFullYear(), + month = d.getUTCMonth(), + dayMonth = d.getUTCDate(), + hours = d.getUTCHours(), + startYear = this.startDate.getUTCFullYear(), + startMonth = this.startDate.getUTCMonth(), + endYear = this.endDate.getUTCFullYear(), + endMonth = this.endDate.getUTCMonth() + 1, + currentDate = (new UTCDate(this.date.getUTCFullYear(), this.date.getUTCMonth(), this.date.getUTCDate())).valueOf(), + today = new Date(); + this.setTitle('.datetimepicker-days', dates[this.language].months[month] + ' ' + year) + if (this.formatViewType === 'time') { + var formatted = this.getFormattedDate(); + this.setTitle('.datetimepicker-hours', formatted); + this.setTitle('.datetimepicker-minutes', formatted); + } else { + this.setTitle('.datetimepicker-hours', dayMonth + ' ' + dates[this.language].months[month] + ' ' + year); + this.setTitle('.datetimepicker-minutes', dayMonth + ' ' + dates[this.language].months[month] + ' ' + year); + } + this.picker.find('tfoot th.today') + .text(dates[this.language].today || dates['en'].today) + .toggle(this.todayBtn !== false); + this.picker.find('tfoot th.clear') + .text(dates[this.language].clear || dates['en'].clear) + .toggle(this.clearBtn !== false); + this.updateNavArrows(); + this.fillMonths(); + var prevMonth = UTCDate(year, month - 1, 28, 0, 0, 0, 0), + day = DPGlobal.getDaysInMonth(prevMonth.getUTCFullYear(), prevMonth.getUTCMonth()); + prevMonth.setUTCDate(day); + prevMonth.setUTCDate(day - (prevMonth.getUTCDay() - this.weekStart + 7) % 7); + var nextMonth = new Date(prevMonth); + nextMonth.setUTCDate(nextMonth.getUTCDate() + 42); + nextMonth = nextMonth.valueOf(); + var html = []; + var classes; + while (prevMonth.valueOf() < nextMonth) { + if (prevMonth.getUTCDay() === this.weekStart) { + html.push(''); + } + classes = this.onRenderDay(prevMonth); + if (prevMonth.getUTCFullYear() < year || (prevMonth.getUTCFullYear() === year && prevMonth.getUTCMonth() < month)) { + classes.push('old'); + } else if (prevMonth.getUTCFullYear() > year || (prevMonth.getUTCFullYear() === year && prevMonth.getUTCMonth() > month)) { + classes.push('new'); + } + // Compare internal UTC date with local today, not UTC today + if (this.todayHighlight && + prevMonth.getUTCFullYear() === today.getFullYear() && + prevMonth.getUTCMonth() === today.getMonth() && + prevMonth.getUTCDate() === today.getDate()) { + classes.push('today'); + } + if (prevMonth.valueOf() === currentDate) { + classes.push('active'); + } + if ((prevMonth.valueOf() + 86400000) <= this.startDate || prevMonth.valueOf() > this.endDate || + $.inArray(prevMonth.getUTCDay(), this.daysOfWeekDisabled) !== -1 || + $.inArray(prevMonth.toDateString(), this.datesDisabled) !== -1) { + classes.push('disabled'); + } + html.push(''); + if (prevMonth.getUTCDay() === this.weekEnd) { + html.push(''); + } + prevMonth.setUTCDate(prevMonth.getUTCDate() + 1); + } + this.picker.find('.datetimepicker-days tbody').empty().append(html.join('')); + + html = []; + var txt = '', meridian = '', meridianOld = ''; + var hoursDisabled = this.hoursDisabled || []; + d = new Date(this.viewDate) + for (var i = 0; i < 24; i++) { + d.setUTCHours(i); + classes = this.onRenderHour(d); + if (hoursDisabled.indexOf(i) !== -1) { + classes.push('disabled'); + } + var actual = UTCDate(year, month, dayMonth, i); + // We want the previous hour for the startDate + if ((actual.valueOf() + 3600000) <= this.startDate || actual.valueOf() > this.endDate) { + classes.push('disabled'); + } else if (hours === i) { + classes.push('active'); + } + if (this.showMeridian && dates[this.language].meridiem.length === 2) { + meridian = (i < 12 ? dates[this.language].meridiem[0] : dates[this.language].meridiem[1]); + if (meridian !== meridianOld) { + if (meridianOld !== '') { + html.push(''); + } + html.push('
' + meridian.toUpperCase() + ''); + } + meridianOld = meridian; + txt = (i % 12 ? i % 12 : 12); + if (i < 12) { + classes.push('hour_am'); + } else { + classes.push('hour_pm'); + } + html.push('' + txt + ''); + if (i === 23) { + html.push('
'); + } + } else { + txt = i + ':00'; + html.push('' + txt + ''); + } + } + this.picker.find('.datetimepicker-hours td').html(html.join('')); + + html = []; + txt = ''; + meridian = ''; + meridianOld = ''; + var minutesDisabled = this.minutesDisabled || []; + d = new Date(this.viewDate); + for (var i = 0; i < 60; i += this.minuteStep) { + if (minutesDisabled.indexOf(i) !== -1) continue; + d.setUTCMinutes(i); + d.setUTCSeconds(0); + classes = this.onRenderMinute(d); + if (this.showMeridian && dates[this.language].meridiem.length === 2) { + meridian = (hours < 12 ? dates[this.language].meridiem[0] : dates[this.language].meridiem[1]); + if (meridian !== meridianOld) { + if (meridianOld !== '') { + html.push(''); + } + html.push('
' + meridian.toUpperCase() + ''); + } + meridianOld = meridian; + txt = (hours % 12 ? hours % 12 : 12); + html.push('' + txt + ':' + (i < 10 ? '0' + i : i) + ''); + if (i === 59) { + html.push('
'); + } + } else { + txt = i + ':00'; + html.push('' + hours + ':' + (i < 10 ? '0' + i : i) + ''); + } + } + this.picker.find('.datetimepicker-minutes td').html(html.join('')); + + var currentYear = this.date.getUTCFullYear(); + var months = this.setTitle('.datetimepicker-months', year) + .end() + .find('.month').removeClass('active'); + if (currentYear === year) { + // getUTCMonths() returns 0 based, and we need to select the next one + // To cater bootstrap 2 we don't need to select the next one + months.eq(this.date.getUTCMonth()).addClass('active'); + } + if (year < startYear || year > endYear) { + months.addClass('disabled'); + } + if (year === startYear) { + months.slice(0, startMonth).addClass('disabled'); + } + if (year === endYear) { + months.slice(endMonth).addClass('disabled'); + } + + html = ''; + year = parseInt(year / 10, 10) * 10; + var yearCont = this.setTitle('.datetimepicker-years', year + '-' + (year + 9)) + .end() + .find('td'); + year -= 1; + d = new Date(this.viewDate); + for (var i = -1; i < 11; i++) { + d.setUTCFullYear(year); + classes = this.onRenderYear(d); + if (i === -1 || i === 10) { + classes.push(old); + } + html += '' + year + ''; + year += 1; + } + yearCont.html(html); + this.place(); + }, + + updateNavArrows: function () { + var d = new Date(this.viewDate), + year = d.getUTCFullYear(), + month = d.getUTCMonth(), + day = d.getUTCDate(), + hour = d.getUTCHours(); + switch (this.viewMode) { + case 0: + if (year <= this.startDate.getUTCFullYear() + && month <= this.startDate.getUTCMonth() + && day <= this.startDate.getUTCDate() + && hour <= this.startDate.getUTCHours()) { + this.picker.find('.prev').css({visibility: 'hidden'}); + } else { + this.picker.find('.prev').css({visibility: 'visible'}); + } + if (year >= this.endDate.getUTCFullYear() + && month >= this.endDate.getUTCMonth() + && day >= this.endDate.getUTCDate() + && hour >= this.endDate.getUTCHours()) { + this.picker.find('.next').css({visibility: 'hidden'}); + } else { + this.picker.find('.next').css({visibility: 'visible'}); + } + break; + case 1: + if (year <= this.startDate.getUTCFullYear() + && month <= this.startDate.getUTCMonth() + && day <= this.startDate.getUTCDate()) { + this.picker.find('.prev').css({visibility: 'hidden'}); + } else { + this.picker.find('.prev').css({visibility: 'visible'}); + } + if (year >= this.endDate.getUTCFullYear() + && month >= this.endDate.getUTCMonth() + && day >= this.endDate.getUTCDate()) { + this.picker.find('.next').css({visibility: 'hidden'}); + } else { + this.picker.find('.next').css({visibility: 'visible'}); + } + break; + case 2: + if (year <= this.startDate.getUTCFullYear() + && month <= this.startDate.getUTCMonth()) { + this.picker.find('.prev').css({visibility: 'hidden'}); + } else { + this.picker.find('.prev').css({visibility: 'visible'}); + } + if (year >= this.endDate.getUTCFullYear() + && month >= this.endDate.getUTCMonth()) { + this.picker.find('.next').css({visibility: 'hidden'}); + } else { + this.picker.find('.next').css({visibility: 'visible'}); + } + break; + case 3: + case 4: + if (year <= this.startDate.getUTCFullYear()) { + this.picker.find('.prev').css({visibility: 'hidden'}); + } else { + this.picker.find('.prev').css({visibility: 'visible'}); + } + if (year >= this.endDate.getUTCFullYear()) { + this.picker.find('.next').css({visibility: 'hidden'}); + } else { + this.picker.find('.next').css({visibility: 'visible'}); + } + break; + } + }, + + mousewheel: function (e) { + + e.preventDefault(); + e.stopPropagation(); + + if (this.wheelPause) { + return; + } + + this.wheelPause = true; + + var originalEvent = e.originalEvent; + + var delta = originalEvent.wheelDelta; + + var mode = delta > 0 ? 1 : (delta === 0) ? 0 : -1; + + if (this.wheelViewModeNavigationInverseDirection) { + mode = -mode; + } + + this.showMode(mode); + + setTimeout($.proxy(function () { + + this.wheelPause = false + + }, this), this.wheelViewModeNavigationDelay); + + }, + + click: function (e) { + e.stopPropagation(); + e.preventDefault(); + var target = $(e.target).closest('span, td, th, legend'); + if (target.is('.' + this.icontype)) { + target = $(target).parent().closest('span, td, th, legend'); + } + if (target.length === 1) { + if (target.is('.disabled')) { + this.element.trigger({ + type: 'outOfRange', + date: this.viewDate, + startDate: this.startDate, + endDate: this.endDate + }); + return; + } + switch (target[0].nodeName.toLowerCase()) { + case 'th': + switch (target[0].className) { + case 'switch': + this.showMode(1); + break; + case 'prev': + case 'next': + var dir = DPGlobal.modes[this.viewMode].navStep * (target[0].className === 'prev' ? -1 : 1); + switch (this.viewMode) { + case 0: + this.viewDate = this.moveHour(this.viewDate, dir); + break; + case 1: + this.viewDate = this.moveDate(this.viewDate, dir); + break; + case 2: + this.viewDate = this.moveMonth(this.viewDate, dir); + break; + case 3: + case 4: + this.viewDate = this.moveYear(this.viewDate, dir); + break; + } + this.fill(); + this.element.trigger({ + type: target[0].className + ':' + this.convertViewModeText(this.viewMode), + date: this.viewDate, + startDate: this.startDate, + endDate: this.endDate }); - }, - - notifyEvent = function (e) { - if (e.type === 'dp.change' && ((e.date && e.date.isSame(e.oldDate)) || (!e.date && !e.oldDate))) { - return; - } - element.trigger(e); - }, - - viewUpdate = function (e) { - if (e === 'y') { - e = 'YYYY'; - } - notifyEvent({ - type: 'dp.update', - change: e, - viewDate: viewDate.clone() + break; + case 'clear': + this.reset(); + if (this.autoclose) { + this.hide(); + } + break; + case 'today': + var date = new Date(); + date = UTCDate(date.getFullYear(), date.getMonth(), date.getDate(), date.getHours(), date.getMinutes(), date.getSeconds(), 0); + + // Respect startDate and endDate. + if (date < this.startDate) date = this.startDate; + else if (date > this.endDate) date = this.endDate; + + this.viewMode = this.startViewMode; + this.showMode(0); + this._setDate(date); + this.fill(); + if (this.autoclose) { + this.hide(); + } + break; + } + break; + case 'span': + if (!target.is('.disabled')) { + var year = this.viewDate.getUTCFullYear(), + month = this.viewDate.getUTCMonth(), + day = this.viewDate.getUTCDate(), + hours = this.viewDate.getUTCHours(), + minutes = this.viewDate.getUTCMinutes(), + seconds = this.viewDate.getUTCSeconds(); + + if (target.is('.month')) { + this.viewDate.setUTCDate(1); + month = target.parent().find('span').index(target); + day = this.viewDate.getUTCDate(); + this.viewDate.setUTCMonth(month); + this.element.trigger({ + type: 'changeMonth', + date: this.viewDate }); - }, - - showMode = function (dir) { - if (!widget) { - return; - } - if (dir) { - currentViewMode = Math.max(minViewModeNumber, Math.min(3, currentViewMode + dir)); - } - widget.find('.datepicker > div').hide().filter('.datepicker-' + datePickerModes[currentViewMode].clsName).show(); - }, - - fillDow = function () { - var row = $(''), - currentDate = viewDate.clone().startOf('w').startOf('d'); - - if (options.calendarWeeks === true) { - row.append($(''); - if (options.calendarWeeks) { - row.append(''); - } - html.push(row); - } - clsNames = ['day']; - if (currentDate.isBefore(viewDate, 'M')) { - clsNames.push('old'); - } - if (currentDate.isAfter(viewDate, 'M')) { - clsNames.push('new'); - } - if (currentDate.isSame(date, 'd') && !unset) { - clsNames.push('active'); - } - if (!isValid(currentDate, 'd')) { - clsNames.push('disabled'); - } - if (currentDate.isSame(getMoment(), 'd')) { - clsNames.push('today'); - } - if (currentDate.day() === 0 || currentDate.day() === 6) { - clsNames.push('weekend'); - } - notifyEvent({ - type: 'dp.classify', - date: currentDate, - classNames: clsNames - }); - row.append(''); - currentDate.add(1, 'd'); - } - - daysView.find('tbody').empty().append(html); - - updateMonths(); - - updateYears(); - - updateDecades(); - }, - - fillHours = function () { - var table = widget.find('.timepicker-hours table'), - currentHour = viewDate.clone().startOf('d'), - html = [], - row = $(''); - - if (viewDate.hour() > 11 && !use24Hours) { - currentHour.hour(12); - } - while (currentHour.isSame(viewDate, 'd') && (use24Hours || (viewDate.hour() < 12 && currentHour.hour() < 12) || viewDate.hour() > 11)) { - if (currentHour.hour() % 4 === 0) { - row = $(''); - html.push(row); - } - row.append(''); - currentHour.add(1, 'h'); - } - table.empty().append(html); - }, - - fillMinutes = function () { - var table = widget.find('.timepicker-minutes table'), - currentMinute = viewDate.clone().startOf('h'), - html = [], - row = $(''), - step = options.stepping === 1 ? 5 : options.stepping; - - while (viewDate.isSame(currentMinute, 'h')) { - if (currentMinute.minute() % (step * 4) === 0) { - row = $(''); - html.push(row); - } - row.append(''); - currentMinute.add(step, 'm'); - } - table.empty().append(html); - }, - - fillSeconds = function () { - var table = widget.find('.timepicker-seconds table'), - currentSecond = viewDate.clone().startOf('m'), - html = [], - row = $(''); - - while (viewDate.isSame(currentSecond, 'm')) { - if (currentSecond.second() % 20 === 0) { - row = $(''); - html.push(row); - } - row.append(''); - currentSecond.add(5, 's'); - } - - table.empty().append(html); - }, - - fillTime = function () { - var toggle, newDate, timeComponents = widget.find('.timepicker span[data-time-component]'); - - if (!use24Hours) { - toggle = widget.find('.timepicker [data-action=togglePeriod]'); - newDate = date.clone().add((date.hours() >= 12) ? -12 : 12, 'h'); - - toggle.text(date.format('A')); - - if (isValid(newDate, 'h')) { - toggle.removeClass('disabled'); - } else { - toggle.addClass('disabled'); - } - } - timeComponents.filter('[data-time-component=hours]').text(date.format(use24Hours ? 'HH' : 'hh')); - timeComponents.filter('[data-time-component=minutes]').text(date.format('mm')); - timeComponents.filter('[data-time-component=seconds]').text(date.format('ss')); - - fillHours(); - fillMinutes(); - fillSeconds(); - }, - - update = function () { - if (!widget) { - return; - } - fillDate(); - fillTime(); - }, - - setValue = function (targetMoment) { - var oldDate = unset ? null : date; - - // case of calling setValue(null or false) - if (!targetMoment) { - unset = true; - input.val(''); - element.data('date', ''); - notifyEvent({ - type: 'dp.change', - date: false, - oldDate: oldDate - }); - update(); - return; - } - - targetMoment = targetMoment.clone().locale(options.locale); - - if (hasTimeZone()) { - targetMoment.tz(options.timeZone); - } - - if (options.stepping !== 1) { - targetMoment.minutes((Math.round(targetMoment.minutes() / options.stepping) * options.stepping)).seconds(0); - - while (options.minDate && targetMoment.isBefore(options.minDate)) { - targetMoment.add(options.stepping, 'minutes'); - } - } - - if (isValid(targetMoment)) { - date = targetMoment; - viewDate = date.clone(); - input.val(date.format(actualFormat)); - element.data('date', date.format(actualFormat)); - unset = false; - update(); - notifyEvent({ - type: 'dp.change', - date: date.clone(), - oldDate: oldDate - }); - } else { - if (!options.keepInvalid) { - input.val(unset ? '' : date.format(actualFormat)); - } else { - notifyEvent({ - type: 'dp.change', - date: targetMoment, - oldDate: oldDate - }); - } - notifyEvent({ - type: 'dp.error', - date: targetMoment, - oldDate: oldDate - }); - } - }, - - /** - * Hides the widget. Possibly will emit dp.hide - */ - hide = function () { - var transitioning = false; - if (!widget) { - return picker; - } - // Ignore event if in the middle of a picker transition - widget.find('.collapse').each(function () { - var collapseData = $(this).data('collapse'); - if (collapseData && collapseData.transitioning) { - transitioning = true; - return false; - } - return true; + if (this.viewSelect >= 4) { + this._setDate(UTCDate(year, month, day, hours, minutes, seconds, 0)); + } + } else if (target.is('.hour')) { + hours = parseInt(target.text(), 10) || 0; + if (target.hasClass('hour_am') || target.hasClass('hour_pm')) { + if (hours === 12 && target.hasClass('hour_am')) { + hours = 0; + } else if (hours !== 12 && target.hasClass('hour_pm')) { + hours += 12; + } + } + this.viewDate.setUTCHours(hours); + this.element.trigger({ + type: 'changeHour', + date: this.viewDate }); - if (transitioning) { - return picker; - } - if (component && component.hasClass('btn')) { - component.toggleClass('active'); - } - widget.hide(); - - $(window).off('resize', place); - widget.off('click', '[data-action]'); - widget.off('mousedown', false); - - widget.remove(); - widget = false; - - notifyEvent({ - type: 'dp.hide', - date: date.clone() + if (this.viewSelect >= 1) { + this._setDate(UTCDate(year, month, day, hours, minutes, seconds, 0)); + } + } else if (target.is('.minute')) { + minutes = parseInt(target.text().substr(target.text().indexOf(':') + 1), 10) || 0; + this.viewDate.setUTCMinutes(minutes); + this.element.trigger({ + type: 'changeMinute', + date: this.viewDate }); - - input.blur(); - - viewDate = date.clone(); - - return picker; - }, - - clear = function () { - setValue(null); - }, - - parseInputDate = function (inputDate) { - if (options.parseInputDate === undefined) { - if (!moment.isMoment(inputDate) || inputDate instanceof Date) { - inputDate = getMoment(inputDate); - } + if (this.viewSelect >= 0) { + this._setDate(UTCDate(year, month, day, hours, minutes, seconds, 0)); + } + } + if (this.viewMode !== 0) { + var oldViewMode = this.viewMode; + this.showMode(-1); + this.fill(); + if (oldViewMode === this.viewMode && this.autoclose) { + this.hide(); + } + } else { + this.fill(); + if (this.autoclose) { + this.hide(); + } + } + } + break; + case 'td': + if (target.is('.day') && !target.is('.disabled')) { + var day = parseInt(target.text(), 10) || 1; + var year = this.viewDate.getUTCFullYear(), + month = this.viewDate.getUTCMonth(), + hours = this.viewDate.getUTCHours(), + minutes = this.viewDate.getUTCMinutes(), + seconds = this.viewDate.getUTCSeconds(); + if (target.is('.old')) { + if (month === 0) { + month = 11; + year -= 1; } else { - inputDate = options.parseInputDate(inputDate); - } - //inputDate.locale(options.locale); - return inputDate; - }, - - /******************************************************************************** - * - * Widget UI interaction functions - * - ********************************************************************************/ - actions = { - next: function () { - var navFnc = datePickerModes[currentViewMode].navFnc; - viewDate.add(datePickerModes[currentViewMode].navStep, navFnc); - fillDate(); - viewUpdate(navFnc); - }, - - previous: function () { - var navFnc = datePickerModes[currentViewMode].navFnc; - viewDate.subtract(datePickerModes[currentViewMode].navStep, navFnc); - fillDate(); - viewUpdate(navFnc); - }, - - pickerSwitch: function () { - showMode(1); - }, - - selectMonth: function (e) { - var month = $(e.target).closest('tbody').find('span').index($(e.target)); - viewDate.month(month); - if (currentViewMode === minViewModeNumber) { - setValue(date.clone().year(viewDate.year()).month(viewDate.month())); - if (!options.inline) { - hide(); - } - } else { - showMode(-1); - fillDate(); - } - viewUpdate('M'); - }, - - selectYear: function (e) { - var year = parseInt($(e.target).text(), 10) || 0; - viewDate.year(year); - if (currentViewMode === minViewModeNumber) { - setValue(date.clone().year(viewDate.year())); - if (!options.inline) { - hide(); - } - } else { - showMode(-1); - fillDate(); - } - viewUpdate('YYYY'); - }, - - selectDecade: function (e) { - var year = parseInt($(e.target).data('selection'), 10) || 0; - viewDate.year(year); - if (currentViewMode === minViewModeNumber) { - setValue(date.clone().year(viewDate.year())); - if (!options.inline) { - hide(); - } - } else { - showMode(-1); - fillDate(); - } - viewUpdate('YYYY'); - }, - - selectDay: function (e) { - var day = viewDate.clone(); - if ($(e.target).is('.old')) { - day.subtract(1, 'M'); - } - if ($(e.target).is('.new')) { - day.add(1, 'M'); - } - setValue(day.date(parseInt($(e.target).text(), 10))); - if (!hasTime() && !options.keepOpen && !options.inline) { - hide(); - } - }, - - incrementHours: function () { - var newDate = date.clone().add(1, 'h'); - if (isValid(newDate, 'h')) { - setValue(newDate); - } - }, - - incrementMinutes: function () { - var newDate = date.clone().add(options.stepping, 'm'); - if (isValid(newDate, 'm')) { - setValue(newDate); - } - }, - - incrementSeconds: function () { - var newDate = date.clone().add(1, 's'); - if (isValid(newDate, 's')) { - setValue(newDate); - } - }, - - decrementHours: function () { - var newDate = date.clone().subtract(1, 'h'); - if (isValid(newDate, 'h')) { - setValue(newDate); - } - }, - - decrementMinutes: function () { - var newDate = date.clone().subtract(options.stepping, 'm'); - if (isValid(newDate, 'm')) { - setValue(newDate); - } - }, - - decrementSeconds: function () { - var newDate = date.clone().subtract(1, 's'); - if (isValid(newDate, 's')) { - setValue(newDate); - } - }, - - togglePeriod: function () { - setValue(date.clone().add((date.hours() >= 12) ? -12 : 12, 'h')); - }, - - togglePicker: function (e) { - var $this = $(e.target), - $parent = $this.closest('ul'), - expanded = $parent.find('.in'), - closed = $parent.find('.collapse:not(.in)'), - collapseData; - - if (expanded && expanded.length) { - collapseData = expanded.data('collapse'); - if (collapseData && collapseData.transitioning) { - return; - } - if (expanded.collapse) { // if collapse plugin is available through bootstrap.js then use it - expanded.collapse('hide'); - closed.collapse('show'); - } else { // otherwise just toggle in class on the two views - expanded.removeClass('in'); - closed.addClass('in'); - } - if ($this.is('span')) { - $this.toggleClass(options.icons.time + ' ' + options.icons.date); - } else { - $this.find('span').toggleClass(options.icons.time + ' ' + options.icons.date); - } - - // NOTE: uncomment if toggled state will be restored in show() - //if (component) { - // component.find('span').toggleClass(options.icons.time + ' ' + options.icons.date); - //} - } - }, - - showPicker: function () { - widget.find('.timepicker > div:not(.timepicker-picker)').hide(); - widget.find('.timepicker .timepicker-picker').show(); - }, - - showHours: function () { - widget.find('.timepicker .timepicker-picker').hide(); - widget.find('.timepicker .timepicker-hours').show(); - }, - - showMinutes: function () { - widget.find('.timepicker .timepicker-picker').hide(); - widget.find('.timepicker .timepicker-minutes').show(); - }, - - showSeconds: function () { - widget.find('.timepicker .timepicker-picker').hide(); - widget.find('.timepicker .timepicker-seconds').show(); - }, - - selectHour: function (e) { - var hour = parseInt($(e.target).text(), 10); - - if (!use24Hours) { - if (date.hours() >= 12) { - if (hour !== 12) { - hour += 12; - } - } else { - if (hour === 12) { - hour = 0; - } - } - } - setValue(date.clone().hours(hour)); - actions.showPicker.call(picker); - }, - - selectMinute: function (e) { - setValue(date.clone().minutes(parseInt($(e.target).text(), 10))); - actions.showPicker.call(picker); - }, - - selectSecond: function (e) { - setValue(date.clone().seconds(parseInt($(e.target).text(), 10))); - actions.showPicker.call(picker); - }, - - clear: clear, - - today: function () { - var todaysDate = getMoment(); - if (isValid(todaysDate, 'd')) { - setValue(todaysDate); - } - }, - - close: hide - }, - - doAction = function (e) { - if ($(e.currentTarget).is('.disabled')) { - return false; - } - actions[$(e.currentTarget).data('action')].apply(picker, arguments); - return false; - }, - - /** - * Shows the widget. Possibly will emit dp.show and dp.change - */ - show = function () { - var currentMoment, - useCurrentGranularity = { - 'year': function (m) { - return m.month(0).date(1).hours(0).seconds(0).minutes(0); - }, - 'month': function (m) { - return m.date(1).hours(0).seconds(0).minutes(0); - }, - 'day': function (m) { - return m.hours(0).seconds(0).minutes(0); - }, - 'hour': function (m) { - return m.seconds(0).minutes(0); - }, - 'minute': function (m) { - return m.seconds(0); - } - }; - - if (input.prop('disabled') || (!options.ignoreReadonly && input.prop('readonly')) || widget) { - return picker; - } - if (input.val() !== undefined && input.val().trim().length !== 0) { - setValue(parseInputDate(input.val().trim())); - } else if (unset && options.useCurrent && (options.inline || (input.is('input') && input.val().trim().length === 0))) { - currentMoment = getMoment(); - if (typeof options.useCurrent === 'string') { - currentMoment = useCurrentGranularity[options.useCurrent](currentMoment); - } - setValue(currentMoment); - } - widget = getTemplate(); - - fillDow(); - fillMonths(); - - widget.find('.timepicker-hours').hide(); - widget.find('.timepicker-minutes').hide(); - widget.find('.timepicker-seconds').hide(); - - update(); - showMode(); - - $(window).on('resize', place); - widget.on('click', '[data-action]', doAction); // this handles clicks on the widget - widget.on('mousedown', false); - - if (component && component.hasClass('btn')) { - component.toggleClass('active'); - } - place(); - widget.show(); - if (options.focusOnShow && !input.is(':focus')) { - input.focus(); - } - - notifyEvent({ - type: 'dp.show' - }); - return picker; - }, - - /** - * Shows or hides the widget - */ - toggle = function () { - return (widget ? hide() : show()); - }, - - keydown = function (e) { - var handler = null, - index, - index2, - pressedKeys = [], - pressedModifiers = {}, - currentKey = e.which, - keyBindKeys, - allModifiersPressed, - pressed = 'p'; - - keyState[currentKey] = pressed; - - for (index in keyState) { - if (keyState.hasOwnProperty(index) && keyState[index] === pressed) { - pressedKeys.push(index); - if (parseInt(index, 10) !== currentKey) { - pressedModifiers[index] = true; - } - } - } - - for (index in options.keyBinds) { - if (options.keyBinds.hasOwnProperty(index) && typeof (options.keyBinds[index]) === 'function') { - keyBindKeys = index.split(' '); - if (keyBindKeys.length === pressedKeys.length && keyMap[currentKey] === keyBindKeys[keyBindKeys.length - 1]) { - allModifiersPressed = true; - for (index2 = keyBindKeys.length - 2; index2 >= 0; index2--) { - if (!(keyMap[keyBindKeys[index2]] in pressedModifiers)) { - allModifiersPressed = false; - break; - } - } - if (allModifiersPressed) { - handler = options.keyBinds[index]; - break; - } - } - } - } - - if (handler) { - handler.call(picker, widget); - e.stopPropagation(); - e.preventDefault(); - } - }, - - keyup = function (e) { - keyState[e.which] = 'r'; - e.stopPropagation(); - e.preventDefault(); - }, - - change = function (e) { - var val = $(e.target).val().trim(), - parsedDate = val ? parseInputDate(val) : null; - setValue(parsedDate); - e.stopImmediatePropagation(); - return false; - }, - - attachDatePickerElementEvents = function () { - input.on({ - 'change': change, - 'blur': options.debug ? '' : hide, - 'keydown': keydown, - 'keyup': keyup, - 'focus': options.allowInputToggle ? show : '' - }); - - if (element.is('input')) { - input.on({ - 'focus': show - }); - } else if (component) { - component.on('click', toggle); - component.on('mousedown', false); + month -= 1; } - }, - - detachDatePickerElementEvents = function () { - input.off({ - 'change': change, - 'blur': blur, - 'keydown': keydown, - 'keyup': keyup, - 'focus': options.allowInputToggle ? hide : '' - }); - - if (element.is('input')) { - input.off({ - 'focus': show - }); - } else if (component) { - component.off('click', toggle); - component.off('mousedown', false); - } - }, - - indexGivenDates = function (givenDatesArray) { - // Store given enabledDates and disabledDates as keys. - // This way we can check their existence in O(1) time instead of looping through whole array. - // (for example: options.enabledDates['2014-02-27'] === true) - var givenDatesIndexed = {}; - $.each(givenDatesArray, function () { - var dDate = parseInputDate(this); - if (dDate.isValid()) { - givenDatesIndexed[dDate.format('YYYY-MM-DD')] = true; - } - }); - return (Object.keys(givenDatesIndexed).length) ? givenDatesIndexed : false; - }, - - indexGivenHours = function (givenHoursArray) { - // Store given enabledHours and disabledHours as keys. - // This way we can check their existence in O(1) time instead of looping through whole array. - // (for example: options.enabledHours['2014-02-27'] === true) - var givenHoursIndexed = {}; - $.each(givenHoursArray, function () { - givenHoursIndexed[this] = true; + } else if (target.is('.new')) { + if (month === 11) { + month = 0; + year += 1; + } else { + month += 1; + } + } + this.viewDate.setUTCFullYear(year); + this.viewDate.setUTCMonth(month, day); + this.element.trigger({ + type: 'changeDay', + date: this.viewDate + }); + if (this.viewSelect >= 2) { + this._setDate(UTCDate(year, month, day, hours, minutes, seconds, 0)); + } + } + var oldViewMode = this.viewMode; + this.showMode(-1); + this.fill(); + if (oldViewMode === this.viewMode && this.autoclose) { + this.hide(); + } + break; + } + } + }, + + _setDate: function (date, which) { + if (!which || which === 'date') + this.date = date; + if (!which || which === 'view') + this.viewDate = date; + this.fill(); + this.setValue(); + var element; + if (this.isInput) { + element = this.element; + } else if (this.component) { + element = this.element.find('input'); + } + if (element) { + element.change(); + } + this.element.trigger({ + type: 'changeDate', + date: this.getDate() + }); + if(date === null) + this.date = this.viewDate; + }, + + moveMinute: function (date, dir) { + if (!dir) return date; + var new_date = new Date(date.valueOf()); + //dir = dir > 0 ? 1 : -1; + new_date.setUTCMinutes(new_date.getUTCMinutes() + (dir * this.minuteStep)); + return new_date; + }, + + moveHour: function (date, dir) { + if (!dir) return date; + var new_date = new Date(date.valueOf()); + //dir = dir > 0 ? 1 : -1; + new_date.setUTCHours(new_date.getUTCHours() + dir); + return new_date; + }, + + moveDate: function (date, dir) { + if (!dir) return date; + var new_date = new Date(date.valueOf()); + //dir = dir > 0 ? 1 : -1; + new_date.setUTCDate(new_date.getUTCDate() + dir); + return new_date; + }, + + moveMonth: function (date, dir) { + if (!dir) return date; + var new_date = new Date(date.valueOf()), + day = new_date.getUTCDate(), + month = new_date.getUTCMonth(), + mag = Math.abs(dir), + new_month, test; + dir = dir > 0 ? 1 : -1; + if (mag === 1) { + test = dir === -1 + // If going back one month, make sure month is not current month + // (eg, Mar 31 -> Feb 31 === Feb 28, not Mar 02) + ? function () { + return new_date.getUTCMonth() === month; + } + // If going forward one month, make sure month is as expected + // (eg, Jan 31 -> Feb 31 === Feb 28, not Mar 02) + : function () { + return new_date.getUTCMonth() !== new_month; + }; + new_month = month + dir; + new_date.setUTCMonth(new_month); + // Dec -> Jan (12) or Jan -> Dec (-1) -- limit expected date to 0-11 + if (new_month < 0 || new_month > 11) + new_month = (new_month + 12) % 12; + } else { + // For magnitudes >1, move one month at a time... + for (var i = 0; i < mag; i++) + // ...which might decrease the day (eg, Jan 31 to Feb 28, etc)... + new_date = this.moveMonth(new_date, dir); + // ...then reset the day, keeping it in the new month + new_month = new_date.getUTCMonth(); + new_date.setUTCDate(day); + test = function () { + return new_month !== new_date.getUTCMonth(); + }; + } + // Common date-resetting loop -- if date is beyond end of month, make it + // end of month + while (test()) { + new_date.setUTCDate(--day); + new_date.setUTCMonth(new_month); + } + return new_date; + }, + + moveYear: function (date, dir) { + return this.moveMonth(date, dir * 12); + }, + + dateWithinRange: function (date) { + return date >= this.startDate && date <= this.endDate; + }, + + keydown: function (e) { + if (this.picker.is(':not(:visible)')) { + if (e.keyCode === 27) // allow escape to hide and re-show picker + this.show(); + return; + } + var dateChanged = false, + dir, newDate, newViewDate; + switch (e.keyCode) { + case 27: // escape + this.hide(); + e.preventDefault(); + break; + case 37: // left + case 39: // right + if (!this.keyboardNavigation) break; + dir = e.keyCode === 37 ? -1 : 1; + var viewMode = this.viewMode; + if (e.ctrlKey) { + viewMode += 2; + } else if (e.shiftKey) { + viewMode += 1; + } + if (viewMode === 4) { + newDate = this.moveYear(this.date, dir); + newViewDate = this.moveYear(this.viewDate, dir); + } else if (viewMode === 3) { + newDate = this.moveMonth(this.date, dir); + newViewDate = this.moveMonth(this.viewDate, dir); + } else if (viewMode === 2) { + newDate = this.moveDate(this.date, dir); + newViewDate = this.moveDate(this.viewDate, dir); + } else if (viewMode === 1) { + newDate = this.moveHour(this.date, dir); + newViewDate = this.moveHour(this.viewDate, dir); + } else if (viewMode === 0) { + newDate = this.moveMinute(this.date, dir); + newViewDate = this.moveMinute(this.viewDate, dir); + } + if (this.dateWithinRange(newDate)) { + this.date = newDate; + this.viewDate = newViewDate; + this.setValue(); + this.update(); + e.preventDefault(); + dateChanged = true; + } + break; + case 38: // up + case 40: // down + if (!this.keyboardNavigation) break; + dir = e.keyCode === 38 ? -1 : 1; + viewMode = this.viewMode; + if (e.ctrlKey) { + viewMode += 2; + } else if (e.shiftKey) { + viewMode += 1; + } + if (viewMode === 4) { + newDate = this.moveYear(this.date, dir); + newViewDate = this.moveYear(this.viewDate, dir); + } else if (viewMode === 3) { + newDate = this.moveMonth(this.date, dir); + newViewDate = this.moveMonth(this.viewDate, dir); + } else if (viewMode === 2) { + newDate = this.moveDate(this.date, dir * 7); + newViewDate = this.moveDate(this.viewDate, dir * 7); + } else if (viewMode === 1) { + if (this.showMeridian) { + newDate = this.moveHour(this.date, dir * 6); + newViewDate = this.moveHour(this.viewDate, dir * 6); + } else { + newDate = this.moveHour(this.date, dir * 4); + newViewDate = this.moveHour(this.viewDate, dir * 4); + } + } else if (viewMode === 0) { + newDate = this.moveMinute(this.date, dir * 4); + newViewDate = this.moveMinute(this.viewDate, dir * 4); + } + if (this.dateWithinRange(newDate)) { + this.date = newDate; + this.viewDate = newViewDate; + this.setValue(); + this.update(); + e.preventDefault(); + dateChanged = true; + } + break; + case 13: // enter + if (this.viewMode !== 0) { + var oldViewMode = this.viewMode; + this.showMode(-1); + this.fill(); + if (oldViewMode === this.viewMode && this.autoclose) { + this.hide(); + } + } else { + this.fill(); + if (this.autoclose) { + this.hide(); + } + } + e.preventDefault(); + break; + case 9: // tab + this.hide(); + break; + } + if (dateChanged) { + var element; + if (this.isInput) { + element = this.element; + } else if (this.component) { + element = this.element.find('input'); + } + if (element) { + element.change(); + } + this.element.trigger({ + type: 'changeDate', + date: this.getDate() + }); + } + }, + + showMode: function (dir) { + if (dir) { + var newViewMode = Math.max(0, Math.min(DPGlobal.modes.length - 1, this.viewMode + dir)); + if (newViewMode >= this.minView && newViewMode <= this.maxView) { + this.element.trigger({ + type: 'changeMode', + date: this.viewDate, + oldViewMode: this.viewMode, + newViewMode: newViewMode + }); + + this.viewMode = newViewMode; + } + } + /* + vitalets: fixing bug of very special conditions: + jquery 1.7.1 + webkit + show inline datetimepicker in bootstrap popover. + Method show() does not set display css correctly and datetimepicker is not shown. + Changed to .css('display', 'block') solve the problem. + See https://github.com/vitalets/x-editable/issues/37 + + In jquery 1.7.2+ everything works fine. + */ + //this.picker.find('>div').hide().filter('.datetimepicker-'+DPGlobal.modes[this.viewMode].clsName).show(); + this.picker.find('>div').hide().filter('.datetimepicker-' + DPGlobal.modes[this.viewMode].clsName).css('display', 'block'); + this.updateNavArrows(); + }, + + reset: function () { + this._setDate(null, 'date'); + }, + + convertViewModeText: function (viewMode) { + switch (viewMode) { + case 4: + return 'decade'; + case 3: + return 'year'; + case 2: + return 'month'; + case 1: + return 'day'; + case 0: + return 'hour'; + } + } + }; + + var old = $.fn.datetimepicker; + $.fn.datetimepicker = function (option) { + var args = Array.apply(null, arguments); + args.shift(); + var internal_return; + this.each(function () { + var $this = $(this), + data = $this.data('datetimepicker'), + options = typeof option === 'object' && option; + if (!data) { + $this.data('datetimepicker', (data = new Datetimepicker(this, $.extend({}, $.fn.datetimepicker.defaults, options)))); + } + if (typeof option === 'string' && typeof data[option] === 'function') { + internal_return = data[option].apply(data, args); + if (internal_return !== undefined) { + return false; + } + } + }); + if (internal_return !== undefined) + return internal_return; + else + return this; + }; + + $.fn.datetimepicker.defaults = { + }; + $.fn.datetimepicker.Constructor = Datetimepicker; + var dates = $.fn.datetimepicker.dates = { + en: { + days: ['Sunday', 'Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday', 'Sunday'], + daysShort: ['Sun', 'Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat', 'Sun'], + daysMin: ['Su', 'Mo', 'Tu', 'We', 'Th', 'Fr', 'Sa', 'Su'], + months: ['January', 'February', 'March', 'April', 'May', 'June', 'July', 'August', 'September', 'October', 'November', 'December'], + monthsShort: ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec'], + meridiem: ['am', 'pm'], + suffix: ['st', 'nd', 'rd', 'th'], + today: 'Today', + clear: 'Clear' + } + }; + + var DPGlobal = { + modes: [ + { + clsName: 'minutes', + navFnc: 'Hours', + navStep: 1 + }, + { + clsName: 'hours', + navFnc: 'Date', + navStep: 1 + }, + { + clsName: 'days', + navFnc: 'Month', + navStep: 1 + }, + { + clsName: 'months', + navFnc: 'FullYear', + navStep: 1 + }, + { + clsName: 'years', + navFnc: 'FullYear', + navStep: 10 + } + ], + isLeapYear: function (year) { + return (((year % 4 === 0) && (year % 100 !== 0)) || (year % 400 === 0)) + }, + getDaysInMonth: function (year, month) { + return [31, (DPGlobal.isLeapYear(year) ? 29 : 28), 31, 30, 31, 30, 31, 31, 30, 31, 30, 31][month] + }, + getDefaultFormat: function (type, field) { + if (type === 'standard') { + if (field === 'input') + return 'yyyy-mm-dd hh:ii'; + else + return 'yyyy-mm-dd hh:ii:ss'; + } else if (type === 'php') { + if (field === 'input') + return 'Y-m-d H:i'; + else + return 'Y-m-d H:i:s'; + } else { + throw new Error('Invalid format type.'); + } + }, + validParts: function (type) { + if (type === 'standard') { + return /t|hh?|HH?|p|P|z|Z|ii?|ss?|dd?|DD?|mm?|MM?|yy(?:yy)?/g; + } else if (type === 'php') { + return /[dDjlNwzFmMnStyYaABgGhHis]/g; + } else { + throw new Error('Invalid format type.'); + } + }, + nonpunctuation: /[^ -\/:-@\[-`{-~\t\n\rTZ]+/g, + parseFormat: function (format, type) { + // IE treats \0 as a string end in inputs (truncating the value), + // so it's a bad format delimiter, anyway + var separators = format.replace(this.validParts(type), '\0').split('\0'), + parts = format.match(this.validParts(type)); + if (!separators || !separators.length || !parts || parts.length === 0) { + throw new Error('Invalid date format.'); + } + return {separators: separators, parts: parts}; + }, + parseDate: function (date, format, language, type, timezone) { + if (date instanceof Date) { + var dateUTC = new Date(date.valueOf() - date.getTimezoneOffset() * 60000); + dateUTC.setMilliseconds(0); + return dateUTC; + } + if (/^\d{4}\-\d{1,2}\-\d{1,2}$/.test(date)) { + format = this.parseFormat('yyyy-mm-dd', type); + } + if (/^\d{4}\-\d{1,2}\-\d{1,2}[T ]\d{1,2}\:\d{1,2}$/.test(date)) { + format = this.parseFormat('yyyy-mm-dd hh:ii', type); + } + if (/^\d{4}\-\d{1,2}\-\d{1,2}[T ]\d{1,2}\:\d{1,2}\:\d{1,2}[Z]{0,1}$/.test(date)) { + format = this.parseFormat('yyyy-mm-dd hh:ii:ss', type); + } + if (/^[-+]\d+[dmwy]([\s,]+[-+]\d+[dmwy])*$/.test(date)) { + var part_re = /([-+]\d+)([dmwy])/, + parts = date.match(/([-+]\d+)([dmwy])/g), + part, dir; + date = new Date(); + for (var i = 0; i < parts.length; i++) { + part = part_re.exec(parts[i]); + dir = parseInt(part[1]); + switch (part[2]) { + case 'd': + date.setUTCDate(date.getUTCDate() + dir); + break; + case 'm': + date = Datetimepicker.prototype.moveMonth.call(Datetimepicker.prototype, date, dir); + break; + case 'w': + date.setUTCDate(date.getUTCDate() + dir * 7); + break; + case 'y': + date = Datetimepicker.prototype.moveYear.call(Datetimepicker.prototype, date, dir); + break; + } + } + return UTCDate(date.getUTCFullYear(), date.getUTCMonth(), date.getUTCDate(), date.getUTCHours(), date.getUTCMinutes(), date.getUTCSeconds(), 0); + } + var parts = date && date.toString().match(this.nonpunctuation) || [], + date = new Date(0, 0, 0, 0, 0, 0, 0), + parsed = {}, + setters_order = ['hh', 'h', 'ii', 'i', 'ss', 's', 'yyyy', 'yy', 'M', 'MM', 'm', 'mm', 'D', 'DD', 'd', 'dd', 'H', 'HH', 'p', 'P', 'z', 'Z'], + setters_map = { + hh: function (d, v) { + return d.setUTCHours(v); + }, + h: function (d, v) { + return d.setUTCHours(v); + }, + HH: function (d, v) { + return d.setUTCHours(v === 12 ? 0 : v); + }, + H: function (d, v) { + return d.setUTCHours(v === 12 ? 0 : v); + }, + ii: function (d, v) { + return d.setUTCMinutes(v); + }, + i: function (d, v) { + return d.setUTCMinutes(v); + }, + ss: function (d, v) { + return d.setUTCSeconds(v); + }, + s: function (d, v) { + return d.setUTCSeconds(v); + }, + yyyy: function (d, v) { + return d.setUTCFullYear(v); + }, + yy: function (d, v) { + return d.setUTCFullYear(2000 + v); + }, + m: function (d, v) { + v -= 1; + while (v < 0) v += 12; + v %= 12; + d.setUTCMonth(v); + while (d.getUTCMonth() !== v) + if (isNaN(d.getUTCMonth())) + return d; + else + d.setUTCDate(d.getUTCDate() - 1); + return d; + }, + d: function (d, v) { + return d.setUTCDate(v); + }, + p: function (d, v) { + return d.setUTCHours(v === 1 ? d.getUTCHours() + 12 : d.getUTCHours()); + }, + z: function () { + return timezone + } + }, + val, filtered, part; + setters_map['M'] = setters_map['MM'] = setters_map['mm'] = setters_map['m']; + setters_map['dd'] = setters_map['d']; + setters_map['P'] = setters_map['p']; + setters_map['Z'] = setters_map['z']; + date = UTCDate(date.getFullYear(), date.getMonth(), date.getDate(), date.getHours(), date.getMinutes(), date.getSeconds()); + if (parts.length === format.parts.length) { + for (var i = 0, cnt = format.parts.length; i < cnt; i++) { + val = parseInt(parts[i], 10); + part = format.parts[i]; + if (isNaN(val)) { + switch (part) { + case 'MM': + filtered = $(dates[language].months).filter(function () { + var m = this.slice(0, parts[i].length), + p = parts[i].slice(0, m.length); + return m === p; }); - return (Object.keys(givenHoursIndexed).length) ? givenHoursIndexed : false; - }, - - initFormatting = function () { - var format = options.format || 'L LT'; - - actualFormat = format.replace(/(\[[^\[]*\])|(\\)?(LTS|LT|LL?L?L?|l{1,4})/g, function (formatInput) { - var newinput = date.localeData().longDateFormat(formatInput) || formatInput; - return newinput.replace(/(\[[^\[]*\])|(\\)?(LTS|LT|LL?L?L?|l{1,4})/g, function (formatInput2) { //temp fix for #740 - return date.localeData().longDateFormat(formatInput2) || formatInput2; - }); + val = $.inArray(filtered[0], dates[language].months) + 1; + break; + case 'M': + filtered = $(dates[language].monthsShort).filter(function () { + var m = this.slice(0, parts[i].length), + p = parts[i].slice(0, m.length); + return m.toLowerCase() === p.toLowerCase(); }); - - - parseFormats = options.extraFormats ? options.extraFormats.slice() : []; - if (parseFormats.indexOf(format) < 0 && parseFormats.indexOf(actualFormat) < 0) { - parseFormats.push(actualFormat); - } - - use24Hours = (actualFormat.toLowerCase().indexOf('a') < 1 && actualFormat.replace(/\[.*?\]/g, '').indexOf('h') < 1); - - if (isEnabled('y')) { - minViewModeNumber = 2; - } - if (isEnabled('M')) { - minViewModeNumber = 1; - } - if (isEnabled('d')) { - minViewModeNumber = 0; - } - - currentViewMode = Math.max(minViewModeNumber, currentViewMode); - - if (!unset) { - setValue(date); - } - }; - - /******************************************************************************** - * - * Public API functions - * ===================== - * - * Important: Do not expose direct references to private objects or the options - * object to the outer world. Always return a clone when returning values or make - * a clone when setting a private variable. - * - ********************************************************************************/ - picker.destroy = function () { - ///Destroys the widget and removes all attached event listeners - hide(); - detachDatePickerElementEvents(); - element.removeData('DateTimePicker'); - element.removeData('date'); + val = $.inArray(filtered[0], dates[language].monthsShort) + 1; + break; + case 'p': + case 'P': + val = $.inArray(parts[i].toLowerCase(), dates[language].meridiem); + break; + case 'z': + case 'Z': + timezone; + break; + + } + } + parsed[part] = val; + } + for (var i = 0, s; i < setters_order.length; i++) { + s = setters_order[i]; + if (s in parsed && !isNaN(parsed[s])) + setters_map[s](date, parsed[s]) + } + } + return date; + }, + formatDate: function (date, format, language, type, timezone) { + if (date === null) { + return ''; + } + var val; + if (type === 'standard') { + val = { + t: date.getTime(), + // year + yy: date.getUTCFullYear().toString().substring(2), + yyyy: date.getUTCFullYear(), + // month + m: date.getUTCMonth() + 1, + M: dates[language].monthsShort[date.getUTCMonth()], + MM: dates[language].months[date.getUTCMonth()], + // day + d: date.getUTCDate(), + D: dates[language].daysShort[date.getUTCDay()], + DD: dates[language].days[date.getUTCDay()], + p: (dates[language].meridiem.length === 2 ? dates[language].meridiem[date.getUTCHours() < 12 ? 0 : 1] : ''), + // hour + h: date.getUTCHours(), + // minute + i: date.getUTCMinutes(), + // second + s: date.getUTCSeconds(), + // timezone + z: timezone }; - picker.toggle = toggle; - - picker.show = show; - - picker.hide = hide; - - picker.disable = function () { - ///Disables the input element, the component is attached to, by adding a disabled="true" attribute to it. - ///If the widget was visible before that call it is hidden. Possibly emits dp.hide - hide(); - if (component && component.hasClass('btn')) { - component.addClass('disabled'); - } - input.prop('disabled', true); - return picker; - }; - - picker.enable = function () { - ///Enables the input element, the component is attached to, by removing disabled attribute from it. - if (component && component.hasClass('btn')) { - component.removeClass('disabled'); - } - input.prop('disabled', false); - return picker; - }; - - picker.ignoreReadonly = function (ignoreReadonly) { - if (arguments.length === 0) { - return options.ignoreReadonly; - } - if (typeof ignoreReadonly !== 'boolean') { - throw new TypeError('ignoreReadonly () expects a boolean parameter'); - } - options.ignoreReadonly = ignoreReadonly; - return picker; - }; - - picker.options = function (newOptions) { - if (arguments.length === 0) { - return $.extend(true, {}, options); - } - - if (!(newOptions instanceof Object)) { - throw new TypeError('options() options parameter should be an object'); - } - $.extend(true, options, newOptions); - $.each(options, function (key, value) { - if (picker[key] !== undefined) { - picker[key](value); - } else { - throw new TypeError('option ' + key + ' is not recognized!'); - } - }); - return picker; - }; - - picker.date = function (newDate) { - /// - ///Returns the component's model current date, a moment object or null if not set. - ///date.clone() - /// - /// - ///Sets the components model current moment to it. Passing a null value unsets the components model current moment. Parsing of the newDate parameter is made using moment library with the options.format and options.useStrict components configuration. - ///Takes string, Date, moment, null parameter. - /// - if (arguments.length === 0) { - if (unset) { - return null; - } - return date.clone(); - } - - if (newDate !== null && typeof newDate !== 'string' && !moment.isMoment(newDate) && !(newDate instanceof Date)) { - throw new TypeError('date() parameter must be one of [null, string, moment or Date]'); - } - - setValue(newDate === null ? null : parseInputDate(newDate)); - return picker; - }; - - picker.format = function (newFormat) { - ///test su - ///info about para - ///returns foo - if (arguments.length === 0) { - return options.format; - } - - if ((typeof newFormat !== 'string') && ((typeof newFormat !== 'boolean') || (newFormat !== false))) { - throw new TypeError('format() expects a string or boolean:false parameter ' + newFormat); - } - - options.format = newFormat; - if (actualFormat) { - initFormatting(); // reinit formatting - } - return picker; - }; - - picker.timeZone = function (newZone) { - if (arguments.length === 0) { - return options.timeZone; - } - - if (typeof newZone !== 'string') { - throw new TypeError('newZone() expects a string parameter'); - } - - options.timeZone = newZone; - - return picker; - }; - - picker.dayViewHeaderFormat = function (newFormat) { - if (arguments.length === 0) { - return options.dayViewHeaderFormat; - } - - if (typeof newFormat !== 'string') { - throw new TypeError('dayViewHeaderFormat() expects a string parameter'); - } - - options.dayViewHeaderFormat = newFormat; - return picker; - }; - - picker.extraFormats = function (formats) { - if (arguments.length === 0) { - return options.extraFormats; - } - - if (formats !== false && !(formats instanceof Array)) { - throw new TypeError('extraFormats() expects an array or false parameter'); - } - - options.extraFormats = formats; - if (parseFormats) { - initFormatting(); // reinit formatting - } - return picker; - }; - - picker.disabledDates = function (dates) { - /// - ///Returns an array with the currently set disabled dates on the component. - ///options.disabledDates - /// - /// - ///Setting this takes precedence over options.minDate, options.maxDate configuration. Also calling this function removes the configuration of - ///options.enabledDates if such exist. - ///Takes an [ string or Date or moment ] of values and allows the user to select only from those days. - /// - if (arguments.length === 0) { - return (options.disabledDates ? $.extend({}, options.disabledDates) : options.disabledDates); - } - - if (!dates) { - options.disabledDates = false; - update(); - return picker; - } - if (!(dates instanceof Array)) { - throw new TypeError('disabledDates() expects an array parameter'); - } - options.disabledDates = indexGivenDates(dates); - options.enabledDates = false; - update(); - return picker; - }; - - picker.enabledDates = function (dates) { - /// - ///Returns an array with the currently set enabled dates on the component. - ///options.enabledDates - /// - /// - ///Setting this takes precedence over options.minDate, options.maxDate configuration. Also calling this function removes the configuration of options.disabledDates if such exist. - ///Takes an [ string or Date or moment ] of values and allows the user to select only from those days. - /// - if (arguments.length === 0) { - return (options.enabledDates ? $.extend({}, options.enabledDates) : options.enabledDates); - } - - if (!dates) { - options.enabledDates = false; - update(); - return picker; - } - if (!(dates instanceof Array)) { - throw new TypeError('enabledDates() expects an array parameter'); - } - options.enabledDates = indexGivenDates(dates); - options.disabledDates = false; - update(); - return picker; - }; - - picker.daysOfWeekDisabled = function (daysOfWeekDisabled) { - if (arguments.length === 0) { - return options.daysOfWeekDisabled.splice(0); - } - - if ((typeof daysOfWeekDisabled === 'boolean') && !daysOfWeekDisabled) { - options.daysOfWeekDisabled = false; - update(); - return picker; - } - - if (!(daysOfWeekDisabled instanceof Array)) { - throw new TypeError('daysOfWeekDisabled() expects an array parameter'); - } - options.daysOfWeekDisabled = daysOfWeekDisabled.reduce(function (previousValue, currentValue) { - currentValue = parseInt(currentValue, 10); - if (currentValue > 6 || currentValue < 0 || isNaN(currentValue)) { - return previousValue; - } - if (previousValue.indexOf(currentValue) === -1) { - previousValue.push(currentValue); - } - return previousValue; - }, []).sort(); - if (options.useCurrent && !options.keepInvalid) { - var tries = 0; - while (!isValid(date, 'd')) { - date.add(1, 'd'); - if (tries === 31) { - throw 'Tried 31 times to find a valid date'; - } - tries++; - } - setValue(date); - } - update(); - return picker; - }; - - picker.maxDate = function (maxDate) { - if (arguments.length === 0) { - return options.maxDate ? options.maxDate.clone() : options.maxDate; - } - - if ((typeof maxDate === 'boolean') && maxDate === false) { - options.maxDate = false; - update(); - return picker; - } - - if (typeof maxDate === 'string') { - if (maxDate === 'now' || maxDate === 'moment') { - maxDate = getMoment(); - } - } - - var parsedDate = parseInputDate(maxDate); - - if (!parsedDate.isValid()) { - throw new TypeError('maxDate() Could not parse date parameter: ' + maxDate); - } - if (options.minDate && parsedDate.isBefore(options.minDate)) { - throw new TypeError('maxDate() date parameter is before options.minDate: ' + parsedDate.format(actualFormat)); - } - options.maxDate = parsedDate; - if (options.useCurrent && !options.keepInvalid && date.isAfter(maxDate)) { - setValue(options.maxDate); - } - if (viewDate.isAfter(parsedDate)) { - viewDate = parsedDate.clone().subtract(options.stepping, 'm'); - } - update(); - return picker; - }; - - picker.minDate = function (minDate) { - if (arguments.length === 0) { - return options.minDate ? options.minDate.clone() : options.minDate; - } - - if ((typeof minDate === 'boolean') && minDate === false) { - options.minDate = false; - update(); - return picker; - } - - if (typeof minDate === 'string') { - if (minDate === 'now' || minDate === 'moment') { - minDate = getMoment(); - } - } - - var parsedDate = parseInputDate(minDate); - - if (!parsedDate.isValid()) { - throw new TypeError('minDate() Could not parse date parameter: ' + minDate); - } - if (options.maxDate && parsedDate.isAfter(options.maxDate)) { - throw new TypeError('minDate() date parameter is after options.maxDate: ' + parsedDate.format(actualFormat)); - } - options.minDate = parsedDate; - if (options.useCurrent && !options.keepInvalid && date.isBefore(minDate)) { - setValue(options.minDate); - } - if (viewDate.isBefore(parsedDate)) { - viewDate = parsedDate.clone().add(options.stepping, 'm'); - } - update(); - return picker; - }; - - picker.defaultDate = function (defaultDate) { - /// - ///Returns a moment with the options.defaultDate option configuration or false if not set - ///date.clone() - /// - /// - ///Will set the picker's inital date. If a boolean:false value is passed the options.defaultDate parameter is cleared. - ///Takes a string, Date, moment, boolean:false - /// - if (arguments.length === 0) { - return options.defaultDate ? options.defaultDate.clone() : options.defaultDate; - } - if (!defaultDate) { - options.defaultDate = false; - return picker; - } - - if (typeof defaultDate === 'string') { - if (defaultDate === 'now' || defaultDate === 'moment') { - defaultDate = getMoment(); - } else { - defaultDate = getMoment(defaultDate); - } - } - - var parsedDate = parseInputDate(defaultDate); - if (!parsedDate.isValid()) { - throw new TypeError('defaultDate() Could not parse date parameter: ' + defaultDate); - } - if (!isValid(parsedDate)) { - throw new TypeError('defaultDate() date passed is invalid according to component setup validations'); - } - - options.defaultDate = parsedDate; - - if ((options.defaultDate && options.inline) || input.val().trim() === '') { - setValue(options.defaultDate); - } - return picker; - }; - - picker.locale = function (locale) { - if (arguments.length === 0) { - return options.locale; - } - - if (!moment.localeData(locale)) { - throw new TypeError('locale() locale ' + locale + ' is not loaded from moment locales!'); - } - - options.locale = locale; - date.locale(options.locale); - viewDate.locale(options.locale); - - if (actualFormat) { - initFormatting(); // reinit formatting - } - if (widget) { - hide(); - show(); - } - return picker; - }; - - picker.stepping = function (stepping) { - if (arguments.length === 0) { - return options.stepping; - } - - stepping = parseInt(stepping, 10); - if (isNaN(stepping) || stepping < 1) { - stepping = 1; - } - options.stepping = stepping; - return picker; - }; - - picker.useCurrent = function (useCurrent) { - var useCurrentOptions = ['year', 'month', 'day', 'hour', 'minute']; - if (arguments.length === 0) { - return options.useCurrent; - } - - if ((typeof useCurrent !== 'boolean') && (typeof useCurrent !== 'string')) { - throw new TypeError('useCurrent() expects a boolean or string parameter'); - } - if (typeof useCurrent === 'string' && useCurrentOptions.indexOf(useCurrent.toLowerCase()) === -1) { - throw new TypeError('useCurrent() expects a string parameter of ' + useCurrentOptions.join(', ')); - } - options.useCurrent = useCurrent; - return picker; - }; - - picker.collapse = function (collapse) { - if (arguments.length === 0) { - return options.collapse; - } - - if (typeof collapse !== 'boolean') { - throw new TypeError('collapse() expects a boolean parameter'); - } - if (options.collapse === collapse) { - return picker; - } - options.collapse = collapse; - if (widget) { - hide(); - show(); - } - return picker; - }; - - picker.icons = function (icons) { - if (arguments.length === 0) { - return $.extend({}, options.icons); - } - - if (!(icons instanceof Object)) { - throw new TypeError('icons() expects parameter to be an Object'); - } - $.extend(options.icons, icons); - if (widget) { - hide(); - show(); - } - return picker; - }; - - picker.tooltips = function (tooltips) { - if (arguments.length === 0) { - return $.extend({}, options.tooltips); - } - - if (!(tooltips instanceof Object)) { - throw new TypeError('tooltips() expects parameter to be an Object'); - } - $.extend(options.tooltips, tooltips); - if (widget) { - hide(); - show(); - } - return picker; - }; - - picker.useStrict = function (useStrict) { - if (arguments.length === 0) { - return options.useStrict; - } - - if (typeof useStrict !== 'boolean') { - throw new TypeError('useStrict() expects a boolean parameter'); - } - options.useStrict = useStrict; - return picker; - }; - - picker.sideBySide = function (sideBySide) { - if (arguments.length === 0) { - return options.sideBySide; - } - - if (typeof sideBySide !== 'boolean') { - throw new TypeError('sideBySide() expects a boolean parameter'); - } - options.sideBySide = sideBySide; - if (widget) { - hide(); - show(); - } - return picker; - }; - - picker.viewMode = function (viewMode) { - if (arguments.length === 0) { - return options.viewMode; - } - - if (typeof viewMode !== 'string') { - throw new TypeError('viewMode() expects a string parameter'); - } - - if (viewModes.indexOf(viewMode) === -1) { - throw new TypeError('viewMode() parameter must be one of (' + viewModes.join(', ') + ') value'); - } - - options.viewMode = viewMode; - currentViewMode = Math.max(viewModes.indexOf(viewMode), minViewModeNumber); - - showMode(); - return picker; - }; - - picker.toolbarPlacement = function (toolbarPlacement) { - if (arguments.length === 0) { - return options.toolbarPlacement; - } - - if (typeof toolbarPlacement !== 'string') { - throw new TypeError('toolbarPlacement() expects a string parameter'); - } - if (toolbarPlacements.indexOf(toolbarPlacement) === -1) { - throw new TypeError('toolbarPlacement() parameter must be one of (' + toolbarPlacements.join(', ') + ') value'); - } - options.toolbarPlacement = toolbarPlacement; - - if (widget) { - hide(); - show(); - } - return picker; - }; - - picker.widgetPositioning = function (widgetPositioning) { - if (arguments.length === 0) { - return $.extend({}, options.widgetPositioning); - } - - if (({}).toString.call(widgetPositioning) !== '[object Object]') { - throw new TypeError('widgetPositioning() expects an object variable'); - } - if (widgetPositioning.horizontal) { - if (typeof widgetPositioning.horizontal !== 'string') { - throw new TypeError('widgetPositioning() horizontal variable must be a string'); - } - widgetPositioning.horizontal = widgetPositioning.horizontal.toLowerCase(); - if (horizontalModes.indexOf(widgetPositioning.horizontal) === -1) { - throw new TypeError('widgetPositioning() expects horizontal parameter to be one of (' + horizontalModes.join(', ') + ')'); - } - options.widgetPositioning.horizontal = widgetPositioning.horizontal; - } - if (widgetPositioning.vertical) { - if (typeof widgetPositioning.vertical !== 'string') { - throw new TypeError('widgetPositioning() vertical variable must be a string'); - } - widgetPositioning.vertical = widgetPositioning.vertical.toLowerCase(); - if (verticalModes.indexOf(widgetPositioning.vertical) === -1) { - throw new TypeError('widgetPositioning() expects vertical parameter to be one of (' + verticalModes.join(', ') + ')'); - } - options.widgetPositioning.vertical = widgetPositioning.vertical; - } - update(); - return picker; - }; - - picker.calendarWeeks = function (calendarWeeks) { - if (arguments.length === 0) { - return options.calendarWeeks; - } - - if (typeof calendarWeeks !== 'boolean') { - throw new TypeError('calendarWeeks() expects parameter to be a boolean value'); - } - - options.calendarWeeks = calendarWeeks; - update(); - return picker; - }; - - picker.showTodayButton = function (showTodayButton) { - if (arguments.length === 0) { - return options.showTodayButton; - } - - if (typeof showTodayButton !== 'boolean') { - throw new TypeError('showTodayButton() expects a boolean parameter'); - } - - options.showTodayButton = showTodayButton; - if (widget) { - hide(); - show(); - } - return picker; - }; - - picker.showClear = function (showClear) { - if (arguments.length === 0) { - return options.showClear; - } - - if (typeof showClear !== 'boolean') { - throw new TypeError('showClear() expects a boolean parameter'); - } - - options.showClear = showClear; - if (widget) { - hide(); - show(); - } - return picker; - }; - - picker.widgetParent = function (widgetParent) { - if (arguments.length === 0) { - return options.widgetParent; - } - - if (typeof widgetParent === 'string') { - widgetParent = $(widgetParent); - } - - if (widgetParent !== null && (typeof widgetParent !== 'string' && !(widgetParent instanceof $))) { - throw new TypeError('widgetParent() expects a string or a jQuery object parameter'); - } - - options.widgetParent = widgetParent; - if (widget) { - hide(); - show(); - } - return picker; - }; - - picker.keepOpen = function (keepOpen) { - if (arguments.length === 0) { - return options.keepOpen; - } - - if (typeof keepOpen !== 'boolean') { - throw new TypeError('keepOpen() expects a boolean parameter'); - } - - options.keepOpen = keepOpen; - return picker; - }; - - picker.focusOnShow = function (focusOnShow) { - if (arguments.length === 0) { - return options.focusOnShow; - } - - if (typeof focusOnShow !== 'boolean') { - throw new TypeError('focusOnShow() expects a boolean parameter'); - } - - options.focusOnShow = focusOnShow; - return picker; - }; - - picker.inline = function (inline) { - if (arguments.length === 0) { - return options.inline; - } - - if (typeof inline !== 'boolean') { - throw new TypeError('inline() expects a boolean parameter'); - } - - options.inline = inline; - return picker; - }; - - picker.clear = function () { - clear(); - return picker; - }; - - picker.keyBinds = function (keyBinds) { - if (arguments.length === 0) { - return options.keyBinds; - } - - options.keyBinds = keyBinds; - return picker; - }; - - picker.getMoment = function (d) { - return getMoment(d); - }; - - picker.debug = function (debug) { - if (typeof debug !== 'boolean') { - throw new TypeError('debug() expects a boolean parameter'); - } - - options.debug = debug; - return picker; - }; - - picker.allowInputToggle = function (allowInputToggle) { - if (arguments.length === 0) { - return options.allowInputToggle; - } - - if (typeof allowInputToggle !== 'boolean') { - throw new TypeError('allowInputToggle() expects a boolean parameter'); - } - - options.allowInputToggle = allowInputToggle; - return picker; - }; - - picker.showClose = function (showClose) { - if (arguments.length === 0) { - return options.showClose; - } - - if (typeof showClose !== 'boolean') { - throw new TypeError('showClose() expects a boolean parameter'); - } - - options.showClose = showClose; - return picker; - }; - - picker.keepInvalid = function (keepInvalid) { - if (arguments.length === 0) { - return options.keepInvalid; - } - - if (typeof keepInvalid !== 'boolean') { - throw new TypeError('keepInvalid() expects a boolean parameter'); - } - options.keepInvalid = keepInvalid; - return picker; - }; - - picker.datepickerInput = function (datepickerInput) { - if (arguments.length === 0) { - return options.datepickerInput; - } - - if (typeof datepickerInput !== 'string') { - throw new TypeError('datepickerInput() expects a string parameter'); - } - - options.datepickerInput = datepickerInput; - return picker; - }; - - picker.parseInputDate = function (parseInputDate) { - if (arguments.length === 0) { - return options.parseInputDate; - } - - if (typeof parseInputDate !== 'function') { - throw new TypeError('parseInputDate() sholud be as function'); - } - - options.parseInputDate = parseInputDate; - - return picker; - }; - - picker.disabledTimeIntervals = function (disabledTimeIntervals) { - /// - ///Returns an array with the currently set disabled dates on the component. - ///options.disabledTimeIntervals - /// - /// - ///Setting this takes precedence over options.minDate, options.maxDate configuration. Also calling this function removes the configuration of - ///options.enabledDates if such exist. - ///Takes an [ string or Date or moment ] of values and allows the user to select only from those days. - /// - if (arguments.length === 0) { - return (options.disabledTimeIntervals ? $.extend({}, options.disabledTimeIntervals) : options.disabledTimeIntervals); - } - - if (!disabledTimeIntervals) { - options.disabledTimeIntervals = false; - update(); - return picker; - } - if (!(disabledTimeIntervals instanceof Array)) { - throw new TypeError('disabledTimeIntervals() expects an array parameter'); - } - options.disabledTimeIntervals = disabledTimeIntervals; - update(); - return picker; - }; - - picker.disabledHours = function (hours) { - /// - ///Returns an array with the currently set disabled hours on the component. - ///options.disabledHours - /// - /// - ///Setting this takes precedence over options.minDate, options.maxDate configuration. Also calling this function removes the configuration of - ///options.enabledHours if such exist. - ///Takes an [ int ] of values and disallows the user to select only from those hours. - /// - if (arguments.length === 0) { - return (options.disabledHours ? $.extend({}, options.disabledHours) : options.disabledHours); - } - - if (!hours) { - options.disabledHours = false; - update(); - return picker; - } - if (!(hours instanceof Array)) { - throw new TypeError('disabledHours() expects an array parameter'); - } - options.disabledHours = indexGivenHours(hours); - options.enabledHours = false; - if (options.useCurrent && !options.keepInvalid) { - var tries = 0; - while (!isValid(date, 'h')) { - date.add(1, 'h'); - if (tries === 24) { - throw 'Tried 24 times to find a valid date'; - } - tries++; - } - setValue(date); - } - update(); - return picker; - }; - - picker.enabledHours = function (hours) { - /// - ///Returns an array with the currently set enabled hours on the component. - ///options.enabledHours - /// - /// - ///Setting this takes precedence over options.minDate, options.maxDate configuration. Also calling this function removes the configuration of options.disabledHours if such exist. - ///Takes an [ int ] of values and allows the user to select only from those hours. - /// - if (arguments.length === 0) { - return (options.enabledHours ? $.extend({}, options.enabledHours) : options.enabledHours); - } - - if (!hours) { - options.enabledHours = false; - update(); - return picker; - } - if (!(hours instanceof Array)) { - throw new TypeError('enabledHours() expects an array parameter'); - } - options.enabledHours = indexGivenHours(hours); - options.disabledHours = false; - if (options.useCurrent && !options.keepInvalid) { - var tries = 0; - while (!isValid(date, 'h')) { - date.add(1, 'h'); - if (tries === 24) { - throw 'Tried 24 times to find a valid date'; - } - tries++; - } - setValue(date); - } - update(); - return picker; - }; - /** - * Returns the component's model current viewDate, a moment object or null if not set. Passing a null value unsets the components model current moment. Parsing of the newDate parameter is made using moment library with the options.format and options.useStrict components configuration. - * @param {Takes string, viewDate, moment, null parameter.} newDate - * @returns {viewDate.clone()} - */ - picker.viewDate = function (newDate) { - if (arguments.length === 0) { - return viewDate.clone(); - } - - if (!newDate) { - viewDate = date.clone(); - return picker; - } - - if (typeof newDate !== 'string' && !moment.isMoment(newDate) && !(newDate instanceof Date)) { - throw new TypeError('viewDate() parameter must be one of [string, moment or Date]'); - } - - viewDate = parseInputDate(newDate); - viewUpdate(); - return picker; - }; - - // initializing element and component attributes - if (element.is('input')) { - input = element; - } else { - input = element.find(options.datepickerInput); - if (input.length === 0) { - input = element.find('input'); - } else if (!input.is('input')) { - throw new Error('CSS class "' + options.datepickerInput + '" cannot be applied to non input element'); - } - } - - if (element.hasClass('input-group')) { - // in case there is more then one 'input-group-addon' Issue #48 - if (element.find('.datepickerbutton').length === 0) { - component = element.find('.input-group-addon'); - } else { - component = element.find('.datepickerbutton'); - } - } - - if (!options.inline && !input.is('input')) { - throw new Error('Could not initialize DateTimePicker without an input element'); - } - - // Set defaults for date here now instead of in var declaration - date = getMoment(); - viewDate = date.clone(); - - $.extend(true, options, dataToOptions()); - - picker.options(options); - - initFormatting(); - - attachDatePickerElementEvents(); - - if (input.prop('disabled')) { - picker.disable(); + if (dates[language].meridiem.length === 2) { + val.H = (val.h % 12 === 0 ? 12 : val.h % 12); } - if (input.is('input') && input.val().trim().length !== 0) { - setValue(parseInputDate(input.val().trim())); + else { + val.H = val.h; } - else if (options.defaultDate && input.attr('placeholder') === undefined) { - setValue(options.defaultDate); - } - if (options.inline) { - show(); - } - return picker; - }; - - /******************************************************************************** - * - * jQuery plugin constructor and defaults object - * - ********************************************************************************/ - - /** - * See (http://jquery.com/). - * @name jQuery - * @class - * See the jQuery Library (http://jquery.com/) for full details. This just - * documents the function and classes that are added to jQuery by this plug-in. - */ - /** - * See (http://jquery.com/) - * @name fn - * @class - * See the jQuery Library (http://jquery.com/) for full details. This just - * documents the function and classes that are added to jQuery by this plug-in. - * @memberOf jQuery - */ - /** - * Show comments - * @class datetimepicker - * @memberOf jQuery.fn - */ - $.fn.datetimepicker = function (options) { - options = options || {}; - - var args = Array.prototype.slice.call(arguments, 1), - isInstance = true, - thisMethods = ['destroy', 'hide', 'show', 'toggle'], - returnValue; - - if (typeof options === 'object') { - return this.each(function () { - var $this = $(this), - _options; - if (!$this.data('DateTimePicker')) { - // create a private copy of the defaults object - _options = $.extend(true, {}, $.fn.datetimepicker.defaults, options); - $this.data('DateTimePicker', dateTimePicker($this, _options)); - } - }); - } else if (typeof options === 'string') { - this.each(function () { - var $this = $(this), - instance = $this.data('DateTimePicker'); - if (!instance) { - throw new Error('bootstrap-datetimepicker("' + options + '") method was called on an element that is not using DateTimePicker'); - } - - returnValue = instance[options].apply(instance, args); - isInstance = returnValue === instance; - }); - - if (isInstance || $.inArray(options, thisMethods) > -1) { - return this; - } - - return returnValue; + val.HH = (val.H < 10 ? '0' : '') + val.H; + val.P = val.p.toUpperCase(); + val.Z = val.z; + val.hh = (val.h < 10 ? '0' : '') + val.h; + val.ii = (val.i < 10 ? '0' : '') + val.i; + val.ss = (val.s < 10 ? '0' : '') + val.s; + val.dd = (val.d < 10 ? '0' : '') + val.d; + val.mm = (val.m < 10 ? '0' : '') + val.m; + } else if (type === 'php') { + // php format + val = { + // year + y: date.getUTCFullYear().toString().substring(2), + Y: date.getUTCFullYear(), + // month + F: dates[language].months[date.getUTCMonth()], + M: dates[language].monthsShort[date.getUTCMonth()], + n: date.getUTCMonth() + 1, + t: DPGlobal.getDaysInMonth(date.getUTCFullYear(), date.getUTCMonth()), + // day + j: date.getUTCDate(), + l: dates[language].days[date.getUTCDay()], + D: dates[language].daysShort[date.getUTCDay()], + w: date.getUTCDay(), // 0 -> 6 + N: (date.getUTCDay() === 0 ? 7 : date.getUTCDay()), // 1 -> 7 + S: (date.getUTCDate() % 10 <= dates[language].suffix.length ? dates[language].suffix[date.getUTCDate() % 10 - 1] : ''), + // hour + a: (dates[language].meridiem.length === 2 ? dates[language].meridiem[date.getUTCHours() < 12 ? 0 : 1] : ''), + g: (date.getUTCHours() % 12 === 0 ? 12 : date.getUTCHours() % 12), + G: date.getUTCHours(), + // minute + i: date.getUTCMinutes(), + // second + s: date.getUTCSeconds() + }; + val.m = (val.n < 10 ? '0' : '') + val.n; + val.d = (val.j < 10 ? '0' : '') + val.j; + val.A = val.a.toString().toUpperCase(); + val.h = (val.g < 10 ? '0' : '') + val.g; + val.H = (val.G < 10 ? '0' : '') + val.G; + val.i = (val.i < 10 ? '0' : '') + val.i; + val.s = (val.s < 10 ? '0' : '') + val.s; + } else { + throw new Error('Invalid format type.'); + } + var date = [], + seps = $.extend([], format.separators); + for (var i = 0, cnt = format.parts.length; i < cnt; i++) { + if (seps.length) { + date.push(seps.shift()); } + date.push(val[format.parts[i]]); + } + if (seps.length) { + date.push(seps.shift()); + } + return date.join(''); + }, + convertViewMode: function (viewMode) { + switch (viewMode) { + case 4: + case 'decade': + viewMode = 4; + break; + case 3: + case 'year': + viewMode = 3; + break; + case 2: + case 'month': + viewMode = 2; + break; + case 1: + case 'day': + viewMode = 1; + break; + case 0: + case 'hour': + viewMode = 0; + break; + } + + return viewMode; + }, + headTemplate: '' + + '' + + '' + + '' + + '' + + '' + + '', + headTemplateV3: '' + + '' + + '' + + '' + + '' + + '' + + '', + contTemplate: '', + footTemplate: '' + + '' + + '' + + '' + }; + DPGlobal.template = '
' + + '
' + + '
' + dates[this.language].daysMin[(dowCnt++) % 7] + '
' + prevMonth.getUTCDate() + '
').addClass('cw').text('#')); - } - - while (currentDate.isBefore(viewDate.clone().endOf('w'))) { - row.append($('').addClass('dow').text(currentDate.format('dd'))); - currentDate.add(1, 'd'); - } - widget.find('.datepicker-days thead').append(row); - }, - - isInDisabledDates = function (testDate) { - return options.disabledDates[testDate.format('YYYY-MM-DD')] === true; - }, - - isInEnabledDates = function (testDate) { - return options.enabledDates[testDate.format('YYYY-MM-DD')] === true; - }, - - isInDisabledHours = function (testDate) { - return options.disabledHours[testDate.format('H')] === true; - }, - - isInEnabledHours = function (testDate) { - return options.enabledHours[testDate.format('H')] === true; - }, - - isValid = function (targetMoment, granularity) { - if (!targetMoment.isValid()) { - return false; - } - if (options.disabledDates && granularity === 'd' && isInDisabledDates(targetMoment)) { - return false; - } - if (options.enabledDates && granularity === 'd' && !isInEnabledDates(targetMoment)) { - return false; - } - if (options.minDate && targetMoment.isBefore(options.minDate, granularity)) { - return false; - } - if (options.maxDate && targetMoment.isAfter(options.maxDate, granularity)) { - return false; - } - if (options.daysOfWeekDisabled && granularity === 'd' && options.daysOfWeekDisabled.indexOf(targetMoment.day()) !== -1) { - return false; - } - if (options.disabledHours && (granularity === 'h' || granularity === 'm' || granularity === 's') && isInDisabledHours(targetMoment)) { - return false; - } - if (options.enabledHours && (granularity === 'h' || granularity === 'm' || granularity === 's') && !isInEnabledHours(targetMoment)) { - return false; - } - if (options.disabledTimeIntervals && (granularity === 'h' || granularity === 'm' || granularity === 's')) { - var found = false; - $.each(options.disabledTimeIntervals, function () { - if (targetMoment.isBetween(this[0], this[1])) { - found = true; - return false; - } - }); - if (found) { - return false; - } - } - return true; - }, - - fillMonths = function () { - var spans = [], - monthsShort = viewDate.clone().startOf('y').startOf('d'); - while (monthsShort.isSame(viewDate, 'y')) { - spans.push($('').attr('data-action', 'selectMonth').addClass('month').text(monthsShort.format('MMM'))); - monthsShort.add(1, 'M'); - } - widget.find('.datepicker-months td').empty().append(spans); - }, - - updateMonths = function () { - var monthsView = widget.find('.datepicker-months'), - monthsViewHeader = monthsView.find('th'), - months = monthsView.find('tbody').find('span'); - - monthsViewHeader.eq(0).find('span').attr('title', options.tooltips.prevYear); - monthsViewHeader.eq(1).attr('title', options.tooltips.selectYear); - monthsViewHeader.eq(2).find('span').attr('title', options.tooltips.nextYear); - - monthsView.find('.disabled').removeClass('disabled'); - - if (!isValid(viewDate.clone().subtract(1, 'y'), 'y')) { - monthsViewHeader.eq(0).addClass('disabled'); - } - - monthsViewHeader.eq(1).text(viewDate.year()); - - if (!isValid(viewDate.clone().add(1, 'y'), 'y')) { - monthsViewHeader.eq(2).addClass('disabled'); - } - - months.removeClass('active'); - if (date.isSame(viewDate, 'y') && !unset) { - months.eq(date.month()).addClass('active'); - } - - months.each(function (index) { - if (!isValid(viewDate.clone().month(index), 'M')) { - $(this).addClass('disabled'); - } + if (this.viewSelect >= 3) { + this._setDate(UTCDate(year, month, day, hours, minutes, seconds, 0)); + } + } else if (target.is('.year')) { + this.viewDate.setUTCDate(1); + year = parseInt(target.text(), 10) || 0; + this.viewDate.setUTCFullYear(year); + this.element.trigger({ + type: 'changeYear', + date: this.viewDate }); - }, - - updateYears = function () { - var yearsView = widget.find('.datepicker-years'), - yearsViewHeader = yearsView.find('th'), - startYear = viewDate.clone().subtract(5, 'y'), - endYear = viewDate.clone().add(6, 'y'), - html = ''; - - yearsViewHeader.eq(0).find('span').attr('title', options.tooltips.prevDecade); - yearsViewHeader.eq(1).attr('title', options.tooltips.selectDecade); - yearsViewHeader.eq(2).find('span').attr('title', options.tooltips.nextDecade); - - yearsView.find('.disabled').removeClass('disabled'); - - if (options.minDate && options.minDate.isAfter(startYear, 'y')) { - yearsViewHeader.eq(0).addClass('disabled'); - } - - yearsViewHeader.eq(1).text(startYear.year() + '-' + endYear.year()); - - if (options.maxDate && options.maxDate.isBefore(endYear, 'y')) { - yearsViewHeader.eq(2).addClass('disabled'); - } - - while (!startYear.isAfter(endYear, 'y')) { - html += '' + startYear.year() + ''; - startYear.add(1, 'y'); - } - - yearsView.find('td').html(html); - }, - - updateDecades = function () { - var decadesView = widget.find('.datepicker-decades'), - decadesViewHeader = decadesView.find('th'), - startDecade = moment({ y: viewDate.year() - (viewDate.year() % 100) - 1 }), - endDecade = startDecade.clone().add(100, 'y'), - startedAt = startDecade.clone(), - minDateDecade = false, - maxDateDecade = false, - endDecadeYear, - html = ''; - - decadesViewHeader.eq(0).find('span').attr('title', options.tooltips.prevCentury); - decadesViewHeader.eq(2).find('span').attr('title', options.tooltips.nextCentury); - - decadesView.find('.disabled').removeClass('disabled'); - - if (startDecade.isSame(moment({ y: 1900 })) || (options.minDate && options.minDate.isAfter(startDecade, 'y'))) { - decadesViewHeader.eq(0).addClass('disabled'); - } - - decadesViewHeader.eq(1).text(startDecade.year() + '-' + endDecade.year()); - - if (startDecade.isSame(moment({ y: 2000 })) || (options.maxDate && options.maxDate.isBefore(endDecade, 'y'))) { - decadesViewHeader.eq(2).addClass('disabled'); - } - - while (!startDecade.isAfter(endDecade, 'y')) { - endDecadeYear = startDecade.year() + 12; - minDateDecade = options.minDate && options.minDate.isAfter(startDecade, 'y') && options.minDate.year() <= endDecadeYear; - maxDateDecade = options.maxDate && options.maxDate.isAfter(startDecade, 'y') && options.maxDate.year() <= endDecadeYear; - html += '' + (startDecade.year() + 1) + ' - ' + (startDecade.year() + 12) + ''; - startDecade.add(12, 'y'); - } - html += ''; //push the dangling block over, at least this way it's even - - decadesView.find('td').html(html); - decadesViewHeader.eq(1).text((startedAt.year() + 1) + '-' + (startDecade.year())); - }, - - fillDate = function () { - var daysView = widget.find('.datepicker-days'), - daysViewHeader = daysView.find('th'), - currentDate, - html = [], - row, - clsNames = [], - i; - - if (!hasDate()) { - return; - } - - daysViewHeader.eq(0).find('span').attr('title', options.tooltips.prevMonth); - daysViewHeader.eq(1).attr('title', options.tooltips.selectMonth); - daysViewHeader.eq(2).find('span').attr('title', options.tooltips.nextMonth); - - daysView.find('.disabled').removeClass('disabled'); - daysViewHeader.eq(1).text(viewDate.format(options.dayViewHeaderFormat)); - - if (!isValid(viewDate.clone().subtract(1, 'M'), 'M')) { - daysViewHeader.eq(0).addClass('disabled'); - } - if (!isValid(viewDate.clone().add(1, 'M'), 'M')) { - daysViewHeader.eq(2).addClass('disabled'); - } - - currentDate = viewDate.clone().startOf('M').startOf('w').startOf('d'); - - for (i = 0; i < 42; i++) { //always display 42 days (should show 6 weeks) - if (currentDate.weekday() === 0) { - row = $('
' + currentDate.week() + '' + currentDate.date() + '
' + currentHour.format(use24Hours ? 'HH' : 'hh') + '
' + currentMinute.format('mm') + '
' + currentSecond.format('ss') + '
' + + DPGlobal.headTemplate + + DPGlobal.contTemplate + + DPGlobal.footTemplate + + '
' + + '' + + '
' + + '' + + DPGlobal.headTemplate + + DPGlobal.contTemplate + + DPGlobal.footTemplate + + '
' + + '
' + + '
' + + '' + + DPGlobal.headTemplate + + '' + + DPGlobal.footTemplate + + '
' + + '
' + + '
' + + '' + + DPGlobal.headTemplate + + DPGlobal.contTemplate + + DPGlobal.footTemplate + + '
' + + '
' + + '
' + + '' + + DPGlobal.headTemplate + + DPGlobal.contTemplate + + DPGlobal.footTemplate + + '
' + + '
' + + ''; + DPGlobal.templateV3 = '
' + + '
' + + '' + + DPGlobal.headTemplateV3 + + DPGlobal.contTemplate + + DPGlobal.footTemplate + + '
' + + '
' + + '
' + + '' + + DPGlobal.headTemplateV3 + + DPGlobal.contTemplate + + DPGlobal.footTemplate + + '
' + + '
' + + '
' + + '' + + DPGlobal.headTemplateV3 + + '' + + DPGlobal.footTemplate + + '
' + + '
' + + '
' + + '' + + DPGlobal.headTemplateV3 + + DPGlobal.contTemplate + + DPGlobal.footTemplate + + '
' + + '
' + + '
' + + '' + + DPGlobal.headTemplateV3 + + DPGlobal.contTemplate + + DPGlobal.footTemplate + + '
' + + '
' + + '
'; + $.fn.datetimepicker.DPGlobal = DPGlobal; + + /* DATETIMEPICKER NO CONFLICT + * =================== */ + + $.fn.datetimepicker.noConflict = function () { + $.fn.datetimepicker = old; + return this; + }; + + /* DATETIMEPICKER DATA-API + * ================== */ + + $(document).on( + 'focus.datetimepicker.data-api click.datetimepicker.data-api', + '[data-provide="datetimepicker"]', + function (e) { + var $this = $(this); + if ($this.data('datetimepicker')) return; + e.preventDefault(); + // component click requires us to explicitly show it + $this.datetimepicker('show'); + } + ); + $(function () { + $('[data-provide="datetimepicker-inline"]').datetimepicker(); + }); - throw new TypeError('Invalid arguments for DateTimePicker: ' + options); - }; - - $.fn.datetimepicker.defaults = { - timeZone: '', - format: false, - dayViewHeaderFormat: 'MMMM YYYY', - extraFormats: false, - stepping: 1, - minDate: false, - maxDate: false, - useCurrent: true, - collapse: true, - locale: moment.locale(), - defaultDate: false, - disabledDates: false, - enabledDates: false, - icons: { - time: 'glyphicon glyphicon-time', - date: 'glyphicon glyphicon-calendar', - up: 'glyphicon glyphicon-chevron-up', - down: 'glyphicon glyphicon-chevron-down', - previous: 'glyphicon glyphicon-chevron-left', - next: 'glyphicon glyphicon-chevron-right', - today: 'glyphicon glyphicon-screenshot', - clear: 'glyphicon glyphicon-trash', - close: 'glyphicon glyphicon-remove' - }, - tooltips: { - today: 'Go to today', - clear: 'Clear selection', - close: 'Close the picker', - selectMonth: 'Select Month', - prevMonth: 'Previous Month', - nextMonth: 'Next Month', - selectYear: 'Select Year', - prevYear: 'Previous Year', - nextYear: 'Next Year', - selectDecade: 'Select Decade', - prevDecade: 'Previous Decade', - nextDecade: 'Next Decade', - prevCentury: 'Previous Century', - nextCentury: 'Next Century', - pickHour: 'Pick Hour', - incrementHour: 'Increment Hour', - decrementHour: 'Decrement Hour', - pickMinute: 'Pick Minute', - incrementMinute: 'Increment Minute', - decrementMinute: 'Decrement Minute', - pickSecond: 'Pick Second', - incrementSecond: 'Increment Second', - decrementSecond: 'Decrement Second', - togglePeriod: 'Toggle Period', - selectTime: 'Select Time' - }, - useStrict: false, - sideBySide: false, - daysOfWeekDisabled: false, - calendarWeeks: false, - viewMode: 'days', - toolbarPlacement: 'default', - showTodayButton: false, - showClear: false, - showClose: false, - widgetPositioning: { - horizontal: 'auto', - vertical: 'auto' - }, - widgetParent: null, - ignoreReadonly: false, - keepOpen: false, - focusOnShow: true, - inline: false, - keepInvalid: false, - datepickerInput: '.datepickerinput', - keyBinds: { - up: function (widget) { - if (!widget) { - return; - } - var d = this.date() || this.getMoment(); - if (widget.find('.datepicker').is(':visible')) { - this.date(d.clone().subtract(7, 'd')); - } else { - this.date(d.clone().add(this.stepping(), 'm')); - } - }, - down: function (widget) { - if (!widget) { - this.show(); - return; - } - var d = this.date() || this.getMoment(); - if (widget.find('.datepicker').is(':visible')) { - this.date(d.clone().add(7, 'd')); - } else { - this.date(d.clone().subtract(this.stepping(), 'm')); - } - }, - 'control up': function (widget) { - if (!widget) { - return; - } - var d = this.date() || this.getMoment(); - if (widget.find('.datepicker').is(':visible')) { - this.date(d.clone().subtract(1, 'y')); - } else { - this.date(d.clone().add(1, 'h')); - } - }, - 'control down': function (widget) { - if (!widget) { - return; - } - var d = this.date() || this.getMoment(); - if (widget.find('.datepicker').is(':visible')) { - this.date(d.clone().add(1, 'y')); - } else { - this.date(d.clone().subtract(1, 'h')); - } - }, - left: function (widget) { - if (!widget) { - return; - } - var d = this.date() || this.getMoment(); - if (widget.find('.datepicker').is(':visible')) { - this.date(d.clone().subtract(1, 'd')); - } - }, - right: function (widget) { - if (!widget) { - return; - } - var d = this.date() || this.getMoment(); - if (widget.find('.datepicker').is(':visible')) { - this.date(d.clone().add(1, 'd')); - } - }, - pageUp: function (widget) { - if (!widget) { - return; - } - var d = this.date() || this.getMoment(); - if (widget.find('.datepicker').is(':visible')) { - this.date(d.clone().subtract(1, 'M')); - } - }, - pageDown: function (widget) { - if (!widget) { - return; - } - var d = this.date() || this.getMoment(); - if (widget.find('.datepicker').is(':visible')) { - this.date(d.clone().add(1, 'M')); - } - }, - enter: function () { - this.hide(); - }, - escape: function () { - this.hide(); - }, - //tab: function (widget) { //this break the flow of the form. disabling for now - // var toggle = widget.find('.picker-switch a[data-action="togglePicker"]'); - // if(toggle.length > 0) toggle.click(); - //}, - 'control space': function (widget) { - if (!widget) { - return; - } - if (widget.find('.timepicker').is(':visible')) { - widget.find('.btn[data-action="togglePeriod"]').click(); - } - }, - t: function () { - this.date(this.getMoment()); - }, - 'delete': function () { - this.clear(); - } - }, - debug: false, - allowInputToggle: false, - disabledTimeIntervals: false, - disabledHours: false, - enabledHours: false, - viewDate: false - }; - - return $.fn.datetimepicker; })); diff --git a/app/assets/javascripts/i18n/bootstrap-datetimepicker.zh-CN.js b/app/assets/javascripts/i18n/bootstrap-datetimepicker.zh-CN.js new file mode 100755 index 000000000..418fb3071 --- /dev/null +++ b/app/assets/javascripts/i18n/bootstrap-datetimepicker.zh-CN.js @@ -0,0 +1,16 @@ +/** + * Simplified Chinese translation for bootstrap-datetimepicker + * Yuan Cheung + */ +;(function($){ + $.fn.datetimepicker.dates['zh-CN'] = { + days: ["星期日", "星期一", "星期二", "星期三", "星期四", "星期五", "星期六", "星期日"], + daysShort: ["周日", "周一", "周二", "周三", "周四", "周五", "周六", "周日"], + daysMin: ["日", "一", "二", "三", "四", "五", "六", "日"], + months: ["一月", "二月", "三月", "四月", "五月", "六月", "七月", "八月", "九月", "十月", "十一月", "十二月"], + monthsShort: ["一月", "二月", "三月", "四月", "五月", "六月", "七月", "八月", "九月", "十月", "十一月", "十二月"], + today: "今天", + suffix: [], + meridiem: ["上午", "下午"] + }; +}(jQuery)); diff --git a/app/assets/stylesheets/admin.scss b/app/assets/stylesheets/admin.scss index 67947ce2c..756a5e241 100644 --- a/app/assets/stylesheets/admin.scss +++ b/app/assets/stylesheets/admin.scss @@ -7,6 +7,7 @@ @import "bootstrap-datepicker.standalone"; @import "jquery.mloading"; @import "jquery-confirm.min"; +@import "bootstrap-datetimepicker.min"; @import "codemirror/lib/codemirror"; @import "editormd/css/editormd.min"; diff --git a/app/assets/stylesheets/bootstrap-datetimepicker.min.css b/app/assets/stylesheets/bootstrap-datetimepicker.min.css new file mode 100755 index 000000000..78485fee7 --- /dev/null +++ b/app/assets/stylesheets/bootstrap-datetimepicker.min.css @@ -0,0 +1,9 @@ +/*! + * Datetimepicker for Bootstrap + * + * Copyright 2012 Stefan Petre + * Improvements by Andrew Rowls + * Licensed under the Apache License v2.0 + * http://www.apache.org/licenses/LICENSE-2.0 + * + */.datetimepicker{padding:4px;margin-top:1px;-webkit-border-radius:4px;-moz-border-radius:4px;border-radius:4px;direction:ltr}.datetimepicker-inline{width:220px}.datetimepicker.datetimepicker-rtl{direction:rtl}.datetimepicker.datetimepicker-rtl table tr td span{float:right}.datetimepicker-dropdown,.datetimepicker-dropdown-left{top:0;left:0}[class*=" datetimepicker-dropdown"]:before{content:'';display:inline-block;border-left:7px solid transparent;border-right:7px solid transparent;border-bottom:7px solid #ccc;border-bottom-color:rgba(0,0,0,0.2);position:absolute}[class*=" datetimepicker-dropdown"]:after{content:'';display:inline-block;border-left:6px solid transparent;border-right:6px solid transparent;border-bottom:6px solid #fff;position:absolute}[class*=" datetimepicker-dropdown-top"]:before{content:'';display:inline-block;border-left:7px solid transparent;border-right:7px solid transparent;border-top:7px solid #ccc;border-top-color:rgba(0,0,0,0.2);border-bottom:0}[class*=" datetimepicker-dropdown-top"]:after{content:'';display:inline-block;border-left:6px solid transparent;border-right:6px solid transparent;border-top:6px solid #fff;border-bottom:0}.datetimepicker-dropdown-bottom-left:before{top:-7px;right:6px}.datetimepicker-dropdown-bottom-left:after{top:-6px;right:7px}.datetimepicker-dropdown-bottom-right:before{top:-7px;left:6px}.datetimepicker-dropdown-bottom-right:after{top:-6px;left:7px}.datetimepicker-dropdown-top-left:before{bottom:-7px;right:6px}.datetimepicker-dropdown-top-left:after{bottom:-6px;right:7px}.datetimepicker-dropdown-top-right:before{bottom:-7px;left:6px}.datetimepicker-dropdown-top-right:after{bottom:-6px;left:7px}.datetimepicker>div{display:none}.datetimepicker.minutes div.datetimepicker-minutes{display:block}.datetimepicker.hours div.datetimepicker-hours{display:block}.datetimepicker.days div.datetimepicker-days{display:block}.datetimepicker.months div.datetimepicker-months{display:block}.datetimepicker.years div.datetimepicker-years{display:block}.datetimepicker table{margin:0}.datetimepicker td,.datetimepicker th{text-align:center;width:20px;height:20px;-webkit-border-radius:4px;-moz-border-radius:4px;border-radius:4px;border:0}.table-striped .datetimepicker table tr td,.table-striped .datetimepicker table tr th{background-color:transparent}.datetimepicker table tr td.minute:hover{background:#eee;cursor:pointer}.datetimepicker table tr td.hour:hover{background:#eee;cursor:pointer}.datetimepicker table tr td.day:hover{background:#eee;cursor:pointer}.datetimepicker table tr td.old,.datetimepicker table tr td.new{color:#999}.datetimepicker table tr td.disabled,.datetimepicker table tr td.disabled:hover{background:0;color:#999;cursor:default}.datetimepicker table tr td.today,.datetimepicker table tr td.today:hover,.datetimepicker table tr td.today.disabled,.datetimepicker table tr td.today.disabled:hover{background-color:#fde19a;background-image:-moz-linear-gradient(top,#fdd49a,#fdf59a);background-image:-ms-linear-gradient(top,#fdd49a,#fdf59a);background-image:-webkit-gradient(linear,0 0,0 100%,from(#fdd49a),to(#fdf59a));background-image:-webkit-linear-gradient(top,#fdd49a,#fdf59a);background-image:-o-linear-gradient(top,#fdd49a,#fdf59a);background-image:linear-gradient(to bottom,#fdd49a,#fdf59a);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#fdd49a',endColorstr='#fdf59a',GradientType=0);border-color:#fdf59a #fdf59a #fbed50;border-color:rgba(0,0,0,0.1) rgba(0,0,0,0.1) rgba(0,0,0,0.25);filter:progid:DXImageTransform.Microsoft.gradient(enabled=false)}.datetimepicker table tr td.today:hover,.datetimepicker table tr td.today:hover:hover,.datetimepicker table tr td.today.disabled:hover,.datetimepicker table tr td.today.disabled:hover:hover,.datetimepicker table tr td.today:active,.datetimepicker table tr td.today:hover:active,.datetimepicker table tr td.today.disabled:active,.datetimepicker table tr td.today.disabled:hover:active,.datetimepicker table tr td.today.active,.datetimepicker table tr td.today:hover.active,.datetimepicker table tr td.today.disabled.active,.datetimepicker table tr td.today.disabled:hover.active,.datetimepicker table tr td.today.disabled,.datetimepicker table tr td.today:hover.disabled,.datetimepicker table tr td.today.disabled.disabled,.datetimepicker table tr td.today.disabled:hover.disabled,.datetimepicker table tr td.today[disabled],.datetimepicker table tr td.today:hover[disabled],.datetimepicker table tr td.today.disabled[disabled],.datetimepicker table tr td.today.disabled:hover[disabled]{background-color:#fdf59a}.datetimepicker table tr td.today:active,.datetimepicker table tr td.today:hover:active,.datetimepicker table tr td.today.disabled:active,.datetimepicker table tr td.today.disabled:hover:active,.datetimepicker table tr td.today.active,.datetimepicker table tr td.today:hover.active,.datetimepicker table tr td.today.disabled.active,.datetimepicker table tr td.today.disabled:hover.active{background-color:#fbf069}.datetimepicker table tr td.active,.datetimepicker table tr td.active:hover,.datetimepicker table tr td.active.disabled,.datetimepicker table tr td.active.disabled:hover{background-color:#006dcc;background-image:-moz-linear-gradient(top,#08c,#04c);background-image:-ms-linear-gradient(top,#08c,#04c);background-image:-webkit-gradient(linear,0 0,0 100%,from(#08c),to(#04c));background-image:-webkit-linear-gradient(top,#08c,#04c);background-image:-o-linear-gradient(top,#08c,#04c);background-image:linear-gradient(to bottom,#08c,#04c);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#0088cc',endColorstr='#0044cc',GradientType=0);border-color:#04c #04c #002a80;border-color:rgba(0,0,0,0.1) rgba(0,0,0,0.1) rgba(0,0,0,0.25);filter:progid:DXImageTransform.Microsoft.gradient(enabled=false);color:#fff;text-shadow:0 -1px 0 rgba(0,0,0,0.25)}.datetimepicker table tr td.active:hover,.datetimepicker table tr td.active:hover:hover,.datetimepicker table tr td.active.disabled:hover,.datetimepicker table tr td.active.disabled:hover:hover,.datetimepicker table tr td.active:active,.datetimepicker table tr td.active:hover:active,.datetimepicker table tr td.active.disabled:active,.datetimepicker table tr td.active.disabled:hover:active,.datetimepicker table tr td.active.active,.datetimepicker table tr td.active:hover.active,.datetimepicker table tr td.active.disabled.active,.datetimepicker table tr td.active.disabled:hover.active,.datetimepicker table tr td.active.disabled,.datetimepicker table tr td.active:hover.disabled,.datetimepicker table tr td.active.disabled.disabled,.datetimepicker table tr td.active.disabled:hover.disabled,.datetimepicker table tr td.active[disabled],.datetimepicker table tr td.active:hover[disabled],.datetimepicker table tr td.active.disabled[disabled],.datetimepicker table tr td.active.disabled:hover[disabled]{background-color:#04c}.datetimepicker table tr td.active:active,.datetimepicker table tr td.active:hover:active,.datetimepicker table tr td.active.disabled:active,.datetimepicker table tr td.active.disabled:hover:active,.datetimepicker table tr td.active.active,.datetimepicker table tr td.active:hover.active,.datetimepicker table tr td.active.disabled.active,.datetimepicker table tr td.active.disabled:hover.active{background-color:#039}.datetimepicker table tr td span{display:block;width:23%;height:54px;line-height:54px;float:left;margin:1%;cursor:pointer;-webkit-border-radius:4px;-moz-border-radius:4px;border-radius:4px}.datetimepicker .datetimepicker-hours span{height:26px;line-height:26px}.datetimepicker .datetimepicker-hours table tr td span.hour_am,.datetimepicker .datetimepicker-hours table tr td span.hour_pm{width:14.6%}.datetimepicker .datetimepicker-hours fieldset legend,.datetimepicker .datetimepicker-minutes fieldset legend{margin-bottom:inherit;line-height:30px}.datetimepicker .datetimepicker-minutes span{height:26px;line-height:26px}.datetimepicker table tr td span:hover{background:#eee}.datetimepicker table tr td span.disabled,.datetimepicker table tr td span.disabled:hover{background:0;color:#999;cursor:default}.datetimepicker table tr td span.active,.datetimepicker table tr td span.active:hover,.datetimepicker table tr td span.active.disabled,.datetimepicker table tr td span.active.disabled:hover{background-color:#006dcc;background-image:-moz-linear-gradient(top,#08c,#04c);background-image:-ms-linear-gradient(top,#08c,#04c);background-image:-webkit-gradient(linear,0 0,0 100%,from(#08c),to(#04c));background-image:-webkit-linear-gradient(top,#08c,#04c);background-image:-o-linear-gradient(top,#08c,#04c);background-image:linear-gradient(to bottom,#08c,#04c);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#0088cc',endColorstr='#0044cc',GradientType=0);border-color:#04c #04c #002a80;border-color:rgba(0,0,0,0.1) rgba(0,0,0,0.1) rgba(0,0,0,0.25);filter:progid:DXImageTransform.Microsoft.gradient(enabled=false);color:#fff;text-shadow:0 -1px 0 rgba(0,0,0,0.25)}.datetimepicker table tr td span.active:hover,.datetimepicker table tr td span.active:hover:hover,.datetimepicker table tr td span.active.disabled:hover,.datetimepicker table tr td span.active.disabled:hover:hover,.datetimepicker table tr td span.active:active,.datetimepicker table tr td span.active:hover:active,.datetimepicker table tr td span.active.disabled:active,.datetimepicker table tr td span.active.disabled:hover:active,.datetimepicker table tr td span.active.active,.datetimepicker table tr td span.active:hover.active,.datetimepicker table tr td span.active.disabled.active,.datetimepicker table tr td span.active.disabled:hover.active,.datetimepicker table tr td span.active.disabled,.datetimepicker table tr td span.active:hover.disabled,.datetimepicker table tr td span.active.disabled.disabled,.datetimepicker table tr td span.active.disabled:hover.disabled,.datetimepicker table tr td span.active[disabled],.datetimepicker table tr td span.active:hover[disabled],.datetimepicker table tr td span.active.disabled[disabled],.datetimepicker table tr td span.active.disabled:hover[disabled]{background-color:#04c}.datetimepicker table tr td span.active:active,.datetimepicker table tr td span.active:hover:active,.datetimepicker table tr td span.active.disabled:active,.datetimepicker table tr td span.active.disabled:hover:active,.datetimepicker table tr td span.active.active,.datetimepicker table tr td span.active:hover.active,.datetimepicker table tr td span.active.disabled.active,.datetimepicker table tr td span.active.disabled:hover.active{background-color:#039}.datetimepicker table tr td span.old{color:#999}.datetimepicker th.switch{width:145px}.datetimepicker th span.glyphicon{pointer-events:none}.datetimepicker thead tr:first-child th,.datetimepicker tfoot th{cursor:pointer}.datetimepicker thead tr:first-child th:hover,.datetimepicker tfoot th:hover{background:#eee}.input-append.date .add-on i,.input-prepend.date .add-on i,.input-group.date .input-group-addon span{cursor:pointer;width:14px;height:14px} \ No newline at end of file