vis-rails 0.0.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.
- checksums.yaml +7 -0
- data/.gitignore +17 -0
- data/.gitmodules +3 -0
- data/.project +11 -0
- data/Gemfile +4 -0
- data/LICENSE +202 -0
- data/README.md +29 -0
- data/Rakefile +1 -0
- data/lib/vis/rails/engine.rb +6 -0
- data/lib/vis/rails/version.rb +5 -0
- data/lib/vis/rails.rb +7 -0
- data/vendor/assets/javascripts/vis.js +1 -0
- data/vendor/assets/stylesheets/vis.css +3 -0
- data/vendor/assets/vis/DataSet.js +936 -0
- data/vendor/assets/vis/DataView.js +281 -0
- data/vendor/assets/vis/EventBus.js +89 -0
- data/vendor/assets/vis/events.js +116 -0
- data/vendor/assets/vis/graph/ClusterMixin.js +1019 -0
- data/vendor/assets/vis/graph/Edge.js +620 -0
- data/vendor/assets/vis/graph/Graph.js +2111 -0
- data/vendor/assets/vis/graph/Groups.js +80 -0
- data/vendor/assets/vis/graph/Images.js +41 -0
- data/vendor/assets/vis/graph/NavigationMixin.js +245 -0
- data/vendor/assets/vis/graph/Node.js +978 -0
- data/vendor/assets/vis/graph/Popup.js +105 -0
- data/vendor/assets/vis/graph/SectorsMixin.js +547 -0
- data/vendor/assets/vis/graph/SelectionMixin.js +515 -0
- data/vendor/assets/vis/graph/dotparser.js +829 -0
- data/vendor/assets/vis/graph/img/downarrow.png +0 -0
- data/vendor/assets/vis/graph/img/leftarrow.png +0 -0
- data/vendor/assets/vis/graph/img/minus.png +0 -0
- data/vendor/assets/vis/graph/img/plus.png +0 -0
- data/vendor/assets/vis/graph/img/rightarrow.png +0 -0
- data/vendor/assets/vis/graph/img/uparrow.png +0 -0
- data/vendor/assets/vis/graph/img/zoomExtends.png +0 -0
- data/vendor/assets/vis/graph/shapes.js +225 -0
- data/vendor/assets/vis/module/exports.js +68 -0
- data/vendor/assets/vis/module/header.js +24 -0
- data/vendor/assets/vis/module/imports.js +32 -0
- data/vendor/assets/vis/shim.js +252 -0
- data/vendor/assets/vis/timeline/Controller.js +172 -0
- data/vendor/assets/vis/timeline/Range.js +553 -0
- data/vendor/assets/vis/timeline/Stack.js +192 -0
- data/vendor/assets/vis/timeline/TimeStep.js +449 -0
- data/vendor/assets/vis/timeline/Timeline.js +476 -0
- data/vendor/assets/vis/timeline/component/Component.js +148 -0
- data/vendor/assets/vis/timeline/component/ContentPanel.js +113 -0
- data/vendor/assets/vis/timeline/component/CurrentTime.js +101 -0
- data/vendor/assets/vis/timeline/component/CustomTime.js +255 -0
- data/vendor/assets/vis/timeline/component/Group.js +129 -0
- data/vendor/assets/vis/timeline/component/GroupSet.js +546 -0
- data/vendor/assets/vis/timeline/component/ItemSet.js +612 -0
- data/vendor/assets/vis/timeline/component/Panel.js +112 -0
- data/vendor/assets/vis/timeline/component/RootPanel.js +215 -0
- data/vendor/assets/vis/timeline/component/TimeAxis.js +522 -0
- data/vendor/assets/vis/timeline/component/css/currenttime.css +5 -0
- data/vendor/assets/vis/timeline/component/css/customtime.css +6 -0
- data/vendor/assets/vis/timeline/component/css/groupset.css +59 -0
- data/vendor/assets/vis/timeline/component/css/item.css +93 -0
- data/vendor/assets/vis/timeline/component/css/itemset.css +17 -0
- data/vendor/assets/vis/timeline/component/css/panel.css +14 -0
- data/vendor/assets/vis/timeline/component/css/timeaxis.css +41 -0
- data/vendor/assets/vis/timeline/component/css/timeline.css +2 -0
- data/vendor/assets/vis/timeline/component/item/Item.js +81 -0
- data/vendor/assets/vis/timeline/component/item/ItemBox.js +302 -0
- data/vendor/assets/vis/timeline/component/item/ItemPoint.js +237 -0
- data/vendor/assets/vis/timeline/component/item/ItemRange.js +251 -0
- data/vendor/assets/vis/timeline/component/item/ItemRangeOverflow.js +91 -0
- data/vendor/assets/vis/util.js +673 -0
- data/vis-rails.gemspec +47 -0
- metadata +142 -0
@@ -0,0 +1,192 @@
|
|
1
|
+
/**
|
2
|
+
* @constructor Stack
|
3
|
+
* Stacks items on top of each other.
|
4
|
+
* @param {ItemSet} parent
|
5
|
+
* @param {Object} [options]
|
6
|
+
*/
|
7
|
+
function Stack (parent, options) {
|
8
|
+
this.parent = parent;
|
9
|
+
|
10
|
+
this.options = options || {};
|
11
|
+
this.defaultOptions = {
|
12
|
+
order: function (a, b) {
|
13
|
+
//return (b.width - a.width) || (a.left - b.left); // TODO: cleanup
|
14
|
+
// Order: ranges over non-ranges, ranged ordered by width, and
|
15
|
+
// lastly ordered by start.
|
16
|
+
if (a instanceof ItemRange) {
|
17
|
+
if (b instanceof ItemRange) {
|
18
|
+
var aInt = (a.data.end - a.data.start);
|
19
|
+
var bInt = (b.data.end - b.data.start);
|
20
|
+
return (aInt - bInt) || (a.data.start - b.data.start);
|
21
|
+
}
|
22
|
+
else {
|
23
|
+
return -1;
|
24
|
+
}
|
25
|
+
}
|
26
|
+
else {
|
27
|
+
if (b instanceof ItemRange) {
|
28
|
+
return 1;
|
29
|
+
}
|
30
|
+
else {
|
31
|
+
return (a.data.start - b.data.start);
|
32
|
+
}
|
33
|
+
}
|
34
|
+
},
|
35
|
+
margin: {
|
36
|
+
item: 10
|
37
|
+
}
|
38
|
+
};
|
39
|
+
|
40
|
+
this.ordered = []; // ordered items
|
41
|
+
}
|
42
|
+
|
43
|
+
/**
|
44
|
+
* Set options for the stack
|
45
|
+
* @param {Object} options Available options:
|
46
|
+
* {ItemSet} parent
|
47
|
+
* {Number} margin
|
48
|
+
* {function} order Stacking order
|
49
|
+
*/
|
50
|
+
Stack.prototype.setOptions = function setOptions (options) {
|
51
|
+
util.extend(this.options, options);
|
52
|
+
|
53
|
+
// TODO: register on data changes at the connected parent itemset, and update the changed part only and immediately
|
54
|
+
};
|
55
|
+
|
56
|
+
/**
|
57
|
+
* Stack the items such that they don't overlap. The items will have a minimal
|
58
|
+
* distance equal to options.margin.item.
|
59
|
+
*/
|
60
|
+
Stack.prototype.update = function update() {
|
61
|
+
this._order();
|
62
|
+
this._stack();
|
63
|
+
};
|
64
|
+
|
65
|
+
/**
|
66
|
+
* Order the items. The items are ordered by width first, and by left position
|
67
|
+
* second.
|
68
|
+
* If a custom order function has been provided via the options, then this will
|
69
|
+
* be used.
|
70
|
+
* @private
|
71
|
+
*/
|
72
|
+
Stack.prototype._order = function _order () {
|
73
|
+
var items = this.parent.items;
|
74
|
+
if (!items) {
|
75
|
+
throw new Error('Cannot stack items: parent does not contain items');
|
76
|
+
}
|
77
|
+
|
78
|
+
// TODO: store the sorted items, to have less work later on
|
79
|
+
var ordered = [];
|
80
|
+
var index = 0;
|
81
|
+
// items is a map (no array)
|
82
|
+
util.forEach(items, function (item) {
|
83
|
+
if (item.visible) {
|
84
|
+
ordered[index] = item;
|
85
|
+
index++;
|
86
|
+
}
|
87
|
+
});
|
88
|
+
|
89
|
+
//if a customer stack order function exists, use it.
|
90
|
+
var order = this.options.order || this.defaultOptions.order;
|
91
|
+
if (!(typeof order === 'function')) {
|
92
|
+
throw new Error('Option order must be a function');
|
93
|
+
}
|
94
|
+
|
95
|
+
ordered.sort(order);
|
96
|
+
|
97
|
+
this.ordered = ordered;
|
98
|
+
};
|
99
|
+
|
100
|
+
/**
|
101
|
+
* Adjust vertical positions of the events such that they don't overlap each
|
102
|
+
* other.
|
103
|
+
* @private
|
104
|
+
*/
|
105
|
+
Stack.prototype._stack = function _stack () {
|
106
|
+
var i,
|
107
|
+
iMax,
|
108
|
+
ordered = this.ordered,
|
109
|
+
options = this.options,
|
110
|
+
orientation = options.orientation || this.defaultOptions.orientation,
|
111
|
+
axisOnTop = (orientation == 'top'),
|
112
|
+
margin;
|
113
|
+
|
114
|
+
if (options.margin && options.margin.item !== undefined) {
|
115
|
+
margin = options.margin.item;
|
116
|
+
}
|
117
|
+
else {
|
118
|
+
margin = this.defaultOptions.margin.item
|
119
|
+
}
|
120
|
+
|
121
|
+
// calculate new, non-overlapping positions
|
122
|
+
for (i = 0, iMax = ordered.length; i < iMax; i++) {
|
123
|
+
var item = ordered[i];
|
124
|
+
var collidingItem = null;
|
125
|
+
do {
|
126
|
+
// TODO: optimize checking for overlap. when there is a gap without items,
|
127
|
+
// you only need to check for items from the next item on, not from zero
|
128
|
+
collidingItem = this.checkOverlap(ordered, i, 0, i - 1, margin);
|
129
|
+
if (collidingItem != null) {
|
130
|
+
// There is a collision. Reposition the event above the colliding element
|
131
|
+
if (axisOnTop) {
|
132
|
+
item.top = collidingItem.top + collidingItem.height + margin;
|
133
|
+
}
|
134
|
+
else {
|
135
|
+
item.top = collidingItem.top - item.height - margin;
|
136
|
+
}
|
137
|
+
}
|
138
|
+
} while (collidingItem);
|
139
|
+
}
|
140
|
+
};
|
141
|
+
|
142
|
+
/**
|
143
|
+
* Check if the destiny position of given item overlaps with any
|
144
|
+
* of the other items from index itemStart to itemEnd.
|
145
|
+
* @param {Array} items Array with items
|
146
|
+
* @param {int} itemIndex Number of the item to be checked for overlap
|
147
|
+
* @param {int} itemStart First item to be checked.
|
148
|
+
* @param {int} itemEnd Last item to be checked.
|
149
|
+
* @return {Object | null} colliding item, or undefined when no collisions
|
150
|
+
* @param {Number} margin A minimum required margin.
|
151
|
+
* If margin is provided, the two items will be
|
152
|
+
* marked colliding when they overlap or
|
153
|
+
* when the margin between the two is smaller than
|
154
|
+
* the requested margin.
|
155
|
+
*/
|
156
|
+
Stack.prototype.checkOverlap = function checkOverlap (items, itemIndex,
|
157
|
+
itemStart, itemEnd, margin) {
|
158
|
+
var collision = this.collision;
|
159
|
+
|
160
|
+
// we loop from end to start, as we suppose that the chance of a
|
161
|
+
// collision is larger for items at the end, so check these first.
|
162
|
+
var a = items[itemIndex];
|
163
|
+
for (var i = itemEnd; i >= itemStart; i--) {
|
164
|
+
var b = items[i];
|
165
|
+
if (collision(a, b, margin)) {
|
166
|
+
if (i != itemIndex) {
|
167
|
+
return b;
|
168
|
+
}
|
169
|
+
}
|
170
|
+
}
|
171
|
+
|
172
|
+
return null;
|
173
|
+
};
|
174
|
+
|
175
|
+
/**
|
176
|
+
* Test if the two provided items collide
|
177
|
+
* The items must have parameters left, width, top, and height.
|
178
|
+
* @param {Component} a The first item
|
179
|
+
* @param {Component} b The second item
|
180
|
+
* @param {Number} margin A minimum required margin.
|
181
|
+
* If margin is provided, the two items will be
|
182
|
+
* marked colliding when they overlap or
|
183
|
+
* when the margin between the two is smaller than
|
184
|
+
* the requested margin.
|
185
|
+
* @return {boolean} true if a and b collide, else false
|
186
|
+
*/
|
187
|
+
Stack.prototype.collision = function collision (a, b, margin) {
|
188
|
+
return ((a.left - margin) < (b.left + b.getWidth()) &&
|
189
|
+
(a.left + a.getWidth() + margin) > b.left &&
|
190
|
+
(a.top - margin) < (b.top + b.height) &&
|
191
|
+
(a.top + a.height + margin) > b.top);
|
192
|
+
};
|
@@ -0,0 +1,449 @@
|
|
1
|
+
/**
|
2
|
+
* @constructor TimeStep
|
3
|
+
* The class TimeStep is an iterator for dates. You provide a start date and an
|
4
|
+
* end date. The class itself determines the best scale (step size) based on the
|
5
|
+
* provided start Date, end Date, and minimumStep.
|
6
|
+
*
|
7
|
+
* If minimumStep is provided, the step size is chosen as close as possible
|
8
|
+
* to the minimumStep but larger than minimumStep. If minimumStep is not
|
9
|
+
* provided, the scale is set to 1 DAY.
|
10
|
+
* The minimumStep should correspond with the onscreen size of about 6 characters
|
11
|
+
*
|
12
|
+
* Alternatively, you can set a scale by hand.
|
13
|
+
* After creation, you can initialize the class by executing first(). Then you
|
14
|
+
* can iterate from the start date to the end date via next(). You can check if
|
15
|
+
* the end date is reached with the function hasNext(). After each step, you can
|
16
|
+
* retrieve the current date via getCurrent().
|
17
|
+
* The TimeStep has scales ranging from milliseconds, seconds, minutes, hours,
|
18
|
+
* days, to years.
|
19
|
+
*
|
20
|
+
* Version: 1.2
|
21
|
+
*
|
22
|
+
* @param {Date} [start] The start date, for example new Date(2010, 9, 21)
|
23
|
+
* or new Date(2010, 9, 21, 23, 45, 00)
|
24
|
+
* @param {Date} [end] The end date
|
25
|
+
* @param {Number} [minimumStep] Optional. Minimum step size in milliseconds
|
26
|
+
*/
|
27
|
+
TimeStep = function(start, end, minimumStep) {
|
28
|
+
// variables
|
29
|
+
this.current = new Date();
|
30
|
+
this._start = new Date();
|
31
|
+
this._end = new Date();
|
32
|
+
|
33
|
+
this.autoScale = true;
|
34
|
+
this.scale = TimeStep.SCALE.DAY;
|
35
|
+
this.step = 1;
|
36
|
+
|
37
|
+
// initialize the range
|
38
|
+
this.setRange(start, end, minimumStep);
|
39
|
+
};
|
40
|
+
|
41
|
+
/// enum scale
|
42
|
+
TimeStep.SCALE = {
|
43
|
+
MILLISECOND: 1,
|
44
|
+
SECOND: 2,
|
45
|
+
MINUTE: 3,
|
46
|
+
HOUR: 4,
|
47
|
+
DAY: 5,
|
48
|
+
WEEKDAY: 6,
|
49
|
+
MONTH: 7,
|
50
|
+
YEAR: 8
|
51
|
+
};
|
52
|
+
|
53
|
+
|
54
|
+
/**
|
55
|
+
* Set a new range
|
56
|
+
* If minimumStep is provided, the step size is chosen as close as possible
|
57
|
+
* to the minimumStep but larger than minimumStep. If minimumStep is not
|
58
|
+
* provided, the scale is set to 1 DAY.
|
59
|
+
* The minimumStep should correspond with the onscreen size of about 6 characters
|
60
|
+
* @param {Date} [start] The start date and time.
|
61
|
+
* @param {Date} [end] The end date and time.
|
62
|
+
* @param {int} [minimumStep] Optional. Minimum step size in milliseconds
|
63
|
+
*/
|
64
|
+
TimeStep.prototype.setRange = function(start, end, minimumStep) {
|
65
|
+
if (!(start instanceof Date) || !(end instanceof Date)) {
|
66
|
+
throw "No legal start or end date in method setRange";
|
67
|
+
}
|
68
|
+
|
69
|
+
this._start = (start != undefined) ? new Date(start.valueOf()) : new Date();
|
70
|
+
this._end = (end != undefined) ? new Date(end.valueOf()) : new Date();
|
71
|
+
|
72
|
+
if (this.autoScale) {
|
73
|
+
this.setMinimumStep(minimumStep);
|
74
|
+
}
|
75
|
+
};
|
76
|
+
|
77
|
+
/**
|
78
|
+
* Set the range iterator to the start date.
|
79
|
+
*/
|
80
|
+
TimeStep.prototype.first = function() {
|
81
|
+
this.current = new Date(this._start.valueOf());
|
82
|
+
this.roundToMinor();
|
83
|
+
};
|
84
|
+
|
85
|
+
/**
|
86
|
+
* Round the current date to the first minor date value
|
87
|
+
* This must be executed once when the current date is set to start Date
|
88
|
+
*/
|
89
|
+
TimeStep.prototype.roundToMinor = function() {
|
90
|
+
// round to floor
|
91
|
+
// IMPORTANT: we have no breaks in this switch! (this is no bug)
|
92
|
+
//noinspection FallthroughInSwitchStatementJS
|
93
|
+
switch (this.scale) {
|
94
|
+
case TimeStep.SCALE.YEAR:
|
95
|
+
this.current.setFullYear(this.step * Math.floor(this.current.getFullYear() / this.step));
|
96
|
+
this.current.setMonth(0);
|
97
|
+
case TimeStep.SCALE.MONTH: this.current.setDate(1);
|
98
|
+
case TimeStep.SCALE.DAY: // intentional fall through
|
99
|
+
case TimeStep.SCALE.WEEKDAY: this.current.setHours(0);
|
100
|
+
case TimeStep.SCALE.HOUR: this.current.setMinutes(0);
|
101
|
+
case TimeStep.SCALE.MINUTE: this.current.setSeconds(0);
|
102
|
+
case TimeStep.SCALE.SECOND: this.current.setMilliseconds(0);
|
103
|
+
//case TimeStep.SCALE.MILLISECOND: // nothing to do for milliseconds
|
104
|
+
}
|
105
|
+
|
106
|
+
if (this.step != 1) {
|
107
|
+
// round down to the first minor value that is a multiple of the current step size
|
108
|
+
switch (this.scale) {
|
109
|
+
case TimeStep.SCALE.MILLISECOND: this.current.setMilliseconds(this.current.getMilliseconds() - this.current.getMilliseconds() % this.step); break;
|
110
|
+
case TimeStep.SCALE.SECOND: this.current.setSeconds(this.current.getSeconds() - this.current.getSeconds() % this.step); break;
|
111
|
+
case TimeStep.SCALE.MINUTE: this.current.setMinutes(this.current.getMinutes() - this.current.getMinutes() % this.step); break;
|
112
|
+
case TimeStep.SCALE.HOUR: this.current.setHours(this.current.getHours() - this.current.getHours() % this.step); break;
|
113
|
+
case TimeStep.SCALE.WEEKDAY: // intentional fall through
|
114
|
+
case TimeStep.SCALE.DAY: this.current.setDate((this.current.getDate()-1) - (this.current.getDate()-1) % this.step + 1); break;
|
115
|
+
case TimeStep.SCALE.MONTH: this.current.setMonth(this.current.getMonth() - this.current.getMonth() % this.step); break;
|
116
|
+
case TimeStep.SCALE.YEAR: this.current.setFullYear(this.current.getFullYear() - this.current.getFullYear() % this.step); break;
|
117
|
+
default: break;
|
118
|
+
}
|
119
|
+
}
|
120
|
+
};
|
121
|
+
|
122
|
+
/**
|
123
|
+
* Check if the there is a next step
|
124
|
+
* @return {boolean} true if the current date has not passed the end date
|
125
|
+
*/
|
126
|
+
TimeStep.prototype.hasNext = function () {
|
127
|
+
return (this.current.valueOf() <= this._end.valueOf());
|
128
|
+
};
|
129
|
+
|
130
|
+
/**
|
131
|
+
* Do the next step
|
132
|
+
*/
|
133
|
+
TimeStep.prototype.next = function() {
|
134
|
+
var prev = this.current.valueOf();
|
135
|
+
|
136
|
+
// Two cases, needed to prevent issues with switching daylight savings
|
137
|
+
// (end of March and end of October)
|
138
|
+
if (this.current.getMonth() < 6) {
|
139
|
+
switch (this.scale) {
|
140
|
+
case TimeStep.SCALE.MILLISECOND:
|
141
|
+
|
142
|
+
this.current = new Date(this.current.valueOf() + this.step); break;
|
143
|
+
case TimeStep.SCALE.SECOND: this.current = new Date(this.current.valueOf() + this.step * 1000); break;
|
144
|
+
case TimeStep.SCALE.MINUTE: this.current = new Date(this.current.valueOf() + this.step * 1000 * 60); break;
|
145
|
+
case TimeStep.SCALE.HOUR:
|
146
|
+
this.current = new Date(this.current.valueOf() + this.step * 1000 * 60 * 60);
|
147
|
+
// in case of skipping an hour for daylight savings, adjust the hour again (else you get: 0h 5h 9h ... instead of 0h 4h 8h ...)
|
148
|
+
var h = this.current.getHours();
|
149
|
+
this.current.setHours(h - (h % this.step));
|
150
|
+
break;
|
151
|
+
case TimeStep.SCALE.WEEKDAY: // intentional fall through
|
152
|
+
case TimeStep.SCALE.DAY: this.current.setDate(this.current.getDate() + this.step); break;
|
153
|
+
case TimeStep.SCALE.MONTH: this.current.setMonth(this.current.getMonth() + this.step); break;
|
154
|
+
case TimeStep.SCALE.YEAR: this.current.setFullYear(this.current.getFullYear() + this.step); break;
|
155
|
+
default: break;
|
156
|
+
}
|
157
|
+
}
|
158
|
+
else {
|
159
|
+
switch (this.scale) {
|
160
|
+
case TimeStep.SCALE.MILLISECOND: this.current = new Date(this.current.valueOf() + this.step); break;
|
161
|
+
case TimeStep.SCALE.SECOND: this.current.setSeconds(this.current.getSeconds() + this.step); break;
|
162
|
+
case TimeStep.SCALE.MINUTE: this.current.setMinutes(this.current.getMinutes() + this.step); break;
|
163
|
+
case TimeStep.SCALE.HOUR: this.current.setHours(this.current.getHours() + this.step); break;
|
164
|
+
case TimeStep.SCALE.WEEKDAY: // intentional fall through
|
165
|
+
case TimeStep.SCALE.DAY: this.current.setDate(this.current.getDate() + this.step); break;
|
166
|
+
case TimeStep.SCALE.MONTH: this.current.setMonth(this.current.getMonth() + this.step); break;
|
167
|
+
case TimeStep.SCALE.YEAR: this.current.setFullYear(this.current.getFullYear() + this.step); break;
|
168
|
+
default: break;
|
169
|
+
}
|
170
|
+
}
|
171
|
+
|
172
|
+
if (this.step != 1) {
|
173
|
+
// round down to the correct major value
|
174
|
+
switch (this.scale) {
|
175
|
+
case TimeStep.SCALE.MILLISECOND: if(this.current.getMilliseconds() < this.step) this.current.setMilliseconds(0); break;
|
176
|
+
case TimeStep.SCALE.SECOND: if(this.current.getSeconds() < this.step) this.current.setSeconds(0); break;
|
177
|
+
case TimeStep.SCALE.MINUTE: if(this.current.getMinutes() < this.step) this.current.setMinutes(0); break;
|
178
|
+
case TimeStep.SCALE.HOUR: if(this.current.getHours() < this.step) this.current.setHours(0); break;
|
179
|
+
case TimeStep.SCALE.WEEKDAY: // intentional fall through
|
180
|
+
case TimeStep.SCALE.DAY: if(this.current.getDate() < this.step+1) this.current.setDate(1); break;
|
181
|
+
case TimeStep.SCALE.MONTH: if(this.current.getMonth() < this.step) this.current.setMonth(0); break;
|
182
|
+
case TimeStep.SCALE.YEAR: break; // nothing to do for year
|
183
|
+
default: break;
|
184
|
+
}
|
185
|
+
}
|
186
|
+
|
187
|
+
// safety mechanism: if current time is still unchanged, move to the end
|
188
|
+
if (this.current.valueOf() == prev) {
|
189
|
+
this.current = new Date(this._end.valueOf());
|
190
|
+
}
|
191
|
+
};
|
192
|
+
|
193
|
+
|
194
|
+
/**
|
195
|
+
* Get the current datetime
|
196
|
+
* @return {Date} current The current date
|
197
|
+
*/
|
198
|
+
TimeStep.prototype.getCurrent = function() {
|
199
|
+
return this.current;
|
200
|
+
};
|
201
|
+
|
202
|
+
/**
|
203
|
+
* Set a custom scale. Autoscaling will be disabled.
|
204
|
+
* For example setScale(SCALE.MINUTES, 5) will result
|
205
|
+
* in minor steps of 5 minutes, and major steps of an hour.
|
206
|
+
*
|
207
|
+
* @param {TimeStep.SCALE} newScale
|
208
|
+
* A scale. Choose from SCALE.MILLISECOND,
|
209
|
+
* SCALE.SECOND, SCALE.MINUTE, SCALE.HOUR,
|
210
|
+
* SCALE.WEEKDAY, SCALE.DAY, SCALE.MONTH,
|
211
|
+
* SCALE.YEAR.
|
212
|
+
* @param {Number} newStep A step size, by default 1. Choose for
|
213
|
+
* example 1, 2, 5, or 10.
|
214
|
+
*/
|
215
|
+
TimeStep.prototype.setScale = function(newScale, newStep) {
|
216
|
+
this.scale = newScale;
|
217
|
+
|
218
|
+
if (newStep > 0) {
|
219
|
+
this.step = newStep;
|
220
|
+
}
|
221
|
+
|
222
|
+
this.autoScale = false;
|
223
|
+
};
|
224
|
+
|
225
|
+
/**
|
226
|
+
* Enable or disable autoscaling
|
227
|
+
* @param {boolean} enable If true, autoascaling is set true
|
228
|
+
*/
|
229
|
+
TimeStep.prototype.setAutoScale = function (enable) {
|
230
|
+
this.autoScale = enable;
|
231
|
+
};
|
232
|
+
|
233
|
+
|
234
|
+
/**
|
235
|
+
* Automatically determine the scale that bests fits the provided minimum step
|
236
|
+
* @param {Number} [minimumStep] The minimum step size in milliseconds
|
237
|
+
*/
|
238
|
+
TimeStep.prototype.setMinimumStep = function(minimumStep) {
|
239
|
+
if (minimumStep == undefined) {
|
240
|
+
return;
|
241
|
+
}
|
242
|
+
|
243
|
+
var stepYear = (1000 * 60 * 60 * 24 * 30 * 12);
|
244
|
+
var stepMonth = (1000 * 60 * 60 * 24 * 30);
|
245
|
+
var stepDay = (1000 * 60 * 60 * 24);
|
246
|
+
var stepHour = (1000 * 60 * 60);
|
247
|
+
var stepMinute = (1000 * 60);
|
248
|
+
var stepSecond = (1000);
|
249
|
+
var stepMillisecond= (1);
|
250
|
+
|
251
|
+
// find the smallest step that is larger than the provided minimumStep
|
252
|
+
if (stepYear*1000 > minimumStep) {this.scale = TimeStep.SCALE.YEAR; this.step = 1000;}
|
253
|
+
if (stepYear*500 > minimumStep) {this.scale = TimeStep.SCALE.YEAR; this.step = 500;}
|
254
|
+
if (stepYear*100 > minimumStep) {this.scale = TimeStep.SCALE.YEAR; this.step = 100;}
|
255
|
+
if (stepYear*50 > minimumStep) {this.scale = TimeStep.SCALE.YEAR; this.step = 50;}
|
256
|
+
if (stepYear*10 > minimumStep) {this.scale = TimeStep.SCALE.YEAR; this.step = 10;}
|
257
|
+
if (stepYear*5 > minimumStep) {this.scale = TimeStep.SCALE.YEAR; this.step = 5;}
|
258
|
+
if (stepYear > minimumStep) {this.scale = TimeStep.SCALE.YEAR; this.step = 1;}
|
259
|
+
if (stepMonth*3 > minimumStep) {this.scale = TimeStep.SCALE.MONTH; this.step = 3;}
|
260
|
+
if (stepMonth > minimumStep) {this.scale = TimeStep.SCALE.MONTH; this.step = 1;}
|
261
|
+
if (stepDay*5 > minimumStep) {this.scale = TimeStep.SCALE.DAY; this.step = 5;}
|
262
|
+
if (stepDay*2 > minimumStep) {this.scale = TimeStep.SCALE.DAY; this.step = 2;}
|
263
|
+
if (stepDay > minimumStep) {this.scale = TimeStep.SCALE.DAY; this.step = 1;}
|
264
|
+
if (stepDay/2 > minimumStep) {this.scale = TimeStep.SCALE.WEEKDAY; this.step = 1;}
|
265
|
+
if (stepHour*4 > minimumStep) {this.scale = TimeStep.SCALE.HOUR; this.step = 4;}
|
266
|
+
if (stepHour > minimumStep) {this.scale = TimeStep.SCALE.HOUR; this.step = 1;}
|
267
|
+
if (stepMinute*15 > minimumStep) {this.scale = TimeStep.SCALE.MINUTE; this.step = 15;}
|
268
|
+
if (stepMinute*10 > minimumStep) {this.scale = TimeStep.SCALE.MINUTE; this.step = 10;}
|
269
|
+
if (stepMinute*5 > minimumStep) {this.scale = TimeStep.SCALE.MINUTE; this.step = 5;}
|
270
|
+
if (stepMinute > minimumStep) {this.scale = TimeStep.SCALE.MINUTE; this.step = 1;}
|
271
|
+
if (stepSecond*15 > minimumStep) {this.scale = TimeStep.SCALE.SECOND; this.step = 15;}
|
272
|
+
if (stepSecond*10 > minimumStep) {this.scale = TimeStep.SCALE.SECOND; this.step = 10;}
|
273
|
+
if (stepSecond*5 > minimumStep) {this.scale = TimeStep.SCALE.SECOND; this.step = 5;}
|
274
|
+
if (stepSecond > minimumStep) {this.scale = TimeStep.SCALE.SECOND; this.step = 1;}
|
275
|
+
if (stepMillisecond*200 > minimumStep) {this.scale = TimeStep.SCALE.MILLISECOND; this.step = 200;}
|
276
|
+
if (stepMillisecond*100 > minimumStep) {this.scale = TimeStep.SCALE.MILLISECOND; this.step = 100;}
|
277
|
+
if (stepMillisecond*50 > minimumStep) {this.scale = TimeStep.SCALE.MILLISECOND; this.step = 50;}
|
278
|
+
if (stepMillisecond*10 > minimumStep) {this.scale = TimeStep.SCALE.MILLISECOND; this.step = 10;}
|
279
|
+
if (stepMillisecond*5 > minimumStep) {this.scale = TimeStep.SCALE.MILLISECOND; this.step = 5;}
|
280
|
+
if (stepMillisecond > minimumStep) {this.scale = TimeStep.SCALE.MILLISECOND; this.step = 1;}
|
281
|
+
};
|
282
|
+
|
283
|
+
/**
|
284
|
+
* Snap a date to a rounded value. The snap intervals are dependent on the
|
285
|
+
* current scale and step.
|
286
|
+
* @param {Date} date the date to be snapped
|
287
|
+
*/
|
288
|
+
TimeStep.prototype.snap = function(date) {
|
289
|
+
if (this.scale == TimeStep.SCALE.YEAR) {
|
290
|
+
var year = date.getFullYear() + Math.round(date.getMonth() / 12);
|
291
|
+
date.setFullYear(Math.round(year / this.step) * this.step);
|
292
|
+
date.setMonth(0);
|
293
|
+
date.setDate(0);
|
294
|
+
date.setHours(0);
|
295
|
+
date.setMinutes(0);
|
296
|
+
date.setSeconds(0);
|
297
|
+
date.setMilliseconds(0);
|
298
|
+
}
|
299
|
+
else if (this.scale == TimeStep.SCALE.MONTH) {
|
300
|
+
if (date.getDate() > 15) {
|
301
|
+
date.setDate(1);
|
302
|
+
date.setMonth(date.getMonth() + 1);
|
303
|
+
// important: first set Date to 1, after that change the month.
|
304
|
+
}
|
305
|
+
else {
|
306
|
+
date.setDate(1);
|
307
|
+
}
|
308
|
+
|
309
|
+
date.setHours(0);
|
310
|
+
date.setMinutes(0);
|
311
|
+
date.setSeconds(0);
|
312
|
+
date.setMilliseconds(0);
|
313
|
+
}
|
314
|
+
else if (this.scale == TimeStep.SCALE.DAY ||
|
315
|
+
this.scale == TimeStep.SCALE.WEEKDAY) {
|
316
|
+
//noinspection FallthroughInSwitchStatementJS
|
317
|
+
switch (this.step) {
|
318
|
+
case 5:
|
319
|
+
case 2:
|
320
|
+
date.setHours(Math.round(date.getHours() / 24) * 24); break;
|
321
|
+
default:
|
322
|
+
date.setHours(Math.round(date.getHours() / 12) * 12); break;
|
323
|
+
}
|
324
|
+
date.setMinutes(0);
|
325
|
+
date.setSeconds(0);
|
326
|
+
date.setMilliseconds(0);
|
327
|
+
}
|
328
|
+
else if (this.scale == TimeStep.SCALE.HOUR) {
|
329
|
+
switch (this.step) {
|
330
|
+
case 4:
|
331
|
+
date.setMinutes(Math.round(date.getMinutes() / 60) * 60); break;
|
332
|
+
default:
|
333
|
+
date.setMinutes(Math.round(date.getMinutes() / 30) * 30); break;
|
334
|
+
}
|
335
|
+
date.setSeconds(0);
|
336
|
+
date.setMilliseconds(0);
|
337
|
+
} else if (this.scale == TimeStep.SCALE.MINUTE) {
|
338
|
+
//noinspection FallthroughInSwitchStatementJS
|
339
|
+
switch (this.step) {
|
340
|
+
case 15:
|
341
|
+
case 10:
|
342
|
+
date.setMinutes(Math.round(date.getMinutes() / 5) * 5);
|
343
|
+
date.setSeconds(0);
|
344
|
+
break;
|
345
|
+
case 5:
|
346
|
+
date.setSeconds(Math.round(date.getSeconds() / 60) * 60); break;
|
347
|
+
default:
|
348
|
+
date.setSeconds(Math.round(date.getSeconds() / 30) * 30); break;
|
349
|
+
}
|
350
|
+
date.setMilliseconds(0);
|
351
|
+
}
|
352
|
+
else if (this.scale == TimeStep.SCALE.SECOND) {
|
353
|
+
//noinspection FallthroughInSwitchStatementJS
|
354
|
+
switch (this.step) {
|
355
|
+
case 15:
|
356
|
+
case 10:
|
357
|
+
date.setSeconds(Math.round(date.getSeconds() / 5) * 5);
|
358
|
+
date.setMilliseconds(0);
|
359
|
+
break;
|
360
|
+
case 5:
|
361
|
+
date.setMilliseconds(Math.round(date.getMilliseconds() / 1000) * 1000); break;
|
362
|
+
default:
|
363
|
+
date.setMilliseconds(Math.round(date.getMilliseconds() / 500) * 500); break;
|
364
|
+
}
|
365
|
+
}
|
366
|
+
else if (this.scale == TimeStep.SCALE.MILLISECOND) {
|
367
|
+
var step = this.step > 5 ? this.step / 2 : 1;
|
368
|
+
date.setMilliseconds(Math.round(date.getMilliseconds() / step) * step);
|
369
|
+
}
|
370
|
+
};
|
371
|
+
|
372
|
+
/**
|
373
|
+
* Check if the current value is a major value (for example when the step
|
374
|
+
* is DAY, a major value is each first day of the MONTH)
|
375
|
+
* @return {boolean} true if current date is major, else false.
|
376
|
+
*/
|
377
|
+
TimeStep.prototype.isMajor = function() {
|
378
|
+
switch (this.scale) {
|
379
|
+
case TimeStep.SCALE.MILLISECOND:
|
380
|
+
return (this.current.getMilliseconds() == 0);
|
381
|
+
case TimeStep.SCALE.SECOND:
|
382
|
+
return (this.current.getSeconds() == 0);
|
383
|
+
case TimeStep.SCALE.MINUTE:
|
384
|
+
return (this.current.getHours() == 0) && (this.current.getMinutes() == 0);
|
385
|
+
// Note: this is no bug. Major label is equal for both minute and hour scale
|
386
|
+
case TimeStep.SCALE.HOUR:
|
387
|
+
return (this.current.getHours() == 0);
|
388
|
+
case TimeStep.SCALE.WEEKDAY: // intentional fall through
|
389
|
+
case TimeStep.SCALE.DAY:
|
390
|
+
return (this.current.getDate() == 1);
|
391
|
+
case TimeStep.SCALE.MONTH:
|
392
|
+
return (this.current.getMonth() == 0);
|
393
|
+
case TimeStep.SCALE.YEAR:
|
394
|
+
return false;
|
395
|
+
default:
|
396
|
+
return false;
|
397
|
+
}
|
398
|
+
};
|
399
|
+
|
400
|
+
|
401
|
+
/**
|
402
|
+
* Returns formatted text for the minor axislabel, depending on the current
|
403
|
+
* date and the scale. For example when scale is MINUTE, the current time is
|
404
|
+
* formatted as "hh:mm".
|
405
|
+
* @param {Date} [date] custom date. if not provided, current date is taken
|
406
|
+
*/
|
407
|
+
TimeStep.prototype.getLabelMinor = function(date) {
|
408
|
+
if (date == undefined) {
|
409
|
+
date = this.current;
|
410
|
+
}
|
411
|
+
|
412
|
+
switch (this.scale) {
|
413
|
+
case TimeStep.SCALE.MILLISECOND: return moment(date).format('SSS');
|
414
|
+
case TimeStep.SCALE.SECOND: return moment(date).format('s');
|
415
|
+
case TimeStep.SCALE.MINUTE: return moment(date).format('HH:mm');
|
416
|
+
case TimeStep.SCALE.HOUR: return moment(date).format('HH:mm');
|
417
|
+
case TimeStep.SCALE.WEEKDAY: return moment(date).format('ddd D');
|
418
|
+
case TimeStep.SCALE.DAY: return moment(date).format('D');
|
419
|
+
case TimeStep.SCALE.MONTH: return moment(date).format('MMM');
|
420
|
+
case TimeStep.SCALE.YEAR: return moment(date).format('YYYY');
|
421
|
+
default: return '';
|
422
|
+
}
|
423
|
+
};
|
424
|
+
|
425
|
+
|
426
|
+
/**
|
427
|
+
* Returns formatted text for the major axis label, depending on the current
|
428
|
+
* date and the scale. For example when scale is MINUTE, the major scale is
|
429
|
+
* hours, and the hour will be formatted as "hh".
|
430
|
+
* @param {Date} [date] custom date. if not provided, current date is taken
|
431
|
+
*/
|
432
|
+
TimeStep.prototype.getLabelMajor = function(date) {
|
433
|
+
if (date == undefined) {
|
434
|
+
date = this.current;
|
435
|
+
}
|
436
|
+
|
437
|
+
//noinspection FallthroughInSwitchStatementJS
|
438
|
+
switch (this.scale) {
|
439
|
+
case TimeStep.SCALE.MILLISECOND:return moment(date).format('HH:mm:ss');
|
440
|
+
case TimeStep.SCALE.SECOND: return moment(date).format('D MMMM HH:mm');
|
441
|
+
case TimeStep.SCALE.MINUTE:
|
442
|
+
case TimeStep.SCALE.HOUR: return moment(date).format('ddd D MMMM');
|
443
|
+
case TimeStep.SCALE.WEEKDAY:
|
444
|
+
case TimeStep.SCALE.DAY: return moment(date).format('MMMM YYYY');
|
445
|
+
case TimeStep.SCALE.MONTH: return moment(date).format('YYYY');
|
446
|
+
case TimeStep.SCALE.YEAR: return '';
|
447
|
+
default: return '';
|
448
|
+
}
|
449
|
+
};
|