activescaffold/active_scaffold

View on GitHub
lib/active_scaffold/bridges/date_picker/helper.rb

Summary

Maintainability
A
3 hrs
Test Coverage
F
59%
module ActiveScaffold::Bridges
  class DatePicker
    module Helper
      UNSUPPORTED_FORMAT_OPTIONS = /%[cUWwxXZ]/
      DATE_FORMAT_CONVERSION = {
        /%a/ => 'D',
        /%A/ => 'DD',
        /%b/ => 'M',
        /%B/ => 'MM',
        /%d/ => 'dd',
        /%e|%-d/ => 'd',
        /%j/ => 'oo',
        /%m/ => 'mm',
        /%-m|%-m/ => 'm',
        /%y/ => 'y',
        /%Y/ => 'yy',
        /%H/ => 'HH', # options ampm => false
        /%I/ => 'hh', # options ampm => true
        /%M/ => 'mm',
        /%p/ => 'tt',
        /%S/ => 'ss',
        /%z/ => 'z',
        UNSUPPORTED_FORMAT_OPTIONS => ''
      }.freeze

      def self.date_options_for_locales
        I18n.available_locales.collect do |locale|
          locale_date_options = date_options(locale)
          "$.datepicker.regional['#{locale}'] = #{locale_date_options.to_json};" if locale_date_options
        end.compact.join('')
      end

      def self.date_options(locale)
        date_picker_options = {
          :closeText => as_(:close),
          :prevText => as_(:previous),
          :nextText => as_(:next),
          :currentText => as_(:today),
          :monthNames => I18n.translate!('date.month_names', :locale => locale)[1..-1],
          :monthNamesShort => I18n.translate!('date.abbr_month_names', :locale => locale)[1..-1],
          :dayNames => I18n.translate!('date.day_names', :locale => locale),
          :dayNamesShort => I18n.translate!('date.abbr_day_names', :locale => locale),
          :dayNamesMin => I18n.translate!('date.abbr_day_names', :locale => locale),
          :changeYear => true,
          :changeMonth => true
        }

        as_date_picker_options = I18n.translate! :date_picker_options, :scope => :active_scaffold, :locale => locale, :default => ''
        date_picker_options.merge!(as_date_picker_options) if as_date_picker_options.is_a? Hash
        Rails.logger.warn "ActiveScaffold: Missing date picker localization for your locale: #{locale}" if as_date_picker_options.blank?

        js_format = to_datepicker_format(I18n.translate!('date.formats.default', :locale => locale, :default => ''))
        date_picker_options[:dateFormat] = js_format if js_format.present?
        date_picker_options
      rescue StandardError
        raise if locale == I18n.locale
      end

      def self.datetime_options_for_locales
        I18n.available_locales.collect do |locale|
          locale_datetime_options = datetime_options(locale)
          "$.timepicker.regional['#{locale}'] = #{locale_datetime_options.to_json};" if locale_datetime_options
        end.compact.join('')
      end

      def self.datetime_options(locale)
        rails_time_format = I18n.translate! 'time.formats.picker', :locale => locale, :default => '%a, %d %b %Y %H:%M:%S'
        datetime_picker_options = {
          :ampm => false,
          :hourText => I18n.translate!('datetime.prompts.hour', :locale => locale),
          :minuteText => I18n.translate!('datetime.prompts.minute', :locale => locale),
          :secondText => I18n.translate!('datetime.prompts.second', :locale => locale)
        }

        as_datetime_picker_options = I18n.translate! :datetime_picker_options, :scope => :active_scaffold, :locale => locale, :default => ''
        datetime_picker_options.merge!(as_datetime_picker_options) if as_datetime_picker_options.is_a? Hash
        Rails.logger.warn "ActiveScaffold: Missing datetime picker localization for your locale: #{locale}" if as_datetime_picker_options.blank?

        datetime_picker_options.merge! format_to_datetime_picker(rails_time_format)
        datetime_picker_options
      rescue StandardError
        raise if locale == I18n.locale
      end

      def self.to_datepicker_format(rails_format)
        return nil if rails_format.nil?
        if rails_format.match?(UNSUPPORTED_FORMAT_OPTIONS)
          options = UNSUPPORTED_FORMAT_OPTIONS.to_s.scan(/\[(.*)\]/).dig(0, 0)&.each_char&.map { |c| "%#{c}" }
          Rails.logger.warn(
            "AS DatePicker::Helper: rails date format #{rails_format} includes options "\
            "which can't be converted to jquery datepicker format. "\
            "Options #{options.join(', ')} are not supported by datepicker and will be removed"
          )
        end
        js_format = rails_format.dup
        js_format.gsub!(/([ ]|^)([^% ]\S*)/, " '\\2'")
        DATE_FORMAT_CONVERSION.each do |key, value|
          js_format.gsub!(key, value)
        end
        js_format
      end

      def self.format_to_datetime_picker(rails_time_format)
        date_format, time_format = split_datetime_format(to_datepicker_format(rails_time_format))
        datetime_picker_options = {}
        datetime_picker_options[:dateFormat] = date_format unless date_format.nil?
        unless time_format.nil?
          datetime_picker_options[:timeFormat] = time_format
          datetime_picker_options[:ampm] = true if rails_time_format.include?('%I')
        end
        datetime_picker_options
      end

      def self.split_datetime_format(datetime_format)
        date_format = datetime_format
        time_format = nil
        time_start_indicators = %w[HH hh mm tt ss]
        unless datetime_format.nil?
          start_indicator = time_start_indicators.detect { |indicator| datetime_format.include?(indicator) }
          unless start_indicator.nil?
            pos_time_format = datetime_format.index(start_indicator)
            date_format = datetime_format.to(pos_time_format - 1).strip
            time_format = datetime_format.from(pos_time_format).strip
          end
        end
        [date_format, time_format]
      end

      module DatepickerColumnHelpers
        def to_datepicker_format(rails_format)
          ActiveScaffold::Bridges::DatePicker::Helper.to_datepicker_format(rails_format)
        end

        def datepicker_format_options(column, format)
          return {} if format == :default
          if column.form_ui == :date_picker
            js_format = to_datepicker_format(I18n.translate!("date.formats.#{format}"))
            js_format.nil? ? {} : {dateFormat: js_format}
          else
            rails_time_format = I18n.translate!("time.formats.#{format}")
            ActiveScaffold::Bridges::DatePicker::Helper.format_to_datetime_picker(rails_time_format)
          end
        end

        def datepicker_format(options, ui)
          options.delete(:format) || (ui == :date_picker ? :default : :picker)
        end
      end

      module SearchColumnHelpers
        def active_scaffold_search_date_picker_field(column, options, current_search, name, ui_options: column.options)
          value =
            if current_search.is_a? Hash
              controller.class.condition_value_for_datetime(column, current_search[name], column.search_ui == :date_picker ? :to_date : :to_time)
            else
              current_search
            end
          options = ui_options.merge(options).except!(:include_blank, :discard_time, :discard_date, :value)
          options = active_scaffold_input_text_options(options)
          format = datepicker_format(options, column.search_ui)
          options[:class] << " #{column.search_ui}"
          options[:style] = 'display: none' if options[:show] == false # hide only if asked to hide
          options[:data] = datepicker_format_options(column, format).reverse_merge!(options[:data] || {})
          value = l(value, :format => format) if value
          options = options.merge(:id => "#{options[:id]}_#{name}", :name => "#{options[:name]}[#{name}]", :object => nil)
          text_field_tag("#{options[:name]}[#{name}]", value, options)
        end
      end

      module FormColumnHelpers
        def active_scaffold_input_date_picker(column, options, ui_options: column.options)
          record = options[:object]
          options = active_scaffold_input_text_options(options.merge(ui_options))
          options[:class] << " #{column.form_ui}"

          format = datepicker_format(options, column.form_ui)
          value = controller.class.condition_value_for_datetime(column, record.send(column.name), column.form_ui == :date_picker ? :to_date : :to_time)
          options[:data] = datepicker_format_options(column, format).reverse_merge!(options[:data] || {})
          options[:value] = (value ? l(value, :format => format) : nil)
          text_field(:record, column.name, options)
        end
      end
    end
  end
end