diff --git a/Gemfile b/Gemfile index f5c9f305..fecaaa0e 100644 --- a/Gemfile +++ b/Gemfile @@ -18,6 +18,7 @@ end gem 'jquery-rails' gem 'select2-rails' +gem 'bootstrap-datepicker-rails' gem 'mysql2' diff --git a/Gemfile.lock b/Gemfile.lock index 34ab6761..6fb4d6cf 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -56,6 +56,8 @@ GEM coderay (>= 1.0.0) erubis (>= 2.7.0) binding_of_caller (0.6.8) + bootstrap-datepicker-rails (1.1.1.1) + railties (>= 3.0) builder (3.0.4) bullet (4.3.0) uniform_notifier @@ -264,6 +266,7 @@ DEPENDENCIES acts_as_versioned! better_errors binding_of_caller + bootstrap-datepicker-rails bullet capistrano (= 2.13.5) capistrano-ext diff --git a/app/assets/javascripts/application.js b/app/assets/javascripts/application.js index effc5d5d..91764a08 100644 --- a/app/assets/javascripts/application.js +++ b/app/assets/javascripts/application.js @@ -4,8 +4,9 @@ //= require select2 //= require twitter/bootstrap //= require jquery.tokeninput -//= require bootstrap-datepicker -//= require bootstrap-datepicker.de +//= require bootstrap-datepicker/core +//= require bootstrap-datepicker/locales/bootstrap-datepicker.de +//= require bootstrap-datepicker/locales/bootstrap-datepicker.nl //= require jquery.observe_field //= require rails.validations //= require_self @@ -141,7 +142,7 @@ $(function() { }); // Use bootstrap datepicker for dateinput - $('.datepicker').datepicker({format: 'yyyy-mm-dd', weekStart: 1, language: 'de'}); + $('.datepicker').datepicker({format: 'yyyy-mm-dd', language: I18n.locale}); // Init table sorting var myBars = $('.sorter-bar'); diff --git a/app/assets/stylesheets/application.css b/app/assets/stylesheets/application.css index 87e30c7e..248d92a4 100644 --- a/app/assets/stylesheets/application.css +++ b/app/assets/stylesheets/application.css @@ -2,4 +2,4 @@ *= require bootstrap_and_overrides *= require select2 *= require token-input-bootstrappy -*/ +*= require bootstrap-datepicker diff --git a/app/assets/stylesheets/bootstrap_and_overrides.css.less b/app/assets/stylesheets/bootstrap_and_overrides.css.less index a87a4017..836b1c3b 100644 --- a/app/assets/stylesheets/bootstrap_and_overrides.css.less +++ b/app/assets/stylesheets/bootstrap_and_overrides.css.less @@ -31,10 +31,6 @@ body { // Example: // @linkColor: #ff0000; - -// Bootstrap datepicker -@import "datepicker"; - // Custom styles // Fix empty dd tags in horizontal dl, see https://github.com/twitter/bootstrap/issues/4062 diff --git a/app/controllers/articles_controller.rb b/app/controllers/articles_controller.rb index 2e834ab5..fdf4a46a 100644 --- a/app/controllers/articles_controller.rb +++ b/app/controllers/articles_controller.rb @@ -32,7 +32,7 @@ class ArticlesController < ApplicationController end def new - @article = @supplier.articles.build(:tax => 7.0) + @article = @supplier.articles.build(:tax => FoodsoftConfig[:tax_default]) render :layout => false end @@ -145,19 +145,22 @@ class ArticlesController < ApplicationController begin @articles = Array.new articles, outlisted_articles = FoodsoftFile::parse(params[:articles]["file"]) + no_category = ArticleCategory.new articles.each do |row| + # fallback to Others category + category = (ArticleCategory.find_by_name(row[:category]) or no_category) # creates a new article and price article = Article.new( :name => row[:name], :note => row[:note], :manufacturer => row[:manufacturer], :origin => row[:origin], :unit => row[:unit], - :article_category => ArticleCategory.find_by_name(row[:category]), + :article_category => category, :price => row[:price], :unit_quantity => row[:unit_quantity], :order_number => row[:number], :deposit => row[:deposit], - :tax => row[:tax]) + :tax => (row[:tax] or FoodsoftConfig[:tax_default])) # stop parsing, when an article isn't valid unless article.valid? raise I18n.t('articles.controller.error_parse', :msg => article.errors.full_messages.join(", "), :line => (articles.index(row) + 2).to_s) diff --git a/app/controllers/group_orders_controller.rb b/app/controllers/group_orders_controller.rb index f93e98ec..31d1178a 100644 --- a/app/controllers/group_orders_controller.rb +++ b/app/controllers/group_orders_controller.rb @@ -77,7 +77,7 @@ class GroupOrdersController < ApplicationController @order = Order.find((params[:order_id] || params[:group_order][:order_id]), :include => [:supplier, :order_articles]) unless @order.open? - flash[:notice] = I18n.t('group_orders.error_closed') + flash[:notice] = I18n.t('group_orders.errors.closed') redirect_to :action => 'index' end end diff --git a/app/views/articles/_edit_all_table.html.haml b/app/views/articles/_edit_all_table.html.haml index 3cbf1412..bd9441f7 100644 --- a/app/views/articles/_edit_all_table.html.haml +++ b/app/views/articles/_edit_all_table.html.haml @@ -19,7 +19,9 @@ - @articles.each_with_index do |article, index| = fields_for "articles[#{article.id || index}]", article do |form| %tr - %td= form.check_box 'availability' + %td + = yield form # allow to add hidden fields to form + = form.check_box 'availability' %td= form.text_field 'name', class: 'input-medium' %td= form.text_field 'unit', class: 'input-mini' %td= form.text_field 'price', class: 'input-mini' diff --git a/app/views/articles/parse_upload.html.haml b/app/views/articles/parse_upload.html.haml index edd34e88..9f322946 100644 --- a/app/views/articles/parse_upload.html.haml +++ b/app/views/articles/parse_upload.html.haml @@ -2,7 +2,9 @@ %p= t('.body').html_safe = form_tag(create_from_upload_supplier_articles_path(@supplier)) do - = render 'edit_all_table' + = render layout: 'edit_all_table' do |form| + = form.hidden_field :manufacturer + = form.hidden_field :origin .form-actions = submit_tag t('.submit', supplier: @supplier.name), class: 'btn btn-primary' = link_to t('ui.or_cancel'), upload_supplier_articles_path(@supplier) diff --git a/app/views/layouts/_header.html.haml b/app/views/layouts/_header.html.haml index c3c16327..dfd5f2f5 100644 --- a/app/views/layouts/_header.html.haml +++ b/app/views/layouts/_header.html.haml @@ -22,5 +22,7 @@ Javascripts \================================================== / Placed at the end of the document so the pages load faster + :javascript + I18n = {locale: '#{j(I18n.locale.to_s)}'} = javascript_include_tag "application" = yield(:javascript) diff --git a/app/views/mailer/order_result.text.haml b/app/views/mailer/order_result.text.haml index e797dadc..8d64fbd2 100644 --- a/app/views/mailer/order_result.text.haml +++ b/app/views/mailer/order_result.text.haml @@ -1,5 +1,5 @@ -= t '.text0', ordergroup: @group_order.ordergroup.name, order: @order.name, when: I18n.l(@order.ends), user: @order.updated_by.nick += raw t '.text0', ordergroup: @group_order.ordergroup.name, order: @order.name, when: I18n.l(@order.ends), user: @order.updated_by.nick - for group_order_article in @group_order.group_order_articles.ordered.all(:include => :order_article) -- article = group_order_article.order_article.article - #{article.name}: #{group_order_article.result} x #{article.unit} = #{group_order_article.result * article.fc_price} -= t '.text1', sum: @group_order.price, order_url: group_order_url(@group_order), foodcoop: FoodsoftConfig[:name] + - article = group_order_article.order_article.article + \- #{article.name}: #{group_order_article.result} x #{article.unit} = #{group_order_article.result * article.fc_price} += raw t '.text1', sum: @group_order.price, order_url: group_order_url(@group_order), foodcoop: FoodsoftConfig[:name] diff --git a/app/workers/user_notifier.rb b/app/workers/user_notifier.rb index 297ae978..7be2d17c 100644 --- a/app/workers/user_notifier.rb +++ b/app/workers/user_notifier.rb @@ -4,7 +4,7 @@ class UserNotifier # Resque style method to perform every class method defined here def self.perform(foodcoop, method_name, *args) - FoodsoftConfig.select_foodcoop(foodcoop) + FoodsoftConfig.select_foodcoop(foodcoop) if FoodsoftConfig[:multi_coop_install] self.send method_name, args end @@ -40,4 +40,4 @@ class UserNotifier end end end -end \ No newline at end of file +end diff --git a/config/app_config.yml.SAMPLE b/config/app_config.yml.SAMPLE index 2436577f..50f1d63b 100644 --- a/config/app_config.yml.SAMPLE +++ b/config/app_config.yml.SAMPLE @@ -37,6 +37,9 @@ default: &defaults # price markup in percent price_markup: 2.0 + # default vat percentage for new articles + tax_default: 7.0 + # tolerance order option: If set to false, article tolerance values do not count # for total article price as long as the order is not finished. tolerance_is_costly: false diff --git a/config/locales/de.yml b/config/locales/de.yml index 80ad0a6c..c2e0b95a 100644 --- a/config/locales/de.yml +++ b/config/locales/de.yml @@ -615,7 +615,7 @@ de: ended: beendet last_edited_by: Zuletzt bearbeitet von name: Lieferantin - no_closed_orders: derzeit gibt es keine beendeten Bestellungen + no_closed_orders: Derzeit gibt es keine beendeten Bestellungen. state: Status summary: changed: Daten wurden verändert! @@ -672,9 +672,9 @@ de: end: Ende everything_cleared: Super, alles schon abgerechnet... group: Gruppe - last_transactions: letzte Überweisungen + last_transactions: Letzte Überweisungen note: Notiz - open_transactions: noch nicht abgerechnet + open_transactions: Noch nicht abgerechnet show_all: alle anzeigen supplier: Lieferantin title: Finanzbereich @@ -1082,11 +1082,10 @@ de: die Bestelling für "%{order}" wurde am %{when} von %{user} beendet. - Für Euch wurden die folgenden Artikel bestellt: ' - text1: ! 'Gesamtpreis: %{sum} + text1: ! 'o Gesamtpreis: %{sum} Bestelling unline einsehen: %{order_url} diff --git a/config/locales/en.yml b/config/locales/en.yml index 26edf73b..078c1def 100644 --- a/config/locales/en.yml +++ b/config/locales/en.yml @@ -614,7 +614,7 @@ en: close: close directly confirm: Do you really want to fully close the order? end: End - ended: ended + ended: closed last_edited_by: Last edited by name: Supplier no_closed_orders: At the moment there are no closed orders. @@ -674,11 +674,11 @@ en: end: End everything_cleared: Great, everything is accounted... group: Group - last_transactions: Last transactions + last_transactions: Recent transactions note: Note - open_transactions: not yet settled + open_transactions: Unsettled orders show_all: show all - supplier: supplier + supplier: Supplier title: Finances unpaid_invoices: Unpaid invoices invoices: @@ -1080,25 +1080,23 @@ en: ' order_result: - subject: ! 'Order was ended: %{name}' + subject: ! 'Order closed: %{name}' text0: ! 'Dear %{ordergroup}, - The order for "%{order}" was placed on %{when} by %{user}. + The order for "%{order}" has been closed by %{user} on %{when}. - - The following articles were ordered by your ordergroup: + The following articles have been ordered for your ordergroup: ' - text1: ! 'Total sum: %{sum} + text1: ! 'o Total sum: %{sum} - See order online: %{order_url} + You can view the order online: %{order_url} - Kind regards from %{foodcoop}. -' + Kind regards from %{foodcoop}.' reset_password: subject: New password for %{username} text: ! 'Hi %{user}, @@ -1298,7 +1296,7 @@ en: number: Number to_address: Shipping address finish: - notice: The order has been ended. + notice: The order has been closed. form: name: Name note: Note diff --git a/config/locales/nl.yml b/config/locales/nl.yml index 8989d734..d059f462 100644 --- a/config/locales/nl.yml +++ b/config/locales/nl.yml @@ -528,7 +528,7 @@ nl: edit_results_by_articles: add_article: Artikel toevoegen amount: Aantal - amount_per_unit: Groothandelsverpakkingsgroote * Aantal + amount_per_unit: Gr.Eenh. article: Artikel gross: Bruto net: Netto @@ -576,7 +576,7 @@ nl: close: direct afsluiten confirm: Weet je zeker dat de je bestelling wilt afsluiten? end: Einde - ended: beëindigd + ended: gesloten last_edited_by: Laatst aangepast door name: Leverancier no_closed_orders: Momenteel zijn er geen gesloten bestellingen. @@ -636,11 +636,11 @@ nl: end: Einde everything_cleared: Mooi zo, alles is verrekend... group: Groep - last_transactions: Laatste transacties + last_transactions: Recente transacties note: Notitie - open_transactions: nog niet afgerekend + open_transactions: Nog niet afgerekend show_all: alle tonen - supplier: leverancier + supplier: Leverancier title: Financiën unpaid_invoices: Onbetaalde facturen invoices: @@ -934,7 +934,7 @@ nl: feedback: desc: Foutje gevonden? Suggesties? Ideeën? Commentaar? title: Feedback - footer: Foodsoft, open source software voor een non-profit voedselcoop. + footer: Foodsoft, open source software voor non-profit voedselcoops. help: Help logout: Uitloggen ordergroup: Mijn huishouden @@ -983,9 +983,23 @@ nl: subject: text: order_result: - subject: - text0: - text1: + subject: ! 'Bestelling gesloten: %{name}' + text0: ! 'Beste %{ordergroup}. + + + De bestelling voor "%{order}" is om %{when} gesloten door %{user}. + + De volgende artikelen zijn voor jouw huishouden besteld: + +' + text1: ! 'o Totaal: %{sum} + + + Je kunt de bestelling online bekijken: %{order_url} + + + + Vriendelijke groet van %{foodcoop}.' reset_password: subject: text: @@ -1157,7 +1171,7 @@ nl: number: to_address: finish: - notice: + notice: De bestelling is gesloten. form: name: note: diff --git a/vendor/assets/javascripts/bootstrap-datepicker.de.js b/vendor/assets/javascripts/bootstrap-datepicker.de.js deleted file mode 100644 index ca0c33f8..00000000 --- a/vendor/assets/javascripts/bootstrap-datepicker.de.js +++ /dev/null @@ -1,14 +0,0 @@ -/** - * German translation for bootstrap-datepicker - * Sam Zurcher - */ -;(function($){ - $.fn.datepicker.dates['de'] = { - days: ["Sonntag", "Montag", "Dienstag", "Mittwoch", "Donnerstag", "Freitag", "Samstag", "Sonntag"], - daysShort: ["Son", "Mon", "Die", "Mit", "Don", "Fre", "Sam", "Son"], - daysMin: ["So", "Mo", "Di", "Mi", "Do", "Fr", "Sa", "So"], - 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" - }; -}(jQuery)); diff --git a/vendor/assets/javascripts/bootstrap-datepicker.js b/vendor/assets/javascripts/bootstrap-datepicker.js deleted file mode 100644 index 378642cd..00000000 --- a/vendor/assets/javascripts/bootstrap-datepicker.js +++ /dev/null @@ -1,818 +0,0 @@ -/* ========================================================= - * bootstrap-datepicker.js - * http://www.eyecon.ro/bootstrap-datepicker - * ========================================================= - * Copyright 2012 Stefan Petre - * Improvements by Andrew Rowls - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * ========================================================= */ - -!function( $ ) { - - function UTCDate(){ - return new Date(Date.UTC.apply(Date, arguments)); - } - function UTCToday(){ - var today = new Date(); - return UTCDate(today.getUTCFullYear(), today.getUTCMonth(), today.getUTCDate()); - } - - // Picker object - - var Datepicker = function(element, options) { - var that = this; - - this.element = $(element); - this.language = options.language||this.element.data('date-language')||"en"; - this.language = this.language in dates ? this.language : "en"; - this.format = DPGlobal.parseFormat(options.format||this.element.data('date-format')||'mm/dd/yyyy'); - this.picker = $(DPGlobal.template) - .appendTo('body') - .on({ - click: $.proxy(this.click, this) - }); - this.isInput = this.element.is('input'); - this.component = this.element.is('.date') ? this.element.find('.add-on') : false; - this.hasInput = this.component && this.element.find('input').length; - if(this.component && this.component.length === 0) - this.component = false; - - if (this.isInput) { - this.element.on({ - focus: $.proxy(this.show, this), - keyup: $.proxy(this.update, this), - keydown: $.proxy(this.keydown, this) - }); - } else { - if (this.component && this.hasInput){ - // For components that are not readonly, allow keyboard nav - this.element.find('input').on({ - focus: $.proxy(this.show, this), - keyup: $.proxy(this.update, this), - keydown: $.proxy(this.keydown, this) - }); - - this.component.on('click', $.proxy(this.show, this)); - } else { - this.element.on('click', $.proxy(this.show, this)); - } - } - - $(document).on('mousedown', function (e) { - // Clicked outside the datepicker, hide it - if ($(e.target).closest('.datepicker').length == 0) { - that.hide(); - } - }); - - this.autoclose = false; - if ('autoclose' in options) { - this.autoclose = options.autoclose; - } else if ('dateAutoclose' in this.element.data()) { - this.autoclose = this.element.data('date-autoclose'); - } - - this.keyboardNavigation = true; - if ('keyboardNavigation' in options) { - this.keyboardNavigation = options.keyboardNavigation; - } else if ('dateKeyboardNavigation' in this.element.data()) { - this.keyboardNavigation = this.element.data('date-keyboard-navigation'); - } - - switch(options.startView || this.element.data('date-start-view')){ - case 2: - case 'decade': - this.viewMode = this.startViewMode = 2; - break; - case 1: - case 'year': - this.viewMode = this.startViewMode = 1; - break; - case 0: - case 'month': - default: - this.viewMode = this.startViewMode = 0; - break; - } - - this.todayBtn = (options.todayBtn||this.element.data('date-today-btn')||false); - this.todayHighlight = (options.todayHighlight||this.element.data('date-today-highlight')||false); - - this.weekStart = ((options.weekStart||this.element.data('date-weekstart')||dates[this.language].weekStart||0) % 7); - this.weekEnd = ((this.weekStart + 6) % 7); - this.startDate = -Infinity; - this.endDate = Infinity; - this.setStartDate(options.startDate||this.element.data('date-startdate')); - this.setEndDate(options.endDate||this.element.data('date-enddate')); - this.fillDow(); - this.fillMonths(); - this.update(); - this.showMode(); - }; - - Datepicker.prototype = { - constructor: Datepicker, - - show: function(e) { - this.picker.show(); - this.height = this.component ? this.component.outerHeight() : this.element.outerHeight(); - this.update(); - this.place(); - $(window).on('resize', $.proxy(this.place, this)); - if (e ) { - e.stopPropagation(); - e.preventDefault(); - } - this.element.trigger({ - type: 'show', - date: this.date - }); - }, - - hide: function(e){ - this.picker.hide(); - $(window).off('resize', this.place); - this.viewMode = this.startViewMode; - this.showMode(); - if (!this.isInput) { - $(document).off('mousedown', this.hide); - } - if (e && e.currentTarget.value) - this.setValue(); - this.element.trigger({ - type: 'hide', - date: this.date - }); - }, - - setValue: function() { - var formatted = DPGlobal.formatDate(this.date, this.format, this.language); - if (!this.isInput) { - if (this.component){ - this.element.find('input').prop('value', formatted); - } - this.element.data('date', formatted); - } else { - this.element.prop('value', formatted); - } - }, - - setStartDate: function(startDate){ - this.startDate = startDate||-Infinity; - if (this.startDate !== -Infinity) { - this.startDate = DPGlobal.parseDate(this.startDate, this.format, this.language); - } - this.update(); - this.updateNavArrows(); - }, - - setEndDate: function(endDate){ - this.endDate = endDate||Infinity; - if (this.endDate !== Infinity) { - this.endDate = DPGlobal.parseDate(this.endDate, this.format, this.language); - } - this.update(); - this.updateNavArrows(); - }, - - place: function(){ - var zIndex = parseInt(this.element.parents().filter(function() { - return $(this).css('z-index') != 'auto'; - }).first().css('z-index'))+10; - var offset = this.component ? this.component.offset() : this.element.offset(); - this.picker.css({ - top: offset.top + this.height, - left: offset.left, - zIndex: zIndex - }); - }, - - update: function(){ - this.date = DPGlobal.parseDate( - this.isInput ? this.element.prop('value') : this.element.data('date') || this.element.find('input').prop('value'), - this.format, this.language - ); - if (this.date < this.startDate) { - this.viewDate = new Date(this.startDate); - } else if (this.date > this.endDate) { - this.viewDate = new Date(this.endDate); - } else { - this.viewDate = new Date(this.date); - } - this.fill(); - }, - - fillDow: function(){ - var dowCnt = this.weekStart; - var html = ''; - while (dowCnt < this.weekStart + 7) { - html += ''+dates[this.language].daysMin[(dowCnt++)%7]+''; - } - html += ''; - this.picker.find('.datepicker-days thead').append(html); - }, - - fillMonths: function(){ - var html = ''; - var i = 0 - while (i < 12) { - html += ''+dates[this.language].monthsShort[i++]+''; - } - this.picker.find('.datepicker-months td').html(html); - }, - - fill: function() { - var d = new Date(this.viewDate), - year = d.getUTCFullYear(), - month = d.getUTCMonth(), - startYear = this.startDate !== -Infinity ? this.startDate.getUTCFullYear() : -Infinity, - startMonth = this.startDate !== -Infinity ? this.startDate.getUTCMonth() : -Infinity, - endYear = this.endDate !== Infinity ? this.endDate.getUTCFullYear() : Infinity, - endMonth = this.endDate !== Infinity ? this.endDate.getUTCMonth() : Infinity, - currentDate = this.date.valueOf(), - today = new Date(); - this.picker.find('.datepicker-days thead th:eq(1)') - .text(dates[this.language].months[month]+' '+year); - this.picker.find('tfoot th.today') - .text(dates[this.language].today) - .toggle(this.todayBtn); - this.updateNavArrows(); - this.fillMonths(); - var prevMonth = UTCDate(year, month-1, 28,0,0,0,0), - day = DPGlobal.getDaysInMonth(prevMonth.getUTCFullYear(), prevMonth.getUTCMonth()); - prevMonth.setUTCDate(day); - prevMonth.setUTCDate(day - (prevMonth.getUTCDay() - this.weekStart + 7)%7); - var nextMonth = new Date(prevMonth); - nextMonth.setUTCDate(nextMonth.getUTCDate() + 42); - nextMonth = nextMonth.valueOf(); - var html = []; - var clsName; - while(prevMonth.valueOf() < nextMonth) { - if (prevMonth.getUTCDay() == this.weekStart) { - html.push(''); - } - clsName = ''; - if (prevMonth.getUTCFullYear() < year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() < month)) { - clsName += ' old'; - } else if (prevMonth.getUTCFullYear() > year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() > month)) { - clsName += ' new'; - } - // Compare internal UTC date with local today, not UTC today - if (this.todayHighlight && - prevMonth.getUTCFullYear() == today.getFullYear() && - prevMonth.getUTCMonth() == today.getMonth() && - prevMonth.getUTCDate() == today.getDate()) { - clsName += ' today'; - } - if (prevMonth.valueOf() == currentDate) { - clsName += ' active'; - } - if (prevMonth.valueOf() < this.startDate || prevMonth.valueOf() > this.endDate) { - clsName += ' disabled'; - } - html.push(''+prevMonth.getUTCDate() + ''); - if (prevMonth.getUTCDay() == this.weekEnd) { - html.push(''); - } - prevMonth.setUTCDate(prevMonth.getUTCDate()+1); - } - this.picker.find('.datepicker-days tbody').empty().append(html.join('')); - var currentYear = this.date.getUTCFullYear(); - - var months = this.picker.find('.datepicker-months') - .find('th:eq(1)') - .text(year) - .end() - .find('span').removeClass('active'); - if (currentYear == year) { - months.eq(this.date.getUTCMonth()).addClass('active'); - } - if (year < startYear || year > endYear) { - months.addClass('disabled'); - } - if (year == startYear) { - months.slice(0, startMonth).addClass('disabled'); - } - if (year == endYear) { - months.slice(endMonth+1).addClass('disabled'); - } - - html = ''; - year = parseInt(year/10, 10) * 10; - var yearCont = this.picker.find('.datepicker-years') - .find('th:eq(1)') - .text(year + '-' + (year + 9)) - .end() - .find('td'); - year -= 1; - for (var i = -1; i < 11; i++) { - html += ''+year+''; - year += 1; - } - yearCont.html(html); - }, - - updateNavArrows: function() { - var d = new Date(this.viewDate), - year = d.getUTCFullYear(), - month = d.getUTCMonth(); - switch (this.viewMode) { - case 0: - if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear() && month <= this.startDate.getUTCMonth()) { - this.picker.find('.prev').css({visibility: 'hidden'}); - } else { - this.picker.find('.prev').css({visibility: 'visible'}); - } - if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear() && month >= this.endDate.getUTCMonth()) { - this.picker.find('.next').css({visibility: 'hidden'}); - } else { - this.picker.find('.next').css({visibility: 'visible'}); - } - break; - case 1: - case 2: - if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear()) { - this.picker.find('.prev').css({visibility: 'hidden'}); - } else { - this.picker.find('.prev').css({visibility: 'visible'}); - } - if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear()) { - this.picker.find('.next').css({visibility: 'hidden'}); - } else { - this.picker.find('.next').css({visibility: 'visible'}); - } - break; - } - }, - - click: function(e) { - e.stopPropagation(); - e.preventDefault(); - var target = $(e.target).closest('span, td, th'); - if (target.length == 1) { - switch(target[0].nodeName.toLowerCase()) { - case 'th': - switch(target[0].className) { - case 'switch': - this.showMode(1); - break; - case 'prev': - case 'next': - var dir = DPGlobal.modes[this.viewMode].navStep * (target[0].className == 'prev' ? -1 : 1); - switch(this.viewMode){ - case 0: - this.viewDate = this.moveMonth(this.viewDate, dir); - break; - case 1: - case 2: - this.viewDate = this.moveYear(this.viewDate, dir); - break; - } - this.fill(); - break; - case 'today': - var date = new Date(); - date.setUTCHours(0); - date.setUTCMinutes(0); - date.setUTCSeconds(0); - date.setUTCMilliseconds(0); - - this.showMode(-2); - var which = this.todayBtn == 'linked' ? null : 'view'; - this._setDate(date, which); - break; - } - break; - case 'span': - if (!target.is('.disabled')) { - this.viewDate.setUTCDate(1); - if (target.is('.month')) { - var month = target.parent().find('span').index(target); - this.viewDate.setUTCMonth(month); - this.element.trigger({ - type: 'changeMonth', - date: this.viewDate - }); - } else { - var year = parseInt(target.text(), 10)||0; - this.viewDate.setUTCFullYear(year); - this.element.trigger({ - type: 'changeYear', - date: this.viewDate - }); - } - this.showMode(-1); - this.fill(); - } - break; - case 'td': - if (target.is('.day') && !target.is('.disabled')){ - var day = parseInt(target.text(), 10)||1; - var year = this.viewDate.getUTCFullYear(), - month = this.viewDate.getUTCMonth(); - if (target.is('.old')) { - if (month == 0) { - month = 11; - year -= 1; - } else { - month -= 1; - } - } else if (target.is('.new')) { - if (month == 11) { - month = 0; - year += 1; - } else { - month += 1; - } - } - this._setDate(UTCDate(year, month, day,0,0,0,0)); - } - break; - } - } - }, - - _setDate: function(date, which){ - if (!which || which == 'date') - this.date = date; - if (!which || which == 'view') - this.viewDate = date; - this.fill(); - this.setValue(); - this.element.trigger({ - type: 'changeDate', - date: this.date - }); - var element; - if (this.isInput) { - element = this.element; - } else if (this.component){ - element = this.element.find('input'); - } - if (element) { - element.change(); - if (this.autoclose) { - this.hide(); - } - } - }, - - moveMonth: function(date, dir){ - if (!dir) return date; - var new_date = new Date(date.valueOf()), - day = new_date.getUTCDate(), - month = new_date.getUTCMonth(), - mag = Math.abs(dir), - new_month, test; - dir = dir > 0 ? 1 : -1; - if (mag == 1){ - test = dir == -1 - // If going back one month, make sure month is not current month - // (eg, Mar 31 -> Feb 31 == Feb 28, not Mar 02) - ? function(){ return new_date.getUTCMonth() == month; } - // If going forward one month, make sure month is as expected - // (eg, Jan 31 -> Feb 31 == Feb 28, not Mar 02) - : function(){ return new_date.getUTCMonth() != new_month; }; - new_month = month + dir; - new_date.setUTCMonth(new_month); - // Dec -> Jan (12) or Jan -> Dec (-1) -- limit expected date to 0-11 - if (new_month < 0 || new_month > 11) - new_month = (new_month + 12) % 12; - } else { - // For magnitudes >1, move one month at a time... - for (var i=0; i= this.startDate && date <= this.endDate; - }, - - keydown: function(e){ - if (this.picker.is(':not(:visible)')){ - if (e.keyCode == 27) // allow escape to hide and re-show picker - this.show(); - return; - } - var dateChanged = false, - dir, day, month, - newDate, newViewDate; - switch(e.keyCode){ - case 27: // escape - this.hide(); - e.preventDefault(); - break; - case 37: // left - case 39: // right - if (!this.keyboardNavigation) break; - dir = e.keyCode == 37 ? -1 : 1; - if (e.ctrlKey){ - newDate = this.moveYear(this.date, dir); - newViewDate = this.moveYear(this.viewDate, dir); - } else if (e.shiftKey){ - newDate = this.moveMonth(this.date, dir); - newViewDate = this.moveMonth(this.viewDate, dir); - } else { - newDate = new Date(this.date); - newDate.setUTCDate(this.date.getUTCDate() + dir); - newViewDate = new Date(this.viewDate); - newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir); - } - if (this.dateWithinRange(newDate)){ - this.date = newDate; - this.viewDate = newViewDate; - this.setValue(); - this.update(); - e.preventDefault(); - dateChanged = true; - } - break; - case 38: // up - case 40: // down - if (!this.keyboardNavigation) break; - dir = e.keyCode == 38 ? -1 : 1; - if (e.ctrlKey){ - newDate = this.moveYear(this.date, dir); - newViewDate = this.moveYear(this.viewDate, dir); - } else if (e.shiftKey){ - newDate = this.moveMonth(this.date, dir); - newViewDate = this.moveMonth(this.viewDate, dir); - } else { - newDate = new Date(this.date); - newDate.setUTCDate(this.date.getUTCDate() + dir * 7); - newViewDate = new Date(this.viewDate); - newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir * 7); - } - if (this.dateWithinRange(newDate)){ - this.date = newDate; - this.viewDate = newViewDate; - this.setValue(); - this.update(); - e.preventDefault(); - dateChanged = true; - } - break; - case 13: // enter - this.hide(); - e.preventDefault(); - break; - case 9: // tab - this.hide(); - break; - } - if (dateChanged){ - this.element.trigger({ - type: 'changeDate', - date: this.date - }); - var element; - if (this.isInput) { - element = this.element; - } else if (this.component){ - element = this.element.find('input'); - } - if (element) { - element.change(); - } - } - }, - - showMode: function(dir) { - if (dir) { - this.viewMode = Math.max(0, Math.min(2, this.viewMode + dir)); - } - this.picker.find('>div').hide().filter('.datepicker-'+DPGlobal.modes[this.viewMode].clsName).show(); - this.updateNavArrows(); - } - }; - - $.fn.datepicker = function ( option ) { - var args = Array.apply(null, arguments); - args.shift(); - return this.each(function () { - var $this = $(this), - data = $this.data('datepicker'), - options = typeof option == 'object' && option; - if (!data) { - $this.data('datepicker', (data = new Datepicker(this, $.extend({}, $.fn.datepicker.defaults,options)))); - } - if (typeof option == 'string' && typeof data[option] == 'function') { - data[option].apply(data, args); - } - }); - }; - - $.fn.datepicker.defaults = { - }; - $.fn.datepicker.Constructor = Datepicker; - var dates = $.fn.datepicker.dates = { - en: { - days: ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"], - daysShort: ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"], - daysMin: ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa", "Su"], - months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"], - monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"], - today: "Today" - } - } - - var DPGlobal = { - modes: [ - { - clsName: 'days', - navFnc: 'Month', - navStep: 1 - }, - { - clsName: 'months', - navFnc: 'FullYear', - navStep: 1 - }, - { - clsName: 'years', - navFnc: 'FullYear', - navStep: 10 - }], - isLeapYear: function (year) { - return (((year % 4 === 0) && (year % 100 !== 0)) || (year % 400 === 0)) - }, - getDaysInMonth: function (year, month) { - return [31, (DPGlobal.isLeapYear(year) ? 29 : 28), 31, 30, 31, 30, 31, 31, 30, 31, 30, 31][month] - }, - validParts: /dd?|mm?|MM?|yy(?:yy)?/g, - nonpunctuation: /[^ -\/:-@\[-`{-~\t\n\r]+/g, - parseFormat: function(format){ - // IE treats \0 as a string end in inputs (truncating the value), - // so it's a bad format delimiter, anyway - var separators = format.replace(this.validParts, '\0').split('\0'), - parts = format.match(this.validParts); - if (!separators || !separators.length || !parts || parts.length == 0){ - throw new Error("Invalid date format."); - } - return {separators: separators, parts: parts}; - }, - parseDate: function(date, format, language) { - if (date instanceof Date) return date; - if (/^[-+]\d+[dmwy]([\s,]+[-+]\d+[dmwy])*$/.test(date)) { - var part_re = /([-+]\d+)([dmwy])/, - parts = date.match(/([-+]\d+)([dmwy])/g), - part, dir; - date = new Date(); - for (var i=0; i'+ - ''+ - ''+ - ''+ - ''+ - ''+ - '', - contTemplate: '', - footTemplate: '' - }; - DPGlobal.template = ''; -}( window.jQuery );