diff --git a/app/assets/javascripts/recurring_select_dialog.js.coffee.erb b/app/assets/javascripts/recurring_select_dialog.js.coffee.erb index 28cbc376..9b6e491c 100644 --- a/app/assets/javascripts/recurring_select_dialog.js.coffee.erb +++ b/app/assets/javascripts/recurring_select_dialog.js.coffee.erb @@ -21,6 +21,8 @@ window.RecurringSelectDialog = @mainEventInit() @freqInit() @summaryInit() + @untilInit() + @freq_select.focus() @outer_holder.trigger "recurring_select:dialog_opened" @@ -71,6 +73,8 @@ window.RecurringSelectDialog = @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 + @content.find('#until').on "change", @untilChanged + @content.find('#indefinite').on 'change tap', @indefiniteChanged freqInit: -> @freq_select = @outer_holder.find ".rs_frequency" @@ -173,6 +177,10 @@ window.RecurringSelectDialog = @summaryUpdate() @content.css {"width": "auto"} + untilInit: => + @content.find('#until').val @current_rule.hash?.until + @content.find('#indefinite').click() if @current_rule.hash?.until? + init_calendar_days: (section) => monthly_calendar = section.find(".rs_calendar_day") monthly_calendar.html "" @@ -215,15 +223,16 @@ window.RecurringSelectDialog = # ========================= Change callbacks =============================== freqChanged: => + old_until = @current_rule.hash?.until @current_rule.hash = null unless $.isPlainObject(@current_rule.hash) # for custom values @current_rule.hash ||= {} + @current_rule.hash.until = old_until @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) + @content.find("input[type=radio], input[type=checkbox]:not(#indefinite)").prop("checked", false) switch @freq_select.val() when "Weekly" @current_rule.hash.rule_type = "IceCube::WeeklyRule" @@ -241,6 +250,7 @@ window.RecurringSelectDialog = @current_rule.hash.rule_type = "IceCube::DailyRule" @current_rule.str = $.fn.recurring_select.texts["daily"] @initDailyOptions() + @current_rule.str = null @summaryUpdate() @positionDialogVert() @@ -291,6 +301,43 @@ window.RecurringSelectDialog = @summaryUpdate() false + untilChanged: (event) => + @setUntil $(event.currentTarget).val() + + setUntil: (date) -> + @current_rule.hash ||= {} + @current_rule.str = null + @current_rule.hash.until = date + @summaryUpdate() + false + + indefiniteChanged: (event) => + $el = $(event.currentTarget) + $untilSection = $el.parents('.until_option').find('.until_input') + $untilInput = $untilSection.find '#until' + if $el.is ':checked' + $untilInput.val('').blur() + $untilSection.hide() + else + if @current_rule.hash?.until + $untilInput.val @current_rule.hash.until + else + $untilInput.val @getUntilDefaultDate() + .focus + $untilInput.blur() + $untilSection.show() + + getUntilDefaultDate: => + today = new Date + year = today.getFullYear() + month = today.getMonth() + day = today.getDate() + switch @current_rule.hash.rule_type + when "IceCube::MonthlyRule" then new Date year + 1, month, day + when "IceCube::YearlyRule" then new Date year + 5, month, day + when "IceCube::WeeklyRule" then new Date year, month + 1, day + else new Date year, month, day + 7 + # ========================= Change callbacks =============================== template: () -> @@ -352,6 +399,18 @@ window.RecurringSelectDialog = #{$.fn.recurring_select.texts["years"]}
++ +
+ +diff --git a/app/assets/stylesheets/jquery-mobile-rs.css.scss.erb b/app/assets/stylesheets/jquery-mobile-rs.css.scss.erb index d31ccd29..5fd43c68 100644 --- a/app/assets/stylesheets/jquery-mobile-rs.css.scss.erb +++ b/app/assets/stylesheets/jquery-mobile-rs.css.scss.erb @@ -6,13 +6,13 @@ .rs_dialog_holder {padding-left:0px; background-color:#333; background-color:rgba(30,30,30,0.3); font-size:1em; position:absolute; .rs_dialog { position:absolute; left:10px; right:10px; margin:0px; display:block; z-index:51; - .rs_dialog_content { label.ui-select { display:inline-block;} div.ui-select {display:inline-block;} input.rs_interval {display:inline-block;} + input.rs_until {display:inline-block;} .freq_option_section { .day_holder {height:36px; diff --git a/app/assets/stylesheets/recurring_select.css.scss b/app/assets/stylesheets/recurring_select.css.scss new file mode 100644 index 00000000..df3d73b6 --- /dev/null +++ b/app/assets/stylesheets/recurring_select.css.scss @@ -0,0 +1,102 @@ +@import "utilities.scss"; + +/* -------- resets ---------------*/ + +.rs_dialog_holder { font-size:14px; color:black; + a {color:black;} + input[type=button] { + font: small/normal Arial,sans-serif; + background: #F5F5F5; color: #444; border: 1px solid #ccc; + font-size: 11px; font-weight: bold; height: 27px; line-height: 27px; outline: none; padding: 0 8px; text-align: center; + + @include rounded_corners(2px); + @include gradiant(#f5f5f5, #f1f1f1); + filter: progid:DXImageTransform.Microsoft.gradient(startColorStr='#f5f5f5',EndColorStr='#f1f1f1'); + + &:hover {border-color:#aaa; color:#222; background-color:#f9f9f9; @include shadows(0px, 1px, 2px, rgba(0,0,0,0.2)); } + &:focus {border-color:#1E90FF;} + &:active {border-color:#1E90FF;} + } +} + +/*------- defaults ------------ */ +.rs_dialog_holder { font-family:helvetica, arial, 'san-serif'; color:#222; font-size:12px;} + +/*------- specifics ------------ */ + +select { + option.bold {font-weight:bold; color:red;} +} + +.rs_dialog_holder { position:fixed; left:0px; right:0px; top:0px; bottom:0px; padding-left:50%; background-color:rgba(255,255,255,0.2); z-index:50; + .rs_dialog { background-color:#f6f6f6; border:1px solid #acacac; @include shadows(1px, 3px, 8px, rgba(0,0,0,0.25)); @include rounded_corners(7px); + display:inline-block; min-width:200px; margin-left:-125px; overflow:hidden; position:relative; + .rs_dialog_content { padding:10px; + h1 { font-size:16px; padding:0px; margin:0 0 10px 0; + a {float:right; display:inline-block; height:16px; width:16px; background-image:url(asset_path("recurring_select/cancel.png", image)); background-position:center; background-repeat:no-repeat;} + } + + p { padding:5px 0; margin:0; + label {margin-right:10px;} + } + + .freq_option_section { display:none; + label { font-weight: bold; } + .rs_interval {width:30px; text-align:center;} + + .day_holder { border-left:1px solid #ccc; position:relative; margin-top:5px; height:26px; + a {display:block; padding:5px 7px; font-size:14px; border-style:solid; border-color:#ccc; border-width:1px 1px 1px 0px; float:left; text-decoration:none; font-weight:bold; @include inset_shadows(0px, 1px, 2px, rgba(0,0,0,0.1)); background-color:#fff;; + &.selected {background-color:#89a; color:#fff; @include inset_shadows(1px, 1px, 2px, rgba(0,0,0,0.2)); @include gradiant(#9ab, #789); } + &:hover { cursor:pointer; background-color:#dde;} + } + } + + .rs_calendar_day, .rs_calendar_week { + width:155px; + a {display:inline-block; text-align:center; width:15px; padding:5px 3px; font-size:12px; border-style:solid; border-color:#ccc; border-width:1px 1px 1px 1px; margin:-1px 0 0 -1px; line-height:10px; background-color:#fff; font-weight:bold; + &.selected {background-color:#89a; color:#fff; @include gradiant(#9ab, #789); @include inset_shadows(0px, 1px, 2px, rgba(0,0,0,0.2)); text-shadow:0 1px 1px #333;} + &:hover { cursor:pointer; background-color:#dde;} + } + a.end_of_month { width: 81px; } + } + .rs_calendar_week { + width: 183px; + span { + display: inline-block; + width: 27px; + } + } + + .monthly_rule_type { + span { + margin-right: 15px; + } + } + } + + #until {width:100px; text-align:center;} + + .rs_summary { padding:0px; margin-top:15px; border-top:1px solid #ccc; + span {font-weight:bold; border-top:1px solid #fff; display:block; padding:10px 0 5px 0;} + &.fetching {color:#999; + span {background-image:url(asset_path("recurring_select/throbber_13x13.gif", image)); background-position:center; background-repeat:no-repeat; display:inline-block; height:13px; width:13px; margin-top:-4px; padding-right:5px;} + } + label {font-weight:normal;} + } + + .controls { padding:10px 0px 5px 0px; min-width:170px; text-align:center; + input[type=button] { margin:0px 5px; width:70px; + &.rs_save {color:#333; } + &.rs_cancel {color:#666;} + &.disabled {color:#aaa; } + } + } + } + + &.animated { + .controls {position:absolute; bottom:10px; left:10px;} + .rs_summary, .freq_option_section {display:none;} + } + + } +} diff --git a/app/assets/stylesheets/recurring_select.css.scss.erb b/app/assets/stylesheets/recurring_select.css.scss.erb index 1e0b286a..3c7cfe02 100644 --- a/app/assets/stylesheets/recurring_select.css.scss.erb +++ b/app/assets/stylesheets/recurring_select.css.scss.erb @@ -74,6 +74,7 @@ select { } } + #until {width:100px; text-align:center;} .rs_summary { padding:0px; margin-top:15px; border-top:1px solid #ccc; span {font-weight:bold; border-top:1px solid #fff; display:block; padding:10px 0 5px 0;} diff --git a/app/helpers/recurring_select_helper.rb b/app/helpers/recurring_select_helper.rb index 5cb1298d..4553c115 100644 --- a/app/helpers/recurring_select_helper.rb +++ b/app/helpers/recurring_select_helper.rb @@ -68,7 +68,7 @@ def ice_cube_rule_to_option(supplied_rule, custom = false) return supplied_rule unless RecurringSelect.is_valid_rule?(supplied_rule) rule = RecurringSelect.dirty_hash_to_rule(supplied_rule) - ar = [rule.to_s, rule.to_hash.to_json] + ar = [rule.to_s, rule_as_json(rule)] if custom ar[0] << "*" @@ -78,6 +78,14 @@ def ice_cube_rule_to_option(supplied_rule, custom = false) ar end + def rule_as_json(rule) + hash = rule.to_hash + if hash.delete(:until) + hash[:until] = rule.until_time.to_date + end + hash.to_json + end + def current_rule_in_defaults?(currently_selected_rule, default_schedules) default_schedules.any?{|option| option == currently_selected_rule or diff --git a/lib/recurring_select.rb b/lib/recurring_select.rb index fc819607..12b75367 100644 --- a/lib/recurring_select.rb +++ b/lib/recurring_select.rb @@ -44,6 +44,21 @@ def self.filter_params(params) params[:interval] = params[:interval].to_i if params[:interval] params[:week_start] = params[:week_start].to_i if params[:week_start] + begin + # IceCube::TimeUtil will serialize a TimeWithZone into a hash, such as: + # {time: Thu, 04 Sep 2014 06:59:59 +0000, zone: "Pacific Time (US & Canada)"} + # So don't try to DateTime.parse the hash. IceCube::TimeUtil will deserialize this for us. + if (until_param = params[:until]) + if until_param.is_a?(String) + # Set to 23:59:59 (in current TZ) to encompass all events on until day + params[:until] = Time.zone.parse(until_param).change(hour: 23, min: 59, sec: 59) + elsif until_param.is_a?(Hash) # ex: {time: Thu, 28 Aug 2014 06:59:59 +0000, zone: "Pacific Time (US & Canada)"} + params[:until] = until_param[:time].in_time_zone(until_param[:zone]) + end + end + rescue ArgumentError + # Invalid date given, attempt to assign :until will fail silently + end params[:validations] ||= {} params[:validations].symbolize_keys! diff --git a/spec/dummy/app/assets/javascripts/application.js b/spec/dummy/app/assets/javascripts/application.js index 3ba65a7d..4e3c6c8c 100644 --- a/spec/dummy/app/assets/javascripts/application.js +++ b/spec/dummy/app/assets/javascripts/application.js @@ -7,4 +7,4 @@ //= require jquery //= require jquery_ujs //= require recurring_select -//= require_tree . +//= require_tree . \ No newline at end of file