diff --git a/cypress/fixtures/datetime_doctype.js b/cypress/fixtures/datetime_doctype.js new file mode 100644 index 0000000000..b8c89ced5c --- /dev/null +++ b/cypress/fixtures/datetime_doctype.js @@ -0,0 +1,48 @@ +export default { + name: 'DateTime Test', + custom: 1, + actions: [], + creation: '2019-03-15 06:29:07.215072', + doctype: 'DocType', + editable_grid: 1, + engine: 'InnoDB', + fields: [ + { + fieldname: 'date', + fieldtype: 'Date', + label: 'Date' + }, + { + fieldname: 'time', + fieldtype: 'Time', + label: 'Time' + }, + { + fieldname: 'datetime', + fieldtype: 'Datetime', + label: 'Datetime' + } + ], + issingle: 1, + links: [], + modified: '2019-12-09 14:40:53.127615', + modified_by: 'Administrator', + module: 'Custom', + owner: 'Administrator', + permissions: [ + { + create: 1, + delete: 1, + email: 1, + print: 1, + read: 1, + role: 'System Manager', + share: 1, + write: 1 + } + ], + quick_entry: 1, + sort_field: 'modified', + sort_order: 'ASC', + track_changes: 1 +}; diff --git a/cypress/integration/api.js b/cypress/integration/api.js index 2db09aec1b..11e18d07c9 100644 --- a/cypress/integration/api.js +++ b/cypress/integration/api.js @@ -6,8 +6,8 @@ context('API Resources', () => { }); it('Creates two Comments', () => { - cy.create_doc('Comment', {comment_type: 'Comment', content: "hello"}); - cy.create_doc('Comment', {comment_type: 'Comment', content: "world"}); + cy.insert_doc('Comment', {comment_type: 'Comment', content: "hello"}); + cy.insert_doc('Comment', {comment_type: 'Comment', content: "world"}); }); it('Lists the Comments', () => { @@ -25,11 +25,11 @@ context('API Resources', () => { }); it('Gets each Comment', () => { - cy.get_list('Comment').then(body => body.data.forEach(comment => { + cy.get_list('Comment').then(body => body.data.forEach(comment => { cy.get_doc('Comment', comment.name); })); }); - + it('Removes the Comments', () => { cy.get_list('Comment').then(body => body.data.forEach(comment => { cy.remove_doc('Comment', comment.name); diff --git a/cypress/integration/datetime.js b/cypress/integration/datetime.js new file mode 100644 index 0000000000..90043434cd --- /dev/null +++ b/cypress/integration/datetime.js @@ -0,0 +1,128 @@ +import datetime_doctype from '../fixtures/datetime_doctype'; +const doctype_name = datetime_doctype.name; + +context('Control Date, Time and DateTime', () => { + before(() => { + cy.login(); + cy.visit('/desk'); + cy.insert_doc('DocType', datetime_doctype, true); + }); + + describe('Date formats', () => { + let date_formats = [ + { + date_format: 'dd-mm-yyyy', + part: 2, + length: 4, + separator: '-' + }, + { + date_format: 'mm/dd/yyyy', + part: 0, + length: 2, + separator: '/' + } + ]; + + date_formats.forEach(d => { + it('test date format ' + d.date_format, () => { + cy.set_value('System Settings', 'System Settings', { + date_format: d.date_format + }); + cy.window() + .its('frappe') + .then(frappe => { + // update sys_defaults value to avoid a reload + frappe.sys_defaults.date_format = d.date_format; + }); + + cy.new_form(doctype_name); + cy.get('.form-control[data-fieldname=date]').focus(); + cy.get('.datepickers-container .datepicker.active') + .should('be.visible'); + cy.get( + '.datepickers-container .datepicker.active .datepicker--cell-day.-current-' + ).click(); + + cy.window() + .its('cur_frm') + .then(cur_frm => { + let formatted_value = cur_frm.get_field('date').input.value; + let parts = formatted_value.split(d.separator); + expect(parts[d.part].length).to.equal(d.length); + }); + }); + }); + }); + + describe('Time formats', () => { + let time_formats = [ + { + time_format: 'HH:mm:ss', + value: ' 11:00:12', + match_value: '11:00:12' + }, + { + time_format: 'HH:mm', + value: ' 11:00:12', + match_value: '11:00' + } + ]; + + time_formats.forEach(d => { + it('test time format ' + d.time_format, () => { + cy.set_value('System Settings', 'System Settings', { + time_format: d.time_format + }); + cy.window() + .its('frappe') + .then(frappe => { + frappe.sys_defaults.time_format = d.time_format; + }); + cy.new_form(doctype_name); + cy.fill_field('time', d.value, 'Time').blur(); + cy.get_field('time').should('have.value', d.match_value); + }); + }); + }); + + describe('DateTime formats', () => { + let datetime_formats = [ + { + date_format: 'dd.mm.yyyy', + time_format: 'HH:mm:ss', + value: ' 02.12.2019 11:00:12', + doc_value: '2019-12-02 11:00:12', + input_value: '02.12.2019 11:00:12' + }, + { + date_format: 'mm-dd-yyyy', + time_format: 'HH:mm', + value: ' 12-02-2019 11:00:00', + doc_value: '2019-12-02 11:00:00', + input_value: '12-02-2019 11:00' + } + ]; + datetime_formats.forEach(d => { + it(`test datetime format ${d.date_format} ${d.time_format}`, () => { + cy.set_value('System Settings', 'System Settings', { + date_format: d.date_format, + time_format: d.time_format + }); + cy.window() + .its('frappe') + .then(frappe => { + frappe.sys_defaults.date_format = d.date_format; + frappe.sys_defaults.time_format = d.time_format; + }); + cy.new_form(doctype_name); + cy.fill_field('datetime', d.value, 'Datetime').blur(); + cy.get_field('datetime').should('have.value', d.input_value); + + cy.window() + .its('cur_frm.doc.datetime') + .should('eq', d.doc_value); + }); + }); + }); +}); diff --git a/cypress/integration/form.js b/cypress/integration/form.js index 455b577f9d..81b52c4421 100644 --- a/cypress/integration/form.js +++ b/cypress/integration/form.js @@ -16,10 +16,12 @@ context('Form', () => { cy.get('.primary-action').click(); cy.visit('/desk#List/ToDo'); cy.location('hash').should('eq', '#List/ToDo/List'); + cy.get('h1').should('be.visible').and('contain', 'To Do'); cy.get('.list-row').should('contain', 'this is a test todo'); }); it('navigates between documents with child table list filters applied', () => { cy.visit('/desk#List/Contact'); + cy.location('hash').should('eq', '#List/Contact/List'); cy.get('.tag-filters-area .btn:contains("Add Filter")').click(); cy.get('.fieldname-select-area').should('exist'); cy.get('.fieldname-select-area input').type('Number{enter}', { force: true }); diff --git a/cypress/support/commands.js b/cypress/support/commands.js index 41d9c16d7b..02f0bf236e 100644 --- a/cypress/support/commands.js +++ b/cypress/support/commands.js @@ -42,95 +42,156 @@ Cypress.Commands.add('login', (email, password) => { }); Cypress.Commands.add('call', (method, args) => { - return cy.window().its('frappe.csrf_token').then(csrf_token => { - return cy.request({ - url: `/api/method/${method}`, - method: 'POST', - body: args, - headers: { - 'Accept': 'application/json', - 'Content-Type': 'application/json', - 'X-Frappe-CSRF-Token': csrf_token - } - }).then(res => { - expect(res.status).eq(200); - return res.body; + return cy + .window() + .its('frappe.csrf_token') + .then(csrf_token => { + return cy + .request({ + url: `/api/method/${method}`, + method: 'POST', + body: args, + headers: { + Accept: 'application/json', + 'Content-Type': 'application/json', + 'X-Frappe-CSRF-Token': csrf_token + } + }) + .then(res => { + expect(res.status).eq(200); + return res.body; + }); }); - }); }); -Cypress.Commands.add('get_list', (doctype, fields=[], filters=[]) => { - return cy.window().its('frappe.csrf_token').then(csrf_token => { - return cy.request({ - method: 'GET', - url: `/api/resource/${doctype}?fields=${JSON.stringify(fields)}&filters=${JSON.stringify(filters)}`, - headers: { - 'Accept': 'application/json', - 'X-Frappe-CSRF-Token': csrf_token - } - }).then(res => { - expect(res.status).eq(200); - return res.body; +Cypress.Commands.add('get_list', (doctype, fields = [], filters = []) => { + filters = JSON.stringify(filters); + fields = JSON.stringify(fields); + let url = `/api/resource/${doctype}?fields=${fields}&filters=${filters}`; + return cy + .window() + .its('frappe.csrf_token') + .then(csrf_token => { + return cy + .request({ + method: 'GET', + url, + headers: { + Accept: 'application/json', + 'X-Frappe-CSRF-Token': csrf_token + } + }) + .then(res => { + expect(res.status).eq(200); + return res.body; + }); }); - }); }); Cypress.Commands.add('get_doc', (doctype, name) => { - return cy.window().its('frappe.csrf_token').then(csrf_token => { - return cy.request({ - method: 'GET', - url: `/api/resource/${doctype}/${name}`, - headers: { - 'Accept': 'application/json', - 'X-Frappe-CSRF-Token': csrf_token - } - }).then(res => { - expect(res.status).eq(200); - return res.body; + return cy + .window() + .its('frappe.csrf_token') + .then(csrf_token => { + return cy + .request({ + method: 'GET', + url: `/api/resource/${doctype}/${name}`, + headers: { + Accept: 'application/json', + 'X-Frappe-CSRF-Token': csrf_token + } + }) + .then(res => { + expect(res.status).eq(200); + return res.body; + }); }); - }); }); -Cypress.Commands.add('create_doc', (doctype, args) => { - return cy.window().its('frappe.csrf_token').then(csrf_token => { - return cy.request({ - method: 'POST', - url: `/api/resource/${doctype}`, - body: args, - headers: { - 'Accept': 'application/json', - 'Content-Type': 'application/json', - 'X-Frappe-CSRF-Token': csrf_token - } - }).then(res => { - expect(res.status).eq(200); - return res.body; +Cypress.Commands.add('insert_doc', (doctype, args, ignore_duplicate) => { + return cy + .window() + .its('frappe.csrf_token') + .then(csrf_token => { + return cy + .request({ + method: 'POST', + url: `/api/resource/${doctype}`, + body: args, + headers: { + Accept: 'application/json', + 'Content-Type': 'application/json', + 'X-Frappe-CSRF-Token': csrf_token + }, + failOnStatusCode: !ignore_duplicate + }) + .then(res => { + let status_codes = [200]; + if (ignore_duplicate) { + status_codes.push(409); + } + expect(res.status).to.be.oneOf(status_codes); + return res.body; + }); }); - }); }); Cypress.Commands.add('remove_doc', (doctype, name) => { - return cy.window().its('frappe.csrf_token').then(csrf_token => { - return cy.request({ - method: 'DELETE', - url: `/api/resource/${doctype}/${name}`, - headers: { - 'Accept': 'application/json', - 'X-Frappe-CSRF-Token': csrf_token - } - }).then(res => { - expect(res.status).eq(202); - return res.body; + return cy + .window() + .its('frappe.csrf_token') + .then(csrf_token => { + return cy + .request({ + method: 'DELETE', + url: `/api/resource/${doctype}/${name}`, + headers: { + Accept: 'application/json', + 'X-Frappe-CSRF-Token': csrf_token + } + }) + .then(res => { + expect(res.status).eq(202); + return res.body; + }); }); - }); }); -Cypress.Commands.add('create_records', (doc) => { - return cy.call('frappe.tests.ui_test_helpers.create_if_not_exists', { doc }) +Cypress.Commands.add('create_records', doc => { + return cy + .call('frappe.tests.ui_test_helpers.create_if_not_exists', { doc }) .then(r => r.message); }); -Cypress.Commands.add('fill_field', (fieldname, value, fieldtype='Data') => { +Cypress.Commands.add('set_value', (doctype, name, obj) => { + return cy.call('frappe.client.set_value', { + doctype, + name, + fieldname: obj + }); +}); + +Cypress.Commands.add('fill_field', (fieldname, value, fieldtype = 'Data') => { + cy.get_field(fieldname, fieldtype).as('input'); + + if (['Date', 'Time', 'Datetime'].includes(fieldtype)) { + cy.get('@input').click().wait(200); + cy.get('.datepickers-container .datepicker.active').should('exist'); + } + if (fieldtype === 'Time') { + cy.get('@input').clear(); + } + + if (fieldtype === 'Select') { + cy.get('@input').select(value); + } else { + cy.get('@input').type(value, { waitForAnimations: false }); + } + return cy.get('@input'); +}); + +Cypress.Commands.add('get_field', (fieldname, fieldtype = 'Data') => { let selector = `.form-control[data-fieldname="${fieldname}"]`; if (fieldtype === 'Text Editor') { @@ -140,34 +201,33 @@ Cypress.Commands.add('fill_field', (fieldname, value, fieldtype='Data') => { selector = `[data-fieldname="${fieldname}"] .ace_text-input`; } - cy.get(selector).as('input'); - - if (fieldtype === 'Select') { - return cy.get('@input').select(value); - } else { - return cy.get('@input').type(value, {waitForAnimations: false}); - } + return cy.get(selector); }); -Cypress.Commands.add('awesomebar', (text) => { +Cypress.Commands.add('awesomebar', text => { cy.get('#navbar-search').type(`${text}{downarrow}{enter}`, { delay: 100 }); }); -Cypress.Commands.add('new_form', (doctype) => { - cy.visit(`/desk#Form/${doctype}/New ${doctype} 1`); +Cypress.Commands.add('new_form', doctype => { + let route = `Form/${doctype}/New ${doctype} 1`; + cy.visit(`/desk#${route}`); + cy.get('body').should('have.attr', 'data-route', route); + cy.get('body').should('have.attr', 'data-ajax-state', 'complete'); }); -Cypress.Commands.add('go_to_list', (doctype) => { +Cypress.Commands.add('go_to_list', doctype => { cy.visit(`/desk#List/${doctype}/List`); }); Cypress.Commands.add('clear_cache', () => { - cy.window().its('frappe').then(frappe => { - frappe.ui.toolbar.clear_cache(); - }); + cy.window() + .its('frappe') + .then(frappe => { + frappe.ui.toolbar.clear_cache(); + }); }); -Cypress.Commands.add('dialog', (opts) => { +Cypress.Commands.add('dialog', opts => { return cy.window().then(win => { var d = new win.frappe.ui.Dialog(opts); d.show(); @@ -180,7 +240,9 @@ Cypress.Commands.add('get_open_dialog', () => { }); Cypress.Commands.add('hide_dialog', () => { - cy.get_open_dialog().find('.btn-modal-close').click(); + cy.get_open_dialog() + .find('.btn-modal-close') + .click(); cy.get('.modal:visible').should('not.exist'); }); diff --git a/frappe/core/doctype/system_settings/system_settings.json b/frappe/core/doctype/system_settings/system_settings.json index 9e8bc8c3fd..41a5a377db 100644 --- a/frappe/core/doctype/system_settings/system_settings.json +++ b/frappe/core/doctype/system_settings/system_settings.json @@ -14,6 +14,7 @@ "setup_complete", "date_and_number_format", "date_format", + "time_format", "column_break_7", "number_format", "float_precision", @@ -118,6 +119,14 @@ "options": "yyyy-mm-dd\ndd-mm-yyyy\ndd/mm/yyyy\ndd.mm.yyyy\nmm/dd/yyyy\nmm-dd-yyyy", "reqd": 1 }, + { + "default": "HH:mm:ss", + "fieldname": "time_format", + "fieldtype": "Select", + "label": "Time Format", + "options": "HH:mm:ss\nHH:mm", + "reqd": 1 + }, { "fieldname": "column_break_7", "fieldtype": "Column Break" @@ -420,4 +429,4 @@ "sort_field": "modified", "sort_order": "ASC", "track_changes": 1 -} \ No newline at end of file +} diff --git a/frappe/desk/page/setup_wizard/setup_wizard.py b/frappe/desk/page/setup_wizard/setup_wizard.py index a13b6c7a8d..43d4e8dde4 100755 --- a/frappe/desk/page/setup_wizard/setup_wizard.py +++ b/frappe/desk/page/setup_wizard/setup_wizard.py @@ -141,6 +141,7 @@ def update_system_settings(args): "time_zone": args.get("timezone"), "float_precision": 3, 'date_format': frappe.db.get_value("Country", args.get("country"), "date_format"), + 'time_format': frappe.db.get_value("Country", args.get("country"), "time_format"), 'number_format': number_format, 'enable_scheduler': 1 if not frappe.flags.in_test else 0, 'backup_limit': 3 # Default for downloadable backups diff --git a/frappe/geo/country_info.py b/frappe/geo/country_info.py index f30f96fba6..4f878325ad 100644 --- a/frappe/geo/country_info.py +++ b/frappe/geo/country_info.py @@ -10,8 +10,10 @@ from frappe.utils.momentjs import get_all_timezones def get_country_info(country=None): data = get_all() data = frappe._dict(data.get(country, {})) - if not 'date_format' in data: + if 'date_format' not in data: data.date_format = "dd-mm-yyyy" + if 'time_format' not in data: + data.time_format = "HH:mm:ss" return data diff --git a/frappe/geo/doctype/country/country.json b/frappe/geo/doctype/country/country.json index 37bcbfdaf5..a164611155 100644 --- a/frappe/geo/doctype/country/country.json +++ b/frappe/geo/doctype/country/country.json @@ -76,6 +76,12 @@ "translatable": 0, "unique": 0 }, + { + "fieldname": "time_format", + "fieldtype": "Data", + "in_list_view": 1, + "label": "Time format" + }, { "allow_bulk_edit": 0, "allow_in_quick_entry": 0, @@ -205,4 +211,4 @@ "track_changes": 1, "track_seen": 0, "track_views": 0 -} \ No newline at end of file +} diff --git a/frappe/public/build.json b/frappe/public/build.json index 76d6d74563..9085606caf 100755 --- a/frappe/public/build.json +++ b/frappe/public/build.json @@ -52,6 +52,22 @@ "website/js/bootstrap-4.js" ], "js/control.min.js": [ + "node_modules/air-datepicker/dist/js/datepicker.min.js", + "node_modules/air-datepicker/dist/js/i18n/datepicker.cs.js", + "node_modules/air-datepicker/dist/js/i18n/datepicker.da.js", + "node_modules/air-datepicker/dist/js/i18n/datepicker.de.js", + "node_modules/air-datepicker/dist/js/i18n/datepicker.en.js", + "node_modules/air-datepicker/dist/js/i18n/datepicker.es.js", + "node_modules/air-datepicker/dist/js/i18n/datepicker.fi.js", + "node_modules/air-datepicker/dist/js/i18n/datepicker.fr.js", + "node_modules/air-datepicker/dist/js/i18n/datepicker.hu.js", + "node_modules/air-datepicker/dist/js/i18n/datepicker.nl.js", + "node_modules/air-datepicker/dist/js/i18n/datepicker.pl.js", + "node_modules/air-datepicker/dist/js/i18n/datepicker.pt-BR.js", + "node_modules/air-datepicker/dist/js/i18n/datepicker.pt.js", + "node_modules/air-datepicker/dist/js/i18n/datepicker.ro.js", + "node_modules/air-datepicker/dist/js/i18n/datepicker.sk.js", + "node_modules/air-datepicker/dist/js/i18n/datepicker.zh.js", "public/js/frappe/ui/capture.js", "public/js/frappe/form/controls/control.js" ], @@ -114,8 +130,6 @@ "public/js/lib/socket.io.min.js", "public/js/lib/jSignature.min.js", "public/js/frappe/translate.js", - "public/js/lib/datepicker/datepicker.min.js", - "public/js/lib/datepicker/locale-all.js", "public/js/lib/leaflet/leaflet.js", "public/js/lib/leaflet/leaflet.draw.js", "public/js/lib/leaflet/L.Control.Locate.js", @@ -314,9 +328,7 @@ ], "js/web_form.min.js": [ "public/js/frappe/utils/datetime.js", - "public/js/frappe/web_form/webform_script.js", - "public/js/lib/datepicker/datepicker.min.js", - "public/js/lib/datepicker/datepicker.en.js" + "public/js/frappe/web_form/webform_script.js" ], "css/web_form.css": [ "public/less/list.less", diff --git a/frappe/public/js/frappe/form/controls/date.js b/frappe/public/js/frappe/form/controls/date.js index dc194c7a6a..da214029be 100644 --- a/frappe/public/js/frappe/form/controls/date.js +++ b/frappe/public/js/frappe/form/controls/date.js @@ -1,14 +1,17 @@ - frappe.ui.form.ControlDate = frappe.ui.form.ControlData.extend({ make_input: function() { this._super(); + this.make_picker(); + }, + make_picker: function() { this.set_date_options(); this.set_datepicker(); this.set_t_for_today(); }, set_formatted_input: function(value) { this._super(value); + if (this.timepicker_only) return; if (!this.datepicker) return; if(!value) { this.datepicker.clear(); @@ -71,19 +74,6 @@ frappe.ui.form.ControlDate = frappe.ui.form.ControlData.extend({ } }; }, - update_datepicker_position: function() { - if(!this.frm) return; - // show datepicker above or below the input - // based on scroll position - var window_height = $(window).height(); - var window_scroll_top = $(window).scrollTop(); - var el_offset_top = this.$input.offset().top + 280; - var position = 'top left'; - if(window_height + window_scroll_top >= el_offset_top) { - position = 'bottom left'; - } - this.datepicker.update('position', position); - }, set_datepicker: function() { this.$input.datepicker(this.datepicker_options); this.datepicker = this.$input.data('datepicker'); @@ -96,6 +86,29 @@ frappe.ui.form.ControlDate = frappe.ui.form.ControlData.extend({ this.datepicker.selectDate(this.get_now_date()); }); }, + update_datepicker_position: function() { + if(!this.frm) return; + // show datepicker above or below the input + // based on scroll position + // We have to bodge around the timepicker getting its position + // wrong by 42px when opening upwards. + const $header = $('.page-head'); + const header_bottom = $header.position().top + $header.outerHeight(); + const picker_height = this.datepicker.$datepicker.outerHeight() + 12; + const picker_top = this.$input.offset().top - $(window).scrollTop() - picker_height; + + var position = 'top left'; + // 12 is the default datepicker.opts[offset] + if (picker_top <= header_bottom) { + position = 'bottom left'; + if (this.timepicker_only) this.datepicker.opts['offset'] = 12; + } else { + // To account for 42px incorrect positioning + if (this.timepicker_only) this.datepicker.opts['offset'] = -30; + } + + this.datepicker.update('position', position); + }, get_now_date: function() { return frappe.datetime.now_date(true); }, diff --git a/frappe/public/js/frappe/form/controls/datetime.js b/frappe/public/js/frappe/form/controls/datetime.js index d525ad5715..b487be6eca 100644 --- a/frappe/public/js/frappe/form/controls/datetime.js +++ b/frappe/public/js/frappe/form/controls/datetime.js @@ -2,10 +2,13 @@ frappe.ui.form.ControlDatetime = frappe.ui.form.ControlDate.extend({ set_date_options: function() { this._super(); this.today_text = __("Now"); + let sysdefaults = frappe.boot.sysdefaults; this.date_format = frappe.defaultDatetimeFormat; + let time_format = sysdefaults && sysdefaults.time_format + ? sysdefaults.time_format : 'HH:mm:ss'; $.extend(this.datepicker_options, { timepicker: true, - timeFormat: "hh:ii:ss" + timeFormat: time_format.toLowerCase().replace("mm", "ii") }); }, get_now_date: function() { @@ -22,5 +25,15 @@ frappe.ui.form.ControlDatetime = frappe.ui.form.ControlDate.extend({ } } this._super(); + }, + set_datepicker: function() { + this._super(); + if (this.datepicker.opts.timeFormat.indexOf('s') == -1) { + // No seconds in time format + const $tp = this.datepicker.timepicker; + $tp.$seconds.parent().css('display', 'none'); + $tp.$secondsText.css('display', 'none'); + $tp.$secondsText.prev().css('display', 'none'); + } } }); diff --git a/frappe/public/js/frappe/form/controls/time.js b/frappe/public/js/frappe/form/controls/time.js index d7e20977d9..d6dcf9d33d 100644 --- a/frappe/public/js/frappe/form/controls/time.js +++ b/frappe/public/js/frappe/form/controls/time.js @@ -1,43 +1,72 @@ -frappe.ui.form.ControlTime = frappe.ui.form.ControlData.extend({ +frappe.ui.form.ControlTime = frappe.ui.form.ControlDate.extend({ + set_formatted_input: function(value) { + this._super(value); + }, make_input: function() { - var me = this; + this.timepicker_only = true; this._super(); - this.$input.datepicker({ + }, + make_picker: function() { + this.set_time_options(); + this.set_datepicker(); + this.refresh(); + }, + set_time_options: function() { + let sysdefaults = frappe.boot.sysdefaults; + + let time_format = sysdefaults && sysdefaults.time_format + ? sysdefaults.time_format : 'HH:mm:ss'; + + this.time_format = frappe.defaultTimeFormat; + this.datepicker_options = { language: "en", timepicker: true, onlyTimepicker: true, - timeFormat: "hh:ii:ss", + timeFormat: time_format.toLowerCase().replace("mm", "ii"), startDate: frappe.datetime.now_time(true), - onSelect: function() { + onSelect: () => { // ignore micro seconds - if (moment(me.get_value(), 'hh:mm:ss').format('HH:mm:ss') != moment(me.value, 'hh:mm:ss').format('HH:mm:ss')) { - me.$input.trigger('change'); - } + if (moment(this.get_value(), time_format).format('HH:mm:ss') != moment(this.value, time_format).format('HH:mm:ss')) { + this.$input.trigger('change'); + } }, - onShow: function() { + onShow: () => { $('.datepicker--button:visible').text(__('Now')); + + this.update_datepicker_position(); }, keyboardNav: false, todayButton: true - }); - this.datepicker = this.$input.data('datepicker'); - this.datepicker.$datepicker - .find('[data-action="today"]') - .click(() => { - this.datepicker.selectDate(frappe.datetime.now_time(true)); - }); - this.refresh(); + }; }, set_input: function(value) { this._super(value); - if(value + if (value && ((this.last_value && this.last_value !== this.value) || (!this.datepicker.selectedDates.length))) { - var date_obj = frappe.datetime.moment_to_date_obj(moment(value, 'HH:mm:ss')); + var date_obj = frappe.datetime.moment_to_date_obj(moment(value, frappe.sys_defaults['time_format'])); this.datepicker.selectDate(date_obj); } }, + set_datepicker: function() { + this.$input.datepicker(this.datepicker_options); + this.datepicker = this.$input.data('datepicker'); + + this.datepicker.$datepicker + .find('[data-action="today"]') + .click(() => { + this.datepicker.selectDate(frappe.datetime.now_time(true)); + this.datepicker.hide(); + }); + if (this.datepicker.opts.timeFormat.indexOf('s') == -1) { + // No seconds in time format + const $tp = this.datepicker.timepicker; + $tp.$seconds.parent().css('display', 'none'); + $tp.$secondsText.css('display', 'none'); + $tp.$secondsText.prev().css('display', 'none'); + } + }, set_description: function() { const { description } = this.df; const { time_zone } = frappe.sys_defaults; @@ -49,5 +78,26 @@ frappe.ui.form.ControlTime = frappe.ui.form.ControlData.extend({ } } this._super(); + }, + parse: function(value) { + if (value) { + return frappe.datetime.user_to_str(value, true); + } + }, + format_for_input: function(value) { + if (value) { + return frappe.datetime.str_to_user(value, true); + } + return ""; + }, + validate: function(value) { + if (value && !frappe.datetime.validate(value)) { + let sysdefaults = frappe.sys_defaults; + let time_format = sysdefaults && sysdefaults.time_format + ? sysdefaults.time_format : 'HH:mm:ss'; + frappe.msgprint(__("Time {0} must be in format: {1}", [value, time_format])); + return ''; + } + return value; } }); diff --git a/frappe/public/js/frappe/form/formatters.js b/frappe/public/js/frappe/form/formatters.js index a829d249be..ff573889e2 100644 --- a/frappe/public/js/frappe/form/formatters.js +++ b/frappe/public/js/frappe/form/formatters.js @@ -156,7 +156,8 @@ frappe.form.formatters = { if(frappe.boot.sysdefaults.time_zone) { m = m.tz(frappe.boot.sysdefaults.time_zone); } - return m.format(frappe.boot.sysdefaults.date_format.toUpperCase() + ', h:mm a z'); + return m.format(frappe.boot.sysdefaults.date_format.toUpperCase() + + ' ' + frappe.boot.sysdefaults.time_format); } else { return ""; } @@ -180,6 +181,13 @@ frappe.form.formatters = { return frappe.form.formatters.Data(value); }, + Time: function(value) { + if (value) { + value = frappe.datetime.str_to_user(value, true); + } + + return value || ""; + }, LikedBy: function(value) { var html = ""; $.each(JSON.parse(value || "[]"), function(i, v) { diff --git a/frappe/public/js/frappe/utils/datetime.js b/frappe/public/js/frappe/utils/datetime.js index 12badc5353..0c6fea2986 100644 --- a/frappe/public/js/frappe/utils/datetime.js +++ b/frappe/public/js/frappe/utils/datetime.js @@ -51,7 +51,7 @@ $.extend(frappe.datetime, { }, obj_to_user: function(d) { - return moment(d).format(frappe.datetime.get_user_fmt().toUpperCase()); + return moment(d).format(frappe.datetime.get_user_date_fmt().toUpperCase()); }, get_diff: function(d1, d2) { @@ -106,23 +106,32 @@ $.extend(frappe.datetime, { return moment().endOf("year").format(); }, - get_user_fmt: function() { + get_user_time_fmt: function() { + return frappe.sys_defaults && frappe.sys_defaults.time_format || "HH:mm:ss"; + }, + + get_user_date_fmt: function() { + return frappe.sys_defaults && frappe.sys_defaults.date_format || "yyyy-mm-dd"; + }, + + get_user_fmt: function() { // For backwards compatibility only return frappe.sys_defaults && frappe.sys_defaults.date_format || "yyyy-mm-dd"; }, str_to_user: function(val, only_time = false) { if(!val) return ""; + var user_time_fmt = frappe.datetime.get_user_time_fmt(); if(only_time) { return moment(val, frappe.defaultTimeFormat) - .format(frappe.defaultTimeFormat); + .format(user_time_fmt); } - var user_fmt = frappe.datetime.get_user_fmt().toUpperCase(); + var user_date_fmt = frappe.datetime.get_user_date_fmt().toUpperCase(); if(typeof val !== "string" || val.indexOf(" ")===-1) { - return moment(val).format(user_fmt); + return moment(val).format(user_date_fmt); } else { - return moment(val, "YYYY-MM-DD HH:mm:ss").format(user_fmt + " HH:mm:ss"); + return moment(val, "YYYY-MM-DD HH:mm:ss").format(user_date_fmt + " " + user_time_fmt); } }, @@ -132,16 +141,17 @@ $.extend(frappe.datetime, { user_to_str: function(val, only_time = false) { + var user_time_fmt = frappe.datetime.get_user_time_fmt(); if(only_time) { - return moment(val, frappe.defaultTimeFormat) + return moment(val, user_time_fmt) .format(frappe.defaultTimeFormat); } - var user_fmt = frappe.datetime.get_user_fmt().toUpperCase(); + var user_fmt = frappe.datetime.get_user_date_fmt().toUpperCase(); var system_fmt = "YYYY-MM-DD"; if(val.indexOf(" ")!==-1) { - user_fmt += " HH:mm:ss"; + user_fmt += " " + user_time_fmt; system_fmt += " HH:mm:ss"; } diff --git a/frappe/public/js/lib/datepicker/datepicker.en.js b/frappe/public/js/lib/datepicker/datepicker.en.js deleted file mode 100644 index 1eaebc70b4..0000000000 --- a/frappe/public/js/lib/datepicker/datepicker.en.js +++ /dev/null @@ -1,12 +0,0 @@ -;(function ($) { $.fn.datepicker.language['en'] = { - days: [__('Sunday'), __('Monday'), __('Tuesday'), __('Wednesday'), __('Thursday'), __('Friday'), __('Saturday')], - daysShort: [__('Sun'), __('Mon'), __('Tue'), __('Wed'), __('Thu'), __('Fri'), __('Sat')], - daysMin: [__('Su'), __('Mo'), __('Tu'), __('We'), __('Th'), __('Fr'), __('Sa')], - 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')], - today: __('Today'), - clear: __('Clear'), - dateFormat: 'mm/dd/yyyy', - timeFormat: 'hh:ii aa', - firstDay: 0 -}; })(jQuery); \ No newline at end of file diff --git a/frappe/public/js/lib/datepicker/datepicker.min.css b/frappe/public/js/lib/datepicker/datepicker.min.css deleted file mode 100644 index 8b6cd1b801..0000000000 --- a/frappe/public/js/lib/datepicker/datepicker.min.css +++ /dev/null @@ -1 +0,0 @@ -.datepicker--cells{display:-ms-flexbox;display:flex;-ms-flex-wrap:wrap;flex-wrap:wrap}.datepicker--cell{border-radius:4px;box-sizing:border-box;cursor:pointer;display:-ms-flexbox;display:flex;position:relative;-ms-flex-align:center;align-items:center;-ms-flex-pack:center;justify-content:center;height:32px;z-index:1}.datepicker--cell.-focus-{background:#f0f0f0}.datepicker--cell.-current-{color:#4EB5E6}.datepicker--cell.-current-.-focus-{color:#4a4a4a}.datepicker--cell.-current-.-in-range-{color:#4EB5E6}.datepicker--cell.-in-range-{background:rgba(92,196,239,.1);color:#4a4a4a;border-radius:0}.datepicker--cell.-in-range-.-focus-{background-color:rgba(92,196,239,.2)}.datepicker--cell.-disabled-{cursor:default;color:#aeaeae}.datepicker--cell.-disabled-.-focus-{color:#aeaeae}.datepicker--cell.-disabled-.-in-range-{color:#a1a1a1}.datepicker--cell.-disabled-.-current-.-focus-{color:#aeaeae}.datepicker--cell.-range-from-{border:1px solid rgba(92,196,239,.5);background-color:rgba(92,196,239,.1);border-radius:4px 0 0 4px}.datepicker--cell.-range-to-{border:1px solid rgba(92,196,239,.5);background-color:rgba(92,196,239,.1);border-radius:0 4px 4px 0}.datepicker--cell.-selected-,.datepicker--cell.-selected-.-current-{color:#fff;background:#5cc4ef}.datepicker--cell.-range-from-.-range-to-{border-radius:4px}.datepicker--cell.-selected-{border:none}.datepicker--cell.-selected-.-focus-{background:#45bced}.datepicker--cell:empty{cursor:default}.datepicker--days-names{display:-ms-flexbox;display:flex;-ms-flex-wrap:wrap;flex-wrap:wrap;margin:8px 0 3px}.datepicker--day-name{color:#FF9A19;display:-ms-flexbox;display:flex;-ms-flex-align:center;align-items:center;-ms-flex-pack:center;justify-content:center;-ms-flex:1;flex:1;text-align:center;text-transform:uppercase;font-size:.8em}.-only-timepicker- .datepicker--content,.datepicker--body,.datepicker-inline .datepicker--pointer{display:none}.datepicker--cell-day{width:14.28571%}.datepicker--cells-months{height:170px}.datepicker--cell-month{width:33.33%;height:25%}.datepicker--cells-years,.datepicker--years{height:170px}.datepicker--cell-year{width:25%;height:33.33%}.datepickers-container{position:absolute;left:0;top:0}@media print{.datepickers-container{display:none}}.datepicker{background:#fff;border:1px solid #dbdbdb;box-shadow:0 4px 12px rgba(0,0,0,.15);border-radius:4px;box-sizing:content-box;font-family:Tahoma,sans-serif;font-size:14px;color:#4a4a4a;width:250px;position:absolute;left:-100000px;opacity:0;transition:opacity .3s ease,transform .3s ease,left 0s .3s;z-index:100}.datepicker.-from-top-{transform:translateY(-8px)}.datepicker.-from-right-{transform:translateX(8px)}.datepicker.-from-bottom-{transform:translateY(8px)}.datepicker.-from-left-{transform:translateX(-8px)}.datepicker.active{opacity:1;transform:translate(0);transition:opacity .3s ease,transform .3s ease,left 0s 0s}.datepicker-inline .datepicker{border-color:#d7d7d7;box-shadow:none;position:static;left:auto;right:auto;opacity:1;transform:none}.datepicker--content{box-sizing:content-box;padding:4px}.datepicker--pointer{position:absolute;background:#fff;border-top:1px solid #dbdbdb;border-right:1px solid #dbdbdb;width:10px;height:10px;z-index:-1}.datepicker--nav-action:hover,.datepicker--nav-title:hover{background:#f0f0f0}.-top-center- .datepicker--pointer,.-top-left- .datepicker--pointer,.-top-right- .datepicker--pointer{top:calc(100% - 4px);transform:rotate(135deg)}.-right-bottom- .datepicker--pointer,.-right-center- .datepicker--pointer,.-right-top- .datepicker--pointer{right:calc(100% - 4px);transform:rotate(225deg)}.-bottom-center- .datepicker--pointer,.-bottom-left- .datepicker--pointer,.-bottom-right- .datepicker--pointer{bottom:calc(100% - 4px);transform:rotate(315deg)}.-left-bottom- .datepicker--pointer,.-left-center- .datepicker--pointer,.-left-top- .datepicker--pointer{left:calc(100% - 4px);transform:rotate(45deg)}.-bottom-left- .datepicker--pointer,.-top-left- .datepicker--pointer{left:10px}.-bottom-right- .datepicker--pointer,.-top-right- .datepicker--pointer{right:10px}.-bottom-center- .datepicker--pointer,.-top-center- .datepicker--pointer{left:calc(50% - 10px / 2)}.-left-top- .datepicker--pointer,.-right-top- .datepicker--pointer{top:10px}.-left-bottom- .datepicker--pointer,.-right-bottom- .datepicker--pointer{bottom:10px}.-left-center- .datepicker--pointer,.-right-center- .datepicker--pointer{top:calc(50% - 10px / 2)}.datepicker--body.active{display:block}.datepicker--nav{display:-ms-flexbox;display:flex;-ms-flex-pack:justify;justify-content:space-between;border-bottom:1px solid #efefef;min-height:32px;padding:4px}.-only-timepicker- .datepicker--nav{display:none}.datepicker--nav-action,.datepicker--nav-title{display:-ms-flexbox;display:flex;cursor:pointer;-ms-flex-align:center;align-items:center;-ms-flex-pack:center;justify-content:center}.datepicker--nav-action{width:32px;border-radius:4px;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none}.datepicker--nav-action.-disabled-{visibility:hidden}.datepicker--nav-action svg{width:32px;height:32px}.datepicker--nav-action path{fill:none;stroke:#9c9c9c;stroke-width:2px}.datepicker--nav-title{border-radius:4px;padding:0 8px}.datepicker--nav-title i{font-style:normal;color:#9c9c9c;margin-left:5px}.datepicker--nav-title.-disabled-{cursor:default;background:0 0}.datepicker--buttons{display:-ms-flexbox;display:flex;padding:4px;border-top:1px solid #efefef}.datepicker--button{color:#4EB5E6;cursor:pointer;border-radius:4px;-ms-flex:1;flex:1;display:-ms-inline-flexbox;display:inline-flex;-ms-flex-pack:center;justify-content:center;-ms-flex-align:center;align-items:center;height:32px}.datepicker--button:hover{color:#4a4a4a;background:#f0f0f0}.datepicker--cell-day.-other-month-,.datepicker--cell-year.-other-decade-{color:#dedede}.datepicker--cell-day.-other-month-:hover,.datepicker--cell-year.-other-decade-:hover{color:#c5c5c5}.-disabled-.-focus-.datepicker--cell-day.-other-month-,.-disabled-.-focus-.datepicker--cell-year.-other-decade-{color:#dedede}.-selected-.datepicker--cell-day.-other-month-,.-selected-.datepicker--cell-year.-other-decade-{color:#fff;background:#a2ddf6}.-selected-.-focus-.datepicker--cell-day.-other-month-,.-selected-.-focus-.datepicker--cell-year.-other-decade-{background:#8ad5f4}.-in-range-.datepicker--cell-day.-other-month-,.-in-range-.datepicker--cell-year.-other-decade-{background-color:rgba(92,196,239,.1);color:#ccc}.-in-range-.-focus-.datepicker--cell-day.-other-month-,.-in-range-.-focus-.datepicker--cell-year.-other-decade-{background-color:rgba(92,196,239,.2)}.datepicker--cell-day.-other-month-:empty,.datepicker--cell-year.-other-decade-:empty{background:0 0;border:none}.datepicker--time{border-top:1px solid #efefef;display:-ms-flexbox;display:flex;-ms-flex-align:center;align-items:center;padding:4px;position:relative}.datepicker--time.-am-pm- .datepicker--time-sliders{-ms-flex:0 1 138px;flex:0 1 138px;max-width:138px}.-only-timepicker- .datepicker--time{border-top:none}.datepicker--time-sliders{-ms-flex:0 1 153px;flex:0 1 153px;margin-right:10px;max-width:153px}.datepicker--time-label{display:none;font-size:12px}.datepicker--time-current{display:-ms-flexbox;display:flex;-ms-flex-align:center;align-items:center;-ms-flex:1;flex:1;font-size:14px;text-align:center;margin:0 10px}.datepicker--time-current-colon{margin:0 2px 3px;line-height:1}.datepicker--time-current-hours,.datepicker--time-current-minutes,.datepicker--time-current-seconds{line-height:1;font-size:14px;font-family:"Century Gothic",CenturyGothic,AppleGothic,sans-serif;position:relative;z-index:1}.datepicker--time-current-hours:after,.datepicker--time-current-minutes:after,.datepicker--time-current-seconds:after{content:'';background:#f0f0f0;border-radius:4px;position:absolute;left:-2px;top:-3px;right:-2px;bottom:-2px;z-index:-1;opacity:0}.datepicker--time-current-hours.-focus-:after,.datepicker--time-current-minutes.-focus-:after,.datepicker--time-current-seconds.-focus-:after{opacity:1}.datepicker--time-current-ampm{text-transform:uppercase;-ms-flex-item-align:start;align-self:flex-start;color:#9c9c9c;margin-left:6px;font-size:11px;margin-bottom:1px}.datepicker--time-row{display:-ms-flexbox;display:flex;-ms-flex-align:center;align-items:center;font-size:11px;height:17px;background:linear-gradient(to right,#dedede,#dedede) left 50%/100% 1px no-repeat}.datepicker--time-row:first-child{margin-bottom:4px}.datepicker--time-row input[type=range]{background:0 0;cursor:pointer;-ms-flex:1;flex:1;height:100%;padding:0;margin:0;-webkit-appearance:none}.datepicker--time-row input[type=range]::-ms-tooltip{display:none}.datepicker--time-row input[type=range]:hover::-webkit-slider-thumb{border-color:#b8b8b8}.datepicker--time-row input[type=range]:hover::-moz-range-thumb{border-color:#b8b8b8}.datepicker--time-row input[type=range]:hover::-ms-thumb{border-color:#b8b8b8}.datepicker--time-row input[type=range]:focus{outline:0}.datepicker--time-row input[type=range]:focus::-webkit-slider-thumb{background:#5cc4ef;border-color:#5cc4ef}.datepicker--time-row input[type=range]:focus::-moz-range-thumb{background:#5cc4ef;border-color:#5cc4ef}.datepicker--time-row input[type=range]:focus::-ms-thumb{background:#5cc4ef;border-color:#5cc4ef}.datepicker--time-row input[type=range]::-webkit-slider-thumb{-webkit-appearance:none;box-sizing:border-box;height:12px;width:12px;border-radius:3px;border:1px solid #dedede;background:#fff;cursor:pointer;transition:background .2s;margin-top:-6px}.datepicker--time-row input[type=range]::-moz-range-thumb{box-sizing:border-box;height:12px;width:12px;border-radius:3px;border:1px solid #dedede;background:#fff;cursor:pointer;transition:background .2s}.datepicker--time-row input[type=range]::-ms-thumb{box-sizing:border-box;height:12px;width:12px;border-radius:3px;border:1px solid #dedede;background:#fff;cursor:pointer;transition:background .2s}.datepicker--time-row input[type=range]::-webkit-slider-runnable-track{border:none;height:1px;cursor:pointer;color:transparent;background:0 0}.datepicker--time-row input[type=range]::-moz-range-track{border:none;height:1px;cursor:pointer;color:transparent;background:0 0}.datepicker--time-row input[type=range]::-ms-track{border:none;height:1px;cursor:pointer;color:transparent;background:0 0}.datepicker--time-row input[type=range]::-ms-fill-lower{background:0 0}.datepicker--time-row input[type=range]::-ms-fill-upper{background:0 0}.datepicker--time-row span{padding:0 12px}.datepicker--time-icon{color:#9c9c9c;border:1px solid;border-radius:50%;font-size:16px;position:relative;margin:0 5px -1px 0;width:1em;height:1em}.datepicker--time-icon:after,.datepicker--time-icon:before{content:'';background:currentColor;position:absolute}.datepicker--time-icon:after{height:.4em;width:1px;left:calc(50% - 1px);top:calc(50% + 1px);transform:translateY(-100%)}.datepicker--time-icon:before{width:.4em;height:1px;top:calc(50% + 1px);left:calc(50% - 1px)} \ No newline at end of file diff --git a/frappe/public/js/lib/datepicker/datepicker.min.js b/frappe/public/js/lib/datepicker/datepicker.min.js deleted file mode 100644 index e08d89c220..0000000000 --- a/frappe/public/js/lib/datepicker/datepicker.min.js +++ /dev/null @@ -1,2 +0,0 @@ -!function(t,e,s){!function(){var i,a,n,h="2.2.3",o="datepicker",r=".datepicker-here",c=!1,d='
',l={classes:"",inline:!1,language:"ru",startDate:new Date,firstDay:"",weekends:[6,0],dateFormat:"",altField:"",altFieldDateFormat:"@",toggleSelected:!0,keyboardNav:!0,position:"bottom left",offset:12,view:"days",minView:"days",showOtherMonths:!0,selectOtherMonths:!0,moveToOtherMonthsOnSelect:!0,showOtherYears:!0,selectOtherYears:!0,moveToOtherYearsOnSelect:!0,minDate:"",maxDate:"",disableNavWhenOutOfRange:!0,multipleDates:!1,multipleDatesSeparator:",",range:!1,todayButton:!1,clearButton:!1,showEvent:"focus",autoClose:!1,monthsField:"monthsShort",prevHtml:'',nextHtml:'',navTitles:{days:"MM, yyyy",months:"yyyy",years:"yyyy1 - yyyy2"},timepicker:!1,onlyTimepicker:!1,dateTimeSeparator:" ",timeFormat:"",minHours:0,maxHours:24,minMinutes:0,maxMinutes:59,minSeconds:0,maxSeconds:59,hoursStep:1,minutesStep:1,secondsStep:1,onSelect:"",onShow:"",onHide:"",onChangeMonth:"",onChangeYear:"",onChangeDecade:"",onChangeView:"",onRenderCell:""},u={ctrlRight:[17,39],ctrlUp:[17,38],ctrlLeft:[17,37],ctrlDown:[17,40],shiftRight:[16,39],shiftUp:[16,38],shiftLeft:[16,37],shiftDown:[16,40],altUp:[18,38],altRight:[18,39],altLeft:[18,37],altDown:[18,40],ctrlShiftUp:[16,17,38]},m=function(t,a){this.el=t,this.$el=e(t),this.opts=e.extend(!0,{},l,a,this.$el.data()),i==s&&(i=e("body")),this.opts.startDate||(this.opts.startDate=new Date),"INPUT"==this.el.nodeName&&(this.elIsInput=!0),this.opts.altField&&(this.$altField="string"==typeof this.opts.altField?e(this.opts.altField):this.opts.altField),this.inited=!1,this.visible=!1,this.silent=!1,this.currentDate=this.opts.startDate,this.currentView=this.opts.view,this._createShortCuts(),this.selectedDates=[],this.views={},this.keys=[],this.minRange="",this.maxRange="",this._prevOnSelectValue="",this.init()};n=m,n.prototype={VERSION:h,viewIndexes:["days","months","years"],init:function(){c||this.opts.inline||!this.elIsInput||this._buildDatepickersContainer(),this._buildBaseHtml(),this._defineLocale(this.opts.language),this._syncWithMinMaxDates(),this.elIsInput&&(this.opts.inline||(this._setPositionClasses(this.opts.position),this._bindEvents()),this.opts.keyboardNav&&!this.opts.onlyTimepicker&&this._bindKeyboardEvents(),this.$datepicker.on("mousedown",this._onMouseDownDatepicker.bind(this)),this.$datepicker.on("mouseup",this._onMouseUpDatepicker.bind(this))),this.opts.classes&&this.$datepicker.addClass(this.opts.classes),this.opts.timepicker&&(this.timepicker=new e.fn.datepicker.Timepicker(this,this.opts),this._bindTimepickerEvents()),this.opts.onlyTimepicker&&this.$datepicker.addClass("-only-timepicker-"),this.views[this.currentView]=new e.fn.datepicker.Body(this,this.currentView,this.opts),this.views[this.currentView].show(),this.nav=new e.fn.datepicker.Navigation(this,this.opts),this.view=this.currentView,this.$el.on("clickCell.adp",this._onClickCell.bind(this)),this.$datepicker.on("mouseenter",".datepicker--cell",this._onMouseEnterCell.bind(this)),this.$datepicker.on("mouseleave",".datepicker--cell",this._onMouseLeaveCell.bind(this)),this.inited=!0},_createShortCuts:function(){this.minDate=this.opts.minDate?this.opts.minDate:new Date((-86399999136e5)),this.maxDate=this.opts.maxDate?this.opts.maxDate:new Date(86399999136e5)},_bindEvents:function(){this.$el.on(this.opts.showEvent+".adp",this._onShowEvent.bind(this)),this.$el.on("mouseup.adp",this._onMouseUpEl.bind(this)),this.$el.on("blur.adp",this._onBlur.bind(this)),this.$el.on("keyup.adp",this._onKeyUpGeneral.bind(this)),e(t).on("resize.adp",this._onResize.bind(this)),e("body").on("mouseup.adp",this._onMouseUpBody.bind(this))},_bindKeyboardEvents:function(){this.$el.on("keydown.adp",this._onKeyDown.bind(this)),this.$el.on("keyup.adp",this._onKeyUp.bind(this)),this.$el.on("hotKey.adp",this._onHotKey.bind(this))},_bindTimepickerEvents:function(){this.$el.on("timeChange.adp",this._onTimeChange.bind(this))},isWeekend:function(t){return this.opts.weekends.indexOf(t)!==-1},_defineLocale:function(t){"string"==typeof t?(this.loc=e.fn.datepicker.language[t],this.loc||(console.warn("Can't find language \""+t+'" in Datepicker.language, will use "ru" instead'),this.loc=e.extend(!0,{},e.fn.datepicker.language.ru)),this.loc=e.extend(!0,{},e.fn.datepicker.language.ru,e.fn.datepicker.language[t])):this.loc=e.extend(!0,{},e.fn.datepicker.language.ru,t),this.opts.dateFormat&&(this.loc.dateFormat=this.opts.dateFormat),this.opts.timeFormat&&(this.loc.timeFormat=this.opts.timeFormat),""!==this.opts.firstDay&&(this.loc.firstDay=this.opts.firstDay),this.opts.timepicker&&(this.loc.dateFormat=[this.loc.dateFormat,this.loc.timeFormat].join(this.opts.dateTimeSeparator)),this.opts.onlyTimepicker&&(this.loc.dateFormat=this.loc.timeFormat);var s=this._getWordBoundaryRegExp;(this.loc.timeFormat.match(s("aa"))||this.loc.timeFormat.match(s("AA")))&&(this.ampm=!0)},_buildDatepickersContainer:function(){c=!0,i.append('
'),a=e("#datepickers-container")},_buildBaseHtml:function(){var t,s=e('
');t="INPUT"==this.el.nodeName?this.opts.inline?s.insertAfter(this.$el):a:s.appendTo(this.$el),this.$datepicker=e(d).appendTo(t),this.$content=e(".datepicker--content",this.$datepicker),this.$nav=e(".datepicker--nav",this.$datepicker)},_triggerOnChange:function(){if(!this.selectedDates.length){if(""===this._prevOnSelectValue)return;return this._prevOnSelectValue="",this.opts.onSelect("","",this)}var t,e=this.selectedDates,s=n.getParsedDate(e[0]),i=this,a=new Date(s.year,s.month,s.date,s.hours,s.minutes,s.seconds);t=e.map(function(t){return i.formatDate(i.loc.dateFormat,t)}).join(this.opts.multipleDatesSeparator),(this.opts.multipleDates||this.opts.range)&&(a=e.map(function(t){var e=n.getParsedDate(t);return new Date(e.year,e.month,e.date,e.hours,e.minutes,e.seconds)})),this._prevOnSelectValue=t,this.opts.onSelect(t,a,this)},next:function(){var t=this.parsedDate,e=this.opts;switch(this.view){case"days":this.date=new Date(t.year,t.month+1,1),e.onChangeMonth&&e.onChangeMonth(this.parsedDate.month,this.parsedDate.year);break;case"months":this.date=new Date(t.year+1,t.month,1),e.onChangeYear&&e.onChangeYear(this.parsedDate.year);break;case"years":this.date=new Date(t.year+10,0,1),e.onChangeDecade&&e.onChangeDecade(this.curDecade)}},prev:function(){var t=this.parsedDate,e=this.opts;switch(this.view){case"days":this.date=new Date(t.year,t.month-1,1),e.onChangeMonth&&e.onChangeMonth(this.parsedDate.month,this.parsedDate.year);break;case"months":this.date=new Date(t.year-1,t.month,1),e.onChangeYear&&e.onChangeYear(this.parsedDate.year);break;case"years":this.date=new Date(t.year-10,0,1),e.onChangeDecade&&e.onChangeDecade(this.curDecade)}},formatDate:function(t,e){e=e||this.date;var s,i=t,a=this._getWordBoundaryRegExp,h=this.loc,o=n.getLeadingZeroNum,r=n.getDecade(e),c=n.getParsedDate(e),d=c.fullHours,l=c.hours,u=t.match(a("aa"))||t.match(a("AA")),m="am",p=this._replacer;switch(this.opts.timepicker&&this.timepicker&&u&&(s=this.timepicker._getValidHoursFromDate(e,u),d=o(s.hours),l=s.hours,m=s.dayPeriod),!0){case/@/.test(i):i=i.replace(/@/,e.getTime());case/aa/.test(i):i=p(i,a("aa"),m);case/AA/.test(i):i=p(i,a("AA"),m.toUpperCase());case/dd/.test(i):i=p(i,a("dd"),c.fullDate);case/d/.test(i):i=p(i,a("d"),c.date);case/DD/.test(i):i=p(i,a("DD"),h.days[c.day]);case/D/.test(i):i=p(i,a("D"),h.daysShort[c.day]);case/mm/.test(i):i=p(i,a("mm"),c.fullMonth);case/m/.test(i):i=p(i,a("m"),c.month+1);case/MM/.test(i):i=p(i,a("MM"),this.loc.months[c.month]);case/M/.test(i):i=p(i,a("M"),h.monthsShort[c.month]);case/ss/.test(i):i=p(i,a("ss"),c.seconds);case/ii/.test(i):i=p(i,a("ii"),c.fullMinutes);case/i/.test(i):i=p(i,a("i"),c.minutes);case/hh/.test(i):i=p(i,a("hh"),d);case/h/.test(i):i=p(i,a("h"),l);case/yyyy/.test(i):i=p(i,a("yyyy"),c.year);case/yyyy1/.test(i):i=p(i,a("yyyy1"),r[0]);case/yyyy2/.test(i):i=p(i,a("yyyy2"),r[1]);case/yy/.test(i):i=p(i,a("yy"),c.year.toString().slice(-2))}return i},_replacer:function(t,e,s){return t.replace(e,function(t,e,i,a){return e+s+a})},_getWordBoundaryRegExp:function(t){var e="\\s|\\.|-|/|\\\\|,|\\$|\\!|\\?|:|;";return new RegExp("(^|>|"+e+")("+t+")($|<|"+e+")","g")},selectDate:function(t){var e=this,s=e.opts,i=e.parsedDate,a=e.selectedDates,h=a.length,o="";if(Array.isArray(t))return void t.forEach(function(t){e.selectDate(t)});if(t instanceof Date){if(this.lastSelectedDate=t,this.timepicker&&this.timepicker._setTime(t),e._trigger("selectDate",t),this.timepicker&&(t.setHours(this.timepicker.hours),t.setMinutes(this.timepicker.minutes),t.setSeconds(this.timepicker.seconds)),"days"==e.view&&t.getMonth()!=i.month&&s.moveToOtherMonthsOnSelect&&(o=new Date(t.getFullYear(),t.getMonth(),1)),"years"==e.view&&t.getFullYear()!=i.year&&s.moveToOtherYearsOnSelect&&(o=new Date(t.getFullYear(),0,1)),o&&(e.silent=!0,e.date=o,e.silent=!1,e.nav._render()),s.multipleDates&&!s.range){if(h===s.multipleDates)return;e._isSelected(t)||e.selectedDates.push(t)}else s.range?2==h?(e.selectedDates=[t],e.minRange=t,e.maxRange=""):1==h?(e.selectedDates.push(t),e.maxRange?e.minRange=t:e.maxRange=t,n.bigger(e.maxRange,e.minRange)&&(e.maxRange=e.minRange,e.minRange=t),e.selectedDates=[e.minRange,e.maxRange]):(e.selectedDates=[t],e.minRange=t):e.selectedDates=[t];e._setInputValue(),s.onSelect&&e._triggerOnChange(),s.autoClose&&!this.timepickerIsActive&&(s.multipleDates||s.range?s.range&&2==e.selectedDates.length&&e.hide():e.hide()),e.views[this.currentView]._render()}},removeDate:function(t){var e=this.selectedDates,s=this;if(t instanceof Date)return e.some(function(i,a){if(n.isSame(i,t))return e.splice(a,1),s.selectedDates.length?s.lastSelectedDate=s.selectedDates[s.selectedDates.length-1]:(s.minRange="",s.maxRange="",s.lastSelectedDate=""),s.views[s.currentView]._render(),s._setInputValue(),s.opts.onSelect&&s._triggerOnChange(),!0})},today:function(){this.silent=!0,this.view=this.opts.minView,this.silent=!1,this.date=new Date,this.opts.todayButton instanceof Date&&this.selectDate(this.opts.todayButton)},clear:function(){this.selectedDates=[],this.minRange="",this.maxRange="",this.views[this.currentView]._render(),this._setInputValue(),this.opts.onSelect&&this._triggerOnChange()},update:function(t,s){var i=arguments.length,a=this.lastSelectedDate;return 2==i?this.opts[t]=s:1==i&&"object"==typeof t&&(this.opts=e.extend(!0,this.opts,t)),this._createShortCuts(),this._syncWithMinMaxDates(),this._defineLocale(this.opts.language),this.nav._addButtonsIfNeed(),this.opts.onlyTimepicker||this.nav._render(),this.views[this.currentView]._render(),this.elIsInput&&!this.opts.inline&&(this._setPositionClasses(this.opts.position),this.visible&&this.setPosition(this.opts.position)),this.opts.classes&&this.$datepicker.addClass(this.opts.classes),this.opts.onlyTimepicker&&this.$datepicker.addClass("-only-timepicker-"),this.opts.timepicker&&(a&&this.timepicker._handleDate(a),this.timepicker._updateRanges(),this.timepicker._updateCurrentTime(),a&&(a.setHours(this.timepicker.hours),a.setMinutes(this.timepicker.minutes),a.setSeconds(this.timepicker.seconds))),this._setInputValue(),this},_syncWithMinMaxDates:function(){var t=this.date.getTime();this.silent=!0,this.minTime>t&&(this.date=this.minDate),this.maxTime=this.minTime&&s<=this.maxTime,month:o>=this.minTime&&r<=this.maxTime,year:i.year>=a.year&&i.year<=h.year};return e?c[e]:c.day},_getDimensions:function(t){var e=t.offset();return{width:t.outerWidth(),height:t.outerHeight(),left:e.left,top:e.top}},_getDateFromCell:function(t){var e=this.parsedDate,i=t.data("year")||e.year,a=t.data("month")==s?e.month:t.data("month"),n=t.data("date")||1;return new Date(i,a,n)},_setPositionClasses:function(t){t=t.split(" ");var e=t[0],s=t[1],i="datepicker -"+e+"-"+s+"- -from-"+e+"-";this.visible&&(i+=" active"),this.$datepicker.removeAttr("class").addClass(i)},setPosition:function(t){t=t||this.opts.position;var e,s,i=this._getDimensions(this.$el),a=this._getDimensions(this.$datepicker),n=t.split(" "),h=this.opts.offset,o=n[0],r=n[1];switch(o){case"top":e=i.top-a.height-h;break;case"right":s=i.left+i.width+h;break;case"bottom":e=i.top+i.height+h;break;case"left":s=i.left-a.width-h}switch(r){case"top":e=i.top;break;case"right":s=i.left+i.width-a.width;break;case"bottom":e=i.top+i.height-a.height;break;case"left":s=i.left;break;case"center":/left|right/.test(o)?e=i.top+i.height/2-a.height/2:s=i.left+i.width/2-a.width/2}this.$datepicker.css({left:s,top:e})},show:function(){var t=this.opts.onShow;this.setPosition(this.opts.position),this.$datepicker.addClass("active"),this.visible=!0,t&&this._bindVisionEvents(t)},hide:function(){var t=this.opts.onHide;this.$datepicker.removeClass("active").css({left:"-100000px"}),this.focused="",this.keys=[],this.inFocus=!1,this.visible=!1,this.$el.blur(),t&&this._bindVisionEvents(t)},down:function(t){this._changeView(t,"down")},up:function(t){this._changeView(t,"up")},_bindVisionEvents:function(t){this.$datepicker.off("transitionend.dp"),t(this,!1),this.$datepicker.one("transitionend.dp",t.bind(this,this,!0))},_changeView:function(t,e){t=t||this.focused||this.date;var s="up"==e?this.viewIndex+1:this.viewIndex-1;s>2&&(s=2),s<0&&(s=0),this.silent=!0,this.date=new Date(t.getFullYear(),t.getMonth(),1),this.silent=!1,this.view=this.viewIndexes[s]},_handleHotKey:function(t){var e,s,i,a=n.getParsedDate(this._getFocusedDate()),h=this.opts,o=!1,r=!1,c=!1,d=a.year,l=a.month,u=a.date;switch(t){case"ctrlRight":case"ctrlUp":l+=1,o=!0;break;case"ctrlLeft":case"ctrlDown":l-=1,o=!0;break;case"shiftRight":case"shiftUp":r=!0,d+=1;break;case"shiftLeft":case"shiftDown":r=!0,d-=1;break;case"altRight":case"altUp":c=!0,d+=10;break;case"altLeft":case"altDown":c=!0,d-=10;break;case"ctrlShiftUp":this.up()}i=n.getDaysCount(new Date(d,l)),s=new Date(d,l,u),ithis.maxTime&&(s=this.maxDate),this.focused=s,e=n.getParsedDate(s),o&&h.onChangeMonth&&h.onChangeMonth(e.month,e.year),r&&h.onChangeYear&&h.onChangeYear(e.year),c&&h.onChangeDecade&&h.onChangeDecade(this.curDecade)},_registerKey:function(t){var e=this.keys.some(function(e){return e==t});e||this.keys.push(t)},_unRegisterKey:function(t){var e=this.keys.indexOf(t);this.keys.splice(e,1)},_isHotKeyPressed:function(){var t,e=!1,s=this,i=this.keys.sort();for(var a in u)t=u[a],i.length==t.length&&t.every(function(t,e){return t==i[e]})&&(s._trigger("hotKey",a),e=!0);return e},_trigger:function(t,e){this.$el.trigger(t,e)},_focusNextCell:function(t,e){e=e||this.cellType;var s=n.getParsedDate(this._getFocusedDate()),i=s.year,a=s.month,h=s.date;if(!this._isHotKeyPressed()){switch(t){case 37:"day"==e?h-=1:"","month"==e?a-=1:"","year"==e?i-=1:"";break;case 38:"day"==e?h-=7:"","month"==e?a-=3:"","year"==e?i-=4:"";break;case 39:"day"==e?h+=1:"","month"==e?a+=1:"","year"==e?i+=1:"";break;case 40:"day"==e?h+=7:"","month"==e?a+=3:"","year"==e?i+=4:""}var o=new Date(i,a,h);o.getTime()this.maxTime&&(o=this.maxDate),this.focused=o}},_getFocusedDate:function(){var t=this.focused||this.selectedDates[this.selectedDates.length-1],e=this.parsedDate;if(!t)switch(this.view){case"days":t=new Date(e.year,e.month,(new Date).getDate());break;case"months":t=new Date(e.year,e.month,1);break;case"years":t=new Date(e.year,0,1)}return t},_getCell:function(t,s){s=s||this.cellType;var i,a=n.getParsedDate(t),h='.datepicker--cell[data-year="'+a.year+'"]';switch(s){case"month":h='[data-month="'+a.month+'"]';break;case"day":h+='[data-month="'+a.month+'"][data-date="'+a.date+'"]'}return i=this.views[this.currentView].$el.find(h),i.length?i:e("")},destroy:function(){var t=this;t.$el.off(".adp").data("datepicker",""),t.selectedDates=[],t.focused="",t.views={},t.keys=[],t.minRange="",t.maxRange="",t.opts.inline||!t.elIsInput?t.$datepicker.closest(".datepicker-inline").remove():t.$datepicker.remove()},_handleAlreadySelectedDates:function(t,e){this.opts.range?this.opts.toggleSelected?this.removeDate(e):2!=this.selectedDates.length&&this._trigger("clickCell",e):this.opts.toggleSelected&&this.removeDate(e),this.opts.toggleSelected||(this.lastSelectedDate=t,this.opts.timepicker&&(this.timepicker._setTime(t),this.timepicker.update()))},_onShowEvent:function(t){this.visible||this.show()},_onBlur:function(){!this.inFocus&&this.visible&&this.hide()},_onMouseDownDatepicker:function(t){this.inFocus=!0},_onMouseUpDatepicker:function(t){this.inFocus=!1,t.originalEvent.inFocus=!0,t.originalEvent.timepickerFocus||this.$el.focus()},_onKeyUpGeneral:function(t){var e=this.$el.val();e||this.clear()},_onResize:function(){this.visible&&this.setPosition()},_onMouseUpBody:function(t){t.originalEvent.inFocus||this.visible&&!this.inFocus&&this.hide()},_onMouseUpEl:function(t){t.originalEvent.inFocus=!0,setTimeout(this._onKeyUpGeneral.bind(this),4)},_onKeyDown:function(t){var e=t.which;if(this._registerKey(e),e>=37&&e<=40&&(t.preventDefault(),this._focusNextCell(e)),13==e&&this.focused){if(this._getCell(this.focused).hasClass("-disabled-"))return;if(this.view!=this.opts.minView)this.down();else{var s=this._isSelected(this.focused,this.cellType);if(!s)return this.timepicker&&(this.focused.setHours(this.timepicker.hours),this.focused.setMinutes(this.timepicker.minutes),this.focused.setSeconds(this.timepicker.seconds)),void this.selectDate(this.focused);this._handleAlreadySelectedDates(s,this.focused)}}27==e&&this.hide()},_onKeyUp:function(t){var e=t.which;this._unRegisterKey(e)},_onHotKey:function(t,e){this._handleHotKey(e)},_onMouseEnterCell:function(t){var s=e(t.target).closest(".datepicker--cell"),i=this._getDateFromCell(s);this.silent=!0,this.focused&&(this.focused=""),s.addClass("-focus-"),this.focused=i,this.silent=!1,this.opts.range&&1==this.selectedDates.length&&(this.minRange=this.selectedDates[0],this.maxRange="",n.less(this.minRange,this.focused)&&(this.maxRange=this.minRange,this.minRange=""),this.views[this.currentView]._update())},_onMouseLeaveCell:function(t){var s=e(t.target).closest(".datepicker--cell");s.removeClass("-focus-"),this.silent=!0,this.focused="",this.silent=!1},_onTimeChange:function(t,e,s,i){var a=new Date,n=this.selectedDates,h=!1;n.length&&(h=!0,a=this.lastSelectedDate),a.setHours(e),a.setMinutes(s),a.setSeconds(i),h||this._getCell(a).hasClass("-disabled-")?(this._setInputValue(),this.opts.onSelect&&this._triggerOnChange()):this.selectDate(a)},_onClickCell:function(t,e){this.timepicker&&(e.setHours(this.timepicker.hours),e.setMinutes(this.timepicker.minutes),e.setSeconds(this.timepicker.seconds)),this.selectDate(e)},set focused(t){if(!t&&this.focused){var e=this._getCell(this.focused);e.length&&e.removeClass("-focus-")}this._focused=t,this.opts.range&&1==this.selectedDates.length&&(this.minRange=this.selectedDates[0],this.maxRange="",n.less(this.minRange,this._focused)&&(this.maxRange=this.minRange,this.minRange="")),this.silent||(this.date=t)},get focused(){return this._focused},get parsedDate(){return n.getParsedDate(this.date)},set date(t){if(t instanceof Date)return this.currentDate=t,this.inited&&!this.silent&&(this.views[this.view]._render(),this.nav._render(),this.visible&&this.elIsInput&&this.setPosition()),t},get date(){return this.currentDate},set view(t){if(this.viewIndex=this.viewIndexes.indexOf(t),!(this.viewIndex<0))return this.prevView=this.currentView,this.currentView=t,this.inited&&(this.views[t]?this.views[t]._render():this.views[t]=new e.fn.datepicker.Body(this,t,this.opts),this.views[this.prevView].hide(),this.views[t].show(),this.nav._render(),this.opts.onChangeView&&this.opts.onChangeView(t),this.elIsInput&&this.visible&&this.setPosition()),t},get view(){return this.currentView},get cellType(){return this.view.substring(0,this.view.length-1)},get minTime(){var t=n.getParsedDate(this.minDate);return new Date(t.year,t.month,t.date).getTime()},get maxTime(){var t=n.getParsedDate(this.maxDate);return new Date(t.year,t.month,t.date).getTime()},get curDecade(){return n.getDecade(this.date)}},n.getDaysCount=function(t){return new Date(t.getFullYear(),t.getMonth()+1,0).getDate()},n.getParsedDate=function(t){return{year:t.getFullYear(),month:t.getMonth(),fullMonth:t.getMonth()+1<10?"0"+(t.getMonth()+1):t.getMonth()+1,date:t.getDate(),fullDate:t.getDate()<10?"0"+t.getDate():t.getDate(),day:t.getDay(),hours:t.getHours(),fullHours:t.getHours()<10?"0"+t.getHours():t.getHours(),minutes:t.getMinutes(),fullMinutes:t.getMinutes()<10?"0"+t.getMinutes():t.getMinutes(),seconds:t.getSeconds()}},n.getDecade=function(t){var e=10*Math.floor(t.getFullYear()/10);return[e,e+9]},n.template=function(t,e){return t.replace(/#\{([\w]+)\}/g,function(t,s){if(e[s]||0===e[s])return e[s]})},n.isSame=function(t,e,s){if(!t||!e)return!1;var i=n.getParsedDate(t),a=n.getParsedDate(e),h=s?s:"day",o={day:i.date==a.date&&i.month==a.month&&i.year==a.year,month:i.month==a.month&&i.year==a.year,year:i.year==a.year};return o[h]},n.less=function(t,e,s){return!(!t||!e)&&e.getTime()t.getTime()},n.getLeadingZeroNum=function(t){return parseInt(t)<10?"0"+t:t},n.resetTime=function(t){if("object"==typeof t)return t=n.getParsedDate(t),new Date(t.year,t.month,t.date)},e.fn.datepicker=function(t){return this.each(function(){if(e.data(this,o)){var s=e.data(this,o);s.opts=e.extend(!0,s.opts,t),s.update()}else e.data(this,o,new m(this,t))})},e.fn.datepicker.Constructor=m,e.fn.datepicker.language={ru:{days:["Воскресенье","Понедельник","Вторник","Среда","Четверг","Пятница","Суббота"],daysShort:["Вос","Пон","Вто","Сре","Чет","Пят","Суб"],daysMin:["Вс","Пн","Вт","Ср","Чт","Пт","Сб"],months:["Январь","Февраль","Март","Апрель","Май","Июнь","Июль","Август","Сентябрь","Октябрь","Ноябрь","Декабрь"],monthsShort:["Янв","Фев","Мар","Апр","Май","Июн","Июл","Авг","Сен","Окт","Ноя","Дек"],today:"Сегодня",clear:"Очистить",dateFormat:"dd.mm.yyyy",timeFormat:"hh:ii",firstDay:1}},e(function(){e(r).datepicker()})}(),function(){var t={days:'
',months:'
',years:'
'},i=e.fn.datepicker,a=i.Constructor;i.Body=function(t,s,i){this.d=t,this.type=s,this.opts=i,this.$el=e(""),this.opts.onlyTimepicker||this.init()},i.Body.prototype={init:function(){this._buildBaseHtml(),this._render(),this._bindEvents()},_bindEvents:function(){this.$el.on("click",".datepicker--cell",e.proxy(this._onClickCell,this))},_buildBaseHtml:function(){this.$el=e(t[this.type]).appendTo(this.d.$content),this.$names=e(".datepicker--days-names",this.$el),this.$cells=e(".datepicker--cells",this.$el)},_getDayNamesHtml:function(t,e,i,a){return e=e!=s?e:t,i=i?i:"",a=a!=s?a:0,a>7?i:7==e?this._getDayNamesHtml(t,0,i,++a):(i+='
'+this.d.loc.daysMin[e]+"
",this._getDayNamesHtml(t,++e,i,++a))},_getCellContents:function(t,e){var s="datepicker--cell datepicker--cell-"+e,i=new Date,n=this.d,h=a.resetTime(n.minRange),o=a.resetTime(n.maxRange),r=n.opts,c=a.getParsedDate(t),d={},l=c.date;switch(e){case"day":n.isWeekend(c.day)&&(s+=" -weekend-"),c.month!=this.d.parsedDate.month&&(s+=" -other-month-",r.selectOtherMonths||(s+=" -disabled-"),r.showOtherMonths||(l=""));break;case"month":l=n.loc[n.opts.monthsField][c.month];break;case"year":var u=n.curDecade;l=c.year,(c.yearu[1])&&(s+=" -other-decade-",r.selectOtherYears||(s+=" -disabled-"),r.showOtherYears||(l=""))}return r.onRenderCell&&(d=r.onRenderCell(t,e)||{},l=d.html?d.html:l,s+=d.classes?" "+d.classes:""),r.range&&(a.isSame(h,t,e)&&(s+=" -range-from-"),a.isSame(o,t,e)&&(s+=" -range-to-"),1==n.selectedDates.length&&n.focused?((a.bigger(h,t)&&a.less(n.focused,t)||a.less(o,t)&&a.bigger(n.focused,t))&&(s+=" -in-range-"),a.less(o,t)&&a.isSame(n.focused,t)&&(s+=" -range-from-"),a.bigger(h,t)&&a.isSame(n.focused,t)&&(s+=" -range-to-")):2==n.selectedDates.length&&a.bigger(h,t)&&a.less(o,t)&&(s+=" -in-range-")),a.isSame(i,t,e)&&(s+=" -current-"),n.focused&&a.isSame(t,n.focused,e)&&(s+=" -focus-"),n._isSelected(t,e)&&(s+=" -selected-"),n._isInRange(t,e)&&!d.disabled||(s+=" -disabled-"),{html:l,classes:s}},_getDaysHtml:function(t){var e=a.getDaysCount(t),s=new Date(t.getFullYear(),t.getMonth(),1).getDay(),i=new Date(t.getFullYear(),t.getMonth(),e).getDay(),n=s-this.d.loc.firstDay,h=6-i+this.d.loc.firstDay;n=n<0?n+7:n,h=h>6?h-7:h;for(var o,r,c=-n+1,d="",l=c,u=e+h;l<=u;l++)r=t.getFullYear(),o=t.getMonth(),d+=this._getDayHtml(new Date(r,o,l));return d},_getDayHtml:function(t){var e=this._getCellContents(t,"day");return'
'+e.html+"
"},_getMonthsHtml:function(t){for(var e="",s=a.getParsedDate(t),i=0;i<12;)e+=this._getMonthHtml(new Date(s.year,i)),i++;return e},_getMonthHtml:function(t){var e=this._getCellContents(t,"month");return'
'+e.html+"
"},_getYearsHtml:function(t){var e=(a.getParsedDate(t),a.getDecade(t)),s=e[0]-1,i="",n=s;for(n;n<=e[1]+1;n++)i+=this._getYearHtml(new Date(n,0));return i},_getYearHtml:function(t){var e=this._getCellContents(t,"year");return'
'+e.html+"
"},_renderTypes:{days:function(){var t=this._getDayNamesHtml(this.d.loc.firstDay),e=this._getDaysHtml(this.d.currentDate);this.$cells.html(e),this.$names.html(t)},months:function(){var t=this._getMonthsHtml(this.d.currentDate);this.$cells.html(t)},years:function(){var t=this._getYearsHtml(this.d.currentDate);this.$cells.html(t)}},_render:function(){this.opts.onlyTimepicker||this._renderTypes[this.type].bind(this)()},_update:function(){var t,s,i,a=e(".datepicker--cell",this.$cells),n=this;a.each(function(a,h){s=e(this),i=n.d._getDateFromCell(e(this)),t=n._getCellContents(i,n.d.cellType),s.attr("class",t.classes)})},show:function(){this.opts.onlyTimepicker||(this.$el.addClass("active"),this.acitve=!0)},hide:function(){this.$el.removeClass("active"),this.active=!1},_handleClick:function(t){var e=t.data("date")||1,s=t.data("month")||0,i=t.data("year")||this.d.parsedDate.year,a=this.d;if(a.view!=this.opts.minView)return void a.down(new Date(i,s,e));var n=new Date(i,s,e),h=this.d._isSelected(n,this.d.cellType);return h?void a._handleAlreadySelectedDates.bind(a,h,n)():void a._trigger("clickCell",n)},_onClickCell:function(t){var s=e(t.target).closest(".datepicker--cell");s.hasClass("-disabled-")||this._handleClick.bind(this)(s)}}}(),function(){var t='
#{prevHtml}
#{title}
#{nextHtml}
',s='
',i='#{label}',a=e.fn.datepicker,n=a.Constructor;a.Navigation=function(t,e){this.d=t,this.opts=e,this.$buttonsContainer="",this.init()},a.Navigation.prototype={init:function(){this._buildBaseHtml(),this._bindEvents()},_bindEvents:function(){this.d.$nav.on("click",".datepicker--nav-action",e.proxy(this._onClickNavButton,this)),this.d.$nav.on("click",".datepicker--nav-title",e.proxy(this._onClickNavTitle,this)),this.d.$datepicker.on("click",".datepicker--button",e.proxy(this._onClickNavButton,this))},_buildBaseHtml:function(){this.opts.onlyTimepicker||this._render(),this._addButtonsIfNeed()},_addButtonsIfNeed:function(){this.opts.todayButton&&this._addButton("today"),this.opts.clearButton&&this._addButton("clear")},_render:function(){var s=this._getTitle(this.d.currentDate),i=n.template(t,e.extend({title:s},this.opts));this.d.$nav.html(i),"years"==this.d.view&&e(".datepicker--nav-title",this.d.$nav).addClass("-disabled-"),this.setNavStatus()},_getTitle:function(t){return this.d.formatDate(this.opts.navTitles[this.d.view],t)},_addButton:function(t){this.$buttonsContainer.length||this._addButtonsContainer();var s={action:t,label:this.d.loc[t]},a=n.template(i,s);e("[data-action="+t+"]",this.$buttonsContainer).length||this.$buttonsContainer.append(a)},_addButtonsContainer:function(){this.d.$datepicker.append(s),this.$buttonsContainer=e(".datepicker--buttons",this.d.$datepicker)},setNavStatus:function(){if((this.opts.minDate||this.opts.maxDate)&&this.opts.disableNavWhenOutOfRange){var t=this.d.parsedDate,e=t.month,s=t.year,i=t.date;switch(this.d.view){case"days":this.d._isInRange(new Date(s,e-1,1),"month")||this._disableNav("prev"),this.d._isInRange(new Date(s,e+1,1),"month")||this._disableNav("next");break;case"months":this.d._isInRange(new Date(s-1,e,i),"year")||this._disableNav("prev"),this.d._isInRange(new Date(s+1,e,i),"year")||this._disableNav("next");break;case"years":var a=n.getDecade(this.d.date);this.d._isInRange(new Date(a[0]-1,0,1),"year")||this._disableNav("prev"),this.d._isInRange(new Date(a[1]+1,0,1),"year")||this._disableNav("next")}}},_disableNav:function(t){e('[data-action="'+t+'"]',this.d.$nav).addClass("-disabled-")},_activateNav:function(t){e('[data-action="'+t+'"]',this.d.$nav).removeClass("-disabled-")},_onClickNavButton:function(t){var s=e(t.target).closest("[data-action]"),i=s.data("action");this.d[i]()},_onClickNavTitle:function(t){if(!e(t.target).hasClass("-disabled-"))return"days"==this.d.view?this.d.view="months":void(this.d.view="years")}}}(),function(){var t='
#{hourVisible} : #{minValue} : #{secValue}
',s=e.fn.datepicker,i=s.Constructor;s.Timepicker=function(t,e){this.d=t,this.opts=e,this.init()},s.Timepicker.prototype={init:function(){var t="input";this._setTime(this.d.date),this._buildHTML(),navigator.userAgent.match(/trident/gi)&&(t="change"),this.d.$el.on("selectDate",this._onSelectDate.bind(this)),this.$ranges.on(t,this._onChangeRange.bind(this)),this.$ranges.on("mouseup",this._onMouseUpRange.bind(this)), -this.$ranges.on("mousemove focus ",this._onMouseEnterRange.bind(this)),this.$ranges.on("mouseout blur",this._onMouseOutRange.bind(this))},_setTime:function(t){var e=i.getParsedDate(t);this._handleDate(t),this.hours=e.hourst.getHours()&&(this.minMinutes=this.opts.minMinutes)},_setMaxTimeFromDate:function(t){this.maxHours=t.getHours(),this.maxMinutes=t.getMinutes(),this.maxSeconds=t.getSeconds(),this.d.lastSelectedDate&&this.d.lastSelectedDate.getHours()t?0:i.minHours,this.minMinutes=i.minMinutes<0||i.minMinutes>e?0:i.minMinutes,this.maxHours=i.maxHours<0||i.maxHours>t?t:i.maxHours,this.maxMinutes=i.maxMinutes<0||i.maxMinutes>e?e:i.maxMinutes,this.minSeconds=i.minSeconds<0||i.minSeconds>s?0:i.minSeconds,this.maxSeconds=i.maxSeconds<0||i.maxSeconds>s?s:i.maxSeconds},_validateHoursMinutes:function(t){this.hoursthis.maxHours&&(this.hours=this.maxHours),this.minutesthis.maxMinutes&&(this.minutes=this.maxMinutes),this.secondsthis.maxSeconds&&(this.seconds=this.maxSeconds)},_buildHTML:function(){var s=i.getLeadingZeroNum,a={hourMin:this.minHours,hourMax:s(this.maxHours),hourStep:this.opts.hoursStep,hourValue:this.hours,hourVisible:s(this.displayHours),minMin:this.minMinutes,minMax:s(this.maxMinutes),minStep:this.opts.minutesStep,minValue:s(this.minutes),secMin:this.minSeconds,secMax:s(this.maxSeconds),secStep:this.opts.secondsStep,secValue:s(this.seconds)},n=i.template(t,a);this.$timepicker=e(n).appendTo(this.d.$datepicker),this.$ranges=e('[type="range"]',this.$timepicker),this.$hours=e('[name="hours"]',this.$timepicker),this.$minutes=e('[name="minutes"]',this.$timepicker),this.$seconds=e('[name="seconds"]',this.$timepicker),this.$hoursText=e(".datepicker--time-current-hours",this.$timepicker),this.$minutesText=e(".datepicker--time-current-minutes",this.$timepicker),this.$secondsText=e(".datepicker--time-current-seconds",this.$timepicker),this.d.ampm&&(this.$ampm=e('').appendTo(e(".datepicker--time-current",this.$timepicker)).html(this.dayPeriod),this.$timepicker.addClass("-am-pm-"))},_updateCurrentTime:function(){var t=i.getLeadingZeroNum(this.displayHours),e=i.getLeadingZeroNum(this.minutes),s=i.getLeadingZeroNum(this.seconds);this.$hoursText.html(t),this.$minutesText.html(e),this.$secondsText.html(s),this.d.ampm&&this.$ampm.html(this.dayPeriod)},_updateRanges:function(){this.$hours.attr({min:this.minHours,max:this.maxHours}).val(this.hours),this.$minutes.attr({min:this.minMinutes,max:this.maxMinutes}).val(this.minutes),this.$seconds.attr({min:this.minSeconds,max:this.maxSeconds}).val(this.seconds)},_handleDate:function(t){this._setDefaultMinMaxTime(),t&&(i.isSame(t,this.d.opts.minDate)?this._setMinTimeFromDate(this.d.opts.minDate):i.isSame(t,this.d.opts.maxDate)&&this._setMaxTimeFromDate(this.d.opts.maxDate)),this._validateHoursMinutes(t)},update:function(){this._updateRanges(),this._updateCurrentTime()},_getValidHoursFromDate:function(t,e){var s=t,a=t;t instanceof Date&&(s=i.getParsedDate(t),a=s.hours);var n=e||this.d.ampm,h="am";if(n)switch(!0){case 0==a:a=12;break;case 12==a:h="pm";break;case a>11:a-=12,h="pm"}return{hours:a,dayPeriod:h}},set hours(t){this._hours=t;var e=this._getValidHoursFromDate(t);this.displayHours=e.hours,this.dayPeriod=e.dayPeriod},get hours(){return this._hours},_onChangeRange:function(t){var s=e(t.target),i=s.attr("name");this.d.timepickerIsActive=!0,this[i]=s.val(),this._updateCurrentTime(),this.d._trigger("timeChange",[this.hours,this.minutes,this.seconds]),this._handleDate(this.d.lastSelectedDate),this.update()},_onSelectDate:function(t,e){this._handleDate(e),this.update()},_onMouseEnterRange:function(t){var s=e(t.target).attr("name");e(".datepicker--time-current-"+s,this.$timepicker).addClass("-focus-")},_onMouseOutRange:function(t){var s=e(t.target).attr("name");this.d.inFocus||e(".datepicker--time-current-"+s,this.$timepicker).removeClass("-focus-")},_onMouseUpRange:function(t){this.d.timepickerIsActive=!1}}}()}(window,jQuery); \ No newline at end of file diff --git a/frappe/public/js/lib/datepicker/locale-all.js b/frappe/public/js/lib/datepicker/locale-all.js deleted file mode 100644 index 0735fdb280..0000000000 --- a/frappe/public/js/lib/datepicker/locale-all.js +++ /dev/null @@ -1,263 +0,0 @@ -;(function ($) { $.fn.datepicker.language['ar'] = { - days: ['الأحد', 'الأثنين', 'الثلاثاء', 'الأربعاء', 'الخميس', 'الجمعه', 'السبت'], - daysShort: ['الأحد', 'الأثنين', 'الثلاثاء', 'الأربعاء', 'الخميس', 'الجمعه', 'السبت'], - daysMin: ['الأحد', 'الأثنين', 'الثلاثاء', 'الأربعاء', 'الخميس', 'الجمعه', 'السبت'], - months: ['يناير','فبراير','مارس','أبريل','مايو','يونيو', 'يوليو','أغسطس','سبتمبر','اكتوبر','نوفمبر','ديسمبر'], - monthsShort: ['يناير','فبراير','مارس','أبريل','مايو','يونيو', 'يوليو','أغسطس','سبتمبر','اكتوبر','نوفمبر','ديسمبر'], - today: 'اليوم', - clear: 'Clear', - dateFormat: 'dd/mm/yyyy', - timeFormat: 'hh:ii aa', - firstDay: 0 -}; })(jQuery); - -;(function ($) { $.fn.datepicker.language['cs'] = { - days: ['Neděle', 'Pondělí', 'Úterý', 'Středa', 'Čtvrtek', 'Pátek', 'Sobota'], - daysShort: ['Ne', 'Po', 'Út', 'St', 'Čt', 'Pá', 'So'], - daysMin: ['Ne', 'Po', 'Út', 'St', 'Čt', 'Pá', 'So'], - months: ['Leden', 'Únor', 'Březen', 'Duben', 'Květen', 'Červen', 'Červenec', 'Srpen', 'Září', 'Říjen', 'Listopad', 'Prosinec'], - monthsShort: ['Led', 'Úno', 'Bře', 'Dub', 'Kvě', 'Čvn', 'Čvc', 'Srp', 'Zář', 'Říj', 'Lis', 'Pro'], - today: 'Dnes', - clear: 'Vymazat', - dateFormat: 'dd.mm.yyyy', - timeFormat: 'hh:ii', - firstDay: 1 -}; })(jQuery); - -;(function ($) { $.fn.datepicker.language['da'] = { - days: ['Søndag', 'Mandag', 'Tirsdag', 'Onsdag', 'Torsdag', 'Fredag', 'Lørdag'], - daysShort: ['Søn', 'Man', 'Tir', 'Ons', 'Tor', 'Fre', 'Lør'], - daysMin: ['Sø', 'Ma', 'Ti', 'On', 'To', 'Fr', 'Lø'], - months: ['Januar','Februar','Marts','April','Maj','Juni', 'Juli','August','September','Oktober','November','December'], - monthsShort: ['Jan', 'Feb', 'Mar', 'Apr', 'Maj', 'Jun', 'Jul', 'Aug', 'Sep', 'Okt', 'Nov', 'Dec'], - today: 'I dag', - clear: 'Nulstil', - dateFormat: 'dd/mm/yyyy', - timeFormat: 'hh:ii', - firstDay: 1 -}; })(jQuery) - -;(function ($) { $.fn.datepicker.language['de'] = { - days: ['Sonntag', 'Montag', 'Dienstag', 'Mittwoch', 'Donnerstag', 'Freitag', 'Samstag'], - daysShort: ['Son', 'Mon', 'Die', 'Mit', 'Don', 'Fre', 'Sam'], - daysMin: ['So', 'Mo', 'Di', 'Mi', 'Do', 'Fr', 'Sa'], - months: ['Januar','Februar','März','April','Mai','Juni', 'Juli','August','September','Oktober','November','Dezember'], - monthsShort: ['Jan', 'Feb', 'Mär', 'Apr', 'Mai', 'Jun', 'Jul', 'Aug', 'Sep', 'Okt', 'Nov', 'Dez'], - today: 'Heute', - clear: 'Aufräumen', - dateFormat: 'dd.mm.yyyy', - timeFormat: 'hh:ii', - firstDay: 1 -}; })(jQuery); - -;(function ($) { $.fn.datepicker.language['en'] = { - days: ['Sunday', 'Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday'], - daysShort: ['Sun', 'Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat'], - daysMin: ['Su', 'Mo', 'Tu', 'We', 'Th', 'Fr', 'Sa'], - 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'], - today: 'Today', - clear: 'Clear', - dateFormat: 'dd/mm/yyyy', - timeFormat: 'hh:ii aa', - firstDay: 0 -}; })(jQuery); - -;(function ($) { $.fn.datepicker.language['en-GB'] = { - days: ['Sunday', 'Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday'], - daysShort: ['Sun', 'Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat'], - daysMin: ['Su', 'Mo', 'Tu', 'We', 'Th', 'Fr', 'Sa'], - 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'], - today: 'Today', - clear: 'Clear', - dateFormat: 'dd/mm/yyyy', - timeFormat: 'hh:ii aa', - firstDay: 1 -}; })(jQuery); - -;(function ($) { $.fn.datepicker.language['en-US'] = { - days: ['Sunday', 'Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday'], - daysShort: ['Sun', 'Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat'], - daysMin: ['Su', 'Mo', 'Tu', 'We', 'Th', 'Fr', 'Sa'], - 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'], - today: 'Today', - clear: 'Clear', - dateFormat: 'mm/dd/yyyy', - timeFormat: 'hh:ii aa', - firstDay: 0 -}; })(jQuery); - - -;(function ($) { $.fn.datepicker.language['es'] = { - days: ['Domingo', 'Lunes', 'Martes', 'Miércoles', 'Jueves', 'Viernes', 'Sábado'], - daysShort: ['Dom', 'Lun', 'Mar', 'Mie', 'Jue', 'Vie', 'Sab'], - daysMin: ['Do', 'Lu', 'Ma', 'Mi', 'Ju', 'Vi', 'Sa'], - months: ['Enero','Febrero','Marzo','Abril','Mayo','Junio', 'Julio','Augosto','Septiembre','Octubre','Noviembre','Diciembre'], - monthsShort: ['Ene', 'Feb', 'Mar', 'Abr', 'May', 'Jun', 'Jul', 'Ago', 'Sep', 'Oct', 'Nov', 'Dic'], - today: 'Hoy', - clear: 'Limpiar', - dateFormat: 'dd/mm/yyyy', - timeFormat: 'hh:ii aa', - firstDay: 1 -}; })(jQuery); - -;(function ($) { $.fn.datepicker.language['fi'] = { - days: ['Sunnuntai', 'Maanantai', 'Tiistai', 'Keskiviikko', 'Torstai', 'Perjantai', 'Lauantai'], - daysShort: ['Su', 'Ma', 'Ti', 'Ke', 'To', 'Pe', 'La'], - daysMin: ['Su', 'Ma', 'Ti', 'Ke', 'To', 'Pe', 'La'], - months: ['Tammikuu','Helmikuu','Maaliskuu','Huhtikuu','Toukokuu','Kesäkuu', 'Heinäkuu','Elokuu','Syyskuu','Lokakuu','Marraskuu','Joulukuu'], - monthsShort: ['Tammi', 'Helmi', 'Maalis', 'Huhti', 'Touko', 'Kesä', 'Heinä', 'Elo', 'Syys', 'Loka', 'Marras', 'Joulu'], - today: 'Tänään', - clear: 'Tyhjennä', - dateFormat: 'dd.mm.yyyy', - timeFormat: 'hh:ii', - firstDay: 1 -}; - })(jQuery); - - ;(function ($) { $.fn.datepicker.language['fr'] = { - days: ['Dimanche', 'Lundi', 'Mardi', 'Mercredi', 'Jeudi', 'Vendredi', 'Samedi'], - daysShort: ['Dim', 'Lun', 'Mar', 'Mer', 'Jeu', 'Ven', 'Sam'], - daysMin: ['Di', 'Lu', 'Ma', 'Me', 'Je', 'Ve', 'Sa'], - months: ['Janvier','Février','Mars','Avril','Mai','Juin', 'Juillet','Août','Septembre','Octobre','Novembre','Decembre'], - monthsShort: ['Jan', 'Fév', 'Mars', 'Avr', 'Mai', 'Juin', 'Juil', 'Août', 'Sep', 'Oct', 'Nov', 'Dec'], - today: "Aujourd'hui", - clear: 'Effacer', - dateFormat: 'dd/mm/yyyy', - timeFormat: 'hh:ii', - firstDay: 1 -}; })(jQuery); - -;(function ($) { $.fn.datepicker.language['gr'] = { - days: ['Κυριακή', 'Δευτέρα', 'Τρίτη', 'Τετάρτη', 'Πέμπτη', 'Παρασκευή', 'Σάββατο'], - daysShort: ['Κυρ', 'Δευ', 'Τρι', 'Τετ', 'Πεμ', 'Παρ', 'Σαβ'], - daysMin: ['Κυ', 'Δε', 'Τρ', 'Τε', 'Πε', 'Πα', 'Σα'], - months: ['Ιανουάριος', 'Φεβρουάριος', 'Μάρτιος', 'Απρίλιος', 'Μάιος', 'Ιούνιος', 'Ιούλιος', 'Αύγουστος', 'Σεπτέμβριος', 'Οκτώβριος', 'Νοέμβριος', 'Δεκέμβριος'], - monthsShort: ['Ιαν', 'Φεβ', 'Μαρ', 'Απρ', 'Μάι', 'Ι/ν', 'Ι/λ', 'Αυγ', 'Σεπ', 'Οκτ', 'Νοε', 'Δεκ'], - today: 'Σήμερα', - clear: 'Καθαρισμός', - dateFormat: 'dd/mm/yyyy', - timeFormat: 'hh:ii aa', - firstDay: 0 -}; })(jQuery); - -;(function ($) { ;(function ($) { $.fn.datepicker.language['hu'] = { - days: ['Vasárnap', 'Hétfő', 'Kedd', 'Szerda', 'Csütörtök', 'Péntek', 'Szombat'], - daysShort: ['Va', 'Hé', 'Ke', 'Sze', 'Cs', 'Pé', 'Szo'], - daysMin: ['V', 'H', 'K', 'Sz', 'Cs', 'P', 'Sz'], - months: ['Január', 'Február', 'Március', 'Április', 'Május', 'Június', 'Július', 'Augusztus', 'Szeptember', 'Október', 'November', 'December'], - monthsShort: ['Jan', 'Feb', 'Már', 'Ápr', 'Máj', 'Jún', 'Júl', 'Aug', 'Szep', 'Okt', 'Nov', 'Dec'], - today: 'Ma', - clear: 'Törlés', - dateFormat: 'yyyy-mm-dd', - timeFormat: 'hh:ii aa', - firstDay: 1 -}; })(jQuery); })(jQuery); - -;(function ($) { $.fn.datepicker.language['it'] = { - days: ['Domenica', 'Lunedì', 'Martedì', 'Mercoledì', 'Giovedì', 'Venerdì', 'Sabato'], - daysShort: ['Dom', 'Lun', 'Mar', 'Mer', 'Gio', 'Ven', 'Sab'], - daysMin: ['Do', 'Lu', 'Ma', 'Me', 'Gi', 'Ve', 'Sa'], - months: ['Gennaio','Febbraio','Marzo','Aprile','Maggio','Giugno','Luglio','Agosto', - 'Settembre','Ottobre','Novembre','Dicembre'], - monthsShort: ['Gen', 'Feb', 'Mar', 'Apr', 'Mag', 'Giu', 'Lug', 'Ago', 'Set', 'Ott', 'Nov', 'Dic'], - today: 'Oggi', - clear: 'Reset', - dateFormat: 'dd/mm/yyyy', - timeFormat: 'hh:ii', - firstDay: 1 -}; })(jQuery); - -;(function ($) { $.fn.datepicker.language['nl'] = { - days: ['zondag', 'maandag', 'dinsdag', 'woensdag', 'donderdag', 'vrijdag', 'zaterdag'], - daysShort: ['zo', 'ma', 'di', 'wo', 'do', 'vr', 'za'], - daysMin: ['zo', 'ma', 'di', 'wo', 'do', 'vr', 'za'], - months: ['Januari', 'Februari', 'Maart', 'April', 'Mei', 'Juni', 'Juli', 'Augustus', 'September', 'Oktober', 'November', 'December'], - monthsShort: ['Jan', 'Feb', 'Mrt', 'Apr', 'Mei', 'Jun', 'Jul', 'Aug', 'Sep', 'Okt', 'Nov', 'Dec'], - today: 'Vandaag', - clear: 'Legen', - dateFormat: 'dd-MM-yy', - timeFormat: 'hh:ii', - firstDay: 0 -}; })(jQuery); - -;(function ($) { $.fn.datepicker.language['pl'] = { - days: ['Niedziela', 'Poniedziałek', 'Wtorek', 'Środa', 'Czwartek', 'Piątek', 'Sobota'], - daysShort: ['Nie', 'Pon', 'Wto', 'Śro', 'Czw', 'Pią', 'Sob'], - daysMin: ['Nd', 'Pn', 'Wt', 'Śr', 'Czw', 'Pt', 'So'], - months: ['Styczeń','Luty','Marzec','Kwiecień','Maj','Czerwiec', 'Lipiec','Sierpień','Wrzesień','Październik','Listopad','Grudzień'], - monthsShort: ['Sty', 'Lut', 'Mar', 'Kwi', 'Maj', 'Cze', 'Lip', 'Sie', 'Wrz', 'Paź', 'Lis', 'Gru'], - today: 'Dzisiaj', - clear: 'Wyczyść', - dateFormat: 'yyyy-mm-dd', - timeFormat: 'hh:ii:aa', - firstDay: 1 -}; - })(jQuery); - - ;(function ($) { $.fn.datepicker.language['pt-BR'] = { - days: ['Domingo', 'Segunda', 'Terça', 'Quarta', 'Quinta', 'Sexta', 'Sábado'], - daysShort: ['Dom', 'Seg', 'Ter', 'Qua', 'Qui', 'Sex', 'Sab'], - daysMin: ['Do', 'Se', 'Te', 'Qu', 'Qu', 'Se', 'Sa'], - months: ['Janeiro', 'Fevereiro', 'Março', 'Abril', 'Maio', 'Junho', 'Julho', 'Agosto', 'Setembro', 'Outubro', 'Novembro', 'Dezembro'], - monthsShort: ['Jan', 'Fev', 'Mar', 'Abr', 'Mai', 'Jun', 'Jul', 'Ago', 'Set', 'Out', 'Nov', 'Dez'], - today: 'Hoje', - clear: 'Limpar', - dateFormat: 'dd/mm/yyyy', - timeFormat: 'hh:ii', - firstDay: 0 -}; })(jQuery); - -;(function ($) { $.fn.datepicker.language['pt'] = { - days: ['Domingo', 'Segunda', 'Terça', 'Quarta', 'Quinta', 'Sexta', 'Sábado'], - daysShort: ['Dom', 'Seg', 'Ter', 'Qua', 'Qui', 'Sex', 'Sab'], - daysMin: ['Do', 'Se', 'Te', 'Qa', 'Qi', 'Sx', 'Sa'], - months: ['Janeiro', 'Fevereiro', 'Março', 'Abril', 'Maio', 'Junho', 'Julho', 'Agosto', 'Setembro', 'Outubro', 'Novembro', 'Dezembro'], - monthsShort: ['Jan', 'Fev', 'Mar', 'Abr', 'Mai', 'Jun', 'Jul', 'Ago', 'Set', 'Out', 'Nov', 'Dez'], - today: 'Hoje', - clear: 'Limpar', - dateFormat: 'dd/mm/yyyy', - timeFormat: 'hh:ii', - firstDay: 1 -}; })(jQuery); - -;(function ($) { $.fn.datepicker.language['ro'] = { - days: ['Duminică', 'Luni', 'Marţi', 'Miercuri', 'Joi', 'Vineri', 'Sâmbătă'], - daysShort: ['Dum', 'Lun', 'Mar', 'Mie', 'Joi', 'Vin', 'Sâm'], - daysMin: ['D', 'L', 'Ma', 'Mi', 'J', 'V', 'S'], - months: ['Ianuarie','Februarie','Martie','Aprilie','Mai','Iunie','Iulie','August','Septembrie','Octombrie','Noiembrie','Decembrie'], - monthsShort: ['Ian', 'Feb', 'Mar', 'Apr', 'Mai', 'Iun', 'Iul', 'Aug', 'Sept', 'Oct', 'Nov', 'Dec'], - today: 'Azi', - clear: 'Şterge', - dateFormat: 'dd.mm.yyyy', - timeFormat: 'hh:ii', - firstDay: 1 -}; })(jQuery); - -;(function ($) { $.fn.datepicker.language['sk'] = { - days: ['Nedeľa', 'Pondelok', 'Utorok', 'Streda', 'Štvrtok', 'Piatok', 'Sobota'], - daysShort: ['Ned', 'Pon', 'Uto', 'Str', 'Štv', 'Pia', 'Sob'], - daysMin: ['Ne', 'Po', 'Ut', 'St', 'Št', 'Pi', 'So'], - months: ['Január','Február','Marec','Apríl','Máj','Jún', 'Júl','August','September','Október','November','December'], - monthsShort: ['Jan', 'Feb', 'Mar', 'Apr', 'Máj', 'Jún', 'Jul', 'Aug', 'Sep', 'Okt', 'Nov', 'Dec'], - today: 'Dnes', - clear: 'Vymazať', - dateFormat: 'dd.mm.yyyy', - timeFormat: 'hh:ii', - firstDay: 1 -}; })(jQuery); - -;(function ($) { $.fn.datepicker.language['zh'] = { - days: ['周日', '周一', '周二', '周三', '周四', '周五', '周六'], - daysShort: ['日', '一', '二', '三', '四', '五', '六'], - daysMin: ['日', '一', '二', '三', '四', '五', '六'], - months: ['一月', '二月', '三月', '四月', '五月', '六月', '七月', '八月', '九月', '十月', '十一月', '十二月'], - monthsShort: ['一月', '二月', '三月', '四月', '五月', '六月', '七月', '八月', '九月', '十月', '十一月', '十二月'], - today: '今天', - clear: '清除', - dateFormat: 'yyyy-mm-dd', - timeFormat: 'hh:ii', - firstDay: 1 -}; })(jQuery); diff --git a/frappe/public/less/datepicker.less b/frappe/public/less/datepicker.less index e3e60490ca..427d0315e8 100644 --- a/frappe/public/less/datepicker.less +++ b/frappe/public/less/datepicker.less @@ -1,5 +1,5 @@ @import "variables.less"; -@import (less) "../js/lib/datepicker/datepicker.min.css"; +@import (less) "../../../node_modules/air-datepicker/dist/css/datepicker.min.css"; .datepicker { font-family: inherit; diff --git a/frappe/tests/test_fmt_datetime.py b/frappe/tests/test_fmt_datetime.py new file mode 100644 index 0000000000..20f2af88ba --- /dev/null +++ b/frappe/tests/test_fmt_datetime.py @@ -0,0 +1,163 @@ +# Copyright (c) 2019, Frappe Technologies Pvt. Ltd. and Contributors +# MIT License. See license.txt +from __future__ import unicode_literals + +import datetime + +import frappe +from frappe.utils import ( + getdate, get_datetime, get_time, + get_user_date_format, get_user_time_format, + formatdate, format_datetime, format_time) +import unittest + +test_date_obj = datetime.datetime.now() +test_date = test_date_obj.strftime('%Y-%m-%d') +test_time = test_date_obj.strftime('%H:%M:%S.%f') +test_datetime = test_date_obj.strftime('%Y-%m-%d %H:%M:%S.%f') +test_date_formats = { + 'yyyy-mm-dd': test_date_obj.strftime('%Y-%m-%d'), + 'dd-mm-yyyy': test_date_obj.strftime('%d-%m-%Y'), + 'dd/mm/yyyy': test_date_obj.strftime('%d/%m/%Y'), + 'dd.mm.yyyy': test_date_obj.strftime('%d.%m.%Y'), + 'mm/dd/yyyy': test_date_obj.strftime('%m/%d/%Y'), + 'mm-dd-yyyy': test_date_obj.strftime('%m-%d-%Y')} +test_time_formats = { + 'HH:mm:ss': test_date_obj.strftime('%H:%M:%S'), + 'HH:mm': test_date_obj.strftime('%H:%M')} + + +class TestFmtDatetime(unittest.TestCase): + """Tests date, time and datetime formatters and some associated + utility functions. These rely on the system-wide date and time + formats. + """ + + # Set up and tidy up routines + + def setUp(self): + # create test domain + self.pre_test_date_format = frappe.db.get_default("date_format") + self.pre_test_time_format = frappe.db.get_default("time_format") + + def tearDown(self): + frappe.db.set_default("date_format", self.pre_test_date_format) + frappe.db.set_default("time_format", self.pre_test_time_format) + frappe.local.user_date_format = None + frappe.local.user_time_format = None + + # Test utility functions + + def test_set_default_date_format(self): + frappe.db.set_default("date_format", "ZYX321") + self.assertEqual(frappe.db.get_default("date_format"), "ZYX321") + + def test_set_default_time_format(self): + frappe.db.set_default("time_format", "XYZ123") + self.assertEqual(frappe.db.get_default("time_format"), "XYZ123") + + def test_get_functions(self): + # Test round-trip through getdate, get_datetime and get_time + self.assertEqual(test_date_obj, get_datetime(test_datetime)) + self.assertEqual(test_date_obj.date(), getdate(test_date)) + self.assertEqual(test_date_obj.time(), get_time(test_time)) + + # Test date formatters + + def test_formatdate_forced(self): + # Test with forced date formats + self.assertEqual( + formatdate(test_date, 'dd-yyyy-mm'), + test_date_obj.strftime('%d-%Y-%m')) + self.assertEqual( + formatdate(test_date, 'dd-yyyy-MM'), + test_date_obj.strftime('%d-%Y-%m')) + + def test_formatdate_forced_broken_locale(self): + # Test with forced date formats + lang = frappe.local.lang + # Force fallback from Babel + try: + frappe.local.lang = 'FAKE' + self.assertEqual( + formatdate(test_date, 'dd-yyyy-mm'), + test_date_obj.strftime('%d-%Y-%m')) + self.assertEqual( + formatdate(test_date, 'dd-yyyy-MM'), + test_date_obj.strftime('%d-%Y-%m')) + finally: + frappe.local.lang = lang + + def test_format_date(self): + # Test formatdate with various default date formats set + for fmt, valid_fmt in test_date_formats.items(): + frappe.db.set_default("date_format", fmt) + frappe.local.user_date_format = None + self.assertEqual(get_user_date_format(), fmt) + self.assertEqual(formatdate(test_date), valid_fmt) + + # Test time formatters + + def test_format_time_forced(self): + # Test with forced time formats + self.assertEqual( + format_time(test_time, 'ss:mm:HH'), + test_date_obj.strftime('%S:%M:%H')) + + @unittest.expectedFailure + def test_format_time_forced_broken_locale(self): + # Test with forced time formats + # Currently format_time defaults to HH:mm:ss if the locale is + # broken, so this is an expected failure. + lang = frappe.local.lang + try: + # Force fallback from Babel + frappe.local.lang = 'FAKE' + self.assertEqual( + format_time(test_time, 'ss:mm:HH'), + test_date_obj.strftime('%S:%M:%H')) + finally: + frappe.local.lang = lang + + def test_format_time(self): + # Test format_time with various default time formats set + for fmt, valid_fmt in test_time_formats.items(): + frappe.db.set_default("time_format", fmt) + frappe.local.user_time_format = None + self.assertEqual(get_user_time_format(), fmt) + self.assertEqual(format_time(test_time), valid_fmt) + + # Test datetime formatters + + def test_format_datetime_forced(self): + # Test with forced date formats + self.assertEqual( + format_datetime(test_datetime, 'dd-yyyy-MM ss:mm:HH'), + test_date_obj.strftime('%d-%Y-%m %S:%M:%H')) + + @unittest.expectedFailure + def test_format_datetime_forced_broken_locale(self): + # Test with forced datetime formats + # Currently format_datetime defaults to yyyy-MM-dd HH:mm:ss + # if the locale is broken, so this is an expected failure. + lang = frappe.local.lang + # Force fallback from Babel + try: + frappe.local.lang = 'FAKE' + self.assertEqual( + format_datetime(test_datetime, 'dd-yyyy-MM ss:mm:HH'), + test_date_obj.strftime('%d-%Y-%m %S:%M:%H')) + finally: + frappe.local.lang = lang + + def test_format_datetime(self): + # Test formatdate with various default date formats set + for date_fmt, valid_date in test_date_formats.items(): + frappe.db.set_default("date_format", date_fmt) + frappe.local.user_date_format = None + for time_fmt, valid_time in test_time_formats.items(): + frappe.db.set_default("time_format", time_fmt) + frappe.local.user_time_format = None + valid_fmt = valid_date + ' ' + valid_time + self.assertEqual( + format_datetime(test_datetime), valid_fmt) diff --git a/frappe/utils/data.py b/frappe/utils/data.py index 77d43b3778..866b91ef72 100644 --- a/frappe/utils/data.py +++ b/frappe/utils/data.py @@ -209,22 +209,31 @@ def get_datetime_str(datetime_obj): datetime_obj = get_datetime(datetime_obj) return datetime_obj.strftime(DATETIME_FORMAT) -def get_user_format(): - if getattr(frappe.local, "user_format", None) is None: - frappe.local.user_format = frappe.db.get_default("date_format") +def get_user_date_format(): + """Get the current user date format. The result will be cached.""" + if getattr(frappe.local, "user_date_format", None) is None: + frappe.local.user_date_format = frappe.db.get_default("date_format") - return frappe.local.user_format or "yyyy-mm-dd" + return frappe.local.user_date_format or "yyyy-mm-dd" -def formatdate(string_date=None, format_string=None): - """ - Converts the given string date to :data:`user_format` - User format specified in defaults +get_user_format = get_user_date_format # for backwards compatibility + +def get_user_time_format(): + """Get the current user time format. The result will be cached.""" + if getattr(frappe.local, "user_time_format", None) is None: + frappe.local.user_time_format = frappe.db.get_default("time_format") + + return frappe.local.user_time_format or "HH:mm:ss" + +def format_date(string_date=None, format_string=None): + """Converts the given string date to :data:`user_date_format` + User format specified in defaults - Examples: + Examples: - * dd-mm-yyyy - * mm-dd-yyyy - * dd/mm/yyyy + * dd-mm-yyyy + * mm-dd-yyyy + * dd/mm/yyyy """ if not string_date: @@ -232,29 +241,60 @@ def formatdate(string_date=None, format_string=None): date = getdate(string_date) if not format_string: - format_string = get_user_format() + format_string = get_user_date_format() format_string = format_string.replace("mm", "MM") try: - formatted_date = babel.dates.format_date(date, format_string, locale=(frappe.local.lang or "").replace("-", "_")) + formatted_date = babel.dates.format_date( + date, format_string, + locale=(frappe.local.lang or "").replace("-", "_")) except UnknownLocaleError: format_string = format_string.replace("MM", "%m").replace("dd", "%d").replace("yyyy", "%Y") formatted_date = date.strftime(format_string) return formatted_date -def format_time(txt): +formatdate = format_date # For backwards compatibility + +def format_time(time_string=None, format_string=None): + """Converts the given string time to :data:`user_time_format` + User format specified in defaults + + Examples: + + * HH:mm:ss + * HH:mm + """ + + if not time_string: + return '' + + time_ = get_time(time_string) + if not format_string: + format_string = get_user_time_format() try: - formatted_time = babel.dates.format_time(get_time(txt), locale=(frappe.local.lang or "").replace("-", "_")) + formatted_time = babel.dates.format_time( + time_, format_string, + locale=(frappe.local.lang or "").replace("-", "_")) except UnknownLocaleError: - formatted_time = get_time(txt).strftime("%H:%M:%S") + formatted_time = time_.strftime("%H:%M:%S") return formatted_time def format_datetime(datetime_string, format_string=None): + """Converts the given string time to :data:`user_datetime_format` + User format specified in defaults + + Examples: + + * dd-mm-yyyy HH:mm:ss + * mm-dd-yyyy HH:mm + """ if not datetime_string: return datetime = get_datetime(datetime_string) if not format_string: - format_string = get_user_format().replace("mm", "MM") + " HH:mm:ss" + format_string = ( + get_user_date_format().replace("mm", "MM") + + ' ' + get_user_time_format()) try: formatted_datetime = babel.dates.format_datetime(datetime, format_string, locale=(frappe.local.lang or "").replace("-", "_")) @@ -363,14 +403,14 @@ def rounded(num, precision=0): # avoid rounding errors num = round(num * multiplier if precision else num, 8) - floor = math.floor(num) - decimal_part = num - floor + floor_num = math.floor(num) + decimal_part = num - floor_num if not precision and decimal_part == 0.5: - num = floor if (floor % 2 == 0) else floor + 1 + num = floor_num if (floor_num % 2 == 0) else floor_num + 1 else: if decimal_part == 0.5: - num = floor + 1 + num = floor_num + 1 else: num = round(num) diff --git a/frappe/utils/install.py b/frappe/utils/install.py index 809ec36e53..e5bf122d81 100644 --- a/frappe/utils/install.py +++ b/frappe/utils/install.py @@ -146,6 +146,7 @@ def add_country_and_currency(name, country): "country_name": name, "code": country.code, "date_format": country.date_format or "dd-mm-yyyy", + "time_format": country.time_format or "HH:mm:ss", "time_zones": "\n".join(country.timezones or []), "docstatus": 0 }).db_insert() diff --git a/frappe/utils/safe_exec.py b/frappe/utils/safe_exec.py index 9972c6a623..5bc2a3157a 100644 --- a/frappe/utils/safe_exec.py +++ b/frappe/utils/safe_exec.py @@ -32,8 +32,10 @@ def get_safe_globals(): datautils = frappe._dict() if frappe.db: date_format = frappe.db.get_default("date_format") or "yyyy-mm-dd" + time_format = frappe.db.get_default("time_format") or "HH:mm:ss" else: - date_format = 'yyyy-mm-dd' + date_format = "yyyy-mm-dd" + time_format = "HH:mm:ss" add_module_properties(frappe.utils.data, datautils, lambda obj: hasattr(obj, "__call__")) @@ -44,54 +46,55 @@ def get_safe_globals(): out = frappe._dict( # make available limited methods of frappe - json = json, - dict = dict, - frappe = frappe._dict( - _ = frappe._, - _dict = frappe._dict, - flags = frappe.flags, - - format = frappe.format_value, - format_value = frappe.format_value, - date_format = date_format, - format_date = frappe.utils.data.global_date_format, - form_dict = getattr(frappe.local, 'form_dict', {}), - - get_meta = frappe.get_meta, - get_doc = frappe.get_doc, - get_cached_doc = frappe.get_cached_doc, - get_list = frappe.get_list, - get_all = frappe.get_all, - get_system_settings = frappe.get_system_settings, - - utils = datautils, - get_url = frappe.utils.get_url, - render_template = frappe.render_template, - msgprint = frappe.msgprint, - - user = user, - get_fullname = frappe.utils.get_fullname, - get_gravatar = frappe.utils.get_gravatar_url, - full_name = frappe.local.session.data.full_name if getattr(frappe.local, "session", None) else "Guest", - request = getattr(frappe.local, 'request', {}), - session = frappe._dict( - user = user, - csrf_token = frappe.local.session.data.csrf_token if getattr(frappe.local, "session", None) else '' + json=json, + dict=dict, + frappe=frappe._dict( + _=frappe._, + _dict=frappe._dict, + flags=frappe.flags, + + format=frappe.format_value, + format_value=frappe.format_value, + date_format=date_format, + time_format=time_format, + format_date=frappe.utils.data.global_date_format, + form_dict=getattr(frappe.local, 'form_dict', {}), + + get_meta=frappe.get_meta, + get_doc=frappe.get_doc, + get_cached_doc=frappe.get_cached_doc, + get_list=frappe.get_list, + get_all=frappe.get_all, + get_system_settings=frappe.get_system_settings, + + utils=datautils, + get_url=frappe.utils.get_url, + render_template=frappe.render_template, + msgprint=frappe.msgprint, + + user=user, + get_fullname=frappe.utils.get_fullname, + get_gravatar=frappe.utils.get_gravatar_url, + full_name=frappe.local.session.data.full_name if getattr(frappe.local, "session", None) else "Guest", + request=getattr(frappe.local, 'request', {}), + session=frappe._dict( + user=user, + csrf_token=frappe.local.session.data.csrf_token if getattr(frappe.local, "session", None) else '' ), - socketio_port = frappe.conf.socketio_port, - get_hooks = frappe.get_hooks, + socketio_port=frappe.conf.socketio_port, + get_hooks=frappe.get_hooks, ), - style = frappe._dict( - border_color = '#d1d8dd' + style=frappe._dict( + border_color='#d1d8dd' ), - get_toc = get_toc, - get_next_link = get_next_link, - _ = frappe._, - get_shade = get_shade, - scrub = scrub, - guess_mimetype = mimetypes.guess_type, - html2text = html2text, - dev_server = 1 if os.environ.get('DEV_SERVER', False) else 0 + get_toc=get_toc, + get_next_link=get_next_link, + _=frappe._, + get_shade=get_shade, + scrub=scrub, + guess_mimetype=mimetypes.guess_type, + html2text=html2text, + dev_server=1 if os.environ.get('DEV_SERVER', False) else 0 ) add_module_properties(frappe.exceptions, out.frappe, lambda obj: inspect.isclass(obj) and issubclass(obj, Exception)) @@ -99,6 +102,7 @@ def get_safe_globals(): if not frappe.flags.in_setup_help: out.get_visible_columns = get_visible_columns out.frappe.date_format = date_format + out.frappe.time_format = time_format out.frappe.db = frappe._dict( get_list = frappe.get_list, get_all = frappe.get_all, diff --git a/package.json b/package.json index 4a29e2f5c2..e14045f862 100644 --- a/package.json +++ b/package.json @@ -19,6 +19,7 @@ "homepage": "https://frappe.io", "dependencies": { "ace-builds": "^1.4.1", + "air-datepicker": "http://github.com/frappe/air-datepicker", "awesomplete": "^1.1.2", "bootstrap": "^4.3.1", "cookie": "^0.3.1", diff --git a/yarn.lock b/yarn.lock index 7a59102a1e..18ac277fc2 100644 --- a/yarn.lock +++ b/yarn.lock @@ -210,6 +210,12 @@ after@0.8.2: resolved "https://registry.yarnpkg.com/after/-/after-0.8.2.tgz#fedb394f9f0e02aa9768e702bda23b505fae7e1f" integrity sha1-/ts5T58OAqqXaOcCvaI7UF+ufh8= +"air-datepicker@http://github.com/frappe/air-datepicker": + version "2.2.3" + resolved "http://github.com/frappe/air-datepicker#ed37b94d95c68d8544357e330be0c89d044a3eea" + dependencies: + jquery ">=2.0.0 <4.0.0" + ajv@^5.1.0: version "5.5.2" resolved "https://registry.yarnpkg.com/ajv/-/ajv-5.5.2.tgz#73b5eeca3fab653e3d3f9422b341ad42205dc965" @@ -2560,6 +2566,11 @@ jpeg-js@^0.3.2: resolved "https://registry.yarnpkg.com/jpeg-js/-/jpeg-js-0.3.6.tgz#c40382aac9506e7d1f2d856eb02f6c7b2a98b37c" integrity sha512-MUj2XlMB8kpe+8DJUGH/3UJm4XpI8XEgZQ+CiHDeyrGoKPdW/8FJv6ku+3UiYm5Fz3CWaL+iXmD8Q4Ap6aC1Jw== +"jquery@>=2.0.0 <4.0.0": + version "3.4.1" + resolved "https://registry.yarnpkg.com/jquery/-/jquery-3.4.1.tgz#714f1f8d9dde4bdfa55764ba37ef214630d80ef2" + integrity sha512-36+AdBzCL+y6qjw5Tx7HgzeGCzC81MDDgaUP8ld2zhx58HdqXGoBd+tHdrBMiyjGQs0Hxs/MLZTu/eHNJJuWPw== + js-base64@^2.1.8, js-base64@^2.1.9: version "2.5.1" resolved "https://registry.yarnpkg.com/js-base64/-/js-base64-2.5.1.tgz#1efa39ef2c5f7980bb1784ade4a8af2de3291121" @@ -5356,9 +5367,9 @@ yargs-parser@^5.0.0: camelcase "^3.0.0" yargs@^14.2: - version "14.2.0" - resolved "https://registry.yarnpkg.com/yargs/-/yargs-14.2.0.tgz#f116a9242c4ed8668790b40759b4906c276e76c3" - integrity sha512-/is78VKbKs70bVZH7w4YaZea6xcJWOAwkhbR0CFuZBmYtfTYF0xjGJF43AYd8g2Uii1yJwmS5GR2vBmrc32sbg== + version "14.2.2" + resolved "https://registry.yarnpkg.com/yargs/-/yargs-14.2.2.tgz#2769564379009ff8597cdd38fba09da9b493c4b5" + integrity sha512-/4ld+4VV5RnrynMhPZJ/ZpOCGSCeghMykZ3BhdFBDa9Wy/RH6uEGNWDJog+aUlq+9OM1CFTgtYRW5Is1Po9NOA== dependencies: cliui "^5.0.0" decamelize "^1.2.0"