Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Browse files

Initial Import

  • Loading branch information...
commit 2dfcfe320ef2ea142cf879ffd8f5649d25263968 0 parents
@brianjlandau authored
Showing with 4,623 additions and 0 deletions.
  1. +19 −0 MIT-LICENSE
  2. +77 −0 README
  3. +22 −0 Rakefile
  4. +9 −0 init.rb
  5. +14 −0 install.rb
  6. +107 −0 lib/12_hour_time.rb
  7. +258 −0 lib/unobtrusive_date_picker.rb
  8. BIN  public/images/backstripes.gif
  9. BIN  public/images/bg_header.jpg
  10. BIN  public/images/bullet1.gif
  11. BIN  public/images/bullet2.gif
  12. BIN  public/images/cal.gif
  13. BIN  public/images/gradient-e5e5e5-ffffff.gif
  14. +1,438 −0 public/javascripts/datepicker.js
  15. +40 −0 public/javascripts/lang/af.js
  16. +50 −0 public/javascripts/lang/ar.js
  17. +40 −0 public/javascripts/lang/de.js
  18. +40 −0 public/javascripts/lang/du.js
  19. +42 −0 public/javascripts/lang/en.js
  20. +41 −0 public/javascripts/lang/es.js
  21. +40 −0 public/javascripts/lang/fi.js
  22. +44 −0 public/javascripts/lang/fr.js
  23. +40 −0 public/javascripts/lang/gr.js
  24. +49 −0 public/javascripts/lang/he.js
  25. +13 −0 public/javascripts/lang/it.js
  26. +40 −0 public/javascripts/lang/nl.js
  27. +40 −0 public/javascripts/lang/no.js
  28. +40 −0 public/javascripts/lang/pt.js
  29. +40 −0 public/javascripts/lang/ro.js
  30. +40 −0 public/javascripts/lang/ru.js
  31. +40 −0 public/javascripts/lang/sp.js
  32. +41 −0 public/javascripts/lang/sv.js
  33. +40 −0 public/javascripts/lang/ua.js
  34. +263 −0 public/stylesheets/datepicker.css
  35. +112 −0 rdoc/classes/UnobtrusiveDatePicker.html
  36. +147 −0 rdoc/classes/UnobtrusiveDatePicker/AssetTagHelper.html
  37. +487 −0 rdoc/classes/UnobtrusiveDatePicker/UnobtrusiveDatePickerHelper.html
  38. +1 −0  rdoc/created.rid
  39. +129 −0 rdoc/files/MIT-LICENSE.html
  40. +194 −0 rdoc/files/README.html
  41. +125 −0 rdoc/files/lib/12_hour_time_rb.html
  42. +109 −0 rdoc/files/lib/unobtrusive_date_picker_rb.html
  43. +29 −0 rdoc/fr_class_index.html
  44. +30 −0 rdoc/fr_file_index.html
  45. +37 −0 rdoc/fr_method_index.html
  46. +24 −0 rdoc/index.html
  47. +208 −0 rdoc/rdoc-style.css
  48. +4 −0 tasks/unobtrusive_date_picker_tasks.rake
  49. +8 −0 test/unobtrusive_date_picker_test.rb
  50. +12 −0 uninstall.rb
19 MIT-LICENSE
@@ -0,0 +1,19 @@
+Copyright (c) 2007 Brian J. Landau, <brianjlandau@gmail.com>
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in
+all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+THE SOFTWARE.
77 README
@@ -0,0 +1,77 @@
+
+= Unobtrusive Date-Picker Widget Plugin
+
+This is a helper for creating a date or date-time picker that uses the
+{Unobtrusive Date-Picker Widgit}[http://www.frequency-decoder.com/2006/10/02/unobtrusive-date-picker-widgit-update]
+to add a clickable calendar image that will bring up a calendar picker if javascript is available.
+
+The two main form helper methods are <tt>unobtrusive_date_picker</tt> and <tt>unobtrusive_datetime_picker</tt>.
+
+It also uses the {12 Hour Time plugin}[http://code.google.com/p/rails-twelve-hour-time-plugin/]
+so that 12 Hour times can be processed by Active Record.
+
+You may want to consider compressing the javascript files with {Dean Edward's Packer}[http://dean.edwards.name/packer/]
+or {Douglas Crockford's JSMin}[http://www.crockford.com/javascript/jsmin.html] before deploying your application.
+
+
+== Usage
+The main options are <tt>start_year</tt>, <tt>end_year</tt>, which control the year range.
+
+==== Example:
+ <% form_for :article, :url => { :action => @form_action, :id => @article } do |f| %>
+ <fieldset>
+ ...
+ <label>Date: <%= f.unobtrusive_datetime_picker :date %></label><br />
+ ...
+ </fieldset>
+ <% end %>
+
+==== Produces (Current date when output: 12/6/07):
+ <form action="/form/create" method="post">
+ <fieldset>
+
+ <label>Date:
+ <select id="article_date-dd" name="article[date(3i)]">
+ <option value="1">1</option>
+ ...
+ <option value="31">31</option>
+ </select>
+ <select id="article_date-mm" name="article[date(2i)]">
+ <option value="1">January</option>
+ ...
+ <option value="12" selected="selected">December</option>
+ </select>
+ <select id="article_date" name="article[date(1i)]" class="split-date">
+ <option value="2002">2002</option>
+ ...
+ <option value="2012">2012</option>
+ </select>
+ &nbsp;
+ <select id="article_date_4i" name="article[date(4i)]">
+ <option value="1">1</option>
+ ...
+ <option value="12">12</option>
+ </select>
+ :
+ <select id="article_date_5i" name="article[date(5i)]">
+ <option value="00">00</option>
+ ...
+ <option value="59">59</option>
+ </select>
+ <select id="article_date_6i" name="article[date(6i)]">
+ <option value="AMPM>AMPM</option>
+ <option value="AMPM>AMPM</option>
+ </select>
+ </label><br />
+
+ </fieldset>
+ </form>
+
+
+
+=== LICENSE
+
+See MIT-LICENSE file for copyright and licensing information for this plugin.
+
+Unobtrusive Date-Picker Widgit is provided under the Creative Commons Attribution-ShareAlike 2.5 license (http://creativecommons.org/licenses/by-sa/2.5/) by frequency-decoder.com
+
22 Rakefile
@@ -0,0 +1,22 @@
+require 'rake'
+require 'rake/testtask'
+require 'rake/rdoctask'
+
+desc 'Default: run unit tests.'
+task :default => :test
+
+desc 'Test the unobtrusive_date_picker plugin.'
+Rake::TestTask.new(:test) do |t|
+ t.libs << 'lib'
+ t.pattern = 'test/**/*_test.rb'
+ t.verbose = true
+end
+
+desc 'Generate documentation for the unobtrusive_date_picker plugin.'
+Rake::RDocTask.new(:rdoc) do |rdoc|
+ rdoc.rdoc_dir = 'rdoc'
+ rdoc.title = 'Unobtrusive Date-Picker'
+ rdoc.options << '--line-numbers' << '--inline-source'
+ rdoc.rdoc_files.add ['lib/**/*.rb', 'README', 'MIT-LICENSE']
+ rdoc.options << '--main' << 'README'
+end
9 init.rb
@@ -0,0 +1,9 @@
+require '12_hour_time'
+require 'unobtrusive_date_picker'
+
+# Include all the necessary functions in to the appropriate point in the Rails framework
+ActionView::Base.send :include, UnobtrusiveDatePicker::UnobtrusiveDatePickerHelper
+ActionView::Helpers::DateHelper.send :include, UnobtrusiveDatePicker::UnobtrusiveDatePickerHelper
+ActionView::Base.send :include, UnobtrusiveDatePicker::AssetTagHelper
+ActionView::Helpers::AssetTagHelper.send :include, UnobtrusiveDatePicker::AssetTagHelper
+ActionView::Helpers::InstanceTag.send :include, UnobtrusiveDatePicker::InstanceTag
14 install.rb
@@ -0,0 +1,14 @@
+require 'fileutils'
+
+# Install all the needed support files (CSS and JavaScript)
+
+js_dir = File.dirname(__FILE__) + '/../../../public/javascripts/'
+datepicker_js = js_dir + 'datepicker.js'
+lang_dir = js_dir + 'lang'
+datepicker_css = File.dirname(__FILE__) + '/../../../public/stylesheets/datepicker.css'
+images_dir = File.dirname(__FILE__) + '/../../../public/images/datepicker'
+
+FileUtils.cp File.dirname(__FILE__) + '/public/javascripts/datepicker.js', datepicker_js unless File.exists?(datepicker_js)
+FileUtils.cp_r File.dirname(__FILE__) + '/public/javascripts/lang/', lang_dir unless File.exists?(lang_dir)
+FileUtils.cp File.dirname(__FILE__) + '/public/stylesheets/datepicker.css', datepicker_css unless File.exists?(datepicker_css)
+FileUtils.cp_r File.dirname(__FILE__) + '/public/images/', images_dir unless File.exists?(images_dir)
107 lib/12_hour_time.rb
@@ -0,0 +1,107 @@
+
+#
+# == Rails Twelve Hour Time Plugin
+#
+# http://code.google.com/p/rails-twelve-hour-time-plugin/
+#
+# ==== Authors
+# * Nick Muerdter (original code)
+# * Maurice Aubrey
+#
+# ==== Used for
+# Allows UnobtrusiveDatePicker::UnobtrusiveDatePickerHelper to use a AM/PM select of it's own,
+# and still be processed correctly by Active Record.
+#
+
+# :enddoc:
+class ActiveRecord::Base # :nodoc: all
+ def extract_callstack_for_multiparameter_attributes_with_ampm(pairs)
+ attributes = extract_callstack_for_multiparameter_attributes_without_ampm(pairs)
+ attributes.each do |name, values|
+ klass = (self.class.reflect_on_aggregation(name) ||
+ column_for_attribute(name)).klass
+ if klass == Time && values.length == 6
+ if values[5] == ActionView::Helpers::DateHelper::AM && values[3] == 12
+ values[3] = 0
+ elsif values[5] == ActionView::Helpers::DateHelper::PM && values[3] != 12
+ values[3] += 12
+ end
+ end
+ end
+ end
+
+ alias_method_chain :extract_callstack_for_multiparameter_attributes, :ampm
+end
+
+module ActionView::Helpers::DateHelper # :nodoc: all
+ AM = 'AM'
+ PM = 'PM'
+
+ def select_hour_with_ampm(datetime, options = {})
+ options[:twelve_hour] or return select_hour_without_ampm(datetime, options)
+
+ val = ''
+ if datetime
+ val = datetime.kind_of?(Fixnum) ? datetime : datetime.hour
+ val -= 12 if val > 12
+ end
+
+ if options[:use_hidden]
+ return hidden_html(options[:field_name] || 'hour', val, options)
+ end
+
+ hour_options = []
+ 1.upto(12) do |hour|
+ selected = (hour == val) ? ' selected="selected"' : ''
+ hour_options << %(<option value="#{leading_zero_on_single_digits(hour)}"#{selected}>#{leading_zero_on_single_digits(hour)}</option>\n)
+ end
+
+ select_html(options[:field_name] || 'hour', hour_options, options)
+ end
+
+ alias_method_chain :select_hour, :ampm
+
+
+ def select_ampm(datetime, options = {})
+ ampm = [AM, PM]
+ val = datetime ? (choices.include?(datetime) ? datetime : datetime.strftime("%p")) : ''
+
+ if options[:use_hidden]
+ return hidden_html(options[:field_name] || 'ampm', val, options)
+ end
+
+ ampm_options = []
+ ampm.each do |meridiem|
+ selected = (meridiem == val) ? ' selected="selected"' : ''
+ ampm_options << %(<option value="#{meridiem}"#{selected}>#{meridiem}</option>\n)
+ end
+
+ select_html(options[:field_name] || 'ampm', ampm_options, options)
+ end
+
+ def select_time_with_ampm(datetime = Time.now, options = {})
+ select = select_time_without_ampm(datetime, options)
+ select << select_ampm(datetime, options) if options[:twelve_hour]
+ select
+ end
+
+ alias_method_chain :select_time, :ampm
+end
+
+class ActionView::Helpers::InstanceTag # :nodoc: all
+ def date_or_time_select_with_ampm(options)
+ options[:twelve_hour] and not options[:discard_hour] or
+ return date_or_time_select_without_ampm(options)
+
+ defaults = { :discard_type => true }
+ options = defaults.merge(options)
+
+ datetime = value(object)
+ datetime ||= Time.now unless options[:include_blank]
+
+ date_or_time_select_without_ampm(options) +
+ select_ampm(datetime, options_with_prefix(6, options.merge(:use_hidden => options[:discard_hour])))
+ end
+
+ alias_method_chain :date_or_time_select, :ampm
+end
258 lib/unobtrusive_date_picker.rb
@@ -0,0 +1,258 @@
+require 'date'
+require 'time'
+
+
+module UnobtrusiveDatePicker
+
+ # == Unobtrusive Date-Picker Helper
+ #
+ # This Module helps to create date and date-time fields that use the
+ # Unobtrusive Date-Picker Javascript Widget.
+ #
+ # They also use the 12-hour AM/PM time format.
+ #
+ module UnobtrusiveDatePickerHelper
+
+ ##
+ # Creates the date picker with the calendar widget.
+ #
+ def unobtrusive_date_picker(object_name, method, options = {})
+ ActionView::Helpers::InstanceTag.new(object_name, method, self, nil, options.delete(:object)).to_datepicker_date_select_tag(options)
+ end
+
+ ##
+ # Creates the date-time picker with the calendar widget, and AM/PM select.
+ #
+ def unobtrusive_datetime_picker(object_name, method, options = {})
+ ActionView::Helpers::InstanceTag.new(object_name, method, self, nil, options.delete(:object)).to_datepicker_datetime_select_tag(options)
+ end
+
+ def datepicker_select_ampm(datetime, options = {})
+ ampm = %w(AM PM)
+ val = datetime ? (ampm.include?(datetime) ? datetime : datetime.strftime("%p")) : ''
+
+ ampm_options = []
+ ampm.each { |meridiem|
+ ampm_options << ((val == ampm) ?
+ %(<option value="#{ampm} selected="selected">#{ampm}</option>\n) :
+ %(<option value="#{ampm}>#{ampm}</option>\n)
+ )
+ }
+
+ select_html(options[:field_name] || 'ampm', ampm_options, options)
+ end
+
+ def datepicker_select_hour(datetime, options = {})
+ val = datetime ? (datetime.kind_of?(Fixnum) ? datetime : datetime.strftime("%I").to_i) : ''
+
+ hour_options = []
+ 1.upto(12) do |hour|
+ hour_options << ((val == hour) ?
+ %(<option value="#{hour}" selected="selected">#{hour}</option>\n) :
+ %(<option value="#{hour}">#{hour}</option>\n)
+ )
+ end
+
+ select_html(options[:field_name] || 'hour', hour_options, options)
+ end
+
+ ##
+ # Can except a "<tt>minute_step</tt>" option which will specify how many minutes to move forward on each loop.
+ #
+ # i.e. if set to 15: 0, 15, 30, and 45 will be the options supplied in the minute select
+ #
+ def datepicker_select_minute(datetime, options = {})
+ val = datetime ? (datetime.kind_of?(Fixnum) ? datetime : datetime.min) : ''
+
+ minute_options = []
+ 0.step(59, options[:minute_step] || 1) do |minute|
+ minute_options << ((val == minute) ?
+ %(<option value="#{leading_zero_on_single_digits(minute)}" selected="selected">#{leading_zero_on_single_digits(minute)}</option>\n) :
+ %(<option value="#{leading_zero_on_single_digits(minute)}">#{leading_zero_on_single_digits(minute)}</option>\n)
+ )
+ end
+
+ select_html(options[:field_name] || 'minute', minute_options, options)
+ end
+
+ def datepicker_select_day(date, options = {})
+ val = date ? (date.kind_of?(Fixnum) ? date : date.day) : ''
+
+ day_options = []
+ 1.upto(31) do |day|
+ day_options << ((val == day) ?
+ %(<option value="#{day}" selected="selected">#{day}</option>\n) :
+ %(<option value="#{day}">#{day}</option>\n)
+ )
+ end
+
+ datepicker_select_html('dd', day_options, options)
+ end
+
+ ##
+ # This method will accept a hash for the option "<tt>use_month_names</tt>" where the key is the month number
+ # and the value is the month name. It will also accept a boolean option "<tt>use_short_mont</tt>" that will
+ # allow the use of short names instead of full names for the option text. Will also take "<tt>use_month_numbers</tt>",
+ # and "<tt>add_month_numbers</tt>" which use or add month numbers to the option item text.
+ #
+ def datepicker_select_month(date, options = {})
+ val = date ? (date.kind_of?(Fixnum) ? date : date.month) : ''
+
+ month_options = []
+ month_names = options[:use_month_names] || (options[:use_short_month] ? Date::ABBR_MONTHNAMES : Date::MONTHNAMES)
+ month_names.unshift(nil) if month_names.size < 13
+ 1.upto(12) do |month_number|
+ month_name = if options[:use_month_numbers]
+ month_number
+ elsif options[:add_month_numbers]
+ month_number.to_s + ' - ' + month_names[month_number]
+ else
+ month_names[month_number]
+ end
+
+ month_options << ((val == month_number) ?
+ %(<option value="#{month_number}" selected="selected">#{month_name}</option>\n) :
+ %(<option value="#{month_number}">#{month_name}</option>\n)
+ )
+ end
+
+ datepicker_select_html('mm', month_options, options)
+ end
+
+ ##
+ # This method will accept a "<tt>start_year</tt>" and "<tt>end_year</tt>" options.
+ #
+ def datepicker_select_year(date, options = {})
+ val = date ? (date.kind_of?(Fixnum) ? date : date.year) : ''
+
+ y = date ? (date.kind_of?(Fixnum) ? (y = (date == 0) ? Date.today.year : date) : date.year) : Date.today.year
+ start_year, end_year = (options[:start_year] || y-5), (options[:end_year] || y+5)
+ step_val = start_year < end_year ? 1 : -1
+
+ year_options = []
+ start_year.step(end_year, step_val) do |year|
+ year_options << ((val == year) ?
+ %(<option value="#{year}" selected="selected">#{year}</option>\n) :
+ %(<option value="#{year}">#{year}</option>\n)
+ )
+ end
+
+ options[:class] = options[:class] ? "#{options[:class]} split-date" : 'split-date'
+ datepicker_select_html('', year_options, options)
+ end
+
+ protected
+
+ def datepicker_select_html(type, date_options, html_options = {})
+ datepicker_name_and_id(type, html_options)
+
+ select_html = %(<select id="#{html_options[:id]}" name="#{html_options[:name]}")
+ select_html << %( class="#{html_options[:class]}") if html_options[:class]
+ select_html << %( title="#{html_options[:title]}") if html_options[:title]
+ select_html << %( disabled="disabled") if html_options[:disabled]
+ select_html << %(>\n)
+ select_html << %(<option value=""></option>\n) if html_options[:include_blank]
+ select_html << date_options.to_s
+ select_html << "</select>\n"
+ end
+
+ def datepicker_name_and_id(type, html_options)
+ if html_options[:id_prefix]
+ html_options[:id] = (type == '') ? "#{html_options[:id_prefix]}" : "#{html_options[:id_prefix]}-#{type}"
+ else
+ html_options[:id] = (type == '') ? ActionView::Helpers::DateHelper::DEFAULT_PREFIX : ActionView::Helpers::DateHelper::DEFAULT_PREFIX + "-#{type}"
+ html_options[:name] = ActionView::Helpers::DateHelper::DEFAULT_PREFIX
+ end
+ end
+
+ end
+
+ module AssetTagHelper
+ ##
+ # This will add the necessary <link> and <script> tags to include the necessary stylesheet and
+ # javascripts.
+ #
+ def unobtrusive_datepicker_includes
+ output = []
+ output << javascript_include_tag('datepicker')
+ output << stylesheet_link_tag('datepicker')
+ output * "\n"
+ end
+ end
+
+ module InstanceTag # :nodoc: all
+ include UnobtrusiveDatePicker::UnobtrusiveDatePickerHelper
+
+ def to_datepicker_datetime_select_tag(options)
+ datepicker_select(options, true)
+ end
+
+ def to_datepicker_date_select_tag(options)
+ datepicker_select(options, false)
+ end
+
+ private
+
+ def datepicker_select(options, time)
+ defaults = { :discard_type => true }
+ options = defaults.merge(options)
+ datetime = value(object)
+ datetime ||= Time.now unless options[:include_blank]
+
+ position = { :year => 1, :month => 2, :day => 3, :hour => 4, :minute => 5, :ampm => 6 }
+ order = (options[:order] ||= [:day, :month, :year])
+
+ # Maintain valid dates by including hidden fields for discarded elements
+ [:day, :month, :year].each { |o| order.unshift(o) unless order.include?(o) }
+
+ # Ensure proper ordering of :hour, :minute and :second
+ if time
+ [:hour, :minute, :ampm].each { |o| order.delete(o); order.push(o) }
+ end
+
+ date_or_time_select = ''
+ order.reverse.each do |param|
+ date_or_time_select.insert(0, self.send("datepicker_select_#{param}", datetime, datepicker_options_with_prefix(position[param], options)))
+ date_or_time_select.insert(0,
+ case param
+ when :hour then " &nbsp; "
+ when :minute then " : "
+ else ""
+ end)
+ end
+
+ date_or_time_select
+ end
+
+ def datepicker_options_with_prefix(position, options)
+ prefix = "#{@object_name}"
+ if options[:index]
+ prefix << "[#{options[:index]}]"
+ elsif @auto_index
+ prefix << "[#{@auto_index}]"
+ end
+ options[:name] = "#{prefix}[#{@method_name}(#{position}i)]"
+ options[:id_prefix] = "#{@object_name}_#{@method_name}"
+ options.merge(:prefix => "#{prefix}[#{@method_name}(#{position}i)]")
+ end
+
+ end
+
+end
+# /UnobtrusiveDatePicker
+
+
+module ActionView # :nodoc: all
+ module Helpers
+ class FormBuilder
+ def unobtrusive_date_picker(method, options = {})
+ @template.unobtrusive_date_picker(@object_name, method, options.merge(:object => @object))
+ end
+
+ def unobtrusive_datetime_picker(method, options = {})
+ @template.unobtrusive_datetime_picker(@object_name, method, options.merge(:object => @object))
+ end
+ end
+ end
+end
+
BIN  public/images/backstripes.gif
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
BIN  public/images/bg_header.jpg
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
BIN  public/images/bullet1.gif
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
BIN  public/images/bullet2.gif
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
BIN  public/images/cal.gif
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
BIN  public/images/gradient-e5e5e5-ffffff.gif
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
1,438 public/javascripts/datepicker.js
@@ -0,0 +1,1438 @@
+/*
+ DatePicker v4.2 by frequency-decoder.com
+
+ Released under a creative commons Attribution-ShareAlike 2.5 license (http://creativecommons.org/licenses/by-sa/2.5/)
+
+ Please credit frequency-decoder in any derivative work - thanks.
+
+ You are free:
+
+ * to copy, distribute, display, and perform the work
+ * to make derivative works
+ * to make commercial use of the work
+
+ Under the following conditions:
+
+ by Attribution.
+ --------------
+ You must attribute the work in the manner specified by the author or licensor.
+
+ sa
+ --
+ Share Alike. If you alter, transform, or build upon this work, you may distribute the resulting work only under a license identical to this one.
+
+ * For any reuse or distribution, you must make clear to others the license terms of this work.
+ * Any of these conditions can be waived if you get permission from the copyright holder.
+*/
+var datePickerController;
+
+(function() {
+
+// Detect the browser language
+datePicker.languageinfo = navigator.language ? navigator.language : navigator.userLanguage;
+datePicker.languageinfo = datePicker.languageinfo ? datePicker.languageinfo.toLowerCase().replace(/-[a-z]+$/, "") : 'en';
+
+// Load the appropriate language file
+var scriptFiles = document.getElementsByTagName('head')[0].getElementsByTagName('script');
+var loc = scriptFiles[scriptFiles.length - 1].src.substr(0, scriptFiles[scriptFiles.length - 1].src.lastIndexOf("/")) + "/lang/" + datePicker.languageinfo + ".js";
+
+var script = document.createElement('script');
+script.type = "text/javascript";
+script.src = loc;
+script.setAttribute("charset", "utf-8");
+/*@cc_on
+/*@if(@_win32)
+ var bases = document.getElementsByTagName('base');
+ if (bases.length && bases[0].childNodes.length) {
+ bases[0].appendChild(script);
+ } else {
+ document.getElementsByTagName('head')[0].appendChild(script);
+ };
+@else @*/
+document.getElementsByTagName('head')[0].appendChild(script);
+/*@end
+@*/
+script = null;
+
+// Defaults should the locale file not load
+datePicker.months = ["January","February","March","April","May","June","July","August","September","October","November","December"];
+datePicker.fullDay = ["Monday","Tuesday","Wednesday","Thursday","Friday","Saturday","Sunday"];
+datePicker.titles = ["Previous month","Next month","Previous year","Next year", "Today", "Show Calendar"];
+
+datePicker.getDaysPerMonth = function(nMonth, nYear) {
+ nMonth = (nMonth + 12) % 12;
+ return (((0 == (nYear%4)) && ((0 != (nYear%100)) || (0 == (nYear%400)))) && nMonth == 1) ? 29: [31,28,31,30,31,30,31,31,30,31,30,31][nMonth];
+};
+
+function datePicker(options) {
+
+ this.defaults = {};
+ for(opt in options) { this[opt] = this.defaults[opt] = options[opt]; };
+
+ this.date = new Date();
+ this.yearinc = 1;
+ this.timer = null;
+ this.pause = 1000;
+ this.timerSet = false;
+ this.fadeTimer = null;
+ this.interval = new Date();
+ this.firstDayOfWeek = this.defaults.firstDayOfWeek = this.dayInc = this.monthInc = this.yearInc = this.opacity = this.opacityTo = 0;
+ this.dateSet = null;
+ this.visible = false;
+ this.disabledDates = [];
+ this.enabledDates = [];
+ this.nbsp = String.fromCharCode( 160 );
+ var o = this;
+
+ o.events = {
+ onblur:function(e) {
+ o.removeKeyboardEvents();
+ },
+ onfocus:function(e) {
+ o.addKeyboardEvents();
+ },
+ onkeydown: function (e) {
+ o.stopTimer();
+ if(!o.visible) return false;
+
+ if(e == null) e = document.parentWindow.event;
+ var kc = e.keyCode ? e.keyCode : e.charCode;
+
+ if( kc == 13 ) {
+ // close (return)
+ var td = document.getElementById(o.id + "-date-picker-hover");
+ if(!td || td.className.search(/out-of-range|day-disabled/) != -1) return o.killEvent(e);
+ o.returnFormattedDate();
+ o.hide();
+ return o.killEvent(e);
+ } else if(kc == 27) {
+ // close (esc)
+ o.hide();
+ return o.killEvent(e);
+ } else if(kc == 32 || kc == 0) {
+ // today (space)
+ o.date = new Date();
+ o.updateTable();
+ return o.killEvent(e);
+ };
+
+ // Internet Explorer fires the keydown event faster than the JavaScript engine can
+ // update the interface. The following attempts to fix this.
+ /*@cc_on
+ @if(@_win32)
+ if(new Date().getTime() - o.interval.getTime() < 100) return o.killEvent(e);
+ o.interval = new Date();
+ @end
+ @*/
+
+ if ((kc > 49 && kc < 56) || (kc > 97 && kc < 104)) {
+ if (kc > 96) kc -= (96-48);
+ kc -= 49;
+ o.firstDayOfWeek = (o.firstDayOfWeek + kc) % 7;
+ o.updateTable();
+ return o.killEvent(e);
+ };
+
+ if ( kc < 37 || kc > 40 ) return true;
+
+ var d = new Date( o.date ).valueOf();
+
+ if ( kc == 37 ) {
+ // ctrl + left = previous month
+ if( e.ctrlKey ) {
+ d = new Date( o.date );
+ d.setDate( Math.min(d.getDate(), datePicker.getDaysPerMonth(d.getMonth() - 1,d.getFullYear())) );
+ d.setMonth( d.getMonth() - 1 );
+ } else {
+ d = new Date( o.date.getFullYear(), o.date.getMonth(), o.date.getDate() - 1 );
+ };
+ } else if ( kc == 39 ) {
+ // ctrl + right = next month
+ if( e.ctrlKey ) {
+ d = new Date( o.date );
+ d.setDate( Math.min(d.getDate(), datePicker.getDaysPerMonth(d.getMonth() + 1,d.getFullYear())) );
+ d.setMonth( d.getMonth() + 1 );
+ } else {
+ d = new Date( o.date.getFullYear(), o.date.getMonth(), o.date.getDate() + 1 );
+ };
+ } else if ( kc == 38 ) {
+ // ctrl + up = next year
+ if( e.ctrlKey ) {
+ d = new Date( o.date );
+ d.setDate( Math.min(d.getDate(), datePicker.getDaysPerMonth(d.getMonth(),d.getFullYear() + 1)) );
+ d.setFullYear( d.getFullYear() + 1 );
+ } else {
+ d = new Date( o.date.getFullYear(), o.date.getMonth(), o.date.getDate() - 7 );
+ };
+ } else if ( kc == 40 ) {
+ // ctrl + down = prev year
+ if( e.ctrlKey ) {
+ d = new Date( o.date );
+ d.setDate( Math.min(d.getDate(), datePicker.getDaysPerMonth(d.getMonth(),d.getFullYear() - 1)) );
+ d.setFullYear( d.getFullYear() - 1 );
+ } else {
+ d = new Date( o.date.getFullYear(), o.date.getMonth(), o.date.getDate() + 7 );
+ };
+ };
+
+ var tmpDate = new Date(d);
+
+ if(o.outOfRange(tmpDate)) return o.killEvent(e);
+
+ var cacheDate = new Date(o.date);
+ o.date = tmpDate;
+
+ if(cacheDate.getFullYear() != o.date.getFullYear() || cacheDate.getMonth() != o.date.getMonth()) o.updateTable();
+ else {
+ o.disableTodayButton();
+ var tds = o.table.getElementsByTagName('td');
+ var txt;
+ var start = o.date.getDate() - 6;
+ if(start < 0) start = 0;
+
+ for(var i = start, td; td = tds[i]; i++) {
+ txt = Number(td.firstChild.nodeValue);
+ if(isNaN(txt) || txt != o.date.getDate()) continue;
+ o.removeHighlight();
+ td.id = o.id + "-date-picker-hover";
+ td.className = td.className.replace(/date-picker-hover/g, "") + " date-picker-hover";
+ };
+ };
+ return o.killEvent(e);
+ },
+ gotoToday: function(e) {
+ o.date = new Date();
+ o.updateTable();
+ return o.killEvent(e);
+ },
+ onmousedown: function(e) {
+ if ( e == null ) e = document.parentWindow.event;
+ var el = e.target != null ? e.target : e.srcElement;
+
+ var found = false;
+ while(el.parentNode) {
+ if(el.id && (el.id == "fd-"+o.id || el.id == "fd-but-"+o.id)) {
+ found = true;
+ break;
+ };
+ try {
+ el = el.parentNode;
+ } catch(err) {
+ break;
+ };
+ };
+ if(found) return true;
+ o.stopTimer();
+ datePickerController.hideAll();
+ },
+ onmouseover: function(e) {
+ o.stopTimer();
+ var txt = this.firstChild.nodeValue;
+ if(this.className == "out-of-range" || txt.search(/^[\d]+$/) == -1) return;
+
+ o.removeHighlight();
+
+ this.id = o.id+"-date-picker-hover";
+ this.className = this.className.replace(/date-picker-hover/g, "") + " date-picker-hover";
+
+ o.date.setDate(this.firstChild.nodeValue);
+ o.disableTodayButton();
+ },
+ onclick: function(e) {
+ if(o.opacity != o.opacityTo || this.className.search(/out-of-range|day-disabled/) != -1) return false;
+ if ( e == null ) e = document.parentWindow.event;
+ var el = e.target != null ? e.target : e.srcElement;
+ while ( el.nodeType != 1 ) el = el.parentNode;
+ var d = new Date( o.date );
+ var txt = el.firstChild.data;
+ if(txt.search(/^[\d]+$/) == -1) return;
+ var n = Number( txt );
+ if(isNaN(n)) { return true; };
+ d.setDate( n );
+ o.date = d;
+ o.returnFormattedDate();
+ if(!o.staticPos) o.hide();
+ o.stopTimer();
+ return o.killEvent(e);
+ },
+ incDec: function(e) {
+ if ( e == null ) e = document.parentWindow.event;
+ var el = e.target != null ? e.target : e.srcElement;
+
+ if(el && el.className && el.className.search('fd-disabled') != -1) { return false; }
+ datePickerController.addEvent(document, "mouseup", o.events.clearTimer);
+ o.timerInc = 800;
+ o.dayInc = arguments[1];
+ o.yearInc = arguments[2];
+ o.monthInc = arguments[3];
+ o.timerSet = true;
+
+ o.updateTable();
+ return true;
+ },
+ clearTimer: function(e) {
+ o.stopTimer();
+ o.timerInc = 1000;
+ o.yearInc = 0;
+ o.monthInc = 0;
+ o.dayInc = 0;
+ datePickerController.removeEvent(document, "mouseup", o.events.clearTimer);
+ }
+ };
+ o.stopTimer = function() {
+ o.timerSet = false;
+ window.clearTimeout(o.timer);
+ };
+ o.removeHighlight = function() {
+ if(document.getElementById(o.id+"-date-picker-hover")) {
+ document.getElementById(o.id+"-date-picker-hover").className = document.getElementById(o.id+"-date-picker-hover").className.replace("date-picker-hover", "");
+ document.getElementById(o.id+"-date-picker-hover").id = "";
+ };
+ };
+ o.reset = function() {
+ for(def in o.defaults) { o[def] = o.defaults[def]; };
+ };
+ o.setOpacity = function(op) {
+ o.div.style.opacity = op/100;
+ o.div.style.filter = 'alpha(opacity=' + op + ')';
+ o.opacity = op;
+ };
+ o.fade = function() {
+ window.clearTimeout(o.fadeTimer);
+ o.fadeTimer = null;
+ delete(o.fadeTimer);
+
+ var diff = Math.round(o.opacity + ((o.opacityTo - o.opacity) / 4));
+
+ o.setOpacity(diff);
+
+ if(Math.abs(o.opacityTo - diff) > 3 && !o.noTransparency) {
+ o.fadeTimer = window.setTimeout(o.fade, 50);
+ } else {
+ o.setOpacity(o.opacityTo);
+ if(o.opacityTo == 0) {
+ o.div.style.display = "none";
+ o.visible = false;
+ } else {
+ o.visible = true;
+ };
+ };
+ };
+ o.killEvent = function(e) {
+ e = e || document.parentWindow.event;
+
+ if(e.stopPropagation) {
+ e.stopPropagation();
+ e.preventDefault();
+ };
+
+ /*@cc_on
+ @if(@_win32)
+ e.cancelBubble = true;
+ e.returnValue = false;
+ @end
+ @*/
+ return false;
+ };
+ o.getElem = function() {
+ return document.getElementById(o.id.replace(/^fd-/, '')) || false;
+ };
+ o.setRangeLow = function(range) {
+ if(String(range).search(/^(\d\d?\d\d)(0[1-9]|1[012])(0[1-9]|[12][0-9]|3[01])$/) == -1) range = '';
+ o.low = o.defaults.low = range;
+ if(o.staticPos) o.updateTable(true);
+ };
+ o.setRangeHigh = function(range) {
+ if(String(range).search(/^(\d\d?\d\d)(0[1-9]|1[012])(0[1-9]|[12][0-9]|3[01])$/) == -1) range = '';
+ o.high = o.defaults.high = range;
+ if(o.staticPos) o.updateTable(true);
+ };
+ o.setDisabledDays = function(dayArray) {
+ o.disableDays = o.defaults.disableDays = dayArray;
+ if(o.staticPos) o.updateTable(true);
+ };
+ o.setDisabledDates = function(dateArray) {
+ var fin = [];
+ for(var i = dateArray.length; i-- ;) {
+ if(dateArray[i].match(/^(\d\d\d\d|\*\*\*\*)(0[1-9]|1[012]|\*\*)(0[1-9]|[12][0-9]|3[01])$/) != -1) fin[fin.length] = dateArray[i];
+ };
+ if(fin.length) {
+ o.disabledDates = fin;
+ o.enabledDates = [];
+ if(o.staticPos) o.updateTable(true);
+ };
+ };
+ o.setEnabledDates = function(dateArray) {
+ var fin = [];
+ for(var i = dateArray.length; i-- ;) {
+ if(dateArray[i].match(/^(\d\d\d\d|\*\*\*\*)(0[1-9]|1[012]|\*\*)(0[1-9]|[12][0-9]|3[01]|\*\*)$/) != -1 && dateArray[i] != "********") fin[fin.length] = dateArray[i];
+ };
+ if(fin.length) {
+ o.disabledDates = [];
+ o.enabledDates = fin;
+ if(o.staticPos) o.updateTable(true);
+ };
+ };
+ o.getDisabledDates = function(y, m) {
+ if(o.enabledDates.length) return o.getEnabledDates(y, m);
+ var obj = {};
+ var d = datePicker.getDaysPerMonth(m - 1, y);
+ m = m < 10 ? "0" + String(m) : m;
+ for(var i = o.disabledDates.length; i-- ;) {
+ var tmp = o.disabledDates[i].replace("****", y).replace("**", m);
+ if(tmp < Number(String(y)+m+"01") || tmp > Number(y+String(m)+d)) continue;
+ obj[tmp] = 1;
+ };
+ return obj;
+ };
+ o.getEnabledDates = function(y, m) {
+ var obj = {};
+ var d = datePicker.getDaysPerMonth(m - 1, y);
+ m = m < 10 ? "0" + String(m) : m;
+ var day,tmp,de,me,ye,disabled;
+ for(var dd = 1; dd <= d; dd++) {
+ day = dd < 10 ? "0" + String(dd) : dd;
+ disabled = true;
+ for(var i = o.enabledDates.length; i-- ;) {
+ tmp = o.enabledDates[i];
+ ye = String(o.enabledDates[i]).substr(0,4);
+ me = String(o.enabledDates[i]).substr(4,2);
+ de = String(o.enabledDates[i]).substr(6,2);
+
+ if(ye == y && me == m && de == day) {
+ disabled = false;
+ break;
+ }
+
+ if(ye == "****" || me == "**" || de == "**") {
+ if(ye == "****") tmp = tmp.replace(/^\*\*\*\*/, y);
+ if(me == "**") tmp = tmp = tmp.substr(0,4) + String(m) + tmp.substr(6,2);
+ if(de == "**") tmp = tmp.replace(/\*\*/, day);
+
+ if(tmp == String(y + String(m) + day)) {
+ disabled = false;
+ break;
+ };
+ };
+ };
+ if(disabled) obj[String(y + String(m) + day)] = 1;
+ };
+ return obj;
+ };
+ o.setFirstDayOfWeek = function(e) {
+ if ( e == null ) e = document.parentWindow.event;
+ var elem = e.target != null ? e.target : e.srcElement;
+ if(elem.tagName.toLowerCase() != "th") {
+ while(elem.tagName.toLowerCase() != "th") elem = elem.parentNode;
+ };
+ var cnt = 0;
+ while(elem.previousSibling) {
+ elem = elem.previousSibling;
+ if(elem.tagName.toLowerCase() == "th") cnt++;
+ };
+ o.firstDayOfWeek = (o.firstDayOfWeek + cnt) % 7;
+ o.updateTableHeaders();
+ return o.killEvent(e);
+ };
+ o.truePosition = function(element) {
+ var pos = o.cumulativeOffset(element);
+ if(window.opera) { return pos; }
+ var iebody = (document.compatMode && document.compatMode != "BackCompat")? document.documentElement : document.body;
+ var dsocleft = document.all ? iebody.scrollLeft : window.pageXOffset;
+ var dsoctop = document.all ? iebody.scrollTop : window.pageYOffset;
+ var posReal = o.realOffset(element);
+ return [pos[0] - posReal[0] + dsocleft, pos[1] - posReal[1] + dsoctop];
+ };
+ o.realOffset = function(element) {
+ var t = 0, l = 0;
+ do {
+ t += element.scrollTop || 0;
+ l += element.scrollLeft || 0;
+ element = element.parentNode;
+ } while (element);
+ return [l, t];
+ };
+ o.cumulativeOffset = function(element) {
+ var t = 0, l = 0;
+ do {
+ t += element.offsetTop || 0;
+ l += element.offsetLeft || 0;
+ element = element.offsetParent;
+ } while (element);
+ return [l, t];
+ };
+ o.resize = function() {
+ if(!o.created || !o.getElem()) return;
+
+ o.div.style.visibility = "hidden";
+ if(!o.staticPos) { o.div.style.left = o.div.style.top = "0px"; }
+ o.div.style.display = "block";
+
+ var osh = o.div.offsetHeight;
+ var osw = o.div.offsetWidth;
+
+ o.div.style.visibility = "visible";
+ o.div.style.display = "none";
+
+ if(!o.staticPos) {
+ var elem = document.getElementById('fd-but-' + o.id);
+ var pos = o.truePosition(elem);
+ var trueBody = (document.compatMode && document.compatMode!="BackCompat") ? document.documentElement : document.body;
+ var scrollTop = window.devicePixelRatio || window.opera ? 0 : trueBody.scrollTop;
+ var scrollLeft = window.devicePixelRatio || window.opera ? 0 : trueBody.scrollLeft;
+
+ if(parseInt(trueBody.clientWidth+scrollLeft) < parseInt(osw+pos[0])) {
+ o.div.style.left = Math.abs(parseInt((trueBody.clientWidth+scrollLeft) - osw)) + "px";
+ } else {
+ o.div.style.left = pos[0] + "px";
+ };
+
+ if(parseInt(trueBody.clientHeight+scrollTop) < parseInt(osh+pos[1]+elem.offsetHeight+2)) {
+ o.div.style.top = Math.abs(parseInt(pos[1] - (osh + 2))) + "px";
+ } else {
+ o.div.style.top = Math.abs(parseInt(pos[1] + elem.offsetHeight + 2)) + "px";
+ };
+ };
+ /*@cc_on
+ @if(@_jscript_version <= 5.6)
+ if(o.staticPos) return;
+ o.iePopUp.style.top = o.div.style.top;
+ o.iePopUp.style.left = o.div.style.left;
+ o.iePopUp.style.width = osw + "px";
+ o.iePopUp.style.height = (osh - 2) + "px";
+ @end
+ @*/
+ };
+ o.equaliseDates = function() {
+ var clearDayFound = false;
+ var tmpDate;
+ for(var i = o.low; i <= o.high; i++) {
+ tmpDate = String(i);
+ if(!o.disableDays[new Date(tmpDate.substr(0,4), tmpDate.substr(6,2), tmpDate.substr(4,2)).getDay() - 1]) {
+ clearDayFound = true;
+ break;
+ };
+ };
+ if(!clearDayFound) o.disableDays = o.defaults.disableDays = [0,0,0,0,0,0,0];
+ };
+ o.outOfRange = function(tmpDate) {
+ if(!o.low && !o.high) return false;
+
+ var level = false;
+ if(!tmpDate) {
+ level = true;
+ tmpDate = o.date;
+ };
+
+ var d = (tmpDate.getDate() < 10) ? "0" + tmpDate.getDate() : tmpDate.getDate();
+ var m = ((tmpDate.getMonth() + 1) < 10) ? "0" + (tmpDate.getMonth() + 1) : tmpDate.getMonth() + 1;
+ var y = tmpDate.getFullYear();
+ var dt = String(y)+String(m)+String(d);
+
+ if(o.low && parseInt(dt) < parseInt(o.low)) {
+ if(!level) return true;
+ o.date = new Date(o.low.substr(0,4), o.low.substr(4,2)-1, o.low.substr(6,2), 5, 0, 0);
+ return false;
+ };
+ if(o.high && parseInt(dt) > parseInt(o.high)) {
+ if(!level) return true;
+ o.date = new Date( o.high.substr(0,4), o.high.substr(4,2)-1, o.high.substr(6,2), 5, 0, 0);
+ };
+ return false;
+ };
+ o.create = function() {
+
+ function createTH(details) {
+ var th = document.createElement('th');
+ if(details.thClassName) th.className = details.thClassName;
+ if(details.colspan) {
+ /*@cc_on
+ /*@if (@_win32)
+ th.setAttribute('colSpan',details.colspan);
+ @else @*/
+ th.setAttribute('colspan',details.colspan);
+ /*@end
+ @*/
+ };
+ /*@cc_on
+ /*@if (@_win32)
+ th.unselectable = "on";
+ /*@end@*/
+ return th;
+ };
+
+ function createThAndButton(tr, obj) {
+ for(var i = 0, details; details = obj[i]; i++) {
+ var th = createTH(details);
+ tr.appendChild(th);
+ var but = document.createElement('span');
+ but.className = details.className;
+ but.id = o.id + details.id;
+ but.appendChild(document.createTextNode(details.text));
+ but.title = details.title || "";
+ if(details.onmousedown) but.onmousedown = details.onmousedown;
+ if(details.onclick) but.onclick = details.onclick;
+ if(details.onmouseout) but.onmouseout = details.onmouseout;
+ th.appendChild(but);
+ };
+ };
+
+ /*@cc_on
+ @if(@_jscript_version <= 5.6)
+ if(!document.getElementById("iePopUpHack")) {
+ o.iePopUp = document.createElement('iframe');
+ o.iePopUp.src = "javascript:'<html></html>';";
+ o.iePopUp.setAttribute('className','iehack');
+ o.iePopUp.scrolling="no";
+ o.iePopUp.frameBorder="0";
+ o.iePopUp.name = o.iePopUp.id = "iePopUpHack";
+ document.body.appendChild(o.iePopUp);
+ } else {
+ o.iePopUp = document.getElementById("iePopUpHack");
+ };
+ @end
+ @*/
+
+ if(typeof(fdLocale) == "object" && o.locale) {
+ datePicker.titles = fdLocale.titles;
+ datePicker.months = fdLocale.months;
+ datePicker.fullDay = fdLocale.fullDay;
+ // Optional parameters
+ if(fdLocale.dayAbbr) datePicker.dayAbbr = fdLocale.dayAbbr;
+ if(fdLocale.firstDayOfWeek) o.firstDayOfWeek = o.defaults.firstDayOfWeek = fdLocale.firstDayOfWeek;
+ };
+
+ o.div = document.createElement('div');
+ o.div.style.zIndex = 9999;
+ o.div.id = "fd-"+o.id;
+ o.div.className = "datePicker";
+
+ if(!o.staticPos) {
+ document.getElementsByTagName('body')[0].appendChild(o.div);
+ } else {
+ elem = o.getElem();
+ if(!elem) {
+ o.div = null;
+ return;
+ };
+ o.div.className += " staticDP";
+ o.div.setAttribute("tabIndex", "0");
+ o.div.onfocus = o.events.onfocus;
+ o.div.onblur = o.events.onblur;
+ elem.parentNode.insertBefore(o.div, elem.nextSibling);
+ if(o.hideInput && elem.type && elem.type == "text") elem.setAttribute("type", "hidden");
+ };
+
+ //var nbsp = String.fromCharCode( 160 );
+ var tr, row, col, tableHead, tableBody;
+
+ o.table = document.createElement('table');
+ o.div.appendChild( o.table );
+
+ tableHead = document.createElement('thead');
+ o.table.appendChild( tableHead );
+
+ tr = document.createElement('tr');
+ tableHead.appendChild(tr);
+
+ // Title Bar
+ o.titleBar = createTH({thClassName:"date-picker-title", colspan:7});
+ tr.appendChild( o.titleBar );
+ tr = null;
+
+ var span = document.createElement('span');
+ span.className = "month-display";
+ o.titleBar.appendChild(span);
+
+ span = document.createElement('span');
+ span.className = "year-display";
+ o.titleBar.appendChild(span);
+
+ span = null;
+
+ tr = document.createElement('tr');
+ tableHead.appendChild(tr);
+
+ createThAndButton(tr, [{className:"prev-but", id:"-prev-year-but", text:"\u00AB", title:datePicker.titles[2], onmousedown:function(e) { o.events.incDec(e,0,-1,0); }, onmouseout:o.events.clearTimer },{className:"prev-but", id:"-prev-month-but", text:"\u2039", title:datePicker.titles[0], onmousedown:function(e) { o.events.incDec(e,0,0,-1); }, onmouseout:o.events.clearTimer },{colspan:3, className:"today-but", id:"-today-but", text:datePicker.titles.length > 4 ? datePicker.titles[4] : "Today", onclick:o.events.gotoToday},{className:"next-but", id:"-next-month-but", text:"\u203A", title:datePicker.titles[1], onmousedown:function(e) { o.events.incDec(e,0,0,1); }, onmouseout:o.events.clearTimer },{className:"next-but", id:"-next-year-but", text:"\u00BB", title:datePicker.titles[3], onmousedown:function(e) { o.events.incDec(e,0,1,0); }, onmouseout:o.events.clearTimer }]);
+
+ tableBody = document.createElement('tbody');
+ o.table.appendChild( tableBody );
+
+ for(var rows = 0; rows < 7; rows++) {
+ row = document.createElement('tr');
+
+ if(rows != 0) tableBody.appendChild(row);
+ else tableHead.appendChild(row);
+
+ for(var cols = 0; cols < 7; cols++) {
+ col = (rows == 0) ? document.createElement('th') : document.createElement('td');
+
+ row.appendChild(col);
+ if(rows != 0) {
+ col.appendChild(document.createTextNode(o.nbsp));
+ col.onmouseover = o.events.onmouseover;
+ col.onclick = o.events.onclick;
+ } else {
+ col.className = "date-picker-day-header";
+ col.scope = "col";
+ };
+ col = null;
+ };
+ row = null;
+ };
+
+ // Table headers
+ var but;
+ var ths = o.table.getElementsByTagName('thead')[0].getElementsByTagName('tr')[2].getElementsByTagName('th');
+ for ( var y = 0; y < 7; y++ ) {
+ if(y > 0) {
+ but = document.createElement("span");
+ but.className = "fd-day-header";
+ but.onclick = ths[y].onclick = o.setFirstDayOfWeek;
+ but.appendChild(document.createTextNode(o.nbsp));
+ ths[y].appendChild(but);
+ but = null;
+ } else {
+ ths[y].appendChild(document.createTextNode(o.nbsp));
+ };
+ };
+
+ o.ths = o.table.getElementsByTagName('thead')[0].getElementsByTagName('tr')[2].getElementsByTagName('th');
+ o.trs = o.table.getElementsByTagName('tbody')[0].getElementsByTagName('tr');
+
+ o.updateTableHeaders();
+
+ tableBody = tableHead = tr = createThAndButton = createTH = null;
+ o.created = true;
+
+ if(o.low && o.high && (o.high - o.low < 7)) { o.equaliseDates(); };
+
+ if(o.staticPos) {
+ var yyN = document.getElementById(o.id);
+ datePickerController.addEvent(yyN, "change", o.changeHandler);
+ if(o.splitDate) {
+ var mmN = document.getElementById(o.id+'-mm');
+ var ddN = document.getElementById(o.id+'-dd');
+ datePickerController.addEvent(mmN, "change", o.changeHandler);
+ datePickerController.addEvent(ddN, "change", o.changeHandler);
+ };
+ };
+
+ if(o.staticPos) { o.show(); }
+ else {
+ o.resize();
+ o.fade();
+ };
+ };
+ o.changeHandler = function() {
+ o.setDateFromInput();
+ o.updateTable();
+ };
+ o.setDateFromInput = function() {
+ function m2c(val) {
+ return String(val).length < 2 ? "00".substring(0, 2 - String(val).length) + String(val) : val;
+ };
+
+ o.dateSet = null;
+
+ var elem = o.getElem();
+ if(!elem) return;
+
+ if(!o.splitDate) {
+ var date = datePickerController.dateFormat(elem.value, o.format.search(/m-d-y/i) != -1);
+
+ } else {
+ var mmN = document.getElementById(o.id+'-mm');
+ var ddN = document.getElementById(o.id+'-dd');
+ var tm = parseInt(mmN.tagName.toLowerCase() == "input" ? mmN.value : mmN.options[mmN.selectedIndex].value, 10);
+ var td = parseInt(ddN.tagName.toLowerCase() == "input" ? ddN.value : ddN.options[ddN.selectedIndex].value, 10);
+ var ty = parseInt(elem.tagName.toLowerCase() == "input" ? elem.value : elem.options[elem.selectedIndex || 0].value, 10);
+ var date = datePickerController.dateFormat(tm + "/" + td + "/" + ty, true);
+ };
+
+ var badDate = false;
+ if(!date) {
+ badDate = true;
+ date = String(new Date().getFullYear()) + m2c(new Date().getMonth()+1) + m2c(new Date().getDate());
+ };
+
+ var d,m,y;
+ y = Number(date.substr(0, 4));
+ m = Number(date.substr(4, 2)) - 1;
+ d = Number(date.substr(6, 2));
+
+ var dpm = datePicker.getDaysPerMonth(m, y);
+ if(d > dpm) d = dpm;
+
+ if(new Date( y, m, d ) == 'Invalid Date' || new Date( y, m, d ) == 'NaN') {
+
+ badDate = true;
+ o.date = new Date();
+ o.date.setHours(5);
+ return;
+ };
+
+ o.date = new Date( y, m, d );
+
+ o.date.setHours(5);
+
+ if(!badDate) o.dateSet = new Date(o.date);
+ m2c = null;
+ };
+ o.returnFormattedDate = function() {
+
+ var elem = o.getElem();
+ if(!elem) return;
+
+ var d = (o.date.getDate() < 10) ? "0" + o.date.getDate() : o.date.getDate();
+ var m = ((o.date.getMonth() + 1) < 10) ? "0" + (o.date.getMonth() + 1) : o.date.getMonth() + 1;
+ var yyyy = o.date.getFullYear();
+ var disabledDates = o.getDisabledDates(yyyy, m);
+ var weekDay = ( o.date.getDay() + 6 ) % 7;
+
+ if(!(o.disableDays[weekDay] || String(yyyy)+m+d in disabledDates)) {
+
+ if(o.splitDate) {
+ var ddE = document.getElementById(o.id+"-dd");
+ var mmE = document.getElementById(o.id+"-mm");
+
+ if(ddE.tagName.toLowerCase() == "input") { ddE.value = d; }
+ else { ddE.selectedIndex = d - 1; };
+
+ if(mmE.tagName.toLowerCase() == "input") { mmE.value = m; }
+ else { mmE.selectedIndex = m - 1; };
+
+ if(elem.tagName.toLowerCase() == "input") elem.value = yyyy;
+ else {
+ for(var opt = 0; opt < elem.options.length; opt++) {
+ if(elem.options[opt].value == yyyy) {
+ elem.selectedIndex = opt;
+ break;
+ };
+ };
+ };
+ } else {
+ elem.value = o.format.replace('y',yyyy).replace('m',m).replace('d',d).replace(/-/g,o.divider);
+ };
+ if(!elem.type || elem.type && elem.type != "hidden") elem.focus();
+ if(o.staticPos) {
+ o.dateSet = new Date( o.date );
+ o.updateTable();
+ };
+
+ // Programmatically fire the onchange event
+ if(document.createEvent) {
+ var onchangeEvent = document.createEvent('HTMLEvents');
+ onchangeEvent.initEvent('change', true, false);
+ elem.dispatchEvent(onchangeEvent);
+ } else if(document.createEventObject) {
+ elem.fireEvent('onchange');
+ };
+ };
+ };
+
+ o.disableTodayButton = function() {
+ var today = new Date();
+ document.getElementById(o.id + "-today-but").className = document.getElementById(o.id + "-today-but").className.replace("fd-disabled", "");
+ if(o.outOfRange(today) || (o.date.getDate() == today.getDate() && o.date.getMonth() == today.getMonth() && o.date.getFullYear() == today.getFullYear())) {
+ document.getElementById(o.id + "-today-but").className += " fd-disabled";
+ document.getElementById(o.id + "-today-but").onclick = null;
+ } else {
+ document.getElementById(o.id + "-today-but").onclick = o.events.gotoToday;
+ };
+ };
+
+ o.updateTableHeaders = function() {
+ var d, but;
+ var ths = o.ths;
+ for ( var y = 0; y < 7; y++ ) {
+ d = (o.firstDayOfWeek + y) % 7;
+ ths[y].title = datePicker.fullDay[d];
+
+ if(y > 0) {
+ but = ths[y].getElementsByTagName("span")[0];
+ but.removeChild(but.firstChild);
+ but.appendChild(document.createTextNode(datePicker.dayAbbr ? datePicker.dayAbbr[d] : datePicker.fullDay[d].charAt(0)));
+ but.title = datePicker.fullDay[d];
+ but = null;
+ } else {
+ ths[y].removeChild(ths[y].firstChild);
+ ths[y].appendChild(document.createTextNode(datePicker.dayAbbr ? datePicker.dayAbbr[d] : datePicker.fullDay[d].charAt(0)));
+ };
+ };
+ o.updateTable();
+ };
+
+ o.updateTable = function(noCallback) {
+
+ if(o.timerSet) {
+ var d = new Date(o.date);
+ d.setDate( Math.min(d.getDate()+o.dayInc, datePicker.getDaysPerMonth(d.getMonth()+o.monthInc,d.getFullYear()+o.yearInc)) );
+ d.setMonth( d.getMonth() + o.monthInc );
+ d.setFullYear( d.getFullYear() + o.yearInc );
+ o.date = d;
+ };
+
+ if(!noCallback && "onupdate" in datePickerController && typeof(datePickerController.onupdate) == "function") datePickerController.onupdate(o);
+
+ o.outOfRange();
+ o.disableTodayButton();
+
+ // Set the tmpDate to the second day of this month (to avoid daylight savings time madness on Windows)
+ var tmpDate = new Date( o.date.getFullYear(), o.date.getMonth(), 2 );
+ tmpDate.setHours(5);
+
+ var tdm = tmpDate.getMonth();
+ var tdy = tmpDate.getFullYear();
+
+ // Do the disableDates for this year and month
+ var disabledDates = o.getDisabledDates(o.date.getFullYear(), o.date.getMonth() + 1);
+
+ var today = new Date();
+
+ // Previous buttons out of range
+ var b = document.getElementById(o.id + "-prev-year-but");
+ b.className = b.className.replace("fd-disabled", "");
+ if(o.outOfRange(new Date((tdy - 1), Number(tdm), datePicker.getDaysPerMonth(Number(tdm), tdy-1)))) {
+ b.className += " fd-disabled";
+ if(o.yearInc == -1) o.stopTimer();
+ };
+
+ b = document.getElementById(o.id + "-prev-month-but")
+ b.className = b.className.replace("fd-disabled", "");
+ if(o.outOfRange(new Date(tdy, (Number(tdm) - 1), datePicker.getDaysPerMonth(Number(tdm)-1, tdy)))) {
+ b.className += " fd-disabled";
+ if(o.monthInc == -1) o.stopTimer();
+ };
+
+ // Next buttons out of range
+ b= document.getElementById(o.id + "-next-year-but")
+ b.className = b.className.replace("fd-disabled", "");
+ if(o.outOfRange(new Date((tdy + 1), Number(tdm), 1))) {
+ b.className += " fd-disabled";
+ if(o.yearInc == 1) o.stopTimer();
+ };
+
+ b = document.getElementById(o.id + "-next-month-but")
+ b.className = b.className.replace("fd-disabled", "");
+ if(o.outOfRange(new Date(tdy, Number(tdm) + 1, 1))) {
+ b.className += " fd-disabled";
+ if(o.monthInc == 1) o.stopTimer();
+ };
+
+ b = null;
+
+ var cd = o.date.getDate();
+ var cm = o.date.getMonth();
+ var cy = o.date.getFullYear();
+
+ // Title Bar
+ var span = o.titleBar.getElementsByTagName("span");
+ while(span[0].firstChild) span[0].removeChild(span[0].firstChild);
+ while(span[1].firstChild) span[1].removeChild(span[1].firstChild);
+ span[0].appendChild(document.createTextNode(datePicker.months[cm] + o.nbsp));
+ span[1].appendChild(document.createTextNode(cy));
+
+ tmpDate.setDate( 1 );
+
+ var dt, cName, td, tds, i;
+ var weekDay = ( tmpDate.getDay() + 6 ) % 7;
+ var firstColIndex = (( (weekDay - o.firstDayOfWeek) + 7 ) % 7) - 1;
+ var dpm = datePicker.getDaysPerMonth(cm, cy);
+
+ var todayD = today.getDate();
+ var todayM = today.getMonth();
+ var todayY = today.getFullYear();
+
+ var c = "class";
+ /*@cc_on
+ @if(@_win32)
+ c = "className";
+ @end
+ @*/
+
+ var stub = String(tdy) + (String(tdm+1).length < 2 ? "0" + (tdm+1) : tdm+1);
+
+ for(var row = 0; row < 6; row++) {
+
+ tds = o.trs[row].getElementsByTagName('td');
+
+ for(var col = 0; col < 7; col++) {
+
+ td = tds[col];
+ td.removeChild(td.firstChild);
+
+ td.setAttribute("id", "");
+ td.setAttribute("title", "");
+
+ i = (row * 7) + col;
+
+ if(i > firstColIndex && i <= (firstColIndex + dpm)) {
+ dt = i - firstColIndex;
+ tmpDate.setDate(dt);
+ td.appendChild(document.createTextNode(dt));
+
+ if(o.outOfRange(tmpDate)) {
+ td.setAttribute(c, "out-of-range");
+ } else {
+
+ cName = [];
+ weekDay = ( tmpDate.getDay() + 6 ) % 7;
+
+ if(dt == todayD && tdm == todayM && tdy == todayY) {
+ cName.push("date-picker-today");
+ };
+
+ if(o.dateSet != null && o.dateSet.getDate() == dt && o.dateSet.getMonth() == tdm && o.dateSet.getFullYear() == tdy) {
+ cName.push("date-picker-selected-date");
+ };
+
+ if(o.disableDays[weekDay] || stub + String(dt) in disabledDates) {
+ cName.push("day-disabled");
+ } else if(o.highlightDays[weekDay]) {
+ cName.push("date-picker-highlight");
+ };
+
+ if(cd == dt) {
+ td.setAttribute("id", o.id + "-date-picker-hover");
+ cName.push("date-picker-hover");
+ };
+
+ cName.push("dm-" + dt + '-' + (tdm + 1) + " " + " dmy-" + dt + '-' + (tdm + 1) + '-' + tdy);
+ td.setAttribute(c, cName.join(' '));
+ td.setAttribute("title", datePicker.months[cm] + o.nbsp + dt + "," + o.nbsp + cy);
+ };
+ } else {
+ td.appendChild(document.createTextNode(o.nbsp));
+ td.setAttribute(c, "date-picker-unused");
+ };
+ };
+ };
+
+ if(o.timerSet) {
+ o.timerInc = 50 + Math.round(((o.timerInc - 50) / 1.8));
+ o.timer = window.setTimeout(o.updateTable, o.timerInc);
+ };
+ };
+ o.addKeyboardEvents = function() {
+ datePickerController.addEvent(document, "keypress", o.events.onkeydown);
+ /*@cc_on
+ @if(@_win32)
+ datePickerController.removeEvent(document, "keypress", o.events.onkeydown);
+ datePickerController.addEvent(document, "keydown", o.events.onkeydown);
+ @end
+ @*/
+ if(window.devicePixelRatio) {
+ datePickerController.removeEvent(document, "keypress", o.events.onkeydown);
+ datePickerController.addEvent(document, "keydown", o.events.onkeydown);
+ };
+ };
+ o.removeKeyboardEvents =function() {
+ datePickerController.removeEvent(document, "keypress", o.events.onkeydown);
+ datePickerController.removeEvent(document, "keydown", o.events.onkeydown);
+ };
+ o.show = function() {
+ var elem = o.getElem();
+ if(!elem || o.visible || elem.disabled) return;
+
+ o.reset();
+ o.setDateFromInput();
+ o.updateTable();
+
+ if(!o.staticPos) o.resize();
+
+ datePickerController.addEvent(o.staticPos ? o.table : document, "mousedown", o.events.onmousedown);
+
+ if(!o.staticPos) { o.addKeyboardEvents(); };
+
+ o.opacityTo = o.noTransparency ? 99 : 90;
+ o.div.style.display = "block";
+ /*@cc_on
+ @if(@_jscript_version <= 5.6)
+ if(!o.staticPos) o.iePopUp.style.display = "block";
+ @end
+ @*/
+
+ o.fade();
+ o.visible = true;
+ };
+ o.hide = function() {
+ if(!o.visible) return;
+ o.stopTimer();
+ if(o.staticPos) return;
+
+ datePickerController.removeEvent(document, "mousedown", o.events.onmousedown);
+ datePickerController.removeEvent(document, "mouseup", o.events.clearTimer);
+ o.removeKeyboardEvents();
+
+ /*@cc_on
+ @if(@_jscript_version <= 5.6)
+ o.iePopUp.style.display = "none";
+ @end
+ @*/
+
+ o.opacityTo = 0;
+ o.fade();
+ o.visible = false;
+ var elem = o.getElem();
+ if(elem) elem.focus();
+ };
+ o.destroy = function() {
+ // Cleanup for Internet Explorer
+ datePickerController.removeEvent(o.staticPos ? o.table : document, "mousedown", o.events.onmousedown);
+ datePickerController.removeEvent(document, "mouseup", o.events.clearTimer);
+ o.removeKeyboardEvents();
+
+ if(o.staticPos) {
+ var yyN = document.getElementById(o.id);
+ datePickerController.removeEvent(yyN, "change", o.changeHandler);
+ if(o.splitDate) {
+ var mmN = document.getElementById(o.id+'-mm');
+ var ddN = document.getElementById(o.id+'-dd');
+
+ datePickerController.removeEvent(mmN, "change", o.changeHandler);
+ datePickerController.removeEvent(ddN, "change", o.changeHandler);
+ };
+ o.div.onfocus = o.div.onblur = null;
+ };
+
+ var ths = o.table.getElementsByTagName("th");
+ for(var i = 0, th; th = ths[i]; i++) {
+ th.onmouseover = th.onmouseout = th.onmousedown = th.onclick = null;
+ };
+
+ var tds = o.table.getElementsByTagName("td");
+ for(var i = 0, td; td = tds[i]; i++) {
+ td.onmouseover = td.onclick = null;
+ };
+
+ var butts = o.table.getElementsByTagName("span");
+ for(var i = 0, butt; butt = butts[i]; i++) {
+ butt.onmousedown = butt.onclick = butt.onkeypress = null;
+ };
+
+ o.ths = o.trs = null;
+
+ clearTimeout(o.fadeTimer);
+ clearTimeout(o.timer);
+ o.fadeTimer = o.timer = null;
+
+ /*@cc_on
+ @if(@_jscript_version <= 5.6)
+ o.iePopUp = null;
+ @end
+ @*/
+
+ if(!o.staticPos && document.getElementById(o.id.replace(/^fd-/, 'fd-but-'))) {
+ var butt = document.getElementById(o.id.replace(/^fd-/, 'fd-but-'));
+ butt.onclick = butt.onpress = null;
+ };
+
+ o.titleBar = o.table = o.div = null;
+ o = null;
+ };
+
+ o.create();
+};
+
+datePickerController = function() {
+ var datePickers = {};
+ var uniqueId = 0;
+
+ var addEvent = function(obj, type, fn) {
+ if( obj.attachEvent ) {
+ obj["e"+type+fn] = fn;
+ obj[type+fn] = function(){obj["e"+type+fn]( window.event );};
+ obj.attachEvent( "on"+type, obj[type+fn] );
+ } else {
+ obj.addEventListener( type, fn, true );
+ };
+ };
+ var removeEvent = function(obj, type, fn) {
+ try {
+ if( obj.detachEvent ) {
+ obj.detachEvent( "on"+type, obj[type+fn] );
+ obj[type+fn] = null;
+ } else {
+ obj.removeEventListener( type, fn, true );
+ };
+ } catch(err) {};
+ };
+
+ var hideAll = function(exception) {
+ var dp;
+ for(dp in datePickers) {
+ if(!datePickers[dp].created || datePickers[dp].staticPos) continue;
+ if(exception && exception == datePickers[dp].id) { continue; };
+ if(document.getElementById(datePickers[dp].id)) { datePickers[dp].hide(); };
+ };
+ };
+ var cleanUp = function() {
+ var dp;
+ for(dp in datePickers) {
+ if(!document.getElementById(datePickers[dp].id)) {
+ dpElem = document.getElementById("fd-"+datePickers[dp].id);
+ datePickers[dp].destroy();
+ datePickers[dp] = null;
+ delete datePickers[dp];
+ if(dpElem) {
+ dpElem.parentNode.removeChild(dpElem);
+ };
+ };
+ };
+ };
+ var destroy = function() {
+ for(dp in datePickers) {
+ if(!datePickers[dp].created) continue;
+ datePickers[dp].destroy();
+ datePickers[dp] = null;
+ delete datePickers[dp];
+ };
+ datePickers = null;
+ /*@cc_on
+ @if(@_jscript_version <= 5.6)
+ if(document.getElementById("iePopUpHack")) {
+ document.body.removeChild(document.getElementById("iePopUpHack"));
+ };
+ @end
+ @*/
+ datePicker.script = null;
+ removeEvent(window, 'load', datePickerController.create);
+ removeEvent(window, 'unload', datePickerController.destroy);
+ };
+ var dateFormat = function(dateIn, favourMDY) {
+ var dateTest = [
+ { regExp:/^(0?[1-9]|[12][0-9]|3[01])([- \/.])(0?[1-9]|1[012])([- \/.])((\d\d)?\d\d)$/, d:1, m:3, y:5 }, // dmy
+ { regExp:/^(0?[1-9]|1[012])([- \/.])(0?[1-9]|[12][0-9]|3[01])([- \/.])((\d\d)?\d\d)$/, d:3, m:1, y:5 }, // mdy
+ { regExp:/^(\d\d\d\d)([- \/.])(0?[1-9]|1[012])([- \/.])(0?[1-9]|[12][0-9]|3[01])$/, d:5, m:3, y:1 } // ymd
+ ];
+
+ var start;
+ var cnt = 0;
+ while(cnt < 3) {
+ start = (cnt + (favourMDY ? 4 : 3)) % 3;
+ if(dateIn.match(dateTest[start].regExp)) {
+ res = dateIn.match(dateTest[start].regExp);
+ y = res[dateTest[start].y];
+ m = res[dateTest[start].m];
+ d = res[dateTest[start].d];
+ if(m.length == 1) m = "0" + m;
+ if(d.length == 1) d = "0" + d;
+ if(y.length != 4) y = (parseInt(y) < 50) ? '20' + y : '19' + y;
+ return String(y)+m+d;
+ };
+ cnt++;
+ };
+ return 0;
+ };
+ var joinNodeLists = function() {
+ if(!arguments.length) { return []; }
+ var nodeList = [];
+ for (var i = 0; i < arguments.length; i++) {
+ for (var j = 0, item; item = arguments[i][j]; j++) {
+ nodeList[nodeList.length] = item;
+ };
+ };
+ return nodeList;
+ };
+ var addDatePicker = function(inpId, options) {
+ if(!(inpId in datePickers)) datePickers[inpId] = new datePicker(options);
+ };
+ var getDatePicker = function(inpId) {
+ if(!(inpId in datePickers)) throw "No datePicker has been created for the form element with an id of '" + inpId.toString() + "'";
+ return datePickers[inpId];
+ };
+ var create = function() {
+ if(!(typeof document.createElement != "undefined" && typeof document.documentElement != "undefined" && typeof document.documentElement.offsetWidth == "number")) return;
+
+ cleanUp();
+
+ var inputs = joinNodeLists(document.getElementsByTagName('input'), document.getElementsByTagName('select'));
+ var regExp1 = /disable-days-([1-7]){1,6}/g; // the days to disable
+ var regExp2 = /no-transparency/g; // do not use transparency effects
+ var regExp3 = /highlight-days-([1-7]){1,7}/g; // the days to highlight in red
+ var regExp4 = /range-low-(\d\d\d\d-\d\d-\d\d)/g; // the lowest selectable date
+ var regExp5 = /range-high-(\d\d\d\d-\d\d-\d\d)/g; // the highest selectable date
+ var regExp6 = /format-(d-m-y|m-d-y|y-m-d)/g; // the input/output date format
+ var regExp7 = /divider-(dot|slash|space|dash)/g; // the character used to divide the date
+ var regExp8 = /no-locale/g; // do not attempt to detect the browser language
+ var regExp9 = /no-fade/g; // always show the datepicker
+ var regExp10 = /hide-input/g; // hide the input
+
+ for(var i=0, inp; inp = inputs[i]; i++) {
+ if(inp.className && (inp.className.search(regExp6) != -1 || inp.className.search(/split-date/) != -1) && ((inp.tagName.toLowerCase() == "input" && (inp.type == "text" || inp.type == "hidden")) || inp.tagName.toLowerCase() == "select")) {
+
+ //if(!inp.id) { inp.id = "fdDatePicker-" + datePickerController.uniqueId++; };
+ if(!inp.id) { inp.id = "fdDatePicker-" + uniqueId++; };
+ var options = {
+ id:inp.id,
+ low:"",
+ high:"",
+ divider:"/",
+ format:"d-m-y",
+ highlightDays:[0,0,0,0,0,1,1],
+ disableDays:[0,0,0,0,0,0,0],
+ locale:inp.className.search(regExp8) == -1,
+ splitDate:0,
+ noTransparency:inp.className.search(regExp2) != -1,
+ staticPos:inp.className.search(regExp9) != -1,
+ hideInput:inp.className.search(regExp10) != -1
+ };
+
+ if(!options.staticPos) {
+ options.hideInput = false;
+ } else {
+ options.noTransparency = true;
+ };
+
+ // Split the date into three parts ?
+ if(inp.className.search(/split-date/) != -1) {
+ if(document.getElementById(inp.id+'-dd') && document.getElementById(inp.id+'-mm') && document.getElementById(inp.id+'-dd').tagName.search(/input|select/i) != -1 && document.getElementById(inp.id+'-mm').tagName.search(/input|select/i) != -1) {
+ options.splitDate = 1;
+ };
+ };
+
+ // Date format(variations of d-m-y)
+ if(inp.className.search(regExp6) != -1) {
+ options.format = inp.className.match(regExp6)[0].replace('format-','');
+ };
+
+ // What divider to use, a "/", "-", "." or " "
+ if(inp.className.search(regExp7) != -1) {
+ var dividers = { dot:".", space:" ", dash:"-", slash:"/" };
+ options.divider = (inp.className.search(regExp7) != -1 && inp.className.match(regExp7)[0].replace('divider-','') in dividers) ? dividers[inp.className.match(regExp7)[0].replace('divider-','')] : "/";
+ };
+
+ // The days to highlight
+ if(inp.className.search(regExp3) != -1) {
+ var tmp = inp.className.match(regExp3)[0].replace(/highlight-days-/, '');
+ options.highlightDays = [0,0,0,0,0,0,0];
+ for(var j = 0; j < tmp.length; j++) {
+ options.highlightDays[tmp.charAt(j) - 1] = 1;
+ };
+ };
+
+ // The days to disable
+ if(inp.className.search(regExp1) != -1) {
+ var tmp = inp.className.match(regExp1)[0].replace(/disable-days-/, '');
+ options.disableDays = [0,0,0,0,0,0,0];
+ for(var j = 0; j < tmp.length; j++) {
+ options.disableDays[tmp.charAt(j) - 1] = 1;
+ };
+ };
+
+ // The lower limit
+ if(inp.className.search(/range-low-today/i) != -1) {
+ options.low = datePickerController.dateFormat((new Date().getMonth() + 1) + "/" + new Date().getDate() + "/" + new Date().getFullYear(), true);
+ } else if(inp.className.search(regExp4) != -1) {
+ options.low = datePickerController.dateFormat(inp.className.match(regExp4)[0].replace(/range-low-/, ''), false);
+ if(!options.low) {
+ options.low = '';
+ };
+ };
+
+ // The higher limit
+ if(inp.className.search(/range-high-today/i) != -1 && inp.className.search(/range-low-today/i) == -1) {
+ options.high = datePickerController.dateFormat((new Date().getMonth() + 1) + "/" + new Date().getDate() + "/" + new Date().getFullYear(), true);
+ } else if(inp.className.search(regExp5) != -1) {
+ options.high = datePickerController.dateFormat(inp.className.match(regExp5)[0].replace(/range-high-/, ''), false);
+ if(!options.high) {
+ options.high = '';
+ };
+ };
+
+ // Always round lower & higher limits if a selectList involved
+ if(inp.tagName.search(/select/i) != -1) {
+ options.low = options.low ? Math.min(inp.options[0].value, inp.options[inp.options.length - 1].value) + String(options.low).substr(4,4) : datePickerController.dateFormat(Math.min(inp.options[0].value, inp.options[inp.options.length - 1].value) + "/01/01");
+ options.high = options.high ? Math.max(inp.options[0].value, inp.options[inp.options.length - 1].value) + String(options.high).substr(4,4) : datePickerController.dateFormat(Math.max(inp.options[0].value, inp.options[inp.options.length - 1].value) + "/12/31");
+ };
+
+ // Datepicker is already created so reset it's defaults
+ if(document.getElementById('fd-'+inp.id)) {
+ for(var opt in options) {
+ datePickers[inp.id].defaults[opt] = options[opt];
+ };
+ datePickers[inp.id].enabledDays = datePickers[inp.id].disabledDays = [];
+ };
+
+ // Create the button (if needs be)
+ var but;
+
+ if(!options.staticPos && !document.getElementById("fd-but-" + inp.id)) {
+ but = document.createElement('a');
+ but.href = "#";
+
+ var span = document.createElement('span');
+ span.appendChild(document.createTextNode(String.fromCharCode( 160 )));
+
+ but.className = "date-picker-control";
+ but.title = (typeof(fdLocale) == "object" && options.locale && fdLocale.titles.length > 5) ? fdLocale.titles[5] : "";
+
+ but.id = "fd-but-" + inp.id;
+ but.appendChild(span);
+
+ if(inp.nextSibling) {
+ inp.parentNode.insertBefore(but, inp.nextSibling);
+ } else {
+ inp.parentNode.appendChild(but);
+ };
+ } else if(!options.staticPos) {
+ but = document.getElementById("fd-but-" + inp.id);
+ };
+
+ // Add button events
+ if(!options.staticPos) {
+ but.onclick = but.onpress = function(e) {
+ e = e || window.event;
+ var inpId = this.id.replace('fd-but-','');
+ try { var dp = datePickerController.getDatePicker(inpId); } catch(err) { return false; };
+
+ if(e.type == "press") {
+ var kc = e.keyCode != null ? e.keyCode : e.charCode;
+ if(kc != 13) return true;
+ if(dp.visible) {
+ hideAll();
+ return false;
+ };
+ };
+
+ if(!dp.visible) {
+ datePickerController.hideAll(inpId);
+ dp.show();
+ } else {
+ datePickerController.hideAll();
+ };
+ return false;
+ };
+ };
+
+ // Create the datePicker (if needs be)
+ if(!document.getElementById('fd-'+inp.id)) {
+ //datePickerController.datePickers[inp.id] = new datePicker(options);
+ datePickers[inp.id] = new datePicker(options);
+ };
+
+ but = null;
+ };
+ };
+ }
+
+ return {
+ addEvent:addEvent,
+ removeEvent:removeEvent,
+ create:create,
+ destroy:destroy,
+ cleanUp:cleanUp,
+ addDatePicker:addDatePicker,
+ getDatePicker:getDatePicker,
+ dateFormat:dateFormat,
+ datePickers:datePickers,
+ hideAll:hideAll
+ };
+}();
+
+
+})();
+
+datePickerController.addEvent(window, 'load', datePickerController.create);
+datePickerController.addEvent(window, 'unload', datePickerController.destroy);
40 public/javascripts/lang/af.js
@@ -0,0 +1,40 @@
+var fdLocale = {
+ months:[
+ "Januarie",
+ "Februarie",
+ "Maart",
+ "April",
+ "Mei",
+ "Junie",
+ "Julie",
+ "Augustus",
+ "September",
+ "Oktober",
+ "November",
+ "Desember"
+ ],
+ fullDay:[
+ "Maandag",
+ "Dinsdag",
+ "Woensdag",
+ "Donderdag",
+ "Vrydag",
+ "Saterdag",
+ "Sondag"
+ ],
+ /* Only stipulate the dayAbbr should the first letter of the fullDay not suffice
+
+ dayAbbr:[],
+ */
+
+ /* Only stipulate the firstDayOfWeek should the first day not be Monday
+
+ firstDayOfWeek:0,
+ */
+ titles:[
+ "Vorige maand",
+ "Volgende maand",
+ "Vorige jaar",
+ "Volgende jaar"
+ ]
+};
50 public/javascripts/lang/ar.js
@@ -0,0 +1,50 @@
+var fdLocale = {
+ months:[
+ "يناير",
+ "فبراير",
+ "مارس",
+ "أبريل",
+ "مايو",
+ "يونيو",
+ "يوليو",
+ "أغسطس",
+ "سبتمبر",
+ "أكتوبر",
+ "نوٿمبر",
+ "ديسمبر"
+ ],
+ fullDay:[
+ "الاثنين",
+ "الثلاثاء",
+ "الأربعاء",
+ "الخميس",
+ "الجمعة",
+ "السبت",
+ "الأحد"
+ ],
+
+ /* Only stipulate the dayAbbr should the first letter of the fullDay not suffice */
+
+
+ dayAbbr:[
+ "الاثنين",
+ "الثلاثاء",
+ "الأربعاء",
+ "الخميس",
+ "الجمعة",
+ "السبت",
+ "الأحد"
+ ],
+
+ /* Only stipulate the firstDayOfWeek should the first day not be Monday
+
+ firstDayOfWeek:0,
+ */
+
+ titles:[
+ "الشهر السابق",
+ "الشهر المقبل",
+ "السنة السابقة",
+ "السنة المقبلة"
+ ]
+};
40 public/javascripts/lang/de.js
@@ -0,0 +1,40 @@
+var fdLocale = {
+ months:[
+ "Januar",
+ "Februar",
+ "M\u00e4rz",
+ "April",
+ "Mai",
+ "Juni",
+ "Juli",
+ "August",
+ "September",
+ "Oktober",
+ "November",
+ "Dezember"
+ ],
+ fullDay:[
+ "Montag",
+ "Dienstag",
+ "Mittwoch",
+ "Donnerstag",
+ "Freitag",
+ "Samstag",
+ "Sonntag"
+ ],
+ /* Only stipulate the dayAbbr should the first letter of the fullDay not suffice
+
+ dayAbbr:[],
+ */
+
+ /* Only stipulate the firstDayOfWeek should the first day not be Monday
+
+ firstDayOfWeek:0,
+ */
+ titles:[
+ "Voriger Monat",
+ "N\u00e4chst. Monat",
+ "Voriges Jahr",
+ "N\u00e4chst. Jahr"
+ ]
+};
40 public/javascripts/lang/du.js
@@ -0,0 +1,40 @@
+var fdLocale = {
+ months:[
+