rsence-pre 3.0.0.12 → 3.0.0.14

Sign up to get free protection for your applications and to get access to all the features.
@@ -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
- });