diff --git a/app/assets/javascripts/jquery-mobile-rs.js b/app/assets/javascripts/jquery-mobile-rs.js new file mode 100644 index 00000000..5cac55cc --- /dev/null +++ b/app/assets/javascripts/jquery-mobile-rs.js @@ -0,0 +1,18 @@ +//= require recurring_select +//= require_self + +$(function() { + $(document).on("recurring_select:cancel recurring_select:save", ".recurring_select", function() { + $(this).selectmenu('refresh'); + }); + + $(document).on("recurring_select:dialog_opened", ".rs_dialog_holder", function() { + $(this).find("select").attr("data-theme", $('.recurring_select').data("theme")).attr("data-mini", true).selectmenu(); + $(this).find("input[type=text]").textinput(); + + $(this).on("recurring_select:dialog_positioned", ".rs_dialog", function() { + $(this).css({ + "top" : $(window).scrollTop()+"px"}); + }); + }); +}); diff --git a/app/assets/javascripts/jquery-mobile-rs.js.coffee b/app/assets/javascripts/jquery-mobile-rs.js.coffee deleted file mode 100644 index 2ce8b103..00000000 --- a/app/assets/javascripts/jquery-mobile-rs.js.coffee +++ /dev/null @@ -1,15 +0,0 @@ -#= require recurring_select -#= require_self - -$ -> - $(document).on "recurring_select:cancel recurring_select:save", ".recurring_select", -> - $(this).selectmenu('refresh') - - $(document).on "recurring_select:dialog_opened", ".rs_dialog_holder", -> - $(this).find("select").attr("data-theme", $('.recurring_select').data("theme")).attr("data-mini", true).selectmenu() - $(this).find("input[type=text]").textinput() - - $(this).on "recurring_select:dialog_positioned", ".rs_dialog", -> - $(this).css - "top" : $(window).scrollTop()+"px" - diff --git a/app/assets/javascripts/recurring_select.js b/app/assets/javascripts/recurring_select.js new file mode 100644 index 00000000..c6539c5e --- /dev/null +++ b/app/assets/javascripts/recurring_select.js @@ -0,0 +1,125 @@ +//= require recurring_select_dialog +//= require_self + +const $ = jQuery; +$(function() { + $(document).on("focus", ".recurring_select", function() { + $(this).recurring_select('set_initial_values'); + }); + + $(document).on("change", ".recurring_select", function() { + $(this).recurring_select('changed'); + }); +}); + +const methods = { + set_initial_values() { + this.data('initial-value-hash', this.val()); + this.data('initial-value-str', $(this.find("option").get()[this.prop("selectedIndex")]).text()); + }, + + changed() { + if (this.val() === "custom") { + methods.open_custom.apply(this); + } else { + methods.set_initial_values.apply(this); + } + }, + + open_custom() { + this.data("recurring-select-active", true); + new RecurringSelectDialog(this); + this.blur(); + }, + + save(new_rule) { + this.find("option[data-custom]").remove(); + const new_json_val = JSON.stringify(new_rule.hash); + + // TODO: check for matching name, and replace that value if found + + if ($.inArray(new_json_val, this.find("option").map(function() { $(this).val(); })) === -1) { + methods.insert_option.apply(this, [new_rule.str, new_json_val]); + } + + this.val(new_json_val); + methods.set_initial_values.apply(this); + this.trigger("recurring_select:save"); + }, + + current_rule() { + return { + str: this.data("initial-value-str"), + hash: $.parseJSON(this.data("initial-value-hash")) + }; + }, + + cancel() { + this.val(this.data("initial-value-hash")); + this.data("recurring-select-active", false); + this.trigger("recurring_select:cancel"); + }, + + + insert_option(new_rule_str, new_rule_json) { + let separator = this.find("option:disabled"); + if (separator.length === 0) { + separator = this.find("option"); + } + separator = separator.last(); + + const new_option = $(document.createElement("option")); + new_option.attr("data-custom", true); + + if (new_rule_str.substr(new_rule_str.length - 1) !== "*") { + new_rule_str+="*"; + } + + new_option.text(new_rule_str); + new_option.val(new_rule_json); + new_option.insertBefore(separator); + }, + + methods() { + return methods; + } +}; + +$.fn.recurring_select = function(method) { + if (method in methods) { + return methods[ method ].apply( this, Array.prototype.slice.call( arguments, 1 ) ); + } else { + $.error( `Method ${method} does not exist on jQuery.recurring_select` ); + } +}; + +$.fn.recurring_select.options = { + monthly: { + show_week: [true, true, true, true, false, false] + } +}; + +$.fn.recurring_select.texts = { + locale_iso_code: "en", + repeat: "Repeat", + last_day: "Last Day", + frequency: "Frequency", + daily: "Daily", + weekly: "Weekly", + monthly: "Monthly", + yearly: "Yearly", + every: "Every", + days: "day(s)", + weeks_on: "week(s) on", + months: "month(s)", + years: "year(s)", + day_of_month: "Day of month", + day_of_week: "Day of week", + cancel: "Cancel", + ok: "OK", + summary: "Summary", + first_day_of_week: 0, + days_first_letter: ["S", "M", "T", "W", "T", "F", "S" ], + order: ["1st", "2nd", "3rd", "4th", "5th", "Last"], + show_week: [true, true, true, true, false, false] +}; diff --git a/app/assets/javascripts/recurring_select.js.coffee b/app/assets/javascripts/recurring_select.js.coffee deleted file mode 100644 index c173d51e..00000000 --- a/app/assets/javascripts/recurring_select.js.coffee +++ /dev/null @@ -1,105 +0,0 @@ -#= require recurring_select_dialog -#= require_self - -$ = jQuery -$ -> - $(document).on "focus", ".recurring_select", -> - $(this).recurring_select('set_initial_values') - - $(document).on "change", ".recurring_select", -> - $(this).recurring_select('changed') - -methods = - set_initial_values: -> - @data 'initial-value-hash', @val() - @data 'initial-value-str', $(@find("option").get()[@.prop("selectedIndex")]).text() - - changed: -> - if @val() == "custom" - methods.open_custom.apply(@) - else - methods.set_initial_values.apply(@) - - open_custom: -> - @data "recurring-select-active", true - new RecurringSelectDialog(@) - @blur() - - save: (new_rule) -> - @find("option[data-custom]").remove() - new_json_val = JSON.stringify(new_rule.hash) - - # TODO: check for matching name, and replace that value if found - - if $.inArray(new_json_val, @find("option").map -> $(@).val()) == -1 - methods.insert_option.apply @, [new_rule.str, new_json_val] - - @val new_json_val - methods.set_initial_values.apply @ - @.trigger "recurring_select:save" - - current_rule: -> - str: @data("initial-value-str") - hash: $.parseJSON(@data("initial-value-hash")) - - cancel: -> - @val @data("initial-value-hash") - @data "recurring-select-active", false - @.trigger "recurring_select:cancel" - - - insert_option: (new_rule_str, new_rule_json) -> - separator = @find("option:disabled") - if separator.length == 0 - separator = @find("option") - separator = separator.last() - - new_option = $(document.createElement("option")) - new_option.attr "data-custom", true - - if new_rule_str.substr(new_rule_str.length - 1) != "*" - new_rule_str+="*" - - new_option.text new_rule_str - new_option.val new_rule_json - new_option.insertBefore separator - - methods: -> - methods - -$.fn.recurring_select = (method) -> - if method of methods - return methods[ method ].apply( this, Array.prototype.slice.call( arguments, 1 ) ); - else - $.error( "Method #{method} does not exist on jQuery.recurring_select" ); - -$.fn.recurring_select.options = { - monthly: { - show_week: [true, true, true, true, false, false] - } -} - -$.fn.recurring_select.texts = { - locale_iso_code: "en" - repeat: "Repeat" - last_day: "Last Day" - frequency: "Frequency" - daily: "Daily" - weekly: "Weekly" - monthly: "Monthly" - yearly: "Yearly" - every: "Every" - days: "day(s)" - weeks_on: "week(s) on" - months: "month(s)" - years: "year(s)" - day_of_month: "Day of month" - day_of_week: "Day of week" - cancel: "Cancel" - ok: "OK" - summary: "Summary" - first_day_of_week: 0 - days_first_letter: ["S", "M", "T", "W", "T", "F", "S" ] - order: ["1st", "2nd", "3rd", "4th", "5th", "Last"] - show_week: [true, true, true, true, false, false] -} diff --git a/app/assets/javascripts/recurring_select/fr.js.coffee b/app/assets/javascripts/recurring_select/fr.js similarity index 100% rename from app/assets/javascripts/recurring_select/fr.js.coffee rename to app/assets/javascripts/recurring_select/fr.js diff --git a/app/assets/javascripts/recurring_select_dialog.js.coffee.erb b/app/assets/javascripts/recurring_select_dialog.js.coffee.erb deleted file mode 100644 index baeccd6a..00000000 --- a/app/assets/javascripts/recurring_select_dialog.js.coffee.erb +++ /dev/null @@ -1,372 +0,0 @@ -window.RecurringSelectDialog = - class RecurringSelectDialog - constructor: (@recurring_selector) -> - @current_rule = @recurring_selector.recurring_select('current_rule') - @initDialogBox() - if not @current_rule.hash? or not @current_rule.hash.rule_type? - @freqChanged() - else - setTimeout @positionDialogVert, 10 # allow initial render - - initDialogBox: -> - $(".rs_dialog_holder").remove() - - open_in = $("body") - open_in = $(".ui-page-active") if $(".ui-page-active").length - open_in.append @template() - @outer_holder = $(".rs_dialog_holder") - @inner_holder = @outer_holder.find ".rs_dialog" - @content = @outer_holder.find ".rs_dialog_content" - @positionDialogVert(true) - @mainEventInit() - @freqInit() - @summaryInit() - @outer_holder.trigger "recurring_select:dialog_opened" - @freq_select.focus() - - positionDialogVert: (initial_positioning) => - window_height = $(window).height() - window_width = $(window).width() - dialog_height = @content.outerHeight() - if dialog_height < 80 - dialog_height = 80 - margin_top = (window_height - dialog_height)/2 - 30 - margin_top = 10 if margin_top < 10 - # if dialog_height > window_height - 20 - # dialog_height = window_height - 20 - - new_style_hash = - "margin-top" : margin_top+"px" - "min-height" : dialog_height+"px" - - if initial_positioning? - @inner_holder.css new_style_hash - @inner_holder.trigger "recurring_select:dialog_positioned" - else - @inner_holder.addClass "animated" - @inner_holder.animate new_style_hash, 200, => - @inner_holder.removeClass "animated" - @content.css {"width": "auto"} - @inner_holder.trigger "recurring_select:dialog_positioned" - - cancel: => - @outer_holder.remove() - @recurring_selector.recurring_select('cancel') - - outerCancel: (event) => - if $(event.target).hasClass("rs_dialog_holder") - @cancel() - - save: => - return if !@current_rule.str? - @outer_holder.remove() - @recurring_selector.recurring_select('save', @current_rule) - -# ========================= Init Methods =============================== - - mainEventInit: -> - # Tap hooks are for jQueryMobile - @outer_holder.on 'click tap', @outerCancel - @content.on 'click tap', 'h1 a', @cancel - @save_button = @content.find('input.rs_save').on "click tap", @save - @content.find('input.rs_cancel').on "click tap", @cancel - - freqInit: -> - @freq_select = @outer_holder.find ".rs_frequency" - if @current_rule.hash? && (rule_type = @current_rule.hash.rule_type)? - if rule_type.search(/Weekly/) != -1 - @freq_select.prop('selectedIndex', 1) - @initWeeklyOptions() - else if rule_type.search(/Monthly/) != -1 - @freq_select.prop('selectedIndex', 2) - @initMonthlyOptions() - else if rule_type.search(/Yearly/) != -1 - @freq_select.prop('selectedIndex', 3) - @initYearlyOptions() - else - @initDailyOptions() - @freq_select.on "change", @freqChanged - - initDailyOptions: -> - section = @content.find('.daily_options') - interval_input = section.find('.rs_daily_interval') - interval_input.val(@current_rule.hash.interval) - interval_input.on "change keyup", @intervalChanged - section.show() - - initWeeklyOptions: -> - section = @content.find('.weekly_options') - - # connect the interval field - interval_input = section.find('.rs_weekly_interval') - interval_input.val(@current_rule.hash.interval) - interval_input.on "change keyup", @intervalChanged - - # clear selected days - section.find(".day_holder a").each (index, element) -> - $(element).removeClass("selected") - - # connect the day fields - if @current_rule.hash.validations? && @current_rule.hash.validations.day? - $(@current_rule.hash.validations.day).each (index, val) -> - section.find(".day_holder a[data-value='"+val+"']").addClass("selected") - - section.off('click', '.day_holder a').on "click", ".day_holder a", @daysChanged - - section.show() - - initMonthlyOptions: -> - section = @content.find('.monthly_options') - interval_input = section.find('.rs_monthly_interval') - interval_input.val(@current_rule.hash.interval) - interval_input.on "change keyup", @intervalChanged - - @current_rule.hash.validations ||= {} - @current_rule.hash.validations.day_of_month ||= [] - @current_rule.hash.validations.day_of_week ||= {} - @init_calendar_days(section) - @init_calendar_weeks(section) - - in_week_mode = Object.keys(@current_rule.hash.validations.day_of_week).length > 0 - section.find(".monthly_rule_type_week").prop("checked", in_week_mode) - section.find(".monthly_rule_type_day").prop("checked", !in_week_mode) - @toggle_month_view() - section.find("input[name=monthly_rule_type]").on "change", @toggle_month_view - section.show() - - initYearlyOptions: -> - section = @content.find('.yearly_options') - interval_input = section.find('.rs_yearly_interval') - interval_input.val(@current_rule.hash.interval) - interval_input.on "change keyup", @intervalChanged - section.show() - - - summaryInit: -> - @summary = @outer_holder.find(".rs_summary") - @summaryUpdate() - -# ========================= render methods =============================== - - summaryUpdate: (new_string) => - @summary.width @content.width() - if @current_rule.hash? && @current_rule.str? - @summary.removeClass "fetching" - @save_button.removeClass("disabled") - rule_str = @current_rule.str.replace("*", "") - if rule_str.length < 20 - rule_str = "#{$.fn.recurring_select.texts["summary"]}: "+rule_str - @summary.find("span").html rule_str - else - @summary.addClass "fetching" - @save_button.addClass("disabled") - @summary.find("span").html "" - @summaryFetch() - - summaryFetch: -> - return if !(@current_rule.hash? && (rule_type = @current_rule.hash.rule_type)?) - @current_rule.hash['week_start'] = $.fn.recurring_select.texts["first_day_of_week"] - $.ajax - url: "<%= Rails.application.config.action_controller.relative_url_root %>/recurring_select/translate/#{$.fn.recurring_select.texts["locale_iso_code"]}", - type: "POST", - data: @current_rule.hash - success: @summaryFetchSuccess - - summaryFetchSuccess: (data) => - @current_rule.str = data - @summaryUpdate() - @content.css {"width": "auto"} - - init_calendar_days: (section) => - monthly_calendar = section.find(".rs_calendar_day") - monthly_calendar.html "" - for num in [1..31] - monthly_calendar.append (day_link = $(document.createElement("a")).text(num)) - if $.inArray(num, @current_rule.hash.validations.day_of_month) != -1 - day_link.addClass("selected") - - # add last day of month button - monthly_calendar.append (end_of_month_link = $(document.createElement("a")).text($.fn.recurring_select.texts["last_day"])) - end_of_month_link.addClass("end_of_month") - if $.inArray(-1, @current_rule.hash.validations.day_of_month) != -1 - end_of_month_link.addClass("selected") - - monthly_calendar.find("a").on "click tap", @dateOfMonthChanged - - init_calendar_weeks: (section) => - monthly_calendar = section.find(".rs_calendar_week") - monthly_calendar.html "" - row_labels = $.fn.recurring_select.texts["order"] - show_row = $.fn.recurring_select.options["monthly"]["show_week"] - cell_str = $.fn.recurring_select.texts["days_first_letter"] - - for num, index in [1, 2, 3, 4, 5, -1] - if show_row[index] - monthly_calendar.append "#{row_labels[num - 1]}" - for day_of_week in [$.fn.recurring_select.texts["first_day_of_week"]...(7 + $.fn.recurring_select.texts["first_day_of_week"])] - day_of_week = day_of_week % 7 - day_link = $("", {text: cell_str[day_of_week] }) - day_link.attr("day", day_of_week) - day_link.attr("instance", num) - monthly_calendar.append day_link - - $.each @current_rule.hash.validations.day_of_week, (key, value) -> - $.each value, (index, instance) -> - section.find("a[day='#{key}'][instance='#{instance}']").addClass("selected") - monthly_calendar.find("a").on "click tap", @weekOfMonthChanged - - toggle_month_view: => - week_mode = @content.find(".monthly_rule_type_week").prop("checked") - @content.find(".rs_calendar_week").toggle(week_mode) - @content.find(".rs_calendar_day").toggle(!week_mode) - -# ========================= Change callbacks =============================== - - freqChanged: => - @current_rule.hash = null unless $.isPlainObject(@current_rule.hash) # for custom values - - @current_rule.hash ||= {} - @current_rule.hash.interval = 1 - @current_rule.hash.until = null - @current_rule.hash.count = null - @current_rule.hash.validations = null - @content.find(".freq_option_section").hide(); - @content.find("input[type=radio], input[type=checkbox]").prop("checked", false) - switch @freq_select.val() - when "Weekly" - @current_rule.hash.rule_type = "IceCube::WeeklyRule" - @current_rule.str = $.fn.recurring_select.texts["weekly"] - @initWeeklyOptions() - when "Monthly" - @current_rule.hash.rule_type = "IceCube::MonthlyRule" - @current_rule.str = $.fn.recurring_select.texts["monthly"] - @initMonthlyOptions() - when "Yearly" - @current_rule.hash.rule_type = "IceCube::YearlyRule" - @current_rule.str = $.fn.recurring_select.texts["yearly"] - @initYearlyOptions() - else - @current_rule.hash.rule_type = "IceCube::DailyRule" - @current_rule.str = $.fn.recurring_select.texts["daily"] - @initDailyOptions() - @summaryUpdate() - @positionDialogVert() - - intervalChanged: (event) => - @current_rule.str = null - @current_rule.hash ||= {} - @current_rule.hash.interval = parseInt($(event.currentTarget).val()) - if @current_rule.hash.interval < 1 || isNaN(@current_rule.hash.interval) - @current_rule.hash.interval = 1 - @summaryUpdate() - - daysChanged: (event) => - $(event.currentTarget).toggleClass("selected") - @current_rule.str = null - @current_rule.hash ||= {} - @current_rule.hash.validations = {} - raw_days = @content.find(".day_holder a.selected").map -> parseInt($(this).data("value")) - @current_rule.hash.validations.day = raw_days.get() - @summaryUpdate() - false # this prevents default and propogation - - dateOfMonthChanged: (event) => - $(event.currentTarget).toggleClass("selected") - @current_rule.str = null - @current_rule.hash ||= {} - @current_rule.hash.validations = {} - raw_days = @content.find(".monthly_options .rs_calendar_day a.selected").map -> - res = if $(this).text() == $.fn.recurring_select.texts["last_day"] then -1 else parseInt($(this).text()) - res - @current_rule.hash.validations.day_of_week = {} - @current_rule.hash.validations.day_of_month = raw_days.get() - @summaryUpdate() - false - - weekOfMonthChanged: (event) => - $(event.currentTarget).toggleClass("selected") - @current_rule.str = null - @current_rule.hash ||= {} - @current_rule.hash.validations = {} - @current_rule.hash.validations.day_of_month = [] - @current_rule.hash.validations.day_of_week = {} - @content.find(".monthly_options .rs_calendar_week a.selected").each (index, elm) => - day = parseInt($(elm).attr("day")) - instance = parseInt($(elm).attr("instance")) - @current_rule.hash.validations.day_of_week[day] ||= [] - @current_rule.hash.validations.day_of_week[day].push instance - @summaryUpdate() - false - -# ========================= Change callbacks =============================== - - template: () -> - str = " -
-
-
-

#{$.fn.recurring_select.texts["repeat"]}

-

- - -

- -
-

- #{$.fn.recurring_select.texts["every"]} - - #{$.fn.recurring_select.texts["days"]} -

-
-
-

- #{$.fn.recurring_select.texts["every"]} - - #{$.fn.recurring_select.texts["weeks_on"]}: -

-
- " - for day_of_week in [$.fn.recurring_select.texts["first_day_of_week"]...(7 + $.fn.recurring_select.texts["first_day_of_week"])] - day_of_week = day_of_week % 7 - str += "#{$.fn.recurring_select.texts["days_first_letter"][day_of_week]}" - - str += " -
- . -
-
-

- #{$.fn.recurring_select.texts["every"]} - - #{$.fn.recurring_select.texts["months"]}: -

-

- - -

-

-

-
-
-

- #{$.fn.recurring_select.texts["every"]} - - #{$.fn.recurring_select.texts["years"]} -

-
-

- -

-
- - -
-
-
-
- " diff --git a/app/assets/javascripts/recurring_select_dialog.js.erb b/app/assets/javascripts/recurring_select_dialog.js.erb new file mode 100644 index 00000000..89c95db7 --- /dev/null +++ b/app/assets/javascripts/recurring_select_dialog.js.erb @@ -0,0 +1,449 @@ +window.RecurringSelectDialog = + (RecurringSelectDialog = class RecurringSelectDialog { + constructor(recurring_selector) { + this.positionDialogVert = this.positionDialogVert.bind(this); + this.cancel = this.cancel.bind(this); + this.outerCancel = this.outerCancel.bind(this); + this.save = this.save.bind(this); + this.summaryUpdate = this.summaryUpdate.bind(this); + this.summaryFetchSuccess = this.summaryFetchSuccess.bind(this); + this.init_calendar_days = this.init_calendar_days.bind(this); + this.init_calendar_weeks = this.init_calendar_weeks.bind(this); + this.toggle_month_view = this.toggle_month_view.bind(this); + this.freqChanged = this.freqChanged.bind(this); + this.intervalChanged = this.intervalChanged.bind(this); + this.daysChanged = this.daysChanged.bind(this); + this.dateOfMonthChanged = this.dateOfMonthChanged.bind(this); + this.weekOfMonthChanged = this.weekOfMonthChanged.bind(this); + this.recurring_selector = recurring_selector; + this.current_rule = this.recurring_selector.recurring_select('current_rule'); + this.initDialogBox(); + if ((this.current_rule.hash == null) || (this.current_rule.hash.rule_type == null)) { + this.freqChanged(); + } else { + setTimeout(this.positionDialogVert, 10); // allow initial render + } + } + + initDialogBox() { + $(".rs_dialog_holder").remove(); + + let open_in = $("body"); + if ($(".ui-page-active").length) { open_in = $(".ui-page-active"); } + open_in.append(this.template()); + this.outer_holder = $(".rs_dialog_holder"); + this.inner_holder = this.outer_holder.find(".rs_dialog"); + this.content = this.outer_holder.find(".rs_dialog_content"); + this.positionDialogVert(true); + this.mainEventInit(); + this.freqInit(); + this.summaryInit(); + this.outer_holder.trigger("recurring_select:dialog_opened"); + this.freq_select.focus(); + } + + positionDialogVert(initial_positioning) { + const window_height = $(window).height(); + const window_width = $(window).width(); + let dialog_height = this.content.outerHeight(); + if (dialog_height < 80) { + dialog_height = 80; + } + let margin_top = ((window_height - dialog_height)/2) - 30; + if (margin_top < 10) { margin_top = 10; } + // if dialog_height > window_height - 20 + // dialog_height = window_height - 20 + + const new_style_hash = { + "margin-top" : margin_top+"px", + "min-height" : dialog_height+"px" + }; + + if (initial_positioning != null) { + this.inner_holder.css(new_style_hash); + this.inner_holder.trigger("recurring_select:dialog_positioned"); + } else { + this.inner_holder.addClass("animated"); + this.inner_holder.animate(new_style_hash, 200, () => { + this.inner_holder.removeClass("animated"); + this.content.css({"width": "auto"}); + this.inner_holder.trigger("recurring_select:dialog_positioned"); + }); + } + } + + cancel() { + this.outer_holder.remove(); + this.recurring_selector.recurring_select('cancel'); + } + + outerCancel(event) { + if ($(event.target).hasClass("rs_dialog_holder")) { + this.cancel(); + } + } + + save() { + if ((this.current_rule.str == null)) { return; } + this.outer_holder.remove(); + this.recurring_selector.recurring_select('save', this.current_rule); + } + +// ========================= Init Methods =============================== + + mainEventInit() { + // Tap hooks are for jQueryMobile + this.outer_holder.on('click tap', this.outerCancel); + this.content.on('click tap', 'h1 a', this.cancel); + this.save_button = this.content.find('input.rs_save').on("click tap", this.save); + this.content.find('input.rs_cancel').on("click tap", this.cancel); + } + + freqInit() { + this.freq_select = this.outer_holder.find(".rs_frequency"); + const rule_type = this.current_rule.hash?.rule_type + if (this.current_rule.hash != null && rule_type != null) { + if (rule_type.search(/Weekly/) !== -1) { + this.freq_select.prop('selectedIndex', 1); + this.initWeeklyOptions(); + } else if (rule_type.search(/Monthly/) !== -1) { + this.freq_select.prop('selectedIndex', 2); + this.initMonthlyOptions(); + } else if (rule_type.search(/Yearly/) !== -1) { + this.freq_select.prop('selectedIndex', 3); + this.initYearlyOptions(); + } else { + this.initDailyOptions(); + } + } + this.freq_select.on("change", this.freqChanged); + } + + initDailyOptions() { + const section = this.content.find('.daily_options'); + const interval_input = section.find('.rs_daily_interval'); + interval_input.val(this.current_rule.hash.interval); + interval_input.on("change keyup", this.intervalChanged); + section.show(); + } + + initWeeklyOptions() { + const section = this.content.find('.weekly_options'); + + // connect the interval field + const interval_input = section.find('.rs_weekly_interval'); + interval_input.val(this.current_rule.hash.interval); + interval_input.on("change keyup", this.intervalChanged); + + // clear selected days + section.find(".day_holder a").each(function(index, element) { + $(element).removeClass("selected"); + }); + + // connect the day fields + if ((this.current_rule.hash.validations != null) && (this.current_rule.hash.validations.day != null)) { + $(this.current_rule.hash.validations.day).each(function(index, val) { + section.find(".day_holder a[data-value='"+val+"']").addClass("selected"); + }); + } + + section.off('click', '.day_holder a').on("click", ".day_holder a", this.daysChanged); + + section.show(); + } + + initMonthlyOptions() { + const section = this.content.find('.monthly_options'); + const interval_input = section.find('.rs_monthly_interval'); + interval_input.val(this.current_rule.hash.interval); + interval_input.on("change keyup", this.intervalChanged); + + if (!this.current_rule.hash.validations) { this.current_rule.hash.validations = {} }; + if (!this.current_rule.hash.validations.day_of_month) { this.current_rule.hash.validations.day_of_month = [] }; + if (!this.current_rule.hash.validations.day_of_week) { this.current_rule.hash.validations.day_of_week = {} }; + this.init_calendar_days(section); + this.init_calendar_weeks(section); + + const in_week_mode = Object.keys(this.current_rule.hash.validations.day_of_week).length > 0; + section.find(".monthly_rule_type_week").prop("checked", in_week_mode); + section.find(".monthly_rule_type_day").prop("checked", !in_week_mode); + this.toggle_month_view(); + section.find("input[name=monthly_rule_type]").on("change", this.toggle_month_view); + section.show(); + } + + initYearlyOptions() { + const section = this.content.find('.yearly_options'); + const interval_input = section.find('.rs_yearly_interval'); + interval_input.val(this.current_rule.hash.interval); + interval_input.on("change keyup", this.intervalChanged); + section.show(); + } + + + summaryInit() { + this.summary = this.outer_holder.find(".rs_summary"); + this.summaryUpdate(); + } + +// ========================= render methods =============================== + + summaryUpdate(new_string) { + this.summary.width(this.content.width()); + if ((this.current_rule.hash != null) && (this.current_rule.str != null)) { + this.summary.removeClass("fetching"); + this.save_button.removeClass("disabled"); + let rule_str = this.current_rule.str.replace("*", ""); + if (rule_str.length < 20) { + rule_str = `${$.fn.recurring_select.texts["summary"]}: `+rule_str; + } + this.summary.find("span").html(rule_str); + } else { + this.summary.addClass("fetching"); + this.save_button.addClass("disabled"); + this.summary.find("span").html(""); + this.summaryFetch(); + } + } + + summaryFetch() { + if (!(this.current_rule.hash != null && this.current_rule.hash.rule_type != null)) { return; } + this.current_rule.hash['week_start'] = $.fn.recurring_select.texts["first_day_of_week"]; + $.ajax({ + url: `<%= Rails.application.config.action_controller.relative_url_root %>/recurring_select/translate/${$.fn.recurring_select.texts["locale_iso_code"]}`, + type: "POST", + data: this.current_rule.hash, + success: this.summaryFetchSuccess + }); + } + + summaryFetchSuccess(data) { + this.current_rule.str = data; + this.summaryUpdate(); + this.content.css({"width": "auto"}); + } + + init_calendar_days(section) { + const monthly_calendar = section.find(".rs_calendar_day"); + monthly_calendar.html(""); + for (let num = 1; num <= 31; num++) { + const day_link = $(document.createElement("a")).text(num); + monthly_calendar.append(day_link); + if ($.inArray(num, this.current_rule.hash.validations.day_of_month) !== -1) { + day_link.addClass("selected"); + } + }; + + // add last day of month button + const end_of_month_link = $(document.createElement("a")).text($.fn.recurring_select.texts["last_day"]) + monthly_calendar.append(end_of_month_link); + end_of_month_link.addClass("end_of_month"); + if ($.inArray(-1, this.current_rule.hash.validations.day_of_month) !== -1) { + end_of_month_link.addClass("selected"); + } + + monthly_calendar.find("a").on("click tap", this.dateOfMonthChanged); + } + + init_calendar_weeks(section) { + const monthly_calendar = section.find(".rs_calendar_week"); + monthly_calendar.html(""); + const row_labels = $.fn.recurring_select.texts["order"]; + const show_row = $.fn.recurring_select.options["monthly"]["show_week"]; + const cell_str = $.fn.recurring_select.texts["days_first_letter"]; + + const iterable = [1, 2, 3, 4, 5, -1] + for (let index = 0; index < iterable.length; index++) { + const num = iterable[index]; + if (show_row[index]) { + monthly_calendar.append(`${row_labels[num - 1]}`); + for (let i = $.fn.recurring_select.texts["first_day_of_week"], day_of_week = i, end = 7 + $.fn.recurring_select.texts["first_day_of_week"], asc = $.fn.recurring_select.texts["first_day_of_week"] <= end; asc ? i < end : i > end; asc ? i++ : i--, day_of_week = i) { + day_of_week = day_of_week % 7; + const day_link = $("", {text: cell_str[day_of_week] }); + day_link.attr("day", day_of_week); + day_link.attr("instance", num); + monthly_calendar.append(day_link); + }; + } + }; + + $.each(this.current_rule.hash.validations.day_of_week, function(key, value) { + $.each(value, function(index, instance) { + section.find(`a[day='${key}'][instance='${instance}']`).addClass("selected"); + }); + }); + monthly_calendar.find("a").on("click tap", this.weekOfMonthChanged); + } + + toggle_month_view() { + const week_mode = this.content.find(".monthly_rule_type_week").prop("checked"); + this.content.find(".rs_calendar_week").toggle(week_mode); + this.content.find(".rs_calendar_day").toggle(!week_mode); + } + +// ========================= Change callbacks =============================== + + freqChanged() { + if (!$.isPlainObject(this.current_rule.hash)) { this.current_rule.hash = null; } // for custom values + + if (!this.current_rule.hash) { this.current_rule.hash = {} }; + this.current_rule.hash.interval = 1; + this.current_rule.hash.until = null; + this.current_rule.hash.count = null; + this.current_rule.hash.validations = null; + this.content.find(".freq_option_section").hide();; + this.content.find("input[type=radio], input[type=checkbox]").prop("checked", false); + switch (this.freq_select.val()) { + case "Weekly": + this.current_rule.hash.rule_type = "IceCube::WeeklyRule"; + this.current_rule.str = $.fn.recurring_select.texts["weekly"]; + this.initWeeklyOptions(); + break + case "Monthly": + this.current_rule.hash.rule_type = "IceCube::MonthlyRule"; + this.current_rule.str = $.fn.recurring_select.texts["monthly"]; + this.initMonthlyOptions(); + break + case "Yearly": + this.current_rule.hash.rule_type = "IceCube::YearlyRule"; + this.current_rule.str = $.fn.recurring_select.texts["yearly"]; + this.initYearlyOptions(); + break + default: + this.current_rule.hash.rule_type = "IceCube::DailyRule"; + this.current_rule.str = $.fn.recurring_select.texts["daily"]; + this.initDailyOptions(); + }; + this.summaryUpdate(); + this.positionDialogVert(); + } + + intervalChanged(event) { + this.current_rule.str = null; + if (!this.current_rule.hash) { this.current_rule.hash = {} }; + this.current_rule.hash.interval = parseInt($(event.currentTarget).val()); + if ((this.current_rule.hash.interval < 1) || isNaN(this.current_rule.hash.interval)) { + this.current_rule.hash.interval = 1; + } + this.summaryUpdate(); + } + + daysChanged(event) { + $(event.currentTarget).toggleClass("selected"); + this.current_rule.str = null; + if (!this.current_rule.hash) { this.current_rule.hash = {} }; + this.current_rule.hash.validations = {}; + const raw_days = this.content.find(".day_holder a.selected").map(function() { return parseInt($(this).data("value")); }); + this.current_rule.hash.validations.day = raw_days.get(); + this.summaryUpdate(); + return false; // this prevents default and propogation + } + + dateOfMonthChanged(event) { + $(event.currentTarget).toggleClass("selected"); + this.current_rule.str = null; + if (!this.current_rule.hash) { this.current_rule.hash = {} }; + this.current_rule.hash.validations = {}; + const raw_days = this.content.find(".monthly_options .rs_calendar_day a.selected").map(function() { + return $(this).text() === $.fn.recurring_select.texts["last_day"] ? -1 : parseInt($(this).text()); + }); + this.current_rule.hash.validations.day_of_week = {}; + this.current_rule.hash.validations.day_of_month = raw_days.get(); + this.summaryUpdate(); + return false; + } + + weekOfMonthChanged(event) { + $(event.currentTarget).toggleClass("selected"); + this.current_rule.str = null; + if (!this.current_rule.hash) { this.current_rule.hash = {} }; + this.current_rule.hash.validations = {}; + this.current_rule.hash.validations.day_of_month = []; + this.current_rule.hash.validations.day_of_week = {}; + this.content.find(".monthly_options .rs_calendar_week a.selected").each((index, elm) => { + const day = parseInt($(elm).attr("day")); + const instance = parseInt($(elm).attr("instance")); + if (!this.current_rule.hash.validations.day_of_week[day]) { this.current_rule.hash.validations.day_of_week[day] = [] }; + return this.current_rule.hash.validations.day_of_week[day].push(instance); + }); + this.summaryUpdate(); + return false; + } + +// ========================= Change callbacks =============================== + + template() { + let str = `\ +
\ +
\ +
\ +

${$.fn.recurring_select.texts["repeat"]}

\ +

\ + \ + \ +

\ + \ +
\ +

\ + ${$.fn.recurring_select.texts["every"]} \ + \ + ${$.fn.recurring_select.texts["days"]} \ +

\ +
\ +
\ +

\ + ${$.fn.recurring_select.texts["every"]} \ + \ + ${$.fn.recurring_select.texts["weeks_on"]}: \ +

\ +
\ + `; + for (let i = $.fn.recurring_select.texts["first_day_of_week"], day_of_week = i, end = 7 + $.fn.recurring_select.texts["first_day_of_week"], asc = $.fn.recurring_select.texts["first_day_of_week"] <= end; asc ? i < end : i > end; asc ? i++ : i--, day_of_week = i) { + day_of_week = day_of_week % 7; + str += `${$.fn.recurring_select.texts["days_first_letter"][day_of_week]}`; + }; + + str += `\ +
\ + . \ +
\ +
\ +

\ + ${$.fn.recurring_select.texts["every"]} \ + \ + ${$.fn.recurring_select.texts["months"]}: \ +

\ +

\ + \ + \ +

\ +

\ +

\ +
\ +
\ +

\ + ${$.fn.recurring_select.texts["every"]} \ + \ + ${$.fn.recurring_select.texts["years"]} \ +

\ +
\ +

\ + \ +

\ +
\ + \ + \ +
\ +
\ +
\ +
\ + `; + + return str; + } + });