vis-rails 1.0.2 → 2.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/CHANGELOG.md +1 -1
- data/README.md +2 -0
- data/lib/vis/rails/version.rb +1 -1
- data/vendor/assets/javascripts/module/exports-only-timeline.js +55 -0
- data/vendor/assets/javascripts/vis-only-timeline.js +23 -0
- data/vendor/assets/javascripts/vis.js +3 -3
- data/vendor/assets/stylesheets/vis-only-timeline.css +3 -0
- data/vendor/assets/vis/DataSet.js +106 -130
- data/vendor/assets/vis/DataView.js +35 -37
- data/vendor/assets/vis/graph/Edge.js +225 -45
- data/vendor/assets/vis/graph/Graph.js +120 -24
- data/vendor/assets/vis/graph/Node.js +16 -16
- data/vendor/assets/vis/graph/graphMixins/HierarchicalLayoutMixin.js +1 -1
- data/vendor/assets/vis/graph/graphMixins/ManipulationMixin.js +143 -0
- data/vendor/assets/vis/graph/graphMixins/SelectionMixin.js +81 -3
- data/vendor/assets/vis/graph3d/Graph3d.js +3306 -0
- data/vendor/assets/vis/module/exports.js +2 -3
- data/vendor/assets/vis/timeline/Range.js +93 -80
- data/vendor/assets/vis/timeline/Timeline.js +525 -428
- data/vendor/assets/vis/timeline/component/Component.js +19 -53
- data/vendor/assets/vis/timeline/component/CurrentTime.js +57 -25
- data/vendor/assets/vis/timeline/component/CustomTime.js +55 -19
- data/vendor/assets/vis/timeline/component/Group.js +47 -50
- data/vendor/assets/vis/timeline/component/ItemSet.js +402 -206
- data/vendor/assets/vis/timeline/component/TimeAxis.js +112 -169
- data/vendor/assets/vis/timeline/component/css/animation.css +33 -0
- data/vendor/assets/vis/timeline/component/css/currenttime.css +1 -1
- data/vendor/assets/vis/timeline/component/css/customtime.css +1 -1
- data/vendor/assets/vis/timeline/component/css/item.css +1 -11
- data/vendor/assets/vis/timeline/component/css/itemset.css +13 -18
- data/vendor/assets/vis/timeline/component/css/labelset.css +8 -6
- data/vendor/assets/vis/timeline/component/css/panel.css +56 -13
- data/vendor/assets/vis/timeline/component/css/timeaxis.css +15 -8
- data/vendor/assets/vis/timeline/component/item/Item.js +16 -15
- data/vendor/assets/vis/timeline/component/item/ItemBox.js +30 -30
- data/vendor/assets/vis/timeline/component/item/ItemPoint.js +20 -21
- data/vendor/assets/vis/timeline/component/item/ItemRange.js +23 -24
- data/vendor/assets/vis/timeline/component/item/ItemRangeOverflow.js +10 -10
- data/vendor/assets/vis/timeline/stack.js +5 -5
- data/vendor/assets/vis/util.js +81 -35
- metadata +7 -4
- data/vendor/assets/vis/timeline/component/Panel.js +0 -170
- data/vendor/assets/vis/timeline/component/RootPanel.js +0 -176
@@ -1,14 +1,14 @@
|
|
1
1
|
/**
|
2
2
|
* A horizontal time axis
|
3
|
+
* @param {{dom: Object, domProps: Object, emitter: Emitter, range: Range}} body
|
3
4
|
* @param {Object} [options] See TimeAxis.setOptions for the available
|
4
5
|
* options.
|
5
6
|
* @constructor TimeAxis
|
6
7
|
* @extends Component
|
7
8
|
*/
|
8
|
-
function TimeAxis (options) {
|
9
|
-
this.id = util.randomUUID();
|
10
|
-
|
9
|
+
function TimeAxis (body, options) {
|
11
10
|
this.dom = {
|
11
|
+
foreground: null,
|
12
12
|
majorLines: [],
|
13
13
|
majorTexts: [],
|
14
14
|
minorLines: [],
|
@@ -29,121 +29,124 @@ function TimeAxis (options) {
|
|
29
29
|
lineTop: 0
|
30
30
|
};
|
31
31
|
|
32
|
-
this.options = options || {};
|
33
32
|
this.defaultOptions = {
|
34
33
|
orientation: 'bottom', // supported: 'top', 'bottom'
|
35
34
|
// TODO: implement timeaxis orientations 'left' and 'right'
|
36
35
|
showMinorLabels: true,
|
37
36
|
showMajorLabels: true
|
38
37
|
};
|
38
|
+
this.options = util.extend({}, this.defaultOptions);
|
39
39
|
|
40
|
-
this.
|
40
|
+
this.body = body;
|
41
41
|
|
42
42
|
// create the HTML DOM
|
43
43
|
this._create();
|
44
|
+
|
45
|
+
this.setOptions(options);
|
44
46
|
}
|
45
47
|
|
46
48
|
TimeAxis.prototype = new Component();
|
47
49
|
|
48
|
-
// TODO: comment options
|
49
|
-
TimeAxis.prototype.setOptions = Component.prototype.setOptions;
|
50
|
-
|
51
50
|
/**
|
52
|
-
*
|
51
|
+
* Set options for the TimeAxis.
|
52
|
+
* Parameters will be merged in current options.
|
53
|
+
* @param {Object} options Available options:
|
54
|
+
* {string} [orientation]
|
55
|
+
* {boolean} [showMinorLabels]
|
56
|
+
* {boolean} [showMajorLabels]
|
53
57
|
*/
|
54
|
-
TimeAxis.prototype.
|
55
|
-
|
58
|
+
TimeAxis.prototype.setOptions = function(options) {
|
59
|
+
if (options) {
|
60
|
+
// copy all options that we know
|
61
|
+
util.selectiveExtend(['orientation', 'showMinorLabels', 'showMajorLabels'], this.options, options);
|
62
|
+
}
|
56
63
|
};
|
57
64
|
|
58
65
|
/**
|
59
|
-
*
|
60
|
-
* @param {Range | Object} range A Range or an object containing start and end.
|
66
|
+
* Create the HTML DOM for the TimeAxis
|
61
67
|
*/
|
62
|
-
TimeAxis.prototype.
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
this.
|
68
|
+
TimeAxis.prototype._create = function() {
|
69
|
+
this.dom.foreground = document.createElement('div');
|
70
|
+
this.dom.background = document.createElement('div');
|
71
|
+
|
72
|
+
this.dom.foreground.className = 'timeaxis foreground';
|
73
|
+
this.dom.background.className = 'timeaxis background';
|
68
74
|
};
|
69
75
|
|
70
76
|
/**
|
71
|
-
*
|
72
|
-
* @return {HTMLElement} frame
|
77
|
+
* Destroy the TimeAxis
|
73
78
|
*/
|
74
|
-
TimeAxis.prototype.
|
75
|
-
|
79
|
+
TimeAxis.prototype.destroy = function() {
|
80
|
+
// remove from DOM
|
81
|
+
if (this.dom.foreground.parentNode) {
|
82
|
+
this.dom.foreground.parentNode.removeChild(this.dom.foreground);
|
83
|
+
}
|
84
|
+
if (this.dom.background.parentNode) {
|
85
|
+
this.dom.background.parentNode.removeChild(this.dom.background);
|
86
|
+
}
|
87
|
+
|
88
|
+
this.body = null;
|
76
89
|
};
|
77
90
|
|
78
91
|
/**
|
79
92
|
* Repaint the component
|
80
93
|
* @return {boolean} Returns true if the component is resized
|
81
94
|
*/
|
82
|
-
TimeAxis.prototype.
|
83
|
-
var
|
84
|
-
options = this.options,
|
95
|
+
TimeAxis.prototype.redraw = function () {
|
96
|
+
var options = this.options,
|
85
97
|
props = this.props,
|
86
|
-
|
87
|
-
|
88
|
-
|
89
|
-
|
90
|
-
|
91
|
-
var
|
92
|
-
|
93
|
-
|
94
|
-
|
95
|
-
|
96
|
-
|
97
|
-
|
98
|
-
|
99
|
-
|
100
|
-
|
101
|
-
|
102
|
-
|
103
|
-
|
104
|
-
|
105
|
-
|
106
|
-
|
107
|
-
|
108
|
-
|
109
|
-
|
110
|
-
|
111
|
-
|
112
|
-
|
113
|
-
|
114
|
-
|
115
|
-
|
116
|
-
|
117
|
-
|
118
|
-
|
119
|
-
|
120
|
-
|
121
|
-
|
122
|
-
|
123
|
-
|
124
|
-
|
125
|
-
|
126
|
-
|
127
|
-
|
128
|
-
|
129
|
-
|
130
|
-
|
131
|
-
|
132
|
-
|
133
|
-
|
134
|
-
|
135
|
-
this._repaintLine();
|
136
|
-
|
137
|
-
// put frame online again
|
138
|
-
if (beforeChild) {
|
139
|
-
parent.insertBefore(frame, beforeChild);
|
140
|
-
}
|
141
|
-
else {
|
142
|
-
parent.appendChild(frame)
|
143
|
-
}
|
98
|
+
foreground = this.dom.foreground,
|
99
|
+
background = this.dom.background;
|
100
|
+
|
101
|
+
// determine the correct parent DOM element (depending on option orientation)
|
102
|
+
var parent = (options.orientation == 'top') ? this.body.dom.top : this.body.dom.bottom;
|
103
|
+
var parentChanged = (foreground.parentNode !== parent);
|
104
|
+
|
105
|
+
// calculate character width and height
|
106
|
+
this._calculateCharSize();
|
107
|
+
|
108
|
+
// TODO: recalculate sizes only needed when parent is resized or options is changed
|
109
|
+
var orientation = this.options.orientation,
|
110
|
+
showMinorLabels = this.options.showMinorLabels,
|
111
|
+
showMajorLabels = this.options.showMajorLabels;
|
112
|
+
|
113
|
+
// determine the width and height of the elemens for the axis
|
114
|
+
props.minorLabelHeight = showMinorLabels ? props.minorCharHeight : 0;
|
115
|
+
props.majorLabelHeight = showMajorLabels ? props.majorCharHeight : 0;
|
116
|
+
props.height = props.minorLabelHeight + props.majorLabelHeight;
|
117
|
+
props.width = foreground.offsetWidth;
|
118
|
+
|
119
|
+
props.minorLineHeight = this.body.domProps.root.height - props.majorLabelHeight -
|
120
|
+
(options.orientation == 'top' ? this.body.domProps.bottom.height : this.body.domProps.top.height);
|
121
|
+
props.minorLineWidth = 1; // TODO: really calculate width
|
122
|
+
props.majorLineHeight = props.minorLineHeight + props.majorLabelHeight;
|
123
|
+
props.majorLineWidth = 1; // TODO: really calculate width
|
124
|
+
|
125
|
+
// take foreground and background offline while updating (is almost twice as fast)
|
126
|
+
var foregroundNextSibling = foreground.nextSibling;
|
127
|
+
var backgroundNextSibling = background.nextSibling;
|
128
|
+
foreground.parentNode && foreground.parentNode.removeChild(foreground);
|
129
|
+
background.parentNode && background.parentNode.removeChild(background);
|
130
|
+
|
131
|
+
foreground.style.height = this.props.height + 'px';
|
132
|
+
|
133
|
+
this._repaintLabels();
|
134
|
+
|
135
|
+
// put DOM online again (at the same place)
|
136
|
+
if (foregroundNextSibling) {
|
137
|
+
parent.insertBefore(foreground, foregroundNextSibling);
|
138
|
+
}
|
139
|
+
else {
|
140
|
+
parent.appendChild(foreground)
|
141
|
+
}
|
142
|
+
if (backgroundNextSibling) {
|
143
|
+
this.body.dom.backgroundVertical.insertBefore(background, backgroundNextSibling);
|
144
|
+
}
|
145
|
+
else {
|
146
|
+
this.body.dom.backgroundVertical.appendChild(background)
|
144
147
|
}
|
145
148
|
|
146
|
-
return this._isResized();
|
149
|
+
return this._isResized() || parentChanged;
|
147
150
|
};
|
148
151
|
|
149
152
|
/**
|
@@ -151,13 +154,13 @@ TimeAxis.prototype.repaint = function () {
|
|
151
154
|
* @private
|
152
155
|
*/
|
153
156
|
TimeAxis.prototype._repaintLabels = function () {
|
154
|
-
var orientation = this.
|
157
|
+
var orientation = this.options.orientation;
|
155
158
|
|
156
159
|
// calculate range and step (step such that we have space for 7 characters per label)
|
157
|
-
var start = util.convert(this.range.start, 'Number'),
|
158
|
-
end = util.convert(this.range.end, 'Number'),
|
159
|
-
minimumStep = this.
|
160
|
-
-this.
|
160
|
+
var start = util.convert(this.body.range.start, 'Number'),
|
161
|
+
end = util.convert(this.body.range.end, 'Number'),
|
162
|
+
minimumStep = this.body.util.toTime((this.props.minorCharWidth || 10) * 7).valueOf()
|
163
|
+
-this.body.util.toTime(0).valueOf();
|
161
164
|
var step = new TimeStep(new Date(start), new Date(end), minimumStep);
|
162
165
|
this.step = step;
|
163
166
|
|
@@ -180,16 +183,16 @@ TimeAxis.prototype._repaintLabels = function () {
|
|
180
183
|
while (step.hasNext() && max < 1000) {
|
181
184
|
max++;
|
182
185
|
var cur = step.getCurrent(),
|
183
|
-
x = this.
|
186
|
+
x = this.body.util.toScreen(cur),
|
184
187
|
isMajor = step.isMajor();
|
185
188
|
|
186
189
|
// TODO: lines must have a width, such that we can create css backgrounds
|
187
190
|
|
188
|
-
if (this.
|
191
|
+
if (this.options.showMinorLabels) {
|
189
192
|
this._repaintMinorText(x, step.getLabelMinor(), orientation);
|
190
193
|
}
|
191
194
|
|
192
|
-
if (isMajor && this.
|
195
|
+
if (isMajor && this.options.showMajorLabels) {
|
193
196
|
if (x > 0) {
|
194
197
|
if (xFirstMajorLabel == undefined) {
|
195
198
|
xFirstMajorLabel = x;
|
@@ -206,8 +209,8 @@ TimeAxis.prototype._repaintLabels = function () {
|
|
206
209
|
}
|
207
210
|
|
208
211
|
// create a major label on the left when needed
|
209
|
-
if (this.
|
210
|
-
var leftTime = this.
|
212
|
+
if (this.options.showMajorLabels) {
|
213
|
+
var leftTime = this.body.util.toTime(0),
|
211
214
|
leftText = step.getLabelMajor(leftTime),
|
212
215
|
widthText = leftText.length * (this.props.majorCharWidth || 10) + 10; // upper bound estimation
|
213
216
|
|
@@ -244,20 +247,13 @@ TimeAxis.prototype._repaintMinorText = function (x, text, orientation) {
|
|
244
247
|
label = document.createElement('div');
|
245
248
|
label.appendChild(content);
|
246
249
|
label.className = 'text minor';
|
247
|
-
this.
|
250
|
+
this.dom.foreground.appendChild(label);
|
248
251
|
}
|
249
252
|
this.dom.minorTexts.push(label);
|
250
253
|
|
251
254
|
label.childNodes[0].nodeValue = text;
|
252
255
|
|
253
|
-
|
254
|
-
label.style.top = this.props.majorLabelHeight + 'px';
|
255
|
-
label.style.bottom = '';
|
256
|
-
}
|
257
|
-
else {
|
258
|
-
label.style.top = '';
|
259
|
-
label.style.bottom = this.props.majorLabelHeight + 'px';
|
260
|
-
}
|
256
|
+
label.style.top = (orientation == 'top') ? (this.props.majorLabelHeight + 'px') : '0';
|
261
257
|
label.style.left = x + 'px';
|
262
258
|
//label.title = title; // TODO: this is a heavy operation
|
263
259
|
};
|
@@ -279,21 +275,14 @@ TimeAxis.prototype._repaintMajorText = function (x, text, orientation) {
|
|
279
275
|
label = document.createElement('div');
|
280
276
|
label.className = 'text major';
|
281
277
|
label.appendChild(content);
|
282
|
-
this.
|
278
|
+
this.dom.foreground.appendChild(label);
|
283
279
|
}
|
284
280
|
this.dom.majorTexts.push(label);
|
285
281
|
|
286
282
|
label.childNodes[0].nodeValue = text;
|
287
283
|
//label.title = title; // TODO: this is a heavy operation
|
288
284
|
|
289
|
-
|
290
|
-
label.style.top = '0px';
|
291
|
-
label.style.bottom = '';
|
292
|
-
}
|
293
|
-
else {
|
294
|
-
label.style.top = '';
|
295
|
-
label.style.bottom = '0px';
|
296
|
-
}
|
285
|
+
label.style.top = (orientation == 'top') ? '0' : (this.props.minorLabelHeight + 'px');
|
297
286
|
label.style.left = x + 'px';
|
298
287
|
};
|
299
288
|
|
@@ -311,18 +300,16 @@ TimeAxis.prototype._repaintMinorLine = function (x, orientation) {
|
|
311
300
|
// create vertical line
|
312
301
|
line = document.createElement('div');
|
313
302
|
line.className = 'grid vertical minor';
|
314
|
-
this.
|
303
|
+
this.dom.background.appendChild(line);
|
315
304
|
}
|
316
305
|
this.dom.minorLines.push(line);
|
317
306
|
|
318
307
|
var props = this.props;
|
319
308
|
if (orientation == 'top') {
|
320
|
-
line.style.top =
|
321
|
-
line.style.bottom = '';
|
309
|
+
line.style.top = props.majorLabelHeight + 'px';
|
322
310
|
}
|
323
311
|
else {
|
324
|
-
line.style.top = '';
|
325
|
-
line.style.bottom = this.props.majorLabelHeight + 'px';
|
312
|
+
line.style.top = this.body.domProps.top.height + 'px';
|
326
313
|
}
|
327
314
|
line.style.height = props.minorLineHeight + 'px';
|
328
315
|
line.style.left = (x - props.minorLineWidth / 2) + 'px';
|
@@ -342,72 +329,28 @@ TimeAxis.prototype._repaintMajorLine = function (x, orientation) {
|
|
342
329
|
// create vertical line
|
343
330
|
line = document.createElement('DIV');
|
344
331
|
line.className = 'grid vertical major';
|
345
|
-
this.
|
332
|
+
this.dom.background.appendChild(line);
|
346
333
|
}
|
347
334
|
this.dom.majorLines.push(line);
|
348
335
|
|
349
336
|
var props = this.props;
|
350
337
|
if (orientation == 'top') {
|
351
|
-
line.style.top = '
|
352
|
-
line.style.bottom = '';
|
338
|
+
line.style.top = '0';
|
353
339
|
}
|
354
340
|
else {
|
355
|
-
line.style.top = '';
|
356
|
-
line.style.bottom = '0px';
|
341
|
+
line.style.top = this.body.domProps.top.height + 'px';
|
357
342
|
}
|
358
343
|
line.style.left = (x - props.majorLineWidth / 2) + 'px';
|
359
344
|
line.style.height = props.majorLineHeight + 'px';
|
360
345
|
};
|
361
346
|
|
362
|
-
|
363
|
-
/**
|
364
|
-
* Repaint the horizontal line for the axis
|
365
|
-
* @private
|
366
|
-
*/
|
367
|
-
TimeAxis.prototype._repaintLine = function() {
|
368
|
-
var line = this.dom.line,
|
369
|
-
frame = this.frame,
|
370
|
-
orientation = this.getOption('orientation');
|
371
|
-
|
372
|
-
// line before all axis elements
|
373
|
-
if (this.getOption('showMinorLabels') || this.getOption('showMajorLabels')) {
|
374
|
-
if (line) {
|
375
|
-
// put this line at the end of all childs
|
376
|
-
frame.removeChild(line);
|
377
|
-
frame.appendChild(line);
|
378
|
-
}
|
379
|
-
else {
|
380
|
-
// create the axis line
|
381
|
-
line = document.createElement('div');
|
382
|
-
line.className = 'grid horizontal major';
|
383
|
-
frame.appendChild(line);
|
384
|
-
this.dom.line = line;
|
385
|
-
}
|
386
|
-
|
387
|
-
if (orientation == 'top') {
|
388
|
-
line.style.top = this.height + 'px';
|
389
|
-
line.style.bottom = '';
|
390
|
-
}
|
391
|
-
else {
|
392
|
-
line.style.top = '';
|
393
|
-
line.style.bottom = this.height + 'px';
|
394
|
-
}
|
395
|
-
}
|
396
|
-
else {
|
397
|
-
if (line && line.parentNode) {
|
398
|
-
line.parentNode.removeChild(line);
|
399
|
-
delete this.dom.line;
|
400
|
-
}
|
401
|
-
}
|
402
|
-
};
|
403
|
-
|
404
347
|
/**
|
405
348
|
* Determine the size of text on the axis (both major and minor axis).
|
406
349
|
* The size is calculated only once and then cached in this.props.
|
407
350
|
* @private
|
408
351
|
*/
|
409
352
|
TimeAxis.prototype._calculateCharSize = function () {
|
410
|
-
// Note: We calculate char size with every
|
353
|
+
// Note: We calculate char size with every redraw. Size may change, for
|
411
354
|
// example when any of the timelines parents had display:none for example.
|
412
355
|
|
413
356
|
// determine the char width and height on the minor axis
|
@@ -417,7 +360,7 @@ TimeAxis.prototype._calculateCharSize = function () {
|
|
417
360
|
this.dom.measureCharMinor.style.position = 'absolute';
|
418
361
|
|
419
362
|
this.dom.measureCharMinor.appendChild(document.createTextNode('0'));
|
420
|
-
this.
|
363
|
+
this.dom.foreground.appendChild(this.dom.measureCharMinor);
|
421
364
|
}
|
422
365
|
this.props.minorCharHeight = this.dom.measureCharMinor.clientHeight;
|
423
366
|
this.props.minorCharWidth = this.dom.measureCharMinor.clientWidth;
|
@@ -429,7 +372,7 @@ TimeAxis.prototype._calculateCharSize = function () {
|
|
429
372
|
this.dom.measureCharMajor.style.position = 'absolute';
|
430
373
|
|
431
374
|
this.dom.measureCharMajor.appendChild(document.createTextNode('0'));
|
432
|
-
this.
|
375
|
+
this.dom.foreground.appendChild(this.dom.measureCharMajor);
|
433
376
|
}
|
434
377
|
this.props.majorCharHeight = this.dom.measureCharMajor.clientHeight;
|
435
378
|
this.props.majorCharWidth = this.dom.measureCharMajor.clientWidth;
|
@@ -441,6 +384,6 @@ TimeAxis.prototype._calculateCharSize = function () {
|
|
441
384
|
* @param {Date} date the date to be snapped.
|
442
385
|
* @return {Date} snappedDate
|
443
386
|
*/
|
444
|
-
TimeAxis.prototype.snap = function
|
387
|
+
TimeAxis.prototype.snap = function(date) {
|
445
388
|
return this.step.snap(date);
|
446
389
|
};
|
@@ -0,0 +1,33 @@
|
|
1
|
+
.vis.timeline.root {
|
2
|
+
/*
|
3
|
+
-webkit-transition: height .4s ease-in-out;
|
4
|
+
transition: height .4s ease-in-out;
|
5
|
+
*/
|
6
|
+
}
|
7
|
+
|
8
|
+
.vis.timeline .vispanel {
|
9
|
+
/*
|
10
|
+
-webkit-transition: height .4s ease-in-out, top .4s ease-in-out;
|
11
|
+
transition: height .4s ease-in-out, top .4s ease-in-out;
|
12
|
+
*/
|
13
|
+
}
|
14
|
+
|
15
|
+
.vis.timeline .axis {
|
16
|
+
/*
|
17
|
+
-webkit-transition: top .4s ease-in-out;
|
18
|
+
transition: top .4s ease-in-out;
|
19
|
+
*/
|
20
|
+
}
|
21
|
+
|
22
|
+
/* TODO: get animation working nicely
|
23
|
+
|
24
|
+
.vis.timeline .item {
|
25
|
+
-webkit-transition: top .4s ease-in-out;
|
26
|
+
transition: top .4s ease-in-out;
|
27
|
+
}
|
28
|
+
|
29
|
+
.vis.timeline .item.line {
|
30
|
+
-webkit-transition: height .4s ease-in-out, top .4s ease-in-out;
|
31
|
+
transition: height .4s ease-in-out, top .4s ease-in-out;
|
32
|
+
}
|
33
|
+
/**/
|