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.
@@ -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
- });