rsence-pre 3.0.0.12 → 3.0.0.14
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/VERSION +1 -1
- data/client/js/controls/textcontrol/textcontrol.coffee +3 -2
- data/client/js/core/class/class.js +12 -2
- data/client/js/core/elem/elem.coffee +2 -0
- data/client/js/datetime/timesheet/themes/default/timesheet.css +28 -37
- data/client/js/datetime/timesheet/themes/default/timesheet.html +3 -3
- data/client/js/datetime/timesheet/timesheet.coffee +610 -0
- data/client/js/datetime/timesheet_item/themes/default/timesheet_item.css +33 -35
- data/client/js/datetime/timesheet_item/themes/default/timesheet_item.html +6 -10
- data/client/js/datetime/timesheet_item/timesheet_item.coffee +144 -0
- data/client/js/foundation/control/eventresponder/eventresponder.js +3 -4
- data/client/js/foundation/eventmanager/eventmanager.coffee +6 -0
- data/client/js/tables/table/table.coffee +89 -37
- data/client/js/tables/table/themes/default/table.css +7 -7
- data/client/js/tables/table/themes/default/table.html +3 -3
- metadata +4 -4
- data/client/js/datetime/timesheet/timesheet.js +0 -852
- data/client/js/datetime/timesheet_item/timesheet_item.js +0 -210
@@ -1,852 +0,0 @@
|
|
1
|
-
|
2
|
-
HLocale.components.HTimeSheet = {
|
3
|
-
strings: {
|
4
|
-
newItemLabel: 'New item'
|
5
|
-
}
|
6
|
-
};
|
7
|
-
|
8
|
-
|
9
|
-
var//RSence.DateTime
|
10
|
-
HTimeSheet = HControl.extend({
|
11
|
-
|
12
|
-
debug: false,
|
13
|
-
|
14
|
-
componentName: 'timesheet',
|
15
|
-
markupElemNames: [
|
16
|
-
'label', 'value', 'timeline'
|
17
|
-
],
|
18
|
-
|
19
|
-
defaultEvents: {
|
20
|
-
draggable: true,
|
21
|
-
click: true,
|
22
|
-
doubleClick: true,
|
23
|
-
resize: true
|
24
|
-
},
|
25
|
-
|
26
|
-
controlDefaults: HControlDefaults.extend({
|
27
|
-
timeStart: 0, // 1970-01-01 00:00:00
|
28
|
-
timeEnd: 86399, // 1970-01-01 23:59:59
|
29
|
-
tzOffset: 0, // timezone offset in seconds; eg: 7200 => UTC+2
|
30
|
-
itemMinHeight: 16,
|
31
|
-
hideHours: false,
|
32
|
-
autoLabel: false, // automatically set the label to the date
|
33
|
-
autoLabelFn: 'formatDate',
|
34
|
-
autoLabelFnOptions: { longWeekDay: true },
|
35
|
-
notchesPerHour: 4, // by default 1/4 of an hour precision (15 minutes)
|
36
|
-
itemOffsetLeft: 64, // Theme settings; don't enter in options
|
37
|
-
itemOffsetRight: 0, // Theme settings; don't enter in options
|
38
|
-
itemOffsetTop: 20, // Theme settings; don't enter in options
|
39
|
-
itemOffsetBottom: 0, // Theme settings; don't enter in options
|
40
|
-
itemDisplayTime: true,
|
41
|
-
allowClickCreate: false,
|
42
|
-
iconImage: 'timesheet_item_icons.png',
|
43
|
-
allowDoubleClickCreate: true,
|
44
|
-
minDragSize: 5, // minimum amount of pixels dragged required for accepting a drag
|
45
|
-
hourOffsetTop: -4, // Theme settings; don't enter in options
|
46
|
-
dummyValue: {
|
47
|
-
label: '',
|
48
|
-
start: 0,
|
49
|
-
color: '#000000'
|
50
|
-
}
|
51
|
-
}),
|
52
|
-
|
53
|
-
customOptions: function( _options ){
|
54
|
-
this.localeStrings = HLocale.components.HTimeSheet.strings;
|
55
|
-
if( _options.defaultLabel === undefined ){ _options.defaultLabel = this.localeStrings.newItemLabel; }
|
56
|
-
},
|
57
|
-
|
58
|
-
themeSettings: function( _itemOffsetLeft, _itemOffsetTop, _itemOffsetRight, _itemOffsetBottom, _hourOffsetTop ){
|
59
|
-
if( this.options.hideHours ){
|
60
|
-
ELEM.addClassName( this.elemId, 'nohours' );
|
61
|
-
this.options.itemOffsetLeft = 0;
|
62
|
-
}
|
63
|
-
else if( _itemOffsetLeft !== undefined ) {
|
64
|
-
this.options.itemOffsetLeft = _itemOffsetLeft;
|
65
|
-
}
|
66
|
-
if( _itemOffsetTop !== undefined ) {
|
67
|
-
this.options.itemOffsetTop = _itemOffsetTop;
|
68
|
-
}
|
69
|
-
if( _itemOffsetRight !== undefined ) {
|
70
|
-
this.options.itemOffsetRight = _itemOffsetRight;
|
71
|
-
}
|
72
|
-
if( _itemOffsetBottom !== undefined ) {
|
73
|
-
this.options.itemOffsetBottom = _itemOffsetBottom;
|
74
|
-
}
|
75
|
-
if( _hourOffsetTop !== undefined ) {
|
76
|
-
this.options.hourOffsetTop = _hourOffsetTop;
|
77
|
-
}
|
78
|
-
},
|
79
|
-
|
80
|
-
autoLabel: function(){
|
81
|
-
var
|
82
|
-
_locale = HLocale.dateTime,
|
83
|
-
_label = _locale[this.options.autoLabelFn]( this.options.timeStart, this.options.autoLabelFnOptions );
|
84
|
-
if( this.label !== _label ){
|
85
|
-
this.label = _label;
|
86
|
-
this.refreshLabel();
|
87
|
-
}
|
88
|
-
},
|
89
|
-
|
90
|
-
clearHours: function(){
|
91
|
-
for( var i = 0; i < this.hourItems.length; i++ ){
|
92
|
-
ELEM.del( this.hourItems[i] );
|
93
|
-
}
|
94
|
-
},
|
95
|
-
|
96
|
-
drawHours: function(){
|
97
|
-
|
98
|
-
var
|
99
|
-
_parentElemId = this.markupElemIds.timeline,
|
100
|
-
_dateStart = new Date( this.options.timeStart * 1000 ),
|
101
|
-
_dateEnd = new Date( this.options.timeEnd * 1000 ),
|
102
|
-
_hourStart = _dateStart.getUTCHours(),
|
103
|
-
_hourEnd = _dateEnd.getUTCHours(),
|
104
|
-
_hours = (_hourEnd - _hourStart) + 1,
|
105
|
-
_rectHeight = ELEM.getSize( _parentElemId )[1],
|
106
|
-
_topOffset = this.options.itemOffsetTop,
|
107
|
-
_height = _rectHeight - _topOffset - this.options.itemOffsetBottom,
|
108
|
-
_pxPerHour = _height / _hours,
|
109
|
-
_notchesPerHour = this.options.notchesPerHour,
|
110
|
-
_pxPerLine = _pxPerHour / _notchesPerHour,
|
111
|
-
_hour = _hourStart,
|
112
|
-
_bottomPos = _rectHeight-_height-_topOffset-2,
|
113
|
-
_hourItem,
|
114
|
-
_lineItem,
|
115
|
-
_hourTop,
|
116
|
-
_lineTop,
|
117
|
-
i,
|
118
|
-
_pxPerNotch = _pxPerHour / _notchesPerHour,
|
119
|
-
_notchItem,
|
120
|
-
_notchTop;
|
121
|
-
|
122
|
-
ELEM.setStyle( _parentElemId, 'visibility', 'hidden', true );
|
123
|
-
|
124
|
-
ELEM.setStyle( this.markupElemIds.value, 'bottom', _bottomPos+'px' );
|
125
|
-
|
126
|
-
if( this['hourItems'] !== undefined ){
|
127
|
-
this.clearHours();
|
128
|
-
}
|
129
|
-
this.itemOptions = {
|
130
|
-
notchHeight: _pxPerNotch,
|
131
|
-
notches: (_hours * _notchesPerHour),
|
132
|
-
offsetTop: _topOffset,
|
133
|
-
offsetBottom: _bottomPos,
|
134
|
-
height: _height
|
135
|
-
};
|
136
|
-
this.hourItems = [];
|
137
|
-
for( ; _hour < (_hourEnd+1); _hour++ ){
|
138
|
-
_hourItem = ELEM.make( this.markupElemIds.timeline, 'div' );
|
139
|
-
_lineTop = Math.round(_topOffset + (_hour*_pxPerHour));
|
140
|
-
if( _hour !== _hourStart ){
|
141
|
-
_hourTop = _lineTop + this.options.hourOffsetTop;
|
142
|
-
ELEM.setStyle( _hourItem, 'top', _hourTop+'px' );
|
143
|
-
ELEM.addClassName( _hourItem, 'timesheet_timeline_hour' );
|
144
|
-
ELEM.setHTML( _hourItem, _hour+':00' );
|
145
|
-
this.hourItems.push( _hourItem );
|
146
|
-
_lineItem = ELEM.make( _parentElemId, 'div' );
|
147
|
-
ELEM.addClassName( _lineItem, 'timesheet_timeline_line' );
|
148
|
-
ELEM.setStyle( _lineItem, 'top', _lineTop + 'px' );
|
149
|
-
this.hourItems.push( _lineItem );
|
150
|
-
}
|
151
|
-
for( i=1; i < _notchesPerHour; i++ ){
|
152
|
-
_notchItem = ELEM.make( _parentElemId, 'div' );
|
153
|
-
ELEM.addClassName( _notchItem, 'timesheet_timeline_notch' );
|
154
|
-
_notchTop = Math.round(_lineTop + (_pxPerNotch*i));
|
155
|
-
ELEM.setStyle( _notchItem, 'top', _notchTop+'px' );
|
156
|
-
this.hourItems.push( _notchItem );
|
157
|
-
}
|
158
|
-
}
|
159
|
-
|
160
|
-
ELEM.setStyle( this.markupElemIds.timeline, 'visibility', 'inherit' );
|
161
|
-
|
162
|
-
},
|
163
|
-
|
164
|
-
// extra hook for refreshing; updates label and hours before doing common things
|
165
|
-
refresh: function(){
|
166
|
-
if( this.drawn ){
|
167
|
-
if( this.options.autoLabel ){
|
168
|
-
this.autoLabel();
|
169
|
-
}
|
170
|
-
this.drawHours();
|
171
|
-
}
|
172
|
-
this.base();
|
173
|
-
},
|
174
|
-
|
175
|
-
// set the timezone offset (in seconds)
|
176
|
-
setTzOffset: function( _tzOffset ){
|
177
|
-
this.options.tzOffset = _tzOffset;
|
178
|
-
this.refresh();
|
179
|
-
},
|
180
|
-
|
181
|
-
// set the start timestamp of the timesheet
|
182
|
-
setTimeStart: function( _timeStart ){
|
183
|
-
this.options.timeStart = _timeStart;
|
184
|
-
this.refresh();
|
185
|
-
},
|
186
|
-
|
187
|
-
// set the end timestamp of the timesheet
|
188
|
-
setTimeEnd: function( _timeEnd ){
|
189
|
-
this.options.timeEnd = _timeEnd;
|
190
|
-
this.refresh();
|
191
|
-
},
|
192
|
-
|
193
|
-
// sets the range of timestams of the timesheet
|
194
|
-
setTimeRange: function( _timeRange ){
|
195
|
-
if( (_timeRange instanceof Array) && (_timeRange.length === 2) ){
|
196
|
-
this.setTimeStart( _timeRange[0] );
|
197
|
-
this.setTimeEnd( _timeRange[1] );
|
198
|
-
}
|
199
|
-
else if(
|
200
|
-
(_timeRange instanceof Object) &&
|
201
|
-
(_timeRange['timeStart'] !== undefined) &&
|
202
|
-
(_timeRange['timeEnd'] !== undefined)
|
203
|
-
){
|
204
|
-
this.setTimeStart( _timeRange.timeStart );
|
205
|
-
this.setTimeEnd( _timeRange.timeEnd );
|
206
|
-
}
|
207
|
-
},
|
208
|
-
|
209
|
-
// sets the timestamp of the timesheet
|
210
|
-
setDate: function( _date ){
|
211
|
-
var
|
212
|
-
_range = (this.options.timeEnd - this.options.timeStart),
|
213
|
-
_newTimeRange = [
|
214
|
-
_date,
|
215
|
-
_date + _range
|
216
|
-
];
|
217
|
-
this.setTimeRange( _newTimeRange );
|
218
|
-
this.refresh();
|
219
|
-
},
|
220
|
-
|
221
|
-
// draw decorations
|
222
|
-
drawSubviews: function(){
|
223
|
-
this.drawHours();
|
224
|
-
var
|
225
|
-
_options = this.options,
|
226
|
-
_minDuration = Math.round(3600/_options.notchesPerHour),
|
227
|
-
_dummyValue = HVM.clone( this.options.dummyValue );
|
228
|
-
_dummyValue['duration'] = _minDuration;
|
229
|
-
this.dragPreviewRect = this.rectFromValue({start:_options.timeStart,duration:_minDuration});
|
230
|
-
this.minDuration = _minDuration;
|
231
|
-
this.dragPreview = HTimeSheetItem.nu(
|
232
|
-
this.dragPreviewRect,
|
233
|
-
this, {
|
234
|
-
value: _dummyValue,
|
235
|
-
visible: false,
|
236
|
-
iconImage: this.options.iconImage,
|
237
|
-
displayTime: this.options.itemDisplayTime
|
238
|
-
}
|
239
|
-
);
|
240
|
-
this.dragPreview.setStyleOfPart('state','color','#fff');
|
241
|
-
},
|
242
|
-
|
243
|
-
// event listener for clicks, simulates double clicks in case of not double click aware browser
|
244
|
-
click: function( x, y, b ){
|
245
|
-
var
|
246
|
-
prevClickTime = false,
|
247
|
-
notCreated = !this.clickCreated && !this.doubleClickCreated && !this.dragCreated;
|
248
|
-
// this.doubleClickSimCreated = false;
|
249
|
-
if( !this.startDragTime && this.prevClickTime ){
|
250
|
-
prevClickTime = this.prevClickTime;
|
251
|
-
}
|
252
|
-
else if (this.startDragTime){
|
253
|
-
prevClickTime = this.startDragTime;
|
254
|
-
}
|
255
|
-
if( notCreated && this.options.allowClickCreate ){
|
256
|
-
// console.log('click create');
|
257
|
-
this.clickCreate( x,y );
|
258
|
-
this.clickCreated = true;
|
259
|
-
this.doubleClickCreated = false;
|
260
|
-
this.prevClickTime = false;
|
261
|
-
}
|
262
|
-
else if( notCreated && this.options.allowDoubleClickCreate ){
|
263
|
-
var
|
264
|
-
currTime = new Date().getTime(),
|
265
|
-
timeDiff = prevClickTime?(currTime - prevClickTime):-1;
|
266
|
-
if( timeDiff > 150 && timeDiff < 500 && !this.doubleClickCreated ){
|
267
|
-
// console.log('click double create');
|
268
|
-
this.clickCreate( x, y );
|
269
|
-
this.clickCreated = false;
|
270
|
-
this.doubleClickCreated = true;
|
271
|
-
this.doubleClickSimCreated = true;
|
272
|
-
}
|
273
|
-
else {
|
274
|
-
this.doubleClickCreated = false;
|
275
|
-
}
|
276
|
-
this.prevClickTime = currTime;
|
277
|
-
}
|
278
|
-
else {
|
279
|
-
this.clickCreated = false;
|
280
|
-
this.doubleClickCreated = false;
|
281
|
-
this.prevClickTime = false;
|
282
|
-
}
|
283
|
-
},
|
284
|
-
|
285
|
-
// creates an item on click
|
286
|
-
clickCreate: function(x,y){
|
287
|
-
var
|
288
|
-
_startTime = this.pxToTime( y-this.pageY() ),
|
289
|
-
_endTime = _startTime + this.minDuration;
|
290
|
-
this.refreshDragPreview( _startTime, _endTime );
|
291
|
-
this.dragPreview.bringToFront();
|
292
|
-
this.dragPreview.show();
|
293
|
-
if( this.activateEditor( this.dragPreview ) ){
|
294
|
-
this.editor.createItem( this.cloneObject( this.dragPreview.value ) );
|
295
|
-
}
|
296
|
-
else {
|
297
|
-
this.dragPreview.hide();
|
298
|
-
}
|
299
|
-
},
|
300
|
-
|
301
|
-
// event listener for double clicks
|
302
|
-
doubleClick: function(x,y){
|
303
|
-
this.prevClickTime = false;
|
304
|
-
this.doubleClickCreated = false;
|
305
|
-
var notCreated = !this.clickCreated && !this.doubleClickCreated && !this.doubleClickSimCreated && !this.dragCreated;
|
306
|
-
if( !this.options.allowDoubleClickCreate && this.options.allowClickCreate && notCreated ){
|
307
|
-
this.click(x,y);
|
308
|
-
}
|
309
|
-
else if ( this.options.allowDoubleClickCreate && !this.options.allowClickCreate && notCreated ){
|
310
|
-
// console.log('double click create');
|
311
|
-
this.clickCreate( x, y );
|
312
|
-
this.clickCreated = false;
|
313
|
-
this.doubleClickCreated = true;
|
314
|
-
}
|
315
|
-
else {
|
316
|
-
// console.log('no double click create');
|
317
|
-
this.clickCreated = false;
|
318
|
-
}
|
319
|
-
this.doubleClickSimCreated = false;
|
320
|
-
},
|
321
|
-
|
322
|
-
// update the preview area
|
323
|
-
refreshDragPreview: function( _startTime, _endTime ){
|
324
|
-
this.dragPreviewRect.setTop( this.timeToPx( _startTime, true ) );
|
325
|
-
this.dragPreviewRect.setBottom( this.timeToPx( _endTime, true ) );
|
326
|
-
if( this.dragPreviewRect.height < this.options.itemMinHeight ){
|
327
|
-
this.dragPreviewRect.setHeight( this.options.itemMinHeight );
|
328
|
-
}
|
329
|
-
this.dragPreview.drawRect();
|
330
|
-
this.dragPreview.value.start = _startTime;
|
331
|
-
this.dragPreview.value.duration = _endTime - _startTime;
|
332
|
-
this.dragPreview.refreshValue();
|
333
|
-
},
|
334
|
-
|
335
|
-
// drag & drop event listeners, used for dragging new timesheet items
|
336
|
-
startDrag: function( x, y, b ){
|
337
|
-
// console.log('st');
|
338
|
-
this._startDragY = y;
|
339
|
-
this.startDragTime = this.pxToTime( y-this.pageY() );
|
340
|
-
this.refreshDragPreview( this.startDragTime, this.startDragTime + this.minDuration );
|
341
|
-
this.dragPreview.bringToFront();
|
342
|
-
this.dragPreview.show();
|
343
|
-
return true;
|
344
|
-
},
|
345
|
-
|
346
|
-
drag: function( x, y, b ){
|
347
|
-
// console.log('dr');
|
348
|
-
var
|
349
|
-
_dragTime = this.pxToTime( y-this.pageY() ),
|
350
|
-
_startTime,
|
351
|
-
_endTime;
|
352
|
-
if( _dragTime < this.startDragTime ){
|
353
|
-
_startTime = _dragTime;
|
354
|
-
_endTime = this.startDragTime;
|
355
|
-
}
|
356
|
-
else {
|
357
|
-
_endTime = _dragTime;
|
358
|
-
_startTime = this.startDragTime;
|
359
|
-
}
|
360
|
-
this.refreshDragPreview( _startTime, _endTime );
|
361
|
-
return true;
|
362
|
-
},
|
363
|
-
|
364
|
-
endDrag: function( x, y, b ){
|
365
|
-
// console.log('ed');
|
366
|
-
var
|
367
|
-
_dragTime = this.pxToTime( y-this.pageY() ),
|
368
|
-
_minDistanceSatisfied = Math.abs( this._startDragY - y ) >= this.options.minDragSize;
|
369
|
-
// if( this.options.allowClickCreate ){
|
370
|
-
// _minDistanceSatisfied = true;
|
371
|
-
// }
|
372
|
-
this.dragPreview.hide();
|
373
|
-
if( _dragTime !== this.startDragTime ){
|
374
|
-
if( _minDistanceSatisfied ){
|
375
|
-
if( this.activateEditor( this.dragPreview ) ){
|
376
|
-
// console.log('drag create');
|
377
|
-
this.dragCreated = true;
|
378
|
-
this.editor.createItem( this.cloneObject( this.dragPreview.value ) );
|
379
|
-
return true;
|
380
|
-
}
|
381
|
-
}
|
382
|
-
this.dragCreated = false;
|
383
|
-
}
|
384
|
-
else {
|
385
|
-
this.dragCreated = false;
|
386
|
-
this.clickCreated = false;
|
387
|
-
this.startDragTime = false;
|
388
|
-
this.click( x, y, b );
|
389
|
-
return true;
|
390
|
-
}
|
391
|
-
return false;
|
392
|
-
},
|
393
|
-
|
394
|
-
// a resize triggers refresh, of which the important part is refreshValue, which triggers redraw of the time sheet items
|
395
|
-
resize: function(){
|
396
|
-
this.base();
|
397
|
-
this.refresh();
|
398
|
-
},
|
399
|
-
|
400
|
-
// snaps the time to grid
|
401
|
-
snapTime: function( _timeSecs ){
|
402
|
-
var
|
403
|
-
_options = this.options,
|
404
|
-
_pxDate = new Date( Math.round(_timeSecs) * 1000 ),
|
405
|
-
_snapSecs = Math.round( 3600 / _options.notchesPerHour ),
|
406
|
-
_halfSnapSecs = _snapSecs * 0.5,
|
407
|
-
_hourSecs = (_pxDate.getUTCMinutes()*60) + _pxDate.getUTCSeconds(),
|
408
|
-
_remSecs = _hourSecs % _snapSecs;
|
409
|
-
if( _remSecs > _halfSnapSecs ){
|
410
|
-
_timeSecs += _snapSecs-_remSecs;
|
411
|
-
}
|
412
|
-
else {
|
413
|
-
_timeSecs -= _remSecs;
|
414
|
-
}
|
415
|
-
return _timeSecs;
|
416
|
-
},
|
417
|
-
|
418
|
-
// snaps the pixel to grid
|
419
|
-
snapPx: function( _px ){
|
420
|
-
var
|
421
|
-
_timeSecs = this.pxToTime( _px );
|
422
|
-
_timeSecs = this.snapTime( _timeSecs );
|
423
|
-
return this.timeToPx( _timeSecs );
|
424
|
-
},
|
425
|
-
|
426
|
-
// activates the editor; _item is the timesheet item to edit
|
427
|
-
activateEditor: function( _item ){
|
428
|
-
if( this['editor'] ){
|
429
|
-
var _editor = this.editor;
|
430
|
-
_editor.setTimeSheetItem( _item );
|
431
|
-
_item.bringToFront();
|
432
|
-
_editor.bringToFront();
|
433
|
-
_editor.show();
|
434
|
-
return true;
|
435
|
-
}
|
436
|
-
return false;
|
437
|
-
},
|
438
|
-
|
439
|
-
/** = Description
|
440
|
-
* Sets the editor given as parameter as the editor of instance.
|
441
|
-
*
|
442
|
-
* = Parameters
|
443
|
-
* +_editor+::
|
444
|
-
*
|
445
|
-
**/
|
446
|
-
setEditor: function( _editor ){
|
447
|
-
this.editor = _editor;
|
448
|
-
},
|
449
|
-
|
450
|
-
/** = Description
|
451
|
-
* Destructor; destroys the editor first and commences inherited die.
|
452
|
-
*
|
453
|
-
**/
|
454
|
-
die: function(){
|
455
|
-
this.editor && this.editor.die();
|
456
|
-
this.editor = null;
|
457
|
-
this.base();
|
458
|
-
},
|
459
|
-
|
460
|
-
// converts pixels to time
|
461
|
-
pxToTime: function( _px, _noSnap ){
|
462
|
-
var
|
463
|
-
_options = this.options,
|
464
|
-
_timeStart = _options.timeStart,
|
465
|
-
_timeEnd = _options.timeEnd,
|
466
|
-
_timeRange = _timeEnd - _timeStart,
|
467
|
-
_itemOptions = this.itemOptions,
|
468
|
-
_top = _itemOptions.offsetTop+1,
|
469
|
-
_height = _itemOptions.height,
|
470
|
-
_pxPerSec = _height / _timeRange,
|
471
|
-
_timeSecs;
|
472
|
-
_px -= _top;
|
473
|
-
_timeSecs = _timeStart + ( _px / _pxPerSec );
|
474
|
-
if( !_noSnap ){
|
475
|
-
_timeSecs = this.snapTime( _timeSecs );
|
476
|
-
}
|
477
|
-
if( _timeSecs > _options.timeEnd ){
|
478
|
-
_timeSecs = _options.timeEnd;
|
479
|
-
}
|
480
|
-
else if ( _timeSecs < _options.timeStart ) {
|
481
|
-
_timeSecs = _options.timeStart;
|
482
|
-
}
|
483
|
-
return Math.round( _timeSecs );
|
484
|
-
},
|
485
|
-
|
486
|
-
// converts time to pixels
|
487
|
-
timeToPx: function( _time, _snap ){
|
488
|
-
|
489
|
-
if( _snap ){
|
490
|
-
_time = this.snapTime( _time );
|
491
|
-
}
|
492
|
-
|
493
|
-
var
|
494
|
-
_options = this.options,
|
495
|
-
_timeStart = _options.timeStart,
|
496
|
-
_timeEnd = _options.timeEnd;
|
497
|
-
|
498
|
-
if( _time < _timeStart ){
|
499
|
-
_time = _timeStart;
|
500
|
-
}
|
501
|
-
if( _time > _timeEnd ){
|
502
|
-
_time = _timeEnd;
|
503
|
-
}
|
504
|
-
|
505
|
-
var
|
506
|
-
_timeRange = _timeEnd - _timeStart,
|
507
|
-
_itemOptions = this.itemOptions,
|
508
|
-
_top = _itemOptions.offsetTop,
|
509
|
-
_height = _itemOptions.height,
|
510
|
-
_pxPerSec = _height / _timeRange,
|
511
|
-
_timeSecs = _time - _timeStart,
|
512
|
-
_px = _top + ( _timeSecs * _pxPerSec );
|
513
|
-
return Math.round( _px );
|
514
|
-
},
|
515
|
-
|
516
|
-
// converts time to pixels for the rect
|
517
|
-
rectFromValue: function( _value ){
|
518
|
-
var
|
519
|
-
_topPx = this.timeToPx( _value.start ),
|
520
|
-
_bottomPx = this.timeToPx( _value.start + _value.duration ),
|
521
|
-
_leftPx = this.options.itemOffsetLeft,
|
522
|
-
_rightPx = this.rect.width - this.options.itemOffsetRight - 2,
|
523
|
-
_rect;
|
524
|
-
if( _topPx === 'underflow' ){
|
525
|
-
_topPx = _itemOptions.offsetTop;
|
526
|
-
}
|
527
|
-
else if( _topPx === 'overflow' ){
|
528
|
-
return false;
|
529
|
-
}
|
530
|
-
if( _bottomPx === 'underflow' ){
|
531
|
-
return false;
|
532
|
-
}
|
533
|
-
else if( _bottomPx === 'overflow' ){
|
534
|
-
_bottomPx = _itemOptions.offsetTop + _itemOptions.height;
|
535
|
-
}
|
536
|
-
_rect = HRect.nu( _leftPx, _topPx, _rightPx, _bottomPx );
|
537
|
-
if( _rect.height < this.options.itemMinHeight ){
|
538
|
-
_rect.setHeight( this.options.itemMinHeight );
|
539
|
-
}
|
540
|
-
return _rect;
|
541
|
-
},
|
542
|
-
|
543
|
-
// creates a single time sheet item component
|
544
|
-
createTimeSheetItem: function( _value ){
|
545
|
-
var
|
546
|
-
_rect = this.rectFromValue( _value ),
|
547
|
-
_item;
|
548
|
-
if( _rect === false ){
|
549
|
-
return false;
|
550
|
-
}
|
551
|
-
_item = HTimeSheetItem.nu(
|
552
|
-
_rect,
|
553
|
-
this, {
|
554
|
-
value: _value,
|
555
|
-
displayTime: this.options.itemDisplayTime,
|
556
|
-
events: {
|
557
|
-
draggable: true,
|
558
|
-
// click: true,
|
559
|
-
doubleClick: true
|
560
|
-
}
|
561
|
-
}
|
562
|
-
);
|
563
|
-
return _item;
|
564
|
-
},
|
565
|
-
|
566
|
-
// calls createTimeSheetItem with each value of the timesheet value array
|
567
|
-
drawTimeSheetItems: function(){
|
568
|
-
|
569
|
-
var
|
570
|
-
_data = this.value,
|
571
|
-
i = 0,
|
572
|
-
_value,
|
573
|
-
_item,
|
574
|
-
_items = this.timeSheetItems;
|
575
|
-
|
576
|
-
if((_data instanceof Array) && (_data.length > 0)){
|
577
|
-
for( ; i < _data.length; i++){
|
578
|
-
_value = _data[i];
|
579
|
-
_item = this.createTimeSheetItem( _value );
|
580
|
-
if(_item){
|
581
|
-
_items.push( _item );
|
582
|
-
}
|
583
|
-
}
|
584
|
-
}
|
585
|
-
},
|
586
|
-
|
587
|
-
|
588
|
-
/** =Description
|
589
|
-
* Create a new timeSheetItems if it hasn't been done already,
|
590
|
-
* otherwise destroy the items of the old one before proceeding.
|
591
|
-
**/
|
592
|
-
_initTimeSheetItems: function(){
|
593
|
-
if(this.timeSheetItems === undefined){
|
594
|
-
this.timeSheetItems = [];
|
595
|
-
}
|
596
|
-
else if(this.timeSheetItems.length > 0){
|
597
|
-
for( var i=0; i<this.timeSheetItems.length; i++){
|
598
|
-
this.timeSheetItems[i].die();
|
599
|
-
}
|
600
|
-
this.timeSheetItems = [];
|
601
|
-
}
|
602
|
-
},
|
603
|
-
|
604
|
-
// finds the index in the array which contains most sequential items
|
605
|
-
_findLargestSequence: function( _arr ){
|
606
|
-
var
|
607
|
-
i = 1,
|
608
|
-
_index = 0,
|
609
|
-
_length = 1,
|
610
|
-
_maxLength = 1,
|
611
|
-
_bestIndex = 0;
|
612
|
-
for( ; i < _arr.length; i++ ){
|
613
|
-
// grow:
|
614
|
-
if( ( _arr[i] - _arr[i-1] === 1 ) && ( _index === i-_length ) ){
|
615
|
-
_length += 1;
|
616
|
-
}
|
617
|
-
// reset:
|
618
|
-
else {
|
619
|
-
_index = i;
|
620
|
-
_length = 1;
|
621
|
-
}
|
622
|
-
if( _length > _maxLength ){
|
623
|
-
_bestIndex = _index;
|
624
|
-
_maxLength = _length;
|
625
|
-
}
|
626
|
-
}
|
627
|
-
return [ _bestIndex, _maxLength ];
|
628
|
-
},
|
629
|
-
|
630
|
-
// find the amount of overlapping time sheet items
|
631
|
-
_findOverlapCount: function( _items ){
|
632
|
-
var
|
633
|
-
_overlaps = [],
|
634
|
-
_testRects = this._getTestRects( _items ),
|
635
|
-
i,j;
|
636
|
-
|
637
|
-
for( i = 0; i < _items.length; i++){
|
638
|
-
_overlaps[i] = 0;
|
639
|
-
}
|
640
|
-
|
641
|
-
for( i = 0; i < _items.length - 1; i++ ){
|
642
|
-
for( j = i + 1; j < _items.length; j++ ){
|
643
|
-
if( _items[i].rect.intersects( _testRects[j] ) ){
|
644
|
-
_overlaps[i] += 1;
|
645
|
-
_overlaps[j] += 1;
|
646
|
-
}
|
647
|
-
}
|
648
|
-
}
|
649
|
-
return Math.max.apply( Math, _overlaps );
|
650
|
-
},
|
651
|
-
|
652
|
-
_getTestRects: function( _items ){
|
653
|
-
var _rects = [];
|
654
|
-
for( var i = 0; i < _items.length; i++){
|
655
|
-
_rects[i] = HRect.nu( _items[i].rect );
|
656
|
-
_rects[i].insetBy( 1, 1 );
|
657
|
-
}
|
658
|
-
return _rects;
|
659
|
-
},
|
660
|
-
|
661
|
-
// returns a sorted copy of the timeSheetItems array
|
662
|
-
_sortedTimeSheetItems: function( _sortFn ){
|
663
|
-
if( _sortFn === undefined ){
|
664
|
-
_sortFn = function(a,b){
|
665
|
-
return ( b.rect.height - a.rect.height);
|
666
|
-
};
|
667
|
-
}
|
668
|
-
var
|
669
|
-
i = 0,
|
670
|
-
_arr = [],
|
671
|
-
_items = this.timeSheetItems;
|
672
|
-
for( ; i < _items.length; i++ ){
|
673
|
-
_arr.push( _items[i] );
|
674
|
-
}
|
675
|
-
_arr = _arr.sort(_sortFn);
|
676
|
-
return _arr;
|
677
|
-
},
|
678
|
-
|
679
|
-
|
680
|
-
// Optimizes the left and right position of each timesheet item to fit
|
681
|
-
_updateTimelineRects: function(){
|
682
|
-
var
|
683
|
-
// loop indexes:
|
684
|
-
i, j, k, l,
|
685
|
-
_options = this.options,
|
686
|
-
_rect = this.rect,
|
687
|
-
_availWidth = ( _rect.width - _options.itemOffsetRight - _options.itemOffsetLeft ),
|
688
|
-
_left = _options.itemOffsetLeft,
|
689
|
-
// get a list of timesheet items sorted by height (larger to smaller order)
|
690
|
-
_items = this._sortedTimeSheetItems(),
|
691
|
-
_itemCount = _items.length,
|
692
|
-
// amount of items ovelapping (max, actual number might be smaller after optimization)
|
693
|
-
_overlapCount = this._findOverlapCount( _items ),
|
694
|
-
_width = Math.floor( _availWidth / (_overlapCount+1) ),
|
695
|
-
_itemRect,
|
696
|
-
_testRect,
|
697
|
-
_leftPos,
|
698
|
-
_rightPos,
|
699
|
-
_maxCol = 0,
|
700
|
-
_origCol,
|
701
|
-
_origColById = [],
|
702
|
-
_overlapCols,
|
703
|
-
_vacantCols,
|
704
|
-
_optimalColAndLength,
|
705
|
-
_col,
|
706
|
-
_colWidth,
|
707
|
-
_overlaps,
|
708
|
-
_testRects;
|
709
|
-
|
710
|
-
// No overlapping; nothing to do
|
711
|
-
if( _overlapCount === 0 ){
|
712
|
-
return false;
|
713
|
-
}
|
714
|
-
|
715
|
-
// move all items initially to one column right of the max overlaps
|
716
|
-
_leftPos = _left+(_width*(_overlapCount+1));
|
717
|
-
for( i = 0; i < _itemCount; i++ ){
|
718
|
-
_itemRect = _items[i].rect;
|
719
|
-
_itemRect.setLeft( _leftPos );
|
720
|
-
_itemRect.setRight( _leftPos+_width );
|
721
|
-
}
|
722
|
-
|
723
|
-
// optimize gaps by traversing each combination
|
724
|
-
// and finding the first column with no gaps
|
725
|
-
// the top-level loops three times in the following modes:
|
726
|
-
// 0: place items into the first vacant column and find the actual max columns
|
727
|
-
// 1: stretch columns to final column width
|
728
|
-
// 2: stretch columns to fit multiple columns, if space is vacant
|
729
|
-
for( l = 0; l < 3; l++ ){
|
730
|
-
for( i = 0; i < _itemCount; i++){
|
731
|
-
_itemRect = _items[i].rect;
|
732
|
-
// in mode 1, just the column widths are changed
|
733
|
-
if( l === 1 ){
|
734
|
-
_leftPos = _left + (_origColById[i]*_width);
|
735
|
-
_itemRect.setLeft( _leftPos );
|
736
|
-
_itemRect.setRight( _leftPos + _width );
|
737
|
-
continue;
|
738
|
-
}
|
739
|
-
_overlapCols = [];
|
740
|
-
_vacantCols = [];
|
741
|
-
_testRects = this._getTestRects( _items );
|
742
|
-
_testRect = HRect.nu( _itemRect );
|
743
|
-
// test each column position (modes 0 and 2)
|
744
|
-
for( k = 0; k < _overlapCount+1; k++ ){
|
745
|
-
_leftPos = _left + (k*_width);
|
746
|
-
_testRect.setLeft( _leftPos );
|
747
|
-
_testRect.setRight( _leftPos + _width );
|
748
|
-
for( j = 0; j < _itemCount; j++){
|
749
|
-
if( i !==j && _testRect.intersects( _testRects[j] ) ){
|
750
|
-
if( !~_overlapCols.indexOf( k ) ){
|
751
|
-
_overlapCols.push( k );
|
752
|
-
}
|
753
|
-
}
|
754
|
-
}
|
755
|
-
if( !~_vacantCols.indexOf( k ) && !~_overlapCols.indexOf( k ) ){
|
756
|
-
_vacantCols.push( k );
|
757
|
-
}
|
758
|
-
}
|
759
|
-
|
760
|
-
// on the first run (mode 0) place items into the first column:
|
761
|
-
if( l === 0 ){
|
762
|
-
_origCol = _vacantCols[0];
|
763
|
-
_origColById.push( _origCol );
|
764
|
-
_leftPos = _left+(_origCol*_width);
|
765
|
-
_rightPos = _leftPos + _width;
|
766
|
-
if( _maxCol < _origCol ){
|
767
|
-
_maxCol = _origCol;
|
768
|
-
}
|
769
|
-
}
|
770
|
-
else {
|
771
|
-
// on mode 2: stretch to fill multiple column widths,
|
772
|
-
// because no item moving is done anymore at this stage, so we know what's free and what's not
|
773
|
-
if( _vacantCols.length > 0 ){
|
774
|
-
_optimalColAndLength = this._findLargestSequence( _vacantCols );
|
775
|
-
_col = _vacantCols[ _optimalColAndLength[0] ];
|
776
|
-
_colWidth = _optimalColAndLength[1];
|
777
|
-
}
|
778
|
-
else {
|
779
|
-
_origCol = _origColById[i];
|
780
|
-
_col = _origCol;
|
781
|
-
_colWidth = 1;
|
782
|
-
}
|
783
|
-
_leftPos = _left+(_col*_width);
|
784
|
-
_rightPos = _leftPos+(_colWidth*_width);
|
785
|
-
}
|
786
|
-
_itemRect.setLeft( _leftPos );
|
787
|
-
_itemRect.setRight( _rightPos );
|
788
|
-
}
|
789
|
-
// afther the first run (mode 0) we know the actual amount of columns, so adjust column width accordingly
|
790
|
-
if( l === 0 ){
|
791
|
-
_overlapCount = _maxCol;
|
792
|
-
_width = Math.floor( _availWidth / (_maxCol+1) );
|
793
|
-
}
|
794
|
-
}
|
795
|
-
return true;
|
796
|
-
},
|
797
|
-
|
798
|
-
// draws the timeline (sub-routine of refreshValue)
|
799
|
-
drawTimeline: function(){
|
800
|
-
this._initTimeSheetItems();
|
801
|
-
this.drawTimeSheetItems();
|
802
|
-
this._updateTimelineRects();
|
803
|
-
// use the dimensions of the views
|
804
|
-
for( var i = 0; i < this.timeSheetItems.length; i++){
|
805
|
-
this.timeSheetItems[i].drawRect();
|
806
|
-
}
|
807
|
-
},
|
808
|
-
|
809
|
-
_sha: SHA.nu(8),
|
810
|
-
|
811
|
-
/*
|
812
|
-
|
813
|
-
Each item looks like this, any extra attributes are allowed,
|
814
|
-
but not used and not guaranteed to be preserved:
|
815
|
-
|
816
|
-
{
|
817
|
-
id: 'abcdef1234567890', // identifier, used in server to map id's
|
818
|
-
label: 'Event title', // label of event title
|
819
|
-
start: 1299248619, // epoch timestamp of event start
|
820
|
-
duration: 3600, // duration of event in seconds
|
821
|
-
locked: true, // when false, prevents editing the item
|
822
|
-
icons: [ 1, 3, 6 ], // icon id's to display
|
823
|
-
color: '#ffffff' // defaults to '#ffffff' if undefined
|
824
|
-
}
|
825
|
-
|
826
|
-
|
827
|
-
*/
|
828
|
-
/** = Description
|
829
|
-
* Redraws and refreshes the values on timesheet.
|
830
|
-
*
|
831
|
-
**/
|
832
|
-
refreshValue: function(){
|
833
|
-
|
834
|
-
if(!this.itemOptions){
|
835
|
-
return;
|
836
|
-
}
|
837
|
-
|
838
|
-
// optimization that ensures the rect and previous value are different before redrawing
|
839
|
-
var
|
840
|
-
_valueStr = this.encodeObject( this.value ),
|
841
|
-
_rectStr = this.rect.toString(),
|
842
|
-
_timeRangeStr = this.options.timeStart+':'+this.options.timeEnd,
|
843
|
-
_shaSum = this._sha.strSHA1( _valueStr+_rectStr+_timeRangeStr );
|
844
|
-
if( this._prevSum !== _shaSum ){
|
845
|
-
// the preview timesheet item is hidden when new data arrives (including what it created)
|
846
|
-
this.dragPreview.hide();
|
847
|
-
this._prevSum = _shaSum;
|
848
|
-
this.drawTimeline();
|
849
|
-
}
|
850
|
-
}
|
851
|
-
|
852
|
-
});
|