recurring_select 3.0.1 → 4.0.0.rc1

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 823b393395bb4608d998ea0a5b8f9b38b985ae8ee91107523d5383aaa94a97ae
4
- data.tar.gz: ecf294db986e90ea87ba5d2c6481053fbd466267a18949915201cdb04501774e
3
+ metadata.gz: 31e198de02d94e75927a32460fe0a56601c1da1f52b9df05d878ea7c2857d637
4
+ data.tar.gz: 351c9d4727459ffb8e5c58eb523a08123ca49b51de8d63fe1e66c13e85d04d87
5
5
  SHA512:
6
- metadata.gz: 4870f4d76cc947ee2f20cdf3c917e05ddb804acae5a2852d4f0faf4576019f7f0656d8b70c7608b2a22b7487902da908f4c0d3d174cf768b42e0c4ab6f68314a
7
- data.tar.gz: 3006029c17dab3353f0f89ba025996d280978e4e7fdf6cdf764dd67f930eac23f62f0da1877769df5d3adc5aa4616aa3f07dd15e67d4d4b750d794ea25bdde2a
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
- ```coffeescript
103
- $.fn.recurring_select.texts = {
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
- ```coffeescript
129
- $.fn.recurring_select.options = {
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/Gemfile.rails-4.0.x bundle install
155
- BUNDLE_GEMFILE=spec/gemfiles/Gemfile.rails-4.0.x bundle exec rspec spec
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
- $.fn.recurring_select.texts = {
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