dhtmlx-scheduler 7.2.10 → 7.2.12

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.
@@ -1,6 +1,6 @@
1
1
  /** @license
2
2
 
3
- dhtmlxScheduler v.7.2.10 Standard
3
+ dhtmlxScheduler v.7.2.12 Standard
4
4
 
5
5
  To use dhtmlxScheduler in non-GPL projects (and get Pro version of the product), please obtain Commercial/Enterprise or Ultimate license on our site https://dhtmlx.com/docs/products/dhtmlxScheduler/#licensing or contact us at sales@dhtmlx.com
6
6
 
@@ -1297,7 +1297,7 @@ function createHandlers(scheduler2) {
1297
1297
  if (scheduler2._dp && scheduler2._dp._in_progress[event2.id]) {
1298
1298
  return;
1299
1299
  }
1300
- if (type === "add-event") {
1300
+ if (type === "add-event" && scheduler2._dp) {
1301
1301
  for (const id in scheduler2._dp._in_progress) {
1302
1302
  if (scheduler2._dp.getState(id) === "inserted") {
1303
1303
  scheduler2._dp.attachEvent("onFullSync", function() {
@@ -1336,9 +1336,13 @@ function createHandlers(scheduler2) {
1336
1336
  console.warn(`Event with ID ${eventData.id} already exists. Skipping add.`);
1337
1337
  return;
1338
1338
  }
1339
- eventData.start_date = scheduler2.templates.parse_date(eventData.start_date);
1340
- eventData.end_date = scheduler2.templates.parse_date(eventData.end_date);
1341
- if (eventData.original_start) {
1339
+ if (typeof eventData.start_date === "string") {
1340
+ eventData.start_date = scheduler2.templates.parse_date(eventData.start_date);
1341
+ }
1342
+ if (typeof eventData.end_date === "string") {
1343
+ eventData.end_date = scheduler2.templates.parse_date(eventData.end_date);
1344
+ }
1345
+ if (eventData.original_start && typeof eventData.original_start === "string") {
1342
1346
  eventData.original_start = scheduler2.templates.parse_date(eventData.original_start);
1343
1347
  }
1344
1348
  ignore(() => {
@@ -1358,9 +1362,13 @@ function createHandlers(scheduler2) {
1358
1362
  existingEvent[key] = eventData[key];
1359
1363
  }
1360
1364
  }
1361
- existingEvent.start_date = scheduler2.templates.parse_date(eventData.start_date);
1362
- existingEvent.end_date = scheduler2.templates.parse_date(eventData.end_date);
1363
- if (eventData.original_start) {
1365
+ if (typeof eventData.start_date === "string") {
1366
+ eventData.start_date = scheduler2.templates.parse_date(eventData.start_date);
1367
+ }
1368
+ if (typeof eventData.end_date === "string") {
1369
+ eventData.end_date = scheduler2.templates.parse_date(eventData.end_date);
1370
+ }
1371
+ if (eventData.original_start && typeof eventData.original_start === "string") {
1364
1372
  eventData.original_start = scheduler2.templates.parse_date(eventData.original_start);
1365
1373
  }
1366
1374
  scheduler2.callEvent("onEventChanged", [sid, existingEvent]);
@@ -2091,18 +2099,21 @@ function extend$j(scheduler2) {
2091
2099
  });
2092
2100
  return views.concat(date).concat(nav);
2093
2101
  }
2102
+ scheduler2._getInitialState = function(provided) {
2103
+ const initialDate = provided.date || this._currentDate();
2104
+ const initialView = provided.mode || "week";
2105
+ return { date: initialDate, mode: initialView };
2106
+ };
2094
2107
  scheduler2.init = function(id, date, mode) {
2095
2108
  if (this.$destroyed) {
2096
2109
  return;
2097
2110
  }
2098
- date = date || scheduler2._currentDate();
2099
- mode = mode || "week";
2100
- if (this._obj) {
2101
- this.unset_actions();
2102
- }
2103
2111
  this._obj = typeof id == "string" ? document.getElementById(id) : id;
2104
2112
  this.$container = this._obj;
2105
2113
  this.$root = this._obj;
2114
+ if (this._obj) {
2115
+ this.unset_actions();
2116
+ }
2106
2117
  if (!this.$container.offsetHeight && this.$container.offsetWidth && this.$container.style.height === "100%") {
2107
2118
  window.console.error(scheduler2._commonErrorMessages.collapsedContainer(), this.$container);
2108
2119
  }
@@ -2140,9 +2151,12 @@ function extend$j(scheduler2) {
2140
2151
  this._init_once();
2141
2152
  this._init_touch_events();
2142
2153
  this.set_sizes();
2154
+ const initialState = scheduler2._getInitialState({ date, mode });
2155
+ const initialDate = initialState.date;
2156
+ const initialMode = initialState.mode;
2143
2157
  scheduler2.callEvent("onSchedulerReady", []);
2144
2158
  scheduler2.$initialized = true;
2145
- this.setCurrentView(date, mode);
2159
+ this.setCurrentView(initialDate, initialMode);
2146
2160
  };
2147
2161
  scheduler2.xy = { min_event_height: 20, bar_height: 24, scale_width: 50, scroll_width: 18, scale_height: 20, month_scale_height: 20, menu_width: 25, margin_top: 0, margin_left: 0, editor_width: 140, month_head_height: 22, event_header_height: 14 };
2148
2162
  scheduler2.keys = { edit_save: 13, edit_cancel: 27 };
@@ -2165,7 +2179,25 @@ function extend$j(scheduler2) {
2165
2179
  this.$container.insertBefore(materialScalePlaceholder, this._els["dhx_cal_header"][0]);
2166
2180
  }
2167
2181
  materialScalePlaceholder.style.display = "block";
2168
- this.set_xy(materialScalePlaceholder, w, this.xy.scale_height + 1, 0, this._els["dhx_cal_header"][0].offsetTop);
2182
+ const navElement = scheduler2.$root.querySelector(".dhx_cal_navline");
2183
+ let navHeight = 0;
2184
+ if (navElement) {
2185
+ navHeight += navElement.offsetHeight;
2186
+ }
2187
+ const headerElement = scheduler2.$root.querySelector(".dhx_cal_header");
2188
+ let headerHeight = 0;
2189
+ if (headerElement) {
2190
+ headerHeight += headerElement.offsetHeight;
2191
+ }
2192
+ let offsetTop, offsetHeight;
2193
+ if (!headerHeight) {
2194
+ offsetTop = navHeight - 4;
2195
+ offsetHeight = 5;
2196
+ } else {
2197
+ offsetTop = navHeight + 1;
2198
+ offsetHeight = headerHeight;
2199
+ }
2200
+ this.set_xy(materialScalePlaceholder, w, offsetHeight, 0, offsetTop);
2169
2201
  } else {
2170
2202
  if (materialScalePlaceholder) {
2171
2203
  materialScalePlaceholder.parentNode.removeChild(materialScalePlaceholder);
@@ -3057,11 +3089,8 @@ function extend$j(scheduler2) {
3057
3089
  var container = this._obj;
3058
3090
  var oldClass = "dhx_scheduler_" + this._mode;
3059
3091
  var newClass = "dhx_scheduler_" + mode;
3060
- if (!this._mode || container.className.indexOf(oldClass) == -1) {
3061
- container.className += " " + newClass;
3062
- } else {
3063
- container.className = container.className.replace(oldClass, newClass);
3064
- }
3092
+ container.classList.remove(oldClass);
3093
+ container.classList.add(newClass);
3065
3094
  var dhx_multi_day = "dhx_multi_day";
3066
3095
  var prev_scroll = this._mode == mode && this.config.preserve_scroll ? this._els[dhx_cal_data][0].scrollTop : false;
3067
3096
  var multidayScroll;
@@ -4201,6 +4230,7 @@ function extend$g(scheduler2) {
4201
4230
  this.clearAll();
4202
4231
  if (this.$container) {
4203
4232
  this.$container.innerHTML = "";
4233
+ this.$container.classList.remove(`dhx_scheduler_${this._mode}`);
4204
4234
  }
4205
4235
  if (this._eventRemoveAll) {
4206
4236
  this._eventRemoveAll();
@@ -9237,7 +9267,7 @@ class DatePicker {
9237
9267
  }
9238
9268
  }
9239
9269
  function factoryMethod(extensionManager) {
9240
- const scheduler2 = { version: "7.2.10" };
9270
+ const scheduler2 = { version: "7.2.12" };
9241
9271
  scheduler2.$stateProvider = StateService();
9242
9272
  scheduler2.getState = scheduler2.$stateProvider.getState;
9243
9273
  extend$n(scheduler2);
@@ -9721,7 +9751,10 @@ function agenda_view(scheduler2) {
9721
9751
  scheduler2._colsS = null;
9722
9752
  scheduler2._table_view = true;
9723
9753
  const dateHeader = scheduler2._getNavDateElement();
9724
- dateHeader.innerHTML = scheduler2.templates.agenda_date(scheduler2._date);
9754
+ if (dateHeader) {
9755
+ dateHeader.innerHTML = scheduler2.templates.agenda_date(scheduler2._date);
9756
+ }
9757
+ scheduler2.set_sizes();
9725
9758
  fill_agenda_tab();
9726
9759
  } else {
9727
9760
  scheduler2._table_view = false;
@@ -10286,74 +10319,95 @@ function container_autoresize(scheduler2) {
10286
10319
  }
10287
10320
  }
10288
10321
  function cookie(scheduler2) {
10289
- function setCookie(name, cookie_param, value) {
10290
- const str = `${name}=${value}${cookie_param ? `; ${cookie_param}` : ""}`;
10291
- document.cookie = `${str}; Secure`;
10292
- }
10293
- function getCookie(name) {
10294
- var search = name + "=";
10295
- if (document.cookie.length > 0) {
10296
- var offset = document.cookie.indexOf(search);
10297
- if (offset != -1) {
10298
- offset += search.length;
10299
- var end = document.cookie.indexOf(";", offset);
10300
- if (end == -1)
10301
- end = document.cookie.length;
10302
- return document.cookie.substring(offset, end);
10303
- }
10322
+ function getStorageKey() {
10323
+ return (scheduler2._obj.id || "scheduler") + "_settings";
10324
+ }
10325
+ const dateToString = scheduler2.date.date_to_str("%Y-%m-%d %H:%i");
10326
+ const stringToDate = scheduler2.date.str_to_date("%Y-%m-%d %H:%i");
10327
+ function safeLocalStorageGet(key) {
10328
+ try {
10329
+ return window.localStorage.getItem(key);
10330
+ } catch (e) {
10331
+ return null;
10304
10332
  }
10305
- return "";
10306
10333
  }
10307
- function getCookieName(scheduler3) {
10308
- return (scheduler3._obj.id || "scheduler") + "_settings";
10334
+ function safeLocalStorageSet(key, value) {
10335
+ try {
10336
+ window.localStorage.setItem(key, value);
10337
+ return true;
10338
+ } catch (e) {
10339
+ return false;
10340
+ }
10309
10341
  }
10310
- var first = true;
10311
- scheduler2.attachEvent("onBeforeViewChange", function(oldMode, oldDate, mode, date) {
10312
- if (first && scheduler2._get_url_nav) {
10313
- var urlNavigationPlugin = scheduler2._get_url_nav();
10314
- if (urlNavigationPlugin.date || urlNavigationPlugin.mode || urlNavigationPlugin.event) {
10315
- first = false;
10342
+ function readStoredState() {
10343
+ const storageKey = getStorageKey();
10344
+ const raw = safeLocalStorageGet(storageKey);
10345
+ if (!raw) {
10346
+ return { date: null, mode: null };
10347
+ }
10348
+ let parsed;
10349
+ try {
10350
+ parsed = JSON.parse(raw);
10351
+ } catch (e) {
10352
+ return { date: null, mode: null };
10353
+ }
10354
+ if (!parsed || typeof parsed !== "object") {
10355
+ return { date: null, mode: null };
10356
+ }
10357
+ let parsedDate = null;
10358
+ if (typeof parsed.date === "string" && parsed.date.length > 0) {
10359
+ try {
10360
+ parsedDate = stringToDate(parsed.date);
10361
+ if (isNaN(+parsedDate)) {
10362
+ parsedDate = null;
10363
+ }
10364
+ } catch (e) {
10365
+ parsedDate = null;
10316
10366
  }
10317
10367
  }
10318
- var cookie2 = getCookieName(scheduler2);
10319
- if (first) {
10320
- first = false;
10321
- var schedulerCookie = getCookie(cookie2);
10322
- if (schedulerCookie) {
10323
- if (!scheduler2._min_date) {
10324
- scheduler2._min_date = date;
10368
+ const parsedMode = typeof parsed.mode === "string" ? parsed.mode : null;
10369
+ return { date: parsedDate, mode: parsedMode };
10370
+ }
10371
+ function urlHasExplicitNavigation() {
10372
+ if (!scheduler2._get_url_nav) {
10373
+ return false;
10374
+ }
10375
+ const urlState = scheduler2._get_url_nav();
10376
+ if (!urlState) {
10377
+ return false;
10378
+ }
10379
+ if (urlState.date || urlState.mode || urlState.event) {
10380
+ return true;
10381
+ }
10382
+ return false;
10383
+ }
10384
+ const originalGetInitialState = scheduler2._getInitialState;
10385
+ scheduler2._getInitialState = function(provided) {
10386
+ const baseState = originalGetInitialState.call(this, provided);
10387
+ if (urlHasExplicitNavigation()) {
10388
+ return baseState;
10389
+ }
10390
+ const storedState = readStoredState();
10391
+ const nextState = { date: baseState.date, mode: baseState.mode };
10392
+ if (storedState.date) {
10393
+ nextState.date = storedState.date;
10394
+ }
10395
+ if (storedState.mode) {
10396
+ try {
10397
+ if (this.isViewExists(storedState.mode)) {
10398
+ nextState.mode = storedState.mode;
10325
10399
  }
10326
- schedulerCookie = unescape(schedulerCookie).split("@");
10327
- schedulerCookie[0] = this._helpers.parseDate(schedulerCookie[0]);
10328
- var view = this.isViewExists(schedulerCookie[1]) ? schedulerCookie[1] : mode, date = !isNaN(+schedulerCookie[0]) ? schedulerCookie[0] : date;
10329
- window.setTimeout(function() {
10330
- if (scheduler2.$destroyed) {
10331
- return;
10332
- }
10333
- scheduler2.setCurrentView(date, view);
10334
- }, 1);
10335
- return false;
10400
+ } catch (e) {
10336
10401
  }
10337
10402
  }
10338
- return true;
10339
- });
10403
+ return nextState;
10404
+ };
10340
10405
  scheduler2.attachEvent("onViewChange", function(newMode, newDate) {
10341
- var cookie2 = getCookieName(scheduler2);
10342
- var text = escape(this._helpers.formatDate(newDate) + "@" + newMode);
10343
- setCookie(cookie2, "expires=Sun, 31 Jan 9999 22:00:00 GMT", text);
10406
+ const storageKey = getStorageKey();
10407
+ const payload = { date: dateToString(newDate), mode: newMode };
10408
+ safeLocalStorageSet(storageKey, JSON.stringify(payload));
10409
+ return true;
10344
10410
  });
10345
- var old_load = scheduler2._load;
10346
- scheduler2._load = function() {
10347
- var args = arguments;
10348
- if (!scheduler2._date) {
10349
- var that = this;
10350
- window.setTimeout(function() {
10351
- old_load.apply(that, args);
10352
- }, 1);
10353
- } else {
10354
- old_load.apply(this, args);
10355
- }
10356
- };
10357
10411
  }
10358
10412
  const notImplemented = { alert: (extension, assert2) => {
10359
10413
  assert2(false, `The ${extension} extension is not included in this version of dhtmlxScheduler.<br>
@@ -15419,41 +15473,39 @@ function quick_info(scheduler2) {
15419
15473
  scheduler2.hideQuickInfo();
15420
15474
  };
15421
15475
  scheduler2._init_quick_info = function() {
15422
- if (!this._quick_info_box) {
15423
- var qi = this._quick_info_box = document.createElement("div");
15424
- this._waiAria.quickInfoAttr(qi);
15425
- qi.className = "dhx_cal_quick_info";
15426
- if (scheduler2.$testmode)
15427
- qi.className += " dhx_no_animate";
15428
- if (scheduler2.config.rtl)
15429
- qi.className += " dhx_quick_info_rtl";
15430
- var ariaAttr = this._waiAria.quickInfoHeaderAttrString();
15431
- var html = `
15432
- <div class="dhx_cal_qi_tcontrols">
15433
- <a class="dhx_cal_qi_close_btn scheduler_icon close"></a>
15476
+ let qi = this._quick_info_box = document.createElement("div");
15477
+ this._waiAria.quickInfoAttr(qi);
15478
+ qi.className = "dhx_cal_quick_info";
15479
+ if (scheduler2.$testmode)
15480
+ qi.className += " dhx_no_animate";
15481
+ if (scheduler2.config.rtl)
15482
+ qi.className += " dhx_quick_info_rtl";
15483
+ let ariaAttr = this._waiAria.quickInfoHeaderAttrString();
15484
+ let html = `
15485
+ <div class="dhx_cal_qi_tcontrols">
15486
+ <a class="dhx_cal_qi_close_btn scheduler_icon close"></a>
15487
+ </div>
15488
+ <div class="dhx_cal_qi_title" ${ariaAttr}>
15489
+
15490
+ <div class="dhx_cal_qi_tcontent"></div>
15491
+ <div class="dhx_cal_qi_tdate"></div>
15434
15492
  </div>
15435
- <div class="dhx_cal_qi_title" ${ariaAttr}>
15436
-
15437
- <div class="dhx_cal_qi_tcontent"></div>
15438
- <div class="dhx_cal_qi_tdate"></div>
15439
- </div>
15440
- <div class="dhx_cal_qi_content"></div>`;
15441
- html += '<div class="dhx_cal_qi_controls">';
15442
- var buttons = scheduler2.config.icons_select;
15443
- for (var i = 0; i < buttons.length; i++) {
15444
- var ariaAttr = this._waiAria.quickInfoButtonAttrString(this.locale.labels[buttons[i]]);
15445
- html += `<div ${ariaAttr} class="dhx_qi_big_icon ${buttons[i]}" title="${scheduler2.locale.labels[buttons[i]]}">
15446
- <div class='dhx_menu_icon ${buttons[i]}'></div><div>${scheduler2.locale.labels[buttons[i]]}</div></div>`;
15447
- }
15448
- html += "</div>";
15449
- qi.innerHTML = html;
15450
- scheduler2.event(qi, "click", function(ev) {
15451
- scheduler2._qi_button_click(ev.target || ev.srcElement);
15452
- });
15453
- if (scheduler2.config.quick_info_detached) {
15454
- scheduler2._detachDomEvent(scheduler2._els["dhx_cal_data"][0], "scroll", scheduler2._quick_info_onscroll_handler);
15455
- scheduler2.event(scheduler2._els["dhx_cal_data"][0], "scroll", scheduler2._quick_info_onscroll_handler);
15456
- }
15493
+ <div class="dhx_cal_qi_content"></div>`;
15494
+ html += '<div class="dhx_cal_qi_controls">';
15495
+ let buttons = scheduler2.config.icons_select;
15496
+ for (let i = 0; i < buttons.length; i++) {
15497
+ let ariaAttr2 = this._waiAria.quickInfoButtonAttrString(this.locale.labels[buttons[i]]);
15498
+ html += `<div ${ariaAttr2} class="dhx_qi_big_icon ${buttons[i]}" title="${scheduler2.locale.labels[buttons[i]]}">
15499
+ <div class='dhx_menu_icon ${buttons[i]}'></div><div>${scheduler2.locale.labels[buttons[i]]}</div></div>`;
15500
+ }
15501
+ html += "</div>";
15502
+ qi.innerHTML = html;
15503
+ scheduler2.event(qi, "click", function(ev) {
15504
+ scheduler2._qi_button_click(ev.target || ev.srcElement);
15505
+ });
15506
+ if (scheduler2.config.quick_info_detached) {
15507
+ scheduler2._detachDomEvent(scheduler2._els["dhx_cal_data"][0], "scroll", scheduler2._quick_info_onscroll_handler);
15508
+ scheduler2.event(scheduler2._els["dhx_cal_data"][0], "scroll", scheduler2._quick_info_onscroll_handler);
15457
15509
  }
15458
15510
  return this._quick_info_box;
15459
15511
  };
@@ -18187,6 +18239,73 @@ function rdatesToString(param, rdates, tzid) {
18187
18239
  return "".concat(header).concat(dateString);
18188
18240
  }
18189
18241
  function recurring(scheduler2) {
18242
+ scheduler2.ext.recurring = { confirm: function(context) {
18243
+ }, confirmDefault: function showRecurringConfirmModal(context) {
18244
+ const labels = context.labels || {};
18245
+ const options = Array.isArray(context.options) ? context.options : [];
18246
+ if (options.length === 0) {
18247
+ return Promise.resolve(null);
18248
+ }
18249
+ if (options.length === 1) {
18250
+ return Promise.resolve(options[0]);
18251
+ }
18252
+ const modalOptions = options.map((decision, index) => ({ value: decision, label: labelForDecision(decision, labels), checked: index === 0 }));
18253
+ return new Promise((resolve) => {
18254
+ scheduler2.modalbox({ text: `<div class="dhx_edit_recurrence_options">
18255
+ ${modalOptions.map((option) => `<label class="dhx_styled_radio">
18256
+ <input type="radio" value="${option.value}" name="option" ${option.checked ? "checked" : ""}>
18257
+ ${option.label}
18258
+ </label>`).join("")}
18259
+ </div>`, type: "recurring_mode", title: labels.title || scheduler2.locale.labels.confirm_recurring, width: "auto", position: "middle", buttons: [{ label: labels.ok || scheduler2.locale.labels.message_ok, value: "ok", css: "rec_ok" }, { label: labels.cancel || scheduler2.locale.labels.message_cancel, value: "cancel" }], callback: function onModalClose(value, event2) {
18260
+ if (value === "cancel") {
18261
+ resolve(null);
18262
+ return;
18263
+ }
18264
+ const box = event2.target.closest(".scheduler_modal_box");
18265
+ const checked = box && box.querySelector("input[type='radio']:checked");
18266
+ const selected = checked ? checked.value : null;
18267
+ if (!selected) {
18268
+ resolve(null);
18269
+ return;
18270
+ }
18271
+ resolve(selected);
18272
+ } });
18273
+ });
18274
+ }, _getDecision: async function _getDecision(context) {
18275
+ const confirmHandler = scheduler2.ext.recurring.confirm;
18276
+ let decision;
18277
+ if (typeof confirmHandler === "function") {
18278
+ decision = await confirmHandler(context);
18279
+ } else {
18280
+ decision = void 0;
18281
+ }
18282
+ if (decision === void 0) {
18283
+ decision = await scheduler2.ext.recurring.confirmDefault(context);
18284
+ }
18285
+ if (decision === null) {
18286
+ return null;
18287
+ }
18288
+ if (context.options && context.options.length > 0) {
18289
+ if (context.options.indexOf(decision) === -1) {
18290
+ console.warning(`[recurring extension] - the custom confirm handler returned a value ("${decision}") which is not in the allowed options list. The allowed options are: [${context.options.join(", ")}]. The operation will be cancelled.`);
18291
+ return null;
18292
+ }
18293
+ }
18294
+ return decision;
18295
+ } };
18296
+ scheduler2.ext.recurring.confirm = scheduler2.ext.recurring.confirmDefault;
18297
+ function labelForDecision(decision, labels) {
18298
+ if (decision === "occurrence") {
18299
+ return labels.occurrence || scheduler2.locale.labels.button_edit_occurrence;
18300
+ }
18301
+ if (decision === "following") {
18302
+ return labels.following || scheduler2.locale.labels.button_edit_occurrence_and_following;
18303
+ }
18304
+ if (decision === "series") {
18305
+ return labels.series || scheduler2.locale.labels.button_edit_series;
18306
+ }
18307
+ return decision;
18308
+ }
18190
18309
  function clearMilliseconds(date) {
18191
18310
  return new Date(date.getFullYear(), date.getMonth(), date.getDate(), date.getHours(), date.getMinutes(), date.getSeconds(), 0);
18192
18311
  }
@@ -18226,10 +18345,14 @@ function recurring(scheduler2) {
18226
18345
  if (ev.rrule.includes(";UNTIL=")) {
18227
18346
  ev.rrule = ev.rrule.split(";UNTIL=")[0];
18228
18347
  }
18229
- let parsedRRule = rrulestr(`RRULE:${ev.rrule};UNTIL=${toIcalString(setUTCPartsToDate(ev._end_date || ev.end_date))}`, { dtstart: ev.start_date });
18230
- let newRRULE = new RRule(parsedRRule.origOptions).toString().replace("RRULE:", "");
18231
- newRRULE = newRRULE.split("\n")[1];
18232
- ev.rrule = newRRULE;
18348
+ let parsedRRule;
18349
+ if (ev.rrule.includes(";COUNT=")) {
18350
+ parsedRRule = rrulestr(`RRULE:${ev.rrule};UNTIL=${toIcalString(setUTCPartsToDate(ev._shorten_end_date))}`, { dtstart: ev.start_date });
18351
+ delete parsedRRule.origOptions.count;
18352
+ } else {
18353
+ parsedRRule = rrulestr(`RRULE:${ev.rrule};UNTIL=${toIcalString(setUTCPartsToDate(ev._end_date || ev.end_date))}`, { dtstart: ev.start_date });
18354
+ }
18355
+ ev.rrule = new RRule(parsedRRule.origOptions).toString().replace("RRULE:", "").split("\n")[1];
18233
18356
  }
18234
18357
  function updateFollowingRRULE(id, ev) {
18235
18358
  if (!ev) {
@@ -18237,6 +18360,7 @@ function recurring(scheduler2) {
18237
18360
  }
18238
18361
  let rruleStringparts = ev.rrule.split(";");
18239
18362
  let updatedRRULE = [];
18363
+ let interval;
18240
18364
  for (let i = 0; i < rruleStringparts.length; i++) {
18241
18365
  let splited = rruleStringparts[i].split("=");
18242
18366
  let code = splited[0];
@@ -18253,6 +18377,17 @@ function recurring(scheduler2) {
18253
18377
  ev._end_date = ev.end_date;
18254
18378
  }
18255
18379
  }
18380
+ if (code === "INTERVAL") {
18381
+ interval = Number(name);
18382
+ }
18383
+ if (code === "COUNT") {
18384
+ if (ev._shorten_end_date) {
18385
+ const start = scheduler2.date.date_part(new Date(ev._start_date));
18386
+ const end = scheduler2.date.date_part(new Date(ev._shorten_end_date));
18387
+ const days = Math.floor((end - start) / (1e3 * 60 * 60 * 24 * interval)) + 1;
18388
+ name = name - days;
18389
+ }
18390
+ }
18256
18391
  updatedRRULE.push(code);
18257
18392
  updatedRRULE.push("=");
18258
18393
  updatedRRULE.push(name);
@@ -18392,8 +18527,10 @@ function recurring(scheduler2) {
18392
18527
  for (let i in scheduler2._events) {
18393
18528
  let tev = scheduler2._events[i];
18394
18529
  if (tev.recurring_event_id == id || scheduler2._is_virtual_event(tev.id) && tev.id.split("#")[0] == id) {
18395
- tev.text = data.text;
18396
- scheduler2.updateEvent(tev.id);
18530
+ if (tev.start_date.valueOf() >= data.start_date.valueOf()) {
18531
+ tev.text = data.text;
18532
+ scheduler2.updateEvent(tev.id);
18533
+ }
18397
18534
  }
18398
18535
  }
18399
18536
  }
@@ -18511,7 +18648,7 @@ function recurring(scheduler2) {
18511
18648
  scheduler2.attachEvent("onRecurringEventSave", function(id, data, is_new_event) {
18512
18649
  let ev = this.getEvent(id);
18513
18650
  let tempEvent = scheduler2._lame_clone(ev);
18514
- let tempDataRRULE = data.rrule;
18651
+ let tempData = scheduler2._lame_clone(data);
18515
18652
  if (ev && isSeries(ev)) {
18516
18653
  if (!is_new_event && this._isFollowing(id)) {
18517
18654
  if (ev._removeFollowing) {
@@ -18567,10 +18704,8 @@ function recurring(scheduler2) {
18567
18704
  ev._shorten = true;
18568
18705
  updateFollowingRRULEOnSave(ev);
18569
18706
  scheduler2.callEvent("onEventChanged", [ev.id, ev]);
18570
- let followingEv = { ...tempEvent };
18571
- followingEv.text = data.text;
18572
- followingEv.duration = data.duration;
18573
- followingEv.rrule = tempDataRRULE;
18707
+ let followingEv = { ...tempData };
18708
+ followingEv._end_date = tempEvent._end_date;
18574
18709
  followingEv._start_date = null;
18575
18710
  followingEv.id = scheduler2.uid();
18576
18711
  scheduler2.addEvent(followingEv.start_date, followingEv.end_date, followingEv.text, followingEv.id, followingEv);
@@ -18738,43 +18873,28 @@ function recurring(scheduler2) {
18738
18873
  return this.showLightbox_rec(id);
18739
18874
  }
18740
18875
  if (formSetting === "ask") {
18741
- const locale2 = scheduler2.locale;
18742
- showModalbox([{ value: "Occurrence", label: locale2.labels.button_edit_occurrence, checked: true, callback: () => showRequiredLightbox(id, "Occurrence") }, { value: "Following", label: locale2.labels.button_edit_occurrence_and_following, callback: () => showRequiredLightbox(id, "Following") }, { value: "AllEvents", label: locale2.labels.button_edit_series, callback: () => showRequiredLightbox(id, "AllEvents") }]);
18876
+ showRecurringScopeConfirmForLightbox(id, pid);
18743
18877
  }
18744
- };
18745
- function showModalbox(options, callback) {
18746
- const locale = scheduler2.locale;
18747
- const haveChecked = options.find((o) => o.checked);
18748
- if (!haveChecked) {
18749
- options[0].checked = true;
18750
- }
18751
- const callbacks = options.reduce((result, o) => {
18752
- result[o.value] = o.callback;
18753
- return result;
18754
- }, {});
18755
- scheduler2.modalbox({ text: `<div class="dhx_edit_recurrence_options">
18756
- ${options.map((option) => `<label class="dhx_styled_radio">
18757
- <input type="radio" value="${option.value}" name="option" ${option.checked ? "checked" : ""}>
18758
- ${option.label}
18759
- </label>`).join("")}
18760
- </div>`, type: "recurring_mode", title: locale.labels.confirm_recurring, width: "auto", position: "middle", buttons: [{ label: locale.labels.message_ok, value: "ok", css: "rec_ok" }, { label: locale.labels.message_cancel, value: "cancel" }], callback: function(value, e) {
18761
- if (callback) {
18762
- callback(value, e);
18763
- }
18764
- if (value === "cancel") {
18878
+ async function showRecurringScopeConfirmForLightbox(id2, pid2) {
18879
+ const locale2 = scheduler2.locale;
18880
+ const occurrence = scheduler2.getEvent(id2);
18881
+ const seriesEvent = scheduler2.getEvent(pid2);
18882
+ const context = { origin: "lightbox", occurrence, series: seriesEvent, labels: { title: locale2.labels.confirm_recurring, ok: locale2.labels.message_ok, cancel: locale2.labels.message_cancel, occurrence: locale2.labels.button_edit_occurrence, following: locale2.labels.button_edit_occurrence_and_following, series: locale2.labels.button_edit_series }, options: ["occurrence", "following", "series"] };
18883
+ const decision = await scheduler2.ext.recurring._getDecision(context);
18884
+ if (decision === null) {
18765
18885
  return;
18766
18886
  }
18767
- const box = e.target.closest(".scheduler_modal_box");
18768
- const checked = box.querySelector("input[type='radio']:checked");
18769
- let selectedOption;
18770
- if (checked) {
18771
- selectedOption = checked.value;
18887
+ if (decision === "occurrence") {
18888
+ return showRequiredLightbox(id2, "Occurrence");
18772
18889
  }
18773
- if (selectedOption) {
18774
- callbacks[selectedOption]();
18890
+ if (decision === "following") {
18891
+ return showRequiredLightbox(id2, "Following");
18775
18892
  }
18776
- } });
18777
- }
18893
+ if (decision === "series") {
18894
+ return showRequiredLightbox(id2, "AllEvents");
18895
+ }
18896
+ }
18897
+ };
18778
18898
  scheduler2._showRequiredModalBox = function(id, type) {
18779
18899
  let buttons;
18780
18900
  const locale = scheduler2.locale;
@@ -18896,17 +19016,30 @@ function recurring(scheduler2) {
18896
19016
  }
18897
19017
  }
18898
19018
  };
18899
- const btnAll = { value: "AllEvents", label: locale.labels.button_edit_series, callback: () => handleAllEvents(occurrence) };
18900
- const btnFollowing = { value: "Following", label: locale.labels.button_edit_occurrence_and_following, callback: () => handleFollowing(occurrence) };
18901
- const btnOccurrence = { value: "Occurrence", label: locale.labels.button_edit_occurrence, callback: () => handleOccurrence(occurrence), checked: true };
18902
19019
  if (type === "ask") {
18903
- buttons = [btnOccurrence, btnFollowing, btnAll];
19020
+ buttons = ["occurrence", "following", "series"];
18904
19021
  } else {
18905
- buttons = [btnOccurrence, btnFollowing];
19022
+ buttons = ["occurrence", "following"];
18906
19023
  }
18907
- showModalbox(buttons, (result) => {
18908
- if (result === "cancel") {
19024
+ const context = { origin: "dnd", occurrence, series: event2, labels: { title: locale.labels.confirm_recurring, ok: locale.labels.message_ok, cancel: locale.labels.message_cancel, occurrence: locale.labels.button_edit_occurrence, following: locale.labels.button_edit_occurrence_and_following, series: locale.labels.button_edit_series }, options: buttons };
19025
+ Promise.resolve(scheduler2.ext.recurring._getDecision(context)).then((decision) => {
19026
+ if (!decision) {
18909
19027
  removeTempDraggedEvent();
19028
+ return;
19029
+ }
19030
+ switch (decision) {
19031
+ case "occurrence":
19032
+ handleOccurrence(occurrence);
19033
+ break;
19034
+ case "following":
19035
+ handleFollowing(occurrence);
19036
+ break;
19037
+ case "series":
19038
+ handleAllEvents(occurrence);
19039
+ break;
19040
+ default:
19041
+ removeTempDraggedEvent();
19042
+ return;
18910
19043
  }
18911
19044
  });
18912
19045
  };
@@ -19017,7 +19150,7 @@ function recurring(scheduler2) {
19017
19150
  copy2.start_date = date;
19018
19151
  copy2.id = ev.id + "#" + Math.ceil(date.valueOf());
19019
19152
  copy2.end_date = new Date(date.valueOf() + eventDuration * 1e3);
19020
- if (copy2.end_date.valueOf() < scheduler2._min_date.valueOf()) {
19153
+ if (copy2.end_date.valueOf() <= scheduler2._min_date.valueOf()) {
19021
19154
  continue;
19022
19155
  }
19023
19156
  copy2.end_date = scheduler2._fix_daylight_saving_date(copy2.start_date, copy2.end_date, ev, date, copy2.end_date);
@@ -20958,73 +21091,103 @@ function units_restricted(scheduler2) {
20958
21091
  notImplemented.alert("Units", scheduler2.assert);
20959
21092
  }
20960
21093
  function url(scheduler2) {
20961
- scheduler2._get_url_nav = function() {
20962
- var p = {};
20963
- var data = (document.location.hash || "").replace("#", "").split(",");
20964
- for (var i = 0; i < data.length; i++) {
20965
- var s = data[i].split("=");
20966
- if (s.length == 2)
20967
- p[s[0]] = s[1];
21094
+ function parseHashParameters() {
21095
+ const parameters = {};
21096
+ const raw = (document.location.hash || "").replace("#", "");
21097
+ if (!raw) {
21098
+ return parameters;
21099
+ }
21100
+ const pairs = raw.split(",");
21101
+ for (let i = 0; i < pairs.length; i++) {
21102
+ const parts = pairs[i].split("=");
21103
+ if (parts.length === 2) {
21104
+ parameters[parts[0]] = parts[1];
21105
+ }
20968
21106
  }
20969
- return p;
21107
+ return parameters;
21108
+ }
21109
+ scheduler2._get_url_nav = function() {
21110
+ return parseHashParameters();
20970
21111
  };
20971
- scheduler2.attachEvent("onTemplatesReady", function() {
20972
- var first = true;
20973
- var s2d = scheduler2.date.str_to_date("%Y-%m-%d");
20974
- var d2s = scheduler2.date.date_to_str("%Y-%m-%d");
20975
- var select_event = scheduler2._get_url_nav().event || null;
20976
- scheduler2.attachEvent("onAfterEventDisplay", function(ev) {
20977
- select_event = null;
20978
- return true;
20979
- });
20980
- scheduler2.attachEvent("onBeforeViewChange", function(om, od, m, d) {
20981
- if (first) {
20982
- first = false;
20983
- var p = scheduler2._get_url_nav();
20984
- if (p.event) {
20985
- try {
20986
- if (scheduler2.getEvent(p.event)) {
20987
- setTimeout(function() {
20988
- showEvent(p.event);
20989
- });
20990
- return false;
20991
- } else {
20992
- var handler = scheduler2.attachEvent("onXLE", function() {
20993
- setTimeout(function() {
20994
- showEvent(p.event);
20995
- });
20996
- scheduler2.detachEvent(handler);
20997
- });
20998
- }
20999
- } catch (e) {
21000
- }
21001
- }
21002
- if (p.date || p.mode) {
21003
- try {
21004
- this.setCurrentView(p.date ? s2d(p.date) : null, p.mode || null);
21005
- } catch (e) {
21006
- this.setCurrentView(p.date ? s2d(p.date) : null, m);
21007
- }
21008
- return false;
21112
+ const dateToString = scheduler2.date.date_to_str("%Y-%m-%d");
21113
+ const originalGetInitialState = scheduler2._getInitialState;
21114
+ scheduler2._getInitialState = function(provided) {
21115
+ const baseState = originalGetInitialState.call(this, provided);
21116
+ const url2 = scheduler2._get_url_nav ? scheduler2._get_url_nav() : null;
21117
+ if (!url2) {
21118
+ return baseState;
21119
+ }
21120
+ const nextState = { date: baseState.date, mode: baseState.mode };
21121
+ if (url2.date) {
21122
+ const stringToDate = scheduler2.date.str_to_date("%Y-%m-%d");
21123
+ try {
21124
+ const parsed = stringToDate(url2.date);
21125
+ if (!isNaN(+parsed)) {
21126
+ nextState.date = parsed;
21009
21127
  }
21128
+ } catch (e) {
21010
21129
  }
21011
- var values = ["date=" + d2s(d || od), "mode=" + (m || om)];
21012
- if (select_event) {
21013
- values.push("event=" + select_event);
21130
+ }
21131
+ if (url2.mode) {
21132
+ try {
21133
+ if (this.isViewExists(url2.mode)) {
21134
+ nextState.mode = url2.mode;
21135
+ }
21136
+ } catch (e) {
21014
21137
  }
21015
- var text = "#" + values.join(",");
21016
- document.location.hash = text;
21017
- return true;
21018
- });
21019
- function showEvent(e) {
21138
+ }
21139
+ return nextState;
21140
+ };
21141
+ function updateHashFromState(optionalSelectedEventId) {
21142
+ const currentDate = scheduler2._date || scheduler2.getState().date;
21143
+ const currentMode = scheduler2.getState().mode;
21144
+ const values = ["date=" + dateToString(currentDate), "mode=" + currentMode];
21145
+ if (optionalSelectedEventId) {
21146
+ values.push("event=" + optionalSelectedEventId);
21147
+ }
21148
+ document.location.hash = "#" + values.join(",");
21149
+ }
21150
+ function showEventWhenPossible(eventId) {
21151
+ if (!eventId) {
21152
+ return;
21153
+ }
21154
+ const tryShowNow = function() {
21020
21155
  if (scheduler2.$destroyed) {
21021
- return true;
21156
+ return;
21022
21157
  }
21023
- select_event = e;
21024
- if (scheduler2.getEvent(e)) {
21025
- scheduler2.showEvent(e);
21158
+ if (scheduler2.getEvent(eventId)) {
21159
+ scheduler2.showEvent(eventId);
21160
+ return true;
21026
21161
  }
21162
+ return false;
21163
+ };
21164
+ if (tryShowNow()) {
21165
+ return;
21027
21166
  }
21167
+ const onLoadedId = scheduler2.attachEvent("onParse", function() {
21168
+ tryShowNow();
21169
+ scheduler2.detachEvent(onLoadedId);
21170
+ return true;
21171
+ });
21172
+ }
21173
+ let pendingSelectedEventId = null;
21174
+ scheduler2.attachEvent("onSchedulerReady", function() {
21175
+ const initialState = scheduler2._get_url_nav();
21176
+ if (initialState.event) {
21177
+ pendingSelectedEventId = initialState.event;
21178
+ showEventWhenPossible(initialState.event);
21179
+ }
21180
+ updateHashFromState(pendingSelectedEventId);
21181
+ return true;
21182
+ });
21183
+ scheduler2.attachEvent("onAfterEventDisplay", function() {
21184
+ pendingSelectedEventId = null;
21185
+ updateHashFromState(null);
21186
+ return true;
21187
+ });
21188
+ scheduler2.attachEvent("onViewChange", function() {
21189
+ updateHashFromState(pendingSelectedEventId);
21190
+ return true;
21028
21191
  });
21029
21192
  }
21030
21193
  function week_agenda_restricted(scheduler2) {
@@ -21281,6 +21444,9 @@ function year_view(scheduler2) {
21281
21444
  var locateEvent = scheduler2._locate_event;
21282
21445
  scheduler2._locate_event = function(node) {
21283
21446
  var id = locateEvent.apply(scheduler2, arguments);
21447
+ if (!isYearMode()) {
21448
+ return id;
21449
+ }
21284
21450
  if (!id) {
21285
21451
  var date = getCellDate(node);
21286
21452
  if (!date)