recurring_select 3.0.1 → 4.0.0.rc1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/README.md +7 -13
- data/app/assets/javascripts/defaults.js +31 -0
- data/app/assets/javascripts/jquery-mobile-rs.js +20 -0
- data/app/assets/javascripts/recurring_select/{fr.js.coffee → fr.js} +2 -2
- data/app/assets/javascripts/recurring_select.js +96 -0
- data/app/assets/javascripts/recurring_select_dialog.js.erb +447 -0
- data/app/assets/javascripts/utils.js +70 -0
- data/app/assets/stylesheets/recurring_select.scss +37 -9
- data/lib/helpers/recurring_select_helper.rb +11 -10
- data/lib/recurring_select/engine.rb +1 -0
- data/lib/recurring_select/version.rb +1 -1
- data/lib/recurring_select.rb +1 -1
- metadata +16 -42
- data/app/assets/javascripts/jquery-mobile-rs.js.coffee +0 -15
- data/app/assets/javascripts/recurring_select.js.coffee +0 -105
- data/app/assets/javascripts/recurring_select_dialog.js.coffee.erb +0 -372
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 31e198de02d94e75927a32460fe0a56601c1da1f52b9df05d878ea7c2857d637
|
4
|
+
data.tar.gz: 351c9d4727459ffb8e5c58eb523a08123ca49b51de8d63fe1e66c13e85d04d87
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 9ba0419fbb294618ab4846ed4ce767c057e1574201852b8e61923bcf9889c493cd078db6ab08ea413bb117d92c525c05f39f35d8fe8c1978d8e7104673acda44
|
7
|
+
data.tar.gz: '093da6c4eaf8a91da28dce5ce12878670cd3455d2098e5b7ad50c8641dbf69936bd33cc7c0b47386220489ab3350f0ef66b3e30457e3bdbe86cd5722bd65253e'
|
data/README.md
CHANGED
@@ -26,6 +26,7 @@ gem 'recurring_select'
|
|
26
26
|
- application.css: `//= require recurring_select`
|
27
27
|
|
28
28
|
#### jQuery Mobile Interface:
|
29
|
+
- application.js: `//= require jquery`
|
29
30
|
- application.js: `//= require jquery-mobile-rs`
|
30
31
|
- application.css: `//= require jquery-mobile-rs`
|
31
32
|
|
@@ -99,8 +100,8 @@ You have to translate JavaScript texts too by including the locale file in your
|
|
99
100
|
|
100
101
|
For other languages include a JavaScript file like this:
|
101
102
|
|
102
|
-
```
|
103
|
-
|
103
|
+
```js
|
104
|
+
RecurringSelectDialog.config.texts = {
|
104
105
|
locale_iso_code: "fr"
|
105
106
|
repeat: "Repeat"
|
106
107
|
frequency: "Frequency"
|
@@ -125,8 +126,8 @@ $.fn.recurring_select.texts = {
|
|
125
126
|
|
126
127
|
Options include:
|
127
128
|
|
128
|
-
```
|
129
|
-
|
129
|
+
```js
|
130
|
+
RecurringSelectDialog.config.options = {
|
130
131
|
monthly: {
|
131
132
|
show_week: [true, true, true, true, false, false] //display week 1, 2 .... Last
|
132
133
|
}
|
@@ -135,13 +136,6 @@ $.fn.recurring_select.options = {
|
|
135
136
|
|
136
137
|
## Testing and Development
|
137
138
|
|
138
|
-
The dummy app uses a [Postgres](http://postgresapp.com/) database `recurring_select_development`. To get setup:
|
139
|
-
|
140
|
-
```console
|
141
|
-
bundle
|
142
|
-
rake db:create
|
143
|
-
```
|
144
|
-
|
145
139
|
Start the dummy server for clicking around the interface:
|
146
140
|
|
147
141
|
```console
|
@@ -151,8 +145,8 @@ rails s
|
|
151
145
|
Tests can be ran against different versions of Rails like so:
|
152
146
|
|
153
147
|
```
|
154
|
-
BUNDLE_GEMFILE=spec/gemfiles/
|
155
|
-
BUNDLE_GEMFILE=spec/gemfiles/
|
148
|
+
BUNDLE_GEMFILE=spec/gemfiles/rails-7 bundle install
|
149
|
+
BUNDLE_GEMFILE=spec/gemfiles/rails-7 bundle exec rspec spec
|
156
150
|
```
|
157
151
|
|
158
152
|
Feel free to open issues or send pull requests.
|
@@ -0,0 +1,31 @@
|
|
1
|
+
const defaultConfig = {
|
2
|
+
options: {
|
3
|
+
monthly: {
|
4
|
+
show_week: [true, true, true, true, false, false]
|
5
|
+
}
|
6
|
+
},
|
7
|
+
texts: {
|
8
|
+
locale_iso_code: "en",
|
9
|
+
repeat: "Repeat",
|
10
|
+
last_day: "Last Day",
|
11
|
+
frequency: "Frequency",
|
12
|
+
daily: "Daily",
|
13
|
+
weekly: "Weekly",
|
14
|
+
monthly: "Monthly",
|
15
|
+
yearly: "Yearly",
|
16
|
+
every: "Every",
|
17
|
+
days: "day(s)",
|
18
|
+
weeks_on: "week(s) on",
|
19
|
+
months: "month(s)",
|
20
|
+
years: "year(s)",
|
21
|
+
day_of_month: "Day of month",
|
22
|
+
day_of_week: "Day of week",
|
23
|
+
cancel: "Cancel",
|
24
|
+
ok: "OK",
|
25
|
+
summary: "Summary",
|
26
|
+
first_day_of_week: 0,
|
27
|
+
days_first_letter: ["S", "M", "T", "W", "T", "F", "S" ],
|
28
|
+
order: ["1st", "2nd", "3rd", "4th", "5th", "Last"],
|
29
|
+
show_week: [true, true, true, true, false, false]
|
30
|
+
}
|
31
|
+
}
|
@@ -0,0 +1,20 @@
|
|
1
|
+
//= require recurring_select
|
2
|
+
//= require_self
|
3
|
+
|
4
|
+
const $ = jQuery
|
5
|
+
|
6
|
+
$(function() {
|
7
|
+
$(document).on("recurring_select:cancel recurring_select:save", ".recurring_select", function() {
|
8
|
+
$(this).selectmenu('refresh');
|
9
|
+
});
|
10
|
+
|
11
|
+
$(document).on("recurring_select:dialog_opened", ".rs_dialog_holder", function() {
|
12
|
+
$(this).find("select").attr("data-theme", $('.recurring_select').data("theme")).attr("data-mini", true).selectmenu();
|
13
|
+
$(this).find("input[type=text]").textinput();
|
14
|
+
|
15
|
+
$(this).on("recurring_select:dialog_positioned", ".rs_dialog", function() {
|
16
|
+
$(this).css({
|
17
|
+
"top" : $(window).scrollTop()+"px"});
|
18
|
+
});
|
19
|
+
});
|
20
|
+
});
|
@@ -1,4 +1,4 @@
|
|
1
|
-
|
1
|
+
RecurringSelectDialog.config.texts = {
|
2
2
|
locale_iso_code: "fr"
|
3
3
|
repeat: "Récurrence"
|
4
4
|
last_day: "Dernier jour"
|
@@ -21,4 +21,4 @@ $.fn.recurring_select.texts = {
|
|
21
21
|
days_first_letter: ["D", "L", "M", "M", "J", "V", "S" ]
|
22
22
|
order: ["1er", "2ème", "3ème", "4ème", "5ème", "Dernier"]
|
23
23
|
show_week: [true, true, true, true, false, false]
|
24
|
-
}
|
24
|
+
}
|
@@ -0,0 +1,96 @@
|
|
1
|
+
//= require recurring_select_dialog
|
2
|
+
//= require_self
|
3
|
+
|
4
|
+
document.addEventListener("DOMContentLoaded", () => {
|
5
|
+
document.addEventListener("focusin", (e) => {
|
6
|
+
if (e.target.matches(".recurring_select")) {
|
7
|
+
recurring_select.call(e.target, "set_initial_values")
|
8
|
+
}
|
9
|
+
})
|
10
|
+
|
11
|
+
document.addEventListener("input", (e) => {
|
12
|
+
if (e.target.matches(".recurring_select")) {
|
13
|
+
recurring_select.call(e.target, "changed")
|
14
|
+
}
|
15
|
+
})
|
16
|
+
})
|
17
|
+
|
18
|
+
const methods = {
|
19
|
+
set_initial_values() {
|
20
|
+
const str = this.querySelectorAll('option')[this.selectedIndex].textContent
|
21
|
+
this.setAttribute('data-initial-value-hash', this.value);
|
22
|
+
this.setAttribute('data-initial-value-str', str);
|
23
|
+
},
|
24
|
+
|
25
|
+
changed() {
|
26
|
+
if (this.value == "custom") {
|
27
|
+
methods.open.call(this);
|
28
|
+
} else {
|
29
|
+
methods.set_initial_values.call(this);
|
30
|
+
}
|
31
|
+
},
|
32
|
+
|
33
|
+
open() {
|
34
|
+
this.setAttribute("data-recurring-select-active", true);
|
35
|
+
new RecurringSelectDialog(this);
|
36
|
+
this.blur();
|
37
|
+
},
|
38
|
+
|
39
|
+
save(new_rule) {
|
40
|
+
this.querySelectorAll("option[data-custom]").forEach((el) => el.parentNode.removeChild(el) )
|
41
|
+
const new_json_val = JSON.stringify(new_rule.hash)
|
42
|
+
|
43
|
+
// TODO: check for matching name, and replace that value if found
|
44
|
+
|
45
|
+
const options = Array.from(this.querySelectorAll("option")).map(() => this.value)
|
46
|
+
if (!options.includes(new_json_val)) {
|
47
|
+
methods.insert_option.apply(this, [new_rule.str, new_json_val])
|
48
|
+
}
|
49
|
+
|
50
|
+
this.value = new_json_val
|
51
|
+
methods.set_initial_values.apply(this)
|
52
|
+
this.dispatchEvent(new CustomEvent("recurring_select:save"))
|
53
|
+
},
|
54
|
+
|
55
|
+
current_rule() {
|
56
|
+
return {
|
57
|
+
str: this.getAttribute("data-initial-value-str"),
|
58
|
+
hash: JSON.parse(this.getAttribute("data-initial-value-hash"))
|
59
|
+
};
|
60
|
+
},
|
61
|
+
|
62
|
+
cancel() {
|
63
|
+
this.value = this.getAttribute("data-initial-value-hash")
|
64
|
+
this.setAttribute("data-recurring-select-active", false);
|
65
|
+
this.dispatchEvent(new CustomEvent("recurring_select:cancel"))
|
66
|
+
},
|
67
|
+
|
68
|
+
|
69
|
+
insert_option(new_rule_str, new_rule_json) {
|
70
|
+
let separator = this.querySelectorAll("option[disabled]");
|
71
|
+
if (separator.length === 0) {
|
72
|
+
separator = this.querySelectorAll("option");
|
73
|
+
}
|
74
|
+
separator = separator[separator.length-1]
|
75
|
+
|
76
|
+
const new_option = document.createElement("option")
|
77
|
+
new_option.setAttribute("data-custom", true);
|
78
|
+
|
79
|
+
if (new_rule_str.substr(new_rule_str.length - 1) !== "*") {
|
80
|
+
new_rule_str+="*";
|
81
|
+
}
|
82
|
+
|
83
|
+
new_option.textContent = new_rule_str
|
84
|
+
new_option.value = new_rule_json
|
85
|
+
separator.parentNode.insertBefore(new_option, separator)
|
86
|
+
}
|
87
|
+
};
|
88
|
+
|
89
|
+
function recurring_select(method) {
|
90
|
+
this['recurring_select'] = this['recurring_select'] || recurring_select.bind(this)
|
91
|
+
if (method in methods) {
|
92
|
+
return methods[ method ].apply( this, Array.prototype.slice.call( arguments, 1 ) );
|
93
|
+
} else {
|
94
|
+
throw new Error( `Method ${method} does not exist on recurring_select` );
|
95
|
+
}
|
96
|
+
}
|
@@ -0,0 +1,447 @@
|
|
1
|
+
//= require utils
|
2
|
+
//= require defaults
|
3
|
+
|
4
|
+
class RecurringSelectDialog {
|
5
|
+
constructor(recurring_selector) {
|
6
|
+
this.config = this.constructor.config
|
7
|
+
this.cancel = this.cancel.bind(this);
|
8
|
+
this.outerCancel = this.outerCancel.bind(this);
|
9
|
+
this.save = this.save.bind(this);
|
10
|
+
this.summaryUpdate = this.summaryUpdate.bind(this);
|
11
|
+
this.summaryFetchSuccess = this.summaryFetchSuccess.bind(this);
|
12
|
+
this.init_calendar_days = this.init_calendar_days.bind(this);
|
13
|
+
this.init_calendar_weeks = this.init_calendar_weeks.bind(this);
|
14
|
+
this.toggle_month_view = this.toggle_month_view.bind(this);
|
15
|
+
this.freqChanged = this.freqChanged.bind(this);
|
16
|
+
this.intervalChanged = this.intervalChanged.bind(this);
|
17
|
+
this.daysChanged = this.daysChanged.bind(this);
|
18
|
+
this.dateOfMonthChanged = this.dateOfMonthChanged.bind(this);
|
19
|
+
this.weekOfMonthChanged = this.weekOfMonthChanged.bind(this);
|
20
|
+
this.recurring_selector = recurring_selector;
|
21
|
+
this.current_rule = this.recurring_selector.recurring_select('current_rule');
|
22
|
+
this.initDialogBox();
|
23
|
+
if ((this.current_rule.hash == null) || (this.current_rule.hash.rule_type == null)) {
|
24
|
+
this.freqChanged();
|
25
|
+
}
|
26
|
+
}
|
27
|
+
|
28
|
+
initDialogBox() {
|
29
|
+
document.querySelectorAll(".rs_dialog_holder").forEach(el => el.parentNode.removeChild(el))
|
30
|
+
|
31
|
+
const uiPage = document.querySelector('.ui-page-active')
|
32
|
+
const anchor = uiPage ? uiPage : document.body
|
33
|
+
|
34
|
+
const div = document.createElement("div")
|
35
|
+
div.innerHTML = this.template()
|
36
|
+
anchor.appendChild(div.children[0])
|
37
|
+
|
38
|
+
this.outer_holder = document.querySelector(".rs_dialog_holder");
|
39
|
+
this.inner_holder = this.outer_holder.querySelector(".rs_dialog");
|
40
|
+
this.content = this.outer_holder.querySelector(".rs_dialog_content");
|
41
|
+
|
42
|
+
this.mainEventInit();
|
43
|
+
this.freqInit();
|
44
|
+
this.summaryInit();
|
45
|
+
trigger(this.outer_holder, "recurring_select:dialog_opened");
|
46
|
+
this.freq_select.focus();
|
47
|
+
}
|
48
|
+
|
49
|
+
cancel() {
|
50
|
+
this.outer_holder.remove();
|
51
|
+
this.recurring_selector.recurring_select('cancel');
|
52
|
+
}
|
53
|
+
|
54
|
+
outerCancel(event) {
|
55
|
+
if (event.target.classList.contains("rs_dialog_holder")) {
|
56
|
+
this.cancel();
|
57
|
+
}
|
58
|
+
}
|
59
|
+
|
60
|
+
save() {
|
61
|
+
if ((this.current_rule.str == null)) { return; }
|
62
|
+
this.outer_holder.remove();
|
63
|
+
this.recurring_selector.recurring_select('save', this.current_rule);
|
64
|
+
}
|
65
|
+
|
66
|
+
// ========================= Init Methods ===============================
|
67
|
+
|
68
|
+
mainEventInit() {
|
69
|
+
// Tap hooks are for jQueryMobile
|
70
|
+
on(this.outer_holder, 'click tap', this.outerCancel);
|
71
|
+
on(this.content, 'click tap', 'h1 a', this.cancel);
|
72
|
+
this.save_button = this.content.querySelector('input.rs_save')
|
73
|
+
on(this.save_button, "click tap", this.save)
|
74
|
+
on(this.content.querySelector('input.rs_cancel'), "click tap", this.cancel)
|
75
|
+
}
|
76
|
+
|
77
|
+
freqInit() {
|
78
|
+
this.freq_select = this.outer_holder.querySelector(".rs_frequency");
|
79
|
+
const rule_type = this.current_rule.hash && this.current_rule.hash.rule_type
|
80
|
+
if (this.current_rule.hash != null && rule_type != null) {
|
81
|
+
if (rule_type.search(/Weekly/) !== -1) {
|
82
|
+
this.freq_select.selectedIndex = 1
|
83
|
+
this.initWeeklyOptions();
|
84
|
+
} else if (rule_type.search(/Monthly/) !== -1) {
|
85
|
+
this.freq_select.selectedIndex = 2
|
86
|
+
this.initMonthlyOptions();
|
87
|
+
} else if (rule_type.search(/Yearly/) !== -1) {
|
88
|
+
this.freq_select.selectedIndex = 3
|
89
|
+
this.initYearlyOptions();
|
90
|
+
} else {
|
91
|
+
this.initDailyOptions();
|
92
|
+
}
|
93
|
+
}
|
94
|
+
on(this.freq_select, "change", this.freqChanged);
|
95
|
+
}
|
96
|
+
|
97
|
+
initDailyOptions() {
|
98
|
+
const section = this.content.querySelector('.daily_options')
|
99
|
+
const interval_input = section.querySelector('.rs_daily_interval')
|
100
|
+
interval_input.value = this.current_rule.hash.interval
|
101
|
+
on(interval_input, "change keyup", this.intervalChanged);
|
102
|
+
section.style.display = 'block'
|
103
|
+
}
|
104
|
+
|
105
|
+
initWeeklyOptions() {
|
106
|
+
const section = this.content.querySelector('.weekly_options');
|
107
|
+
|
108
|
+
// connect the interval field
|
109
|
+
const interval_input = section.querySelector('.rs_weekly_interval');
|
110
|
+
interval_input.value = this.current_rule.hash.interval
|
111
|
+
on(interval_input, "change keyup", this.intervalChanged);
|
112
|
+
|
113
|
+
// clear selected days
|
114
|
+
section.querySelectorAll(".day_holder a").forEach(el =>
|
115
|
+
el.classList.remove("selected")
|
116
|
+
)
|
117
|
+
|
118
|
+
// connect the day fields
|
119
|
+
if ((this.current_rule.hash.validations != null) && (this.current_rule.hash.validations.day != null)) {
|
120
|
+
Array.from(this.current_rule.hash.validations.day).forEach((val) =>
|
121
|
+
section.querySelector(".day_holder a[data-value='"+val+"']").classList.add("selected")
|
122
|
+
)
|
123
|
+
}
|
124
|
+
|
125
|
+
off(section, "click")
|
126
|
+
on(section, "click", ".day_holder a", this.daysChanged)
|
127
|
+
|
128
|
+
section.style.display = 'block'
|
129
|
+
}
|
130
|
+
|
131
|
+
initMonthlyOptions() {
|
132
|
+
const section = this.content.querySelector('.monthly_options')
|
133
|
+
const interval_input = section.querySelector('.rs_monthly_interval')
|
134
|
+
interval_input.value = this.current_rule.hash.interval
|
135
|
+
on(interval_input, "change keyup", this.intervalChanged)
|
136
|
+
|
137
|
+
if (!this.current_rule.hash.validations) { this.current_rule.hash.validations = {} };
|
138
|
+
if (!this.current_rule.hash.validations.day_of_month) { this.current_rule.hash.validations.day_of_month = [] };
|
139
|
+
if (!this.current_rule.hash.validations.day_of_week) { this.current_rule.hash.validations.day_of_week = {} };
|
140
|
+
this.init_calendar_days(section);
|
141
|
+
this.init_calendar_weeks(section);
|
142
|
+
|
143
|
+
const in_week_mode = Object.keys(this.current_rule.hash.validations.day_of_week).length > 0;
|
144
|
+
section.querySelector(".monthly_rule_type_week").checked = in_week_mode
|
145
|
+
section.querySelector(".monthly_rule_type_day").checked = !in_week_mode;
|
146
|
+
this.toggle_month_view();
|
147
|
+
section.querySelectorAll("input[name=monthly_rule_type]").forEach((el) => on(el, "change", this.toggle_month_view))
|
148
|
+
section.style.display = 'block'
|
149
|
+
}
|
150
|
+
|
151
|
+
initYearlyOptions() {
|
152
|
+
const section = this.content.querySelector('.yearly_options');
|
153
|
+
const interval_input = section.querySelector('.rs_yearly_interval');
|
154
|
+
interval_input.value = this.current_rule.hash.interval
|
155
|
+
on(interval_input, "change keyup", this.intervalChanged)
|
156
|
+
section.style.display = 'block'
|
157
|
+
}
|
158
|
+
|
159
|
+
|
160
|
+
summaryInit() {
|
161
|
+
this.summary = this.outer_holder.querySelector(".rs_summary");
|
162
|
+
this.summaryUpdate();
|
163
|
+
}
|
164
|
+
|
165
|
+
// ========================= render methods ===============================
|
166
|
+
|
167
|
+
summaryUpdate(new_string) {
|
168
|
+
// this.summary.style.width = `${this.content.getBoundingClientRect().width}px`;
|
169
|
+
if ((this.current_rule.hash != null) && (this.current_rule.str != null)) {
|
170
|
+
this.summary.classList.remove("fetching");
|
171
|
+
this.save_button.classList.remove("disabled");
|
172
|
+
let rule_str = this.current_rule.str.replace("*", "");
|
173
|
+
if (rule_str.length < 20) {
|
174
|
+
rule_str = `${this.config.texts["summary"]}: `+rule_str;
|
175
|
+
}
|
176
|
+
this.summary.querySelector("span").textContent = rule_str
|
177
|
+
} else {
|
178
|
+
this.summary.classList.add("fetching");
|
179
|
+
this.save_button.classList.add("disabled");
|
180
|
+
this.summary.querySelector("span").textContent = ""
|
181
|
+
this.summaryFetch();
|
182
|
+
}
|
183
|
+
}
|
184
|
+
|
185
|
+
summaryFetch() {
|
186
|
+
if (!(this.current_rule.hash != null && this.current_rule.hash.rule_type != null)) { return; }
|
187
|
+
this.current_rule.hash['week_start'] = this.config.texts["first_day_of_week"];
|
188
|
+
|
189
|
+
const url = `<%= Rails.application.config.action_controller.relative_url_root %>/recurring_select/translate/${this.config.texts["locale_iso_code"]}`
|
190
|
+
const headers = { 'X-Requested-With' : 'XMLHttpRequest', 'Content-Type' : 'application/x-www-form-urlencoded' }
|
191
|
+
const body = serialize(this.current_rule.hash)
|
192
|
+
console.log(this.current_rule.hash, body)
|
193
|
+
|
194
|
+
fetch(url, { method: "POST", body, headers })
|
195
|
+
.then(r => r.text())
|
196
|
+
.then(this.summaryFetchSuccess)
|
197
|
+
}
|
198
|
+
|
199
|
+
summaryFetchSuccess(data) {
|
200
|
+
this.current_rule.str = data
|
201
|
+
this.summaryUpdate()
|
202
|
+
css(this.content, { width: "auto" })
|
203
|
+
}
|
204
|
+
|
205
|
+
init_calendar_days(section) {
|
206
|
+
const monthly_calendar = section.querySelector(".rs_calendar_day");
|
207
|
+
monthly_calendar.innerHTML = "";
|
208
|
+
for (let num = 1; num <= 31; num++) {
|
209
|
+
const day_link = document.createElement("a")
|
210
|
+
day_link.innerText = num
|
211
|
+
monthly_calendar.appendChild(day_link)
|
212
|
+
if (Array.from(this.current_rule.hash.validations.day_of_month).includes(num)) {
|
213
|
+
day_link.classList.add("selected");
|
214
|
+
}
|
215
|
+
};
|
216
|
+
|
217
|
+
// add last day of month button
|
218
|
+
const end_of_month_link = document.createElement("a")
|
219
|
+
end_of_month_link.innerText = this.config.texts["last_day"]
|
220
|
+
monthly_calendar.appendChild(end_of_month_link);
|
221
|
+
end_of_month_link.classList.add("end_of_month");
|
222
|
+
if (Array.from(this.current_rule.hash.validations.day_of_month).includes(-1)) {
|
223
|
+
end_of_month_link.classList.add("selected");
|
224
|
+
}
|
225
|
+
|
226
|
+
off(monthly_calendar, "click tap")
|
227
|
+
on(monthly_calendar, "click tap", "a", this.dateOfMonthChanged)
|
228
|
+
}
|
229
|
+
|
230
|
+
init_calendar_weeks(section) {
|
231
|
+
const monthly_calendar = section.querySelector(".rs_calendar_week")
|
232
|
+
monthly_calendar.innerHTML = ""
|
233
|
+
const row_labels = this.config.texts["order"];
|
234
|
+
const show_row = this.config.options["monthly"]["show_week"];
|
235
|
+
const cell_str = this.config.texts["days_first_letter"];
|
236
|
+
|
237
|
+
const iterable = [1, 2, 3, 4, 5, -1]
|
238
|
+
for (let index = 0; index < iterable.length; index++) {
|
239
|
+
const num = iterable[index];
|
240
|
+
if (show_row[index]) {
|
241
|
+
const el = document.createElement("span")
|
242
|
+
el.innerText = row_labels[index]
|
243
|
+
monthly_calendar.appendChild(el);
|
244
|
+
for (let i = this.config.texts["first_day_of_week"], day_of_week = i, end = 7 + this.config.texts["first_day_of_week"], asc = this.config.texts["first_day_of_week"] <= end; asc ? i < end : i > end; asc ? i++ : i--, day_of_week = i) {
|
245
|
+
day_of_week = day_of_week % 7;
|
246
|
+
const day_link = document.createElement("a")
|
247
|
+
day_link.innerText = cell_str[day_of_week]
|
248
|
+
day_link.setAttribute("day", day_of_week);
|
249
|
+
day_link.setAttribute("instance", num);
|
250
|
+
monthly_calendar.appendChild(day_link);
|
251
|
+
};
|
252
|
+
}
|
253
|
+
};
|
254
|
+
|
255
|
+
Object.entries(this.current_rule.hash.validations.day_of_week).forEach(([key, value]) => {
|
256
|
+
Array.from(value).forEach((instance, index) => {
|
257
|
+
section.querySelector(`a[day='${key}'][instance='${instance}']`).classList.add("selected")
|
258
|
+
})
|
259
|
+
})
|
260
|
+
|
261
|
+
off(monthly_calendar, "click tap")
|
262
|
+
on(monthly_calendar, "click tap", "a", this.weekOfMonthChanged)
|
263
|
+
}
|
264
|
+
|
265
|
+
toggle_month_view() {
|
266
|
+
const week_mode = this.content.querySelector(".monthly_rule_type_week").checked
|
267
|
+
if (week_mode) {
|
268
|
+
this.content.querySelector(".rs_calendar_week").style.display = "block"
|
269
|
+
this.content.querySelector(".rs_calendar_day").style.display = "none"
|
270
|
+
} else {
|
271
|
+
this.content.querySelector(".rs_calendar_week").style.display = "none"
|
272
|
+
this.content.querySelector(".rs_calendar_day").style.display = "block"
|
273
|
+
}
|
274
|
+
}
|
275
|
+
|
276
|
+
// ========================= Change callbacks ===============================
|
277
|
+
|
278
|
+
freqChanged() {
|
279
|
+
if (!isPlainObject(this.current_rule.hash)) { this.current_rule.hash = null; } // for custom values
|
280
|
+
|
281
|
+
if (!this.current_rule.hash) { this.current_rule.hash = {} };
|
282
|
+
this.current_rule.hash.interval = 1;
|
283
|
+
this.current_rule.hash.until = null;
|
284
|
+
this.current_rule.hash.count = null;
|
285
|
+
this.current_rule.hash.validations = null;
|
286
|
+
this.content.querySelectorAll(".freq_option_section").forEach(el => el.style.display = 'none')
|
287
|
+
this.content.querySelector("input[type=radio], input[type=checkbox]").checked = false
|
288
|
+
switch (this.freq_select.value) {
|
289
|
+
case "Weekly":
|
290
|
+
this.current_rule.hash.rule_type = "IceCube::WeeklyRule";
|
291
|
+
this.current_rule.str = this.config.texts["weekly"];
|
292
|
+
this.initWeeklyOptions();
|
293
|
+
break
|
294
|
+
case "Monthly":
|
295
|
+
this.current_rule.hash.rule_type = "IceCube::MonthlyRule";
|
296
|
+
this.current_rule.str = this.config.texts["monthly"];
|
297
|
+
this.initMonthlyOptions();
|
298
|
+
break
|
299
|
+
case "Yearly":
|
300
|
+
this.current_rule.hash.rule_type = "IceCube::YearlyRule";
|
301
|
+
this.current_rule.str = this.config.texts["yearly"];
|
302
|
+
this.initYearlyOptions();
|
303
|
+
break
|
304
|
+
default:
|
305
|
+
this.current_rule.hash.rule_type = "IceCube::DailyRule";
|
306
|
+
this.current_rule.str = this.config.texts["daily"];
|
307
|
+
this.initDailyOptions();
|
308
|
+
};
|
309
|
+
this.summaryUpdate();
|
310
|
+
}
|
311
|
+
|
312
|
+
intervalChanged(event) {
|
313
|
+
this.current_rule.str = null;
|
314
|
+
if (!this.current_rule.hash) { this.current_rule.hash = {} };
|
315
|
+
this.current_rule.hash.interval = parseInt(event.currentTarget.value);
|
316
|
+
if ((this.current_rule.hash.interval < 1) || isNaN(this.current_rule.hash.interval)) {
|
317
|
+
this.current_rule.hash.interval = 1;
|
318
|
+
}
|
319
|
+
this.summaryUpdate();
|
320
|
+
}
|
321
|
+
|
322
|
+
daysChanged(event) {
|
323
|
+
event.target.classList.toggle("selected");
|
324
|
+
this.current_rule.str = null;
|
325
|
+
if (!this.current_rule.hash) { this.current_rule.hash = {} };
|
326
|
+
this.current_rule.hash.validations = {};
|
327
|
+
const raw_days = Array.from(this.content.querySelectorAll(".day_holder a.selected"))
|
328
|
+
.map(el => parseInt(el.dataset.value))
|
329
|
+
this.current_rule.hash.validations.day = raw_days
|
330
|
+
this.summaryUpdate();
|
331
|
+
return false;
|
332
|
+
}
|
333
|
+
|
334
|
+
dateOfMonthChanged(event) {
|
335
|
+
event.target.classList.toggle("selected");
|
336
|
+
this.current_rule.str = null;
|
337
|
+
if (!this.current_rule.hash) { this.current_rule.hash = {} };
|
338
|
+
this.current_rule.hash.validations = {};
|
339
|
+
const raw_days = Array.from(this.content.querySelectorAll(".monthly_options .rs_calendar_day a.selected"))
|
340
|
+
.map(el => {
|
341
|
+
return el.innerText === this.config.texts["last_day"] ? -1 : parseInt(el.innerText)
|
342
|
+
})
|
343
|
+
this.current_rule.hash.validations.day_of_week = {};
|
344
|
+
this.current_rule.hash.validations.day_of_month = raw_days;
|
345
|
+
this.summaryUpdate();
|
346
|
+
return false;
|
347
|
+
}
|
348
|
+
|
349
|
+
weekOfMonthChanged(event) {
|
350
|
+
event.target.classList.toggle("selected");
|
351
|
+
this.current_rule.str = null;
|
352
|
+
if (!this.current_rule.hash) { this.current_rule.hash = {} };
|
353
|
+
this.current_rule.hash.validations = {};
|
354
|
+
this.current_rule.hash.validations.day_of_month = [];
|
355
|
+
this.current_rule.hash.validations.day_of_week = {};
|
356
|
+
this.content.querySelectorAll(".monthly_options .rs_calendar_week a.selected")
|
357
|
+
.forEach((elm, index) => {
|
358
|
+
const day = parseInt(elm.getAttribute("day"));
|
359
|
+
const instance = parseInt(elm.getAttribute("instance"));
|
360
|
+
if (!this.current_rule.hash.validations.day_of_week[day]) { this.current_rule.hash.validations.day_of_week[day] = [] };
|
361
|
+
return this.current_rule.hash.validations.day_of_week[day].push(instance);
|
362
|
+
})
|
363
|
+
this.summaryUpdate();
|
364
|
+
return false;
|
365
|
+
}
|
366
|
+
|
367
|
+
// ========================= Change callbacks ===============================
|
368
|
+
|
369
|
+
template() {
|
370
|
+
let str = `\
|
371
|
+
<div class='rs_dialog_holder'> \
|
372
|
+
<div class='rs_dialog'> \
|
373
|
+
<div class='rs_dialog_content'> \
|
374
|
+
<h1>${this.config.texts["repeat"]} <a href='javascript:void(0)' title='${this.config.texts["cancel"]}' alt='${this.config.texts["cancel"]}'></a> </h1> \
|
375
|
+
<p class='frequency-select-wrapper'> \
|
376
|
+
<label for='rs_frequency'>${this.config.texts["frequency"]}:</label> \
|
377
|
+
<select data-wrapper-class='ui-recurring-select' id='rs_frequency' class='rs_frequency' name='rs_frequency'> \
|
378
|
+
<option value='Daily'>${this.config.texts["daily"]}</option> \
|
379
|
+
<option value='Weekly'>${this.config.texts["weekly"]}</option> \
|
380
|
+
<option value='Monthly'>${this.config.texts["monthly"]}</option> \
|
381
|
+
<option value='Yearly'>${this.config.texts["yearly"]}</option> \
|
382
|
+
</select> \
|
383
|
+
</p> \
|
384
|
+
\
|
385
|
+
<div class='daily_options freq_option_section'> \
|
386
|
+
<p> \
|
387
|
+
${this.config.texts["every"]} \
|
388
|
+
<input type='text' data-wrapper-class='ui-recurring-select' name='rs_daily_interval' class='rs_daily_interval rs_interval' value='1' size='2' /> \
|
389
|
+
${this.config.texts["days"]} \
|
390
|
+
</p> \
|
391
|
+
</div> \
|
392
|
+
<div class='weekly_options freq_option_section'> \
|
393
|
+
<p> \
|
394
|
+
${this.config.texts["every"]} \
|
395
|
+
<input type='text' data-wrapper-class='ui-recurring-select' name='rs_weekly_interval' class='rs_weekly_interval rs_interval' value='1' size='2' /> \
|
396
|
+
${this.config.texts["weeks_on"]}: \
|
397
|
+
</p> \
|
398
|
+
<div class='day_holder'>\
|
399
|
+
`;
|
400
|
+
for (let i = this.config.texts["first_day_of_week"], day_of_week = i, end = 7 + this.config.texts["first_day_of_week"], asc = this.config.texts["first_day_of_week"] <= end; asc ? i < end : i > end; asc ? i++ : i--, day_of_week = i) {
|
401
|
+
day_of_week = day_of_week % 7;
|
402
|
+
str += `<a href='#' data-value='${day_of_week}'>${this.config.texts["days_first_letter"][day_of_week]}</a>`;
|
403
|
+
};
|
404
|
+
|
405
|
+
str += `\
|
406
|
+
</div> \
|
407
|
+
<span style='clear:both; visibility:hidden; height:1px;'>.</span> \
|
408
|
+
</div> \
|
409
|
+
<div class='monthly_options freq_option_section'> \
|
410
|
+
<p> \
|
411
|
+
${this.config.texts["every"]} \
|
412
|
+
<input type='text' data-wrapper-class='ui-recurring-select' name='rs_monthly_interval' class='rs_monthly_interval rs_interval' value='1' size='2' /> \
|
413
|
+
${this.config.texts["months"]}: \
|
414
|
+
</p> \
|
415
|
+
<p class='monthly_rule_type'> \
|
416
|
+
<span><label for='monthly_rule_type_day'>${this.config.texts["day_of_month"]}</label><input type='radio' class='monthly_rule_type_day' name='monthly_rule_type' id='monthly_rule_type_day' value='true' /></span> \
|
417
|
+
<span><label for='monthly_rule_type_week'>${this.config.texts["day_of_week"]}</label><input type='radio' class='monthly_rule_type_week' name='monthly_rule_type' id='monthly_rule_type_week' value='true' /></span> \
|
418
|
+
</p> \
|
419
|
+
<p class='rs_calendar_day'></p> \
|
420
|
+
<p class='rs_calendar_week'></p> \
|
421
|
+
</div> \
|
422
|
+
<div class='yearly_options freq_option_section'> \
|
423
|
+
<p> \
|
424
|
+
${this.config.texts["every"]} \
|
425
|
+
<input type='text' data-wrapper-class='ui-recurring-select' name='rs_yearly_interval' class='rs_yearly_interval rs_interval' value='1' size='2' /> \
|
426
|
+
${this.config.texts["years"]} \
|
427
|
+
</p> \
|
428
|
+
</div> \
|
429
|
+
<p class='rs_summary'> \
|
430
|
+
<span></span> \
|
431
|
+
</p> \
|
432
|
+
<div class='controls'> \
|
433
|
+
<input type='button' data-wrapper-class='ui-recurring-select' class='rs_save' value='${this.config.texts["ok"]}' /> \
|
434
|
+
<input type='button' data-wrapper-class='ui-recurring-select' class='rs_cancel' value='${this.config.texts["cancel"]}' /> \
|
435
|
+
</div> \
|
436
|
+
</div> \
|
437
|
+
</div> \
|
438
|
+
</div>\
|
439
|
+
`;
|
440
|
+
|
441
|
+
return str;
|
442
|
+
}
|
443
|
+
}
|
444
|
+
|
445
|
+
RecurringSelectDialog.config = defaultConfig
|
446
|
+
|
447
|
+
window.RecurringSelectDialog = RecurringSelectDialog
|