cx 24.11.4 → 25.1.1

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.
package/dist/ui.js CHANGED
@@ -43,6 +43,7 @@ import {
43
43
  closestParent,
44
44
  Format as Format$1,
45
45
  resolveMinMaxFractionDigits,
46
+ parseDateInvariant,
46
47
  setGetFormatCacheCallback,
47
48
  isNonEmptyArray,
48
49
  debug,
@@ -1591,7 +1592,7 @@ function enableCultureSensitiveFormatting() {
1591
1592
  var culture = Culture.getDateTimeCulture();
1592
1593
  var formatter = culture.getFormatter(format);
1593
1594
  return function (value) {
1594
- return formatter.format(new Date(value));
1595
+ return formatter.format(parseDateInvariant(value));
1595
1596
  };
1596
1597
  });
1597
1598
  Format$1.registerFactory(["time", "t"], function (fmt, format) {
@@ -1601,7 +1602,7 @@ function enableCultureSensitiveFormatting() {
1601
1602
  var culture = Culture.getDateTimeCulture();
1602
1603
  var formatter = culture.getFormatter(format);
1603
1604
  return function (value) {
1604
- return formatter.format(new Date(value));
1605
+ return formatter.format(parseDateInvariant(value));
1605
1606
  };
1606
1607
  });
1607
1608
  Format$1.registerFactory(["datetime", "dt"], function (fmt, format) {
@@ -1611,7 +1612,7 @@ function enableCultureSensitiveFormatting() {
1611
1612
  var culture = Culture.getDateTimeCulture();
1612
1613
  var formatter = culture.getFormatter(format);
1613
1614
  return function (value) {
1614
- return formatter.format(new Date(value));
1615
+ return formatter.format(parseDateInvariant(value));
1615
1616
  };
1616
1617
  });
1617
1618
  setGetFormatCacheCallback(function () {
@@ -3024,7 +3025,7 @@ var HoverSync = /*#__PURE__*/ (function (_PureContainer) {
3024
3025
  report: function report(channel, hoverId, active) {
3025
3026
  var ch = channels[channel];
3026
3027
  if (!ch) return;
3027
- var state = active && hoverId;
3028
+ var state = active && hoverId != null;
3028
3029
  if (ch.state !== state && (ch.state === hoverId || active)) {
3029
3030
  ch.state = state;
3030
3031
  ch.subscribers.notify(state);
package/dist/util.js CHANGED
@@ -125,6 +125,25 @@ function capitalize(str) {
125
125
  return str.charAt(0).toUpperCase() + str.substring(1);
126
126
  }
127
127
 
128
+ // This module addresses a common issue when handling date strings in the format "yyyy-MM-dd" usually returned by backends.
129
+ // In time zones earlier than UTC, creating a Date object from such a string can result in the date being shifted one day earlier.
130
+ // This happens because "yyyy-MM-dd" is interpreted as a UTC date at 00:00, and when the browser displays it in local time, it adjusts backward.
131
+ // To resolve this, the default implementation (`defaultInvariantParseDate`) appends " 00:00" to the date string,
132
+ // explicitly indicating local time. Custom parsing logic can also be registered dynamically using `registerInvariantParseDateImpl`
133
+ // to accommodate other formats or requirements.
134
+ function defaultParseDateInvariant(input) {
135
+ if (typeof input == "string" && input.length == 10 && input[4] == "-" && input[7] == "-")
136
+ return new Date(input + " 00:00");
137
+ return new Date(input);
138
+ }
139
+ var impl = defaultParseDateInvariant;
140
+ function parseDateInvariant(input) {
141
+ return impl(input);
142
+ }
143
+ function overrideParseDateInvariant(newImpl) {
144
+ impl = newImpl;
145
+ }
146
+
128
147
  //Culture dependent formatters are defined in the ui package.
129
148
 
130
149
  var defaultFormatter = function defaultFormatter(v) {
@@ -202,13 +221,13 @@ var formatFactory = {
202
221
  },
203
222
  date: function date() {
204
223
  return function (value) {
205
- var date = new Date(value);
224
+ var date = parseDateInvariant(value);
206
225
  return date.getMonth() + 1 + "/" + date.getDate() + "/" + date.getFullYear();
207
226
  };
208
227
  },
209
228
  time: function time() {
210
229
  return function (value) {
211
- var date = new Date(value);
230
+ var date = parseDateInvariant(value);
212
231
  var h = date.getHours() >= 10 ? date.getHours() : "0" + date.getHours();
213
232
  var m = date.getMinutes() >= 10 ? date.getMinutes() : "0" + date.getMinutes();
214
233
  return h + ":" + m;
@@ -799,7 +818,7 @@ function sameDate(d1, d2) {
799
818
 
800
819
  //https://stackoverflow.com/questions/17415579/how-to-iso-8601-format-a-date-with-timezone-offset-in-javascript
801
820
 
802
- function pad(num) {
821
+ function pad$1(num) {
803
822
  var norm = Math.floor(Math.abs(num));
804
823
  return (norm < 10 ? "0" : "") + norm;
805
824
  }
@@ -809,22 +828,30 @@ function encodeDateWithTimezoneOffset(date) {
809
828
  return (
810
829
  date.getFullYear() +
811
830
  "-" +
812
- pad(date.getMonth() + 1) +
831
+ pad$1(date.getMonth() + 1) +
813
832
  "-" +
814
- pad(date.getDate()) +
833
+ pad$1(date.getDate()) +
815
834
  "T" +
816
- pad(date.getHours()) +
835
+ pad$1(date.getHours()) +
817
836
  ":" +
818
- pad(date.getMinutes()) +
837
+ pad$1(date.getMinutes()) +
819
838
  ":" +
820
- pad(date.getSeconds()) +
839
+ pad$1(date.getSeconds()) +
821
840
  dif +
822
- pad(tzo / 60) +
841
+ pad$1(tzo / 60) +
823
842
  ":" +
824
- pad(tzo % 60)
843
+ pad$1(tzo % 60)
825
844
  );
826
845
  }
827
846
 
847
+ function pad(num) {
848
+ var norm = Math.floor(Math.abs(num));
849
+ return (norm < 10 ? "0" : "") + norm;
850
+ }
851
+ function encodeDate(date) {
852
+ return date.getFullYear() + "-" + pad(date.getMonth() + 1) + "-" + pad(date.getDate());
853
+ }
854
+
828
855
  function hue2rgb(p, q, t) {
829
856
  if (t < 0) t += 1;
830
857
  if (t > 1) t -= 1;
@@ -1515,6 +1542,7 @@ export {
1515
1542
  destroyFlag,
1516
1543
  dummyCallback,
1517
1544
  enableTouchEventDetection,
1545
+ encodeDate,
1518
1546
  encodeDateWithTimezoneOffset,
1519
1547
  escapeSpecialRegexCharacters,
1520
1548
  expandFatArrows,
@@ -1560,7 +1588,9 @@ export {
1560
1588
  monthStart,
1561
1589
  now,
1562
1590
  onIdleCallback,
1591
+ overrideParseDateInvariant,
1563
1592
  parseColor,
1593
+ parseDateInvariant,
1564
1594
  parseHexColor,
1565
1595
  parseHslColor,
1566
1596
  parseRgbColor,
package/dist/widgets.js CHANGED
@@ -107,6 +107,7 @@ import {
107
107
  monthStart,
108
108
  zeroTime,
109
109
  sameDate,
110
+ parseDateInvariant,
110
111
  dateDiff,
111
112
  upperBoundCheck,
112
113
  lowerBoundCheck,
@@ -8088,14 +8089,14 @@ var Calendar = /*#__PURE__*/ (function (_Field) {
8088
8089
  disabled: data.disabled,
8089
8090
  };
8090
8091
  if (data.value) {
8091
- var d = new Date(data.value);
8092
+ var d = parseDateInvariant(data.value);
8092
8093
  if (!isNaN(d.getTime())) {
8093
8094
  data.date = zeroTime(d);
8094
8095
  }
8095
8096
  }
8096
- if (data.refDate) data.refDate = zeroTime(new Date(data.refDate));
8097
- if (data.maxValue) data.maxValue = zeroTime(new Date(data.maxValue));
8098
- if (data.minValue) data.minValue = zeroTime(new Date(data.minValue));
8097
+ if (data.refDate) data.refDate = zeroTime(parseDateInvariant(data.refDate));
8098
+ if (data.maxValue) data.maxValue = zeroTime(parseDateInvariant(data.maxValue));
8099
+ if (data.minValue) data.minValue = zeroTime(parseDateInvariant(data.minValue));
8099
8100
  _Field.prototype.prepareData.apply(this, arguments);
8100
8101
  };
8101
8102
  _proto.validate = function validate(context, instance) {
@@ -8120,7 +8121,7 @@ var Calendar = /*#__PURE__*/ (function (_Field) {
8120
8121
  if (widget.disabledDaysOfWeek.includes(data.date.getDay())) data.error = this.disabledDaysOfWeekErrorText;
8121
8122
  }
8122
8123
  if (data.dayData) {
8123
- var date = new Date(data.value);
8124
+ var date = parseDateInvariant(data.value);
8124
8125
  var info = data.dayData[date.toDateString()];
8125
8126
  if (info && info.disabled) data.error = this.disabledDaysOfWeekErrorText;
8126
8127
  }
@@ -8148,7 +8149,7 @@ var Calendar = /*#__PURE__*/ (function (_Field) {
8148
8149
  if (!validationCheck$1(date, data)) return;
8149
8150
  if (this.onBeforeSelect && instance.invoke("onBeforeSelect", e, instance, date) === false) return;
8150
8151
  if (widget.partial) {
8151
- var mixed = new Date(data.value);
8152
+ var mixed = parseDateInvariant(data.value);
8152
8153
  if (data.value && !isNaN(mixed)) {
8153
8154
  mixed.setFullYear(date.getFullYear());
8154
8155
  mixed.setMonth(date.getMonth());
@@ -10800,14 +10801,17 @@ var MonthPicker = /*#__PURE__*/ (function (_Field) {
10800
10801
  data.stateMods = {
10801
10802
  disabled: data.disabled,
10802
10803
  };
10803
- if (!this.range && data.value) data.date = monthStart(new Date(data.value));
10804
+ if (!this.range && data.value) data.date = monthStart(parseDateInvariant(data.value));
10804
10805
  if (this.range) {
10805
- if (data.from) data.from = monthStart(new Date(data.from));
10806
- if (data.to) data.to = monthStart(new Date(data.to));
10806
+ if (data.from) data.from = monthStart(parseDateInvariant(data.from));
10807
+ if (data.to) {
10808
+ data.to = monthStart(parseDateInvariant(data.to));
10809
+ if (this.inclusiveTo) data.to.setDate(data.to.getDate() + 1);
10810
+ }
10807
10811
  }
10808
- if (data.refDate) data.refDate = monthStart(new Date(data.refDate));
10809
- if (data.maxValue) data.maxValue = monthStart(new Date(data.maxValue));
10810
- if (data.minValue) data.minValue = monthStart(new Date(data.minValue));
10812
+ if (data.refDate) data.refDate = monthStart(parseDateInvariant(data.refDate));
10813
+ if (data.maxValue) data.maxValue = monthStart(parseDateInvariant(data.maxValue));
10814
+ if (data.minValue) data.minValue = monthStart(parseDateInvariant(data.minValue));
10811
10815
  _Field.prototype.prepareData.apply(this, arguments);
10812
10816
  };
10813
10817
  _proto.validate = function validate(context, instance) {
@@ -10851,7 +10855,9 @@ var MonthPicker = /*#__PURE__*/ (function (_Field) {
10851
10855
  if (this.onBeforeSelect && instance.invoke("onBeforeSelect", e, instance, date1, date2) === false) return;
10852
10856
  if (this.range) {
10853
10857
  instance.set("from", encode(date1));
10854
- instance.set("to", encode(date2));
10858
+ var toDate = new Date(date2);
10859
+ if (this.inclusiveTo) toDate.setDate(toDate.getDate() - 1);
10860
+ instance.set("to", encode(toDate));
10855
10861
  } else instance.set("value", encode(date1));
10856
10862
  if (this.onSelect) instance.invoke("onSelect", instance, date1, date2);
10857
10863
  };
@@ -10868,6 +10874,7 @@ MonthPicker.prototype.maxValueErrorText = "Select {0:d} or before.";
10868
10874
  MonthPicker.prototype.maxExclusiveErrorText = "Select a date before {0:d}.";
10869
10875
  MonthPicker.prototype.minValueErrorText = "Select {0:d} or later.";
10870
10876
  MonthPicker.prototype.minExclusiveErrorText = "Select a date after {0:d}.";
10877
+ MonthPicker.prototype.inclusiveTo = false;
10871
10878
  Localization.registerPrototype("cx/widgets/MonthPicker", MonthPicker);
10872
10879
  Widget.alias("month-picker", MonthPicker);
10873
10880
  var validationCheck = function validationCheck(date, data) {
@@ -12084,20 +12091,20 @@ var MonthField = /*#__PURE__*/ (function (_Field) {
12084
12091
  month: "short",
12085
12092
  };
12086
12093
  if (!this.range && data.value) {
12087
- data.date = new Date(data.value);
12094
+ data.date = parseDateInvariant(data.value);
12088
12095
  data.formatted = this.culture.format(data.date, formatOptions);
12089
12096
  } else if (this.range && data.from && data.to) {
12090
- data.from = new Date(data.from);
12091
- data.to = new Date(data.to);
12092
- data.to.setDate(data.to.getDate() - 1);
12097
+ data.from = parseDateInvariant(data.from);
12098
+ data.to = parseDateInvariant(data.to);
12099
+ if (!this.inclusiveTo) data.to.setDate(data.to.getDate() - 1);
12093
12100
  var fromStr = this.culture.format(data.from, formatOptions);
12094
12101
  var toStr = this.culture.format(data.to, formatOptions);
12095
12102
  if (fromStr != toStr) data.formatted = fromStr + " - " + toStr;
12096
12103
  else data.formatted = fromStr;
12097
12104
  }
12098
- if (data.refDate) data.refDate = monthStart(new Date(data.refDate));
12099
- if (data.maxValue) data.maxValue = monthStart(new Date(data.maxValue));
12100
- if (data.minValue) data.minValue = monthStart(new Date(data.minValue));
12105
+ if (data.refDate) data.refDate = monthStart(parseDateInvariant(data.refDate));
12106
+ if (data.maxValue) data.maxValue = monthStart(parseDateInvariant(data.maxValue));
12107
+ if (data.minValue) data.minValue = monthStart(parseDateInvariant(data.minValue));
12101
12108
  instance.lastDropdown = context.lastDropdown;
12102
12109
  };
12103
12110
  _proto.validateRequired = function validateRequired(context, instance) {
@@ -12172,7 +12179,12 @@ var MonthField = /*#__PURE__*/ (function (_Field) {
12172
12179
  });
12173
12180
  if (this.range) {
12174
12181
  var d1 = date1 ? encode(date1) : this.emptyValue;
12175
- var d2 = date2 ? encode(date2) : this.emptyValue;
12182
+ var toDate = date2;
12183
+ if (date2 && this.inclusiveTo) {
12184
+ toDate = new Date(date2);
12185
+ toDate.setDate(toDate.getDate() - 1);
12186
+ }
12187
+ var d2 = toDate ? encode(toDate) : this.emptyValue;
12176
12188
  instance.set("from", d1);
12177
12189
  instance.set("to", d2);
12178
12190
  } else {
@@ -12194,6 +12206,7 @@ MonthField.prototype.showClear = true;
12194
12206
  MonthField.prototype.alwaysShowClear = false;
12195
12207
  MonthField.prototype.range = false;
12196
12208
  MonthField.prototype.reactOn = "enter blur";
12209
+ MonthField.prototype.inclusiveTo = false;
12197
12210
  Localization.registerPrototype("cx/widgets/MonthField", MonthField);
12198
12211
  Widget.alias("monthfield", MonthField);
12199
12212
  var MonthInput = /*#__PURE__*/ (function (_VDOM$Component) {
@@ -12234,6 +12247,7 @@ var MonthInput = /*#__PURE__*/ (function (_VDOM$Component) {
12234
12247
  this.props.monthPicker,
12235
12248
  {
12236
12249
  encoding: widget.encoding,
12250
+ inclusiveTo: widget.inclusiveTo,
12237
12251
  autoFocus: true,
12238
12252
  onFocusOut: function onFocusOut(e) {
12239
12253
  _this2.closeDropdown(e);
@@ -14026,7 +14040,7 @@ var DateTimePickerComponent = /*#__PURE__*/ (function (_VDOM$Component) {
14026
14040
  function DateTimePickerComponent(props) {
14027
14041
  var _this;
14028
14042
  _this = _VDOM$Component.call(this, props) || this;
14029
- var date = props.data.value ? new Date(props.data.value) : new Date();
14043
+ var date = props.data.value ? parseDateInvariant(props.data.value) : new Date();
14030
14044
  if (isNaN(date.getTime())) date = new Date();
14031
14045
  _this.state = {
14032
14046
  date: date,
@@ -14053,7 +14067,7 @@ var DateTimePickerComponent = /*#__PURE__*/ (function (_VDOM$Component) {
14053
14067
  _inheritsLoose(DateTimePickerComponent, _VDOM$Component);
14054
14068
  var _proto2 = DateTimePickerComponent.prototype;
14055
14069
  _proto2.UNSAFE_componentWillReceiveProps = function UNSAFE_componentWillReceiveProps(props) {
14056
- var date = props.data.value ? new Date(props.data.value) : new Date();
14070
+ var date = props.data.value ? parseDateInvariant(props.data.value) : new Date();
14057
14071
  if (isNaN(date.getTime())) date = new Date();
14058
14072
  this.setState({
14059
14073
  date: date,
@@ -14577,7 +14591,9 @@ var DateTimeField = /*#__PURE__*/ (function (_Field) {
14577
14591
  _proto.prepareData = function prepareData(context, instance) {
14578
14592
  var data = instance.data;
14579
14593
  if (data.value) {
14580
- var date = new Date(data.value);
14594
+ var date = parseDateInvariant(data.value);
14595
+ // let date = new Date(data.value);
14596
+
14581
14597
  if (isNaN(date.getTime())) data.formatted = String(data.value);
14582
14598
  else {
14583
14599
  // handle utc edge cases
@@ -14586,9 +14602,9 @@ var DateTimeField = /*#__PURE__*/ (function (_Field) {
14586
14602
  }
14587
14603
  data.date = date;
14588
14604
  } else data.formatted = "";
14589
- if (data.refDate) data.refDate = zeroTime(new Date(data.refDate));
14590
- if (data.maxValue) data.maxValue = new Date(data.maxValue);
14591
- if (data.minValue) data.minValue = new Date(data.minValue);
14605
+ if (data.refDate) data.refDate = zeroTime(parseDateInvariant(data.refDate));
14606
+ if (data.maxValue) data.maxValue = parseDateInvariant(data.maxValue);
14607
+ if (data.minValue) data.minValue = parseDateInvariant(data.minValue);
14592
14608
  if (this.segment == "date") {
14593
14609
  if (data.minValue) data.minValue = zeroTime(data.minValue);
14594
14610
  if (data.maxValue) data.maxValue = zeroTime(data.maxValue);
@@ -15048,7 +15064,7 @@ var DateTimeInput = /*#__PURE__*/ (function (_VDOM$Component) {
15048
15064
  inputError: isNaN(date) && widget.inputErrorText,
15049
15065
  });
15050
15066
  if (!isNaN(date)) {
15051
- var mixed = new Date(baseValue);
15067
+ var mixed = parseDateInvariant(baseValue);
15052
15068
  if (date && baseValue && !isNaN(mixed) && widget.partial) {
15053
15069
  switch (widget.segment) {
15054
15070
  case "date":
@@ -15332,11 +15348,11 @@ var GridRowComponent = /*#__PURE__*/ (function (_VDOM$Component) {
15332
15348
  widget = instance.widget;
15333
15349
  var CSS = widget.CSS;
15334
15350
  var move, up, keyDown, leave;
15335
- if (dragSource || data.hoverId) {
15351
+ if (dragSource || data.hoverId != null) {
15336
15352
  move = this.onMouseMove;
15337
15353
  up = ddMouseUp;
15338
15354
  }
15339
- if (data.hoverId) {
15355
+ if (data.hoverId != null) {
15340
15356
  leave = this.onMouseLeave;
15341
15357
  }
15342
15358
  if (widget.onRowClick) keyDown = this.onKeyDown;
@@ -16004,13 +16020,13 @@ var Grid = /*#__PURE__*/ (function (_Container) {
16004
16020
  onMouseDown: function onMouseDown(e) {
16005
16021
  if (e.buttons != 1) return;
16006
16022
  var resizeOverlayEl = document.createElement("div");
16007
- var headerTBody = e.target.parentElement.parentElement.parentElement;
16023
+ var gridEl = e.target.parentElement.parentElement.parentElement.parentElement.parentElement.parentElement;
16008
16024
  var uniqueColId = e.currentTarget.dataset.uniqueColId;
16009
- var headerCell = findFirstChild(headerTBody, function (el) {
16025
+
16026
+ // if we use fixed columns, rhs resizer of the last fixed column is within regular columns header tbody
16027
+ var headerCell = findFirstChild(gridEl, function (el) {
16010
16028
  return el.tagName == "TH" && el.dataset && el.dataset.uniqueColId == uniqueColId;
16011
16029
  });
16012
- var scrollAreaEl = headerTBody.parentElement.parentElement;
16013
- var gridEl = scrollAreaEl.parentElement;
16014
16030
  var initialWidth = headerCell.offsetWidth;
16015
16031
  var initialPosition = getCursorPos(e);
16016
16032
  resizeOverlayEl.className = CSS.element(baseClass, "resize-overlay");
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "cx",
3
- "version": "24.11.4",
3
+ "version": "25.1.1",
4
4
  "description": "Advanced JavaScript UI framework for admin and dashboard applications with ready to use grid, form and chart components.",
5
5
  "main": "index.js",
6
6
  "jsnext:main": "src/index.js",
@@ -5,12 +5,13 @@ import { Format } from "../../ui/Format";
5
5
  import { isNumber } from "../../util/isNumber";
6
6
  import { zeroTime } from "../../util/date/zeroTime";
7
7
  import { Console } from "../../util/Console";
8
+ import { parseDateInvariant } from "../../util";
8
9
 
9
10
  Format.registerFactory("yearOrMonth", (format) => {
10
11
  let year = Format.parse("datetime;yyyy");
11
12
  let month = Format.parse("datetime;MMM");
12
13
  return function (date) {
13
- let d = new Date(date);
14
+ let d = parseDateInvariant(date);
14
15
  if (d.getMonth() == 0) return year(d);
15
16
  else return month(d);
16
17
  };
@@ -20,7 +21,7 @@ Format.registerFactory("monthOrDay", (format) => {
20
21
  let month = Format.parse("datetime;MMM");
21
22
  let day = Format.parse("datetime;dd");
22
23
  return function (date) {
23
- let d = new Date(date);
24
+ let d = parseDateInvariant(date);
24
25
  if (d.getDate() == 1) return month(d);
25
26
  else return day(d);
26
27
  };
@@ -34,7 +35,7 @@ export class TimeAxis extends Axis {
34
35
 
35
36
  if (this.deadZone) {
36
37
  this.lowerDeadZone = this.deadZone;
37
- pperDeadZone = this.deadZone;
38
+ this.upperDeadZone = this.deadZone;
38
39
  }
39
40
 
40
41
  this.minLabelDistanceFormatOverride = {
@@ -145,7 +146,7 @@ function yearNumber(date) {
145
146
  return monthNumber(date) / 12;
146
147
  }
147
148
 
148
- const miliSeconds = {
149
+ const milliSeconds = {
149
150
  second: 1000,
150
151
  minute: 60 * 1000,
151
152
  hour: 3600 * 1000,
@@ -203,12 +204,12 @@ class TimeScale {
203
204
  let v = this.dateCache[date];
204
205
  if (!v) {
205
206
  if (this.decode) date = this.decode(date);
206
- v = this.dateCache[date] = Date.parse(date);
207
+ v = this.dateCache[date] = parseDateInvariant(date).getTime();
207
208
  }
208
209
  return v;
209
210
 
210
211
  case "number":
211
- return date;
212
+ return parseDateInvariant(date).getTime();
212
213
  }
213
214
  }
214
215
 
@@ -339,13 +340,13 @@ class TimeScale {
339
340
  default:
340
341
  let minOffset = this.getTimezoneOffset(minDate);
341
342
  let maxOffset = this.getTimezoneOffset(maxDate);
342
- let mondayOffset = 4 * miliSeconds.day; //new Date(0).getDay() => 4
343
+ let mondayOffset = 4 * milliSeconds.day; //new Date(0).getDay() => 4
343
344
  smin = Math.floor((smin - minOffset - mondayOffset) / tickSize) * tickSize + minOffset + mondayOffset;
344
345
  smax = Math.ceil((smax - maxOffset - mondayOffset) / tickSize) * tickSize + maxOffset + mondayOffset;
345
346
  break;
346
347
 
347
348
  case "month":
348
- tickSize /= miliSeconds.month;
349
+ tickSize /= milliSeconds.month;
349
350
  let minMonth = monthNumber(minDate);
350
351
  let maxMonth = monthNumber(maxDate);
351
352
  minMonth = Math.floor(minMonth / tickSize) * tickSize;
@@ -355,7 +356,7 @@ class TimeScale {
355
356
  break;
356
357
 
357
358
  case "year":
358
- tickSize /= miliSeconds.year;
359
+ tickSize /= milliSeconds.year;
359
360
  let minYear = yearNumber(minDate);
360
361
  let maxYear = yearNumber(maxDate);
361
362
  minYear = Math.floor(minYear / tickSize) * tickSize;
@@ -439,13 +440,13 @@ class TimeScale {
439
440
 
440
441
  let minRange = 1000;
441
442
 
442
- for (let unit in miliSeconds) {
443
+ for (let unit in milliSeconds) {
443
444
  if (!minReached) {
444
445
  if (unit == this.minTickUnit) minReached = true;
445
446
  else continue;
446
447
  }
447
448
 
448
- let unitSize = miliSeconds[unit];
449
+ let unitSize = milliSeconds[unit];
449
450
  let divisions = this.tickDivisions[unit];
450
451
 
451
452
  if (this.tickSizes.length > 0) {
@@ -512,13 +513,13 @@ class TimeScale {
512
513
  break;
513
514
  }
514
515
 
515
- if (lowerTickUnit && this.minTickUnit && miliSeconds[lowerTickUnit] < miliSeconds[this.minTickUnit])
516
+ if (lowerTickUnit && this.minTickUnit && milliSeconds[lowerTickUnit] < milliSeconds[this.minTickUnit])
516
517
  lowerTickUnit = this.minTickUnit == this.tickMeasure ? null : this.minTickUnit;
517
518
 
518
519
  if (lowerTickUnit != null && this.scale) {
519
520
  let bestMinorTickSize = Infinity;
520
521
  let divisions = this.tickDivisions[lowerTickUnit];
521
- let unitSize = miliSeconds[lowerTickUnit];
522
+ let unitSize = milliSeconds[lowerTickUnit];
522
523
  for (let i = 0; i < divisions.length; i++) {
523
524
  let divs = divisions[i];
524
525
  for (let d = 0; d < divs.length; d++) {
@@ -552,14 +553,14 @@ class TimeScale {
552
553
  minDate,
553
554
  maxDate;
554
555
  if (measure == "year") {
555
- size /= miliSeconds.year;
556
+ size /= milliSeconds.year;
556
557
  minDate = new Date(this.scale.min - this.scale.minPadding);
557
558
  maxDate = new Date(this.scale.max + this.scale.maxPadding);
558
559
  start = Math.ceil(yearNumber(minDate) / size) * size;
559
560
  end = Math.floor(yearNumber(maxDate) / size) * size;
560
561
  for (let i = start; i <= end; i += size) result.push(new Date(i, 0, 1).getTime());
561
562
  } else if (measure == "month") {
562
- size /= miliSeconds.month;
563
+ size /= milliSeconds.month;
563
564
  minDate = new Date(this.scale.min - this.scale.minPadding);
564
565
  maxDate = new Date(this.scale.max + this.scale.maxPadding);
565
566
  start = Math.ceil(monthNumber(minDate) / size) * size;
@@ -567,7 +568,7 @@ class TimeScale {
567
568
  for (let i = start; i <= end; i += size) result.push(new Date(Math.floor(i / 12), i % 12, 1).getTime());
568
569
  } else if (measure == "day" || measure == "week") {
569
570
  let multiplier = measure == "week" ? 7 : 1;
570
- size /= miliSeconds.day;
571
+ size /= milliSeconds.day;
571
572
  minDate = new Date(this.scale.min - this.scale.minPadding);
572
573
  maxDate = new Date(this.scale.max + this.scale.maxPadding);
573
574
  let date = zeroTime(minDate);
@@ -585,7 +586,7 @@ class TimeScale {
585
586
  }
586
587
  } else {
587
588
  let minOffset = this.getTimezoneOffset(new Date(this.scale.min - this.scale.minPadding));
588
- let mondayOffset = 4 * miliSeconds.day;
589
+ let mondayOffset = 4 * milliSeconds.day;
589
590
  let date =
590
591
  Math.ceil((this.scale.min - this.scale.minPadding - minOffset - mondayOffset) / size) * size +
591
592
  minOffset +
@@ -151,7 +151,15 @@ export function expression(str) {
151
151
  case '"':
152
152
  case "'":
153
153
  if (!quote) quote = c;
154
- else if (str[i - 1] != "\\" && quote == c) quote = false;
154
+ else if (quote == c) {
155
+ let at = i - 1;
156
+ let slashCount = 0;
157
+ while (at >= 0 && str[at] === "\\") {
158
+ slashCount++;
159
+ at--;
160
+ }
161
+ if (slashCount % 2 == 0) quote = false;
162
+ }
155
163
 
156
164
  if (curlyBrackets == 0) fb.push(c);
157
165
 
@@ -40,6 +40,18 @@ describe("Expression", function () {
40
40
  let state = {};
41
41
  assert.equal(e(state), '"');
42
42
  });
43
+
44
+ it("properly encodes quotes #3", function () {
45
+ let e = Expression.compile('"\\\\\\""');
46
+ let state = {};
47
+ assert.equal(e(state), '\\"');
48
+ });
49
+
50
+ it("properly encodes quotes #4", function () {
51
+ let e = Expression.compile('"\\\\"');
52
+ let state = {};
53
+ assert.equal(e(state), "\\");
54
+ });
43
55
  });
44
56
 
45
57
  describe("allow subexpressions", function () {