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.
@@ -0,0 +1,610 @@
1
+
2
+ # Default locale (en) strings for HTimeSheet
3
+ HLocale.components.HTimeSheet =
4
+ strings:
5
+ newItemLabel: 'New item'
6
+ HTimeSheet = HControl.extend
7
+
8
+ componentName: 'timesheet'
9
+ markupElemNames: [ 'label', 'value', 'timeline' ]
10
+
11
+ defaultEvents:
12
+ draggable: true
13
+ click: true
14
+ doubleClick: true
15
+ resize: true
16
+
17
+ controlDefaults: HControlDefaults.extend
18
+ timeStart: 0 # 1970-01-01 00:00:00
19
+ timeEnd: 86399 # 1970-01-01 23:59:59
20
+ tzOffset: 0 # For custom timezone offsets in seconds; eg: 7200 => UTC+2
21
+ itemMinHeight: 16 # Smallest allowed size for an item (in pixels)
22
+ hideHours: false # Enable to hide the hours in the gutter
23
+ autoLabel: false # Automatically set the label to the date, when enabled
24
+ autoLabelFn: 'formatDate' # The name of the function to return formatted date/time
25
+ notchesPerHour: 4 # by default 1/4 of an hour precision (15 minutes)
26
+ snapToNotch: true # Snaps time to nearest notch/line
27
+ itemOffsetLeft: 64 # Theme settings; don't enter in options
28
+ itemOffsetRight: 0 # Theme settings; don't enter in options
29
+ itemOffsetTop: 20 # Theme settings; don't enter in options
30
+ itemOffsetBottom: 0 # Theme settings; don't enter in options
31
+ itemDisplayTime: true # Items display their time by default
32
+ allowClickCreate: false # Enable to allow clicking in empty areas to create new items
33
+ iconImage: 'timesheet_item_icons.png' # Icon resources for items
34
+ allowDoubleClickCreate: true # Double-clicking empty areas are shortcuts for new items by default
35
+ minDragSize: 5 # Minimum amount of pixels dragged required for accepting a drag
36
+ hourOffsetTop: -4 # Theme settings; don't enter in options
37
+
38
+ customOptions: ( _opt )->
39
+ @localeStrings = HLocale.components.HTimeSheet.strings
40
+ _opt.defaultLabel = @localeStrings.newItemLabel unless _opt.defaultLabel?
41
+ _opt.autoLabelFnOptions = { longWeekDay: true } unless _opt.autoLabelFnOptions?
42
+ unless _opt.dummyValue?
43
+ _opt.dummyValue =
44
+ label: ''
45
+ start: 0
46
+ color: '#000000'
47
+
48
+ themeSettings: ( _itemOffsetLeft, _itemOffsetTop, _itemOffsetRight, _itemOffsetBottom, _hourOffsetTop )->
49
+ if @options.hideHours
50
+ ELEM.addClassName( @elemId, 'nohours' )
51
+ @options.itemOffsetLeft = 0
52
+ else if _itemOffsetLeft?
53
+ @options.itemOffsetLeft = _itemOffsetLeft
54
+ @options.itemOffsetTop = _itemOffsetTop if _itemOffsetTop?
55
+ @options.itemOffsetRight = _itemOffsetRight if _itemOffsetRight?
56
+ @options.itemOffsetBottom = _itemOffsetBottom if _itemOffsetBottom?
57
+ @options.hourOffsetTop = _hourOffsetTop if _hourOffsetTop?
58
+
59
+ autoLabel: ->
60
+ _locale = HLocale.dateTime
61
+ _opt = @options
62
+ _label = _locale[_opt.autoLabelFn]( _opt.timeStart, _opt.autoLabelFnOptions )
63
+ if @label != _label
64
+ @label = _label
65
+ @refreshLabel()
66
+
67
+ clearHours: ->
68
+ for _hourItemId in @hourItems
69
+ ELEM.del( _hourItemId )
70
+
71
+ drawHours: ->
72
+ _hourParent = @markupElemIds.timeline
73
+ _lineParent = @markupElemIds.value
74
+ _dateStart = new Date( @options.timeStart * 1000 )
75
+ _dateEnd = new Date( @options.timeEnd * 1000 )
76
+ _hourStart = _dateStart.getUTCHours()
77
+ _hourEnd = _dateEnd.getUTCHours()
78
+ _hours = (_hourEnd - _hourStart) + 1
79
+ _rectHeight = ELEM.getSize( _hourParent )[1]
80
+ _topOffset = @options.itemOffsetTop
81
+ _height = _rectHeight - _topOffset - @options.itemOffsetBottom
82
+ _pxPerHour = _height / _hours
83
+ _notchesPerHour = @options.notchesPerHour
84
+ _pxPerLine = _pxPerHour / _notchesPerHour
85
+ _bottomPos = _rectHeight-_height-_topOffset-2
86
+ _pxPerNotch = _pxPerHour / _notchesPerHour
87
+
88
+ ELEM.setStyle( _hourParent, 'visibility', 'hidden', true )
89
+ ELEM.setStyle( @markupElemIds.value, 'bottom', _bottomPos+'px' )
90
+
91
+ @clearHours() if @hourItems?
92
+
93
+ @itemOptions =
94
+ notchHeight: _pxPerNotch
95
+ notches: _hours * _notchesPerHour
96
+ offsetTop: _topOffset
97
+ offsetBottom: _bottomPos
98
+ height: _height
99
+
100
+ @hourItems = []
101
+
102
+ for _hour in [_hourStart.._hourEnd]
103
+ _lineTop = Math.round( _topOffset + (_hour*_pxPerHour) )
104
+ if _hour != _hourStart
105
+ _hourTop = _lineTop + @options.hourOffsetTop
106
+ @hourItems.push( ELEM.make( @markupElemIds.timeline, 'div',
107
+ attr:
108
+ className: 'hour'
109
+ styles:
110
+ top: _hourTop+'px'
111
+ html: _hour+':00'
112
+ ) )
113
+ @hourItems.push( ELEM.make( _lineParent, 'div',
114
+ attr:
115
+ className: 'line'
116
+ styles:
117
+ top: (_lineTop+1)+'px'
118
+ height: Math.round(_pxPerNotch-1)+'px'
119
+ ) )
120
+ for i in [1..._notchesPerHour] by 1
121
+ _notchTop = Math.round(_lineTop + (_pxPerNotch*i))
122
+ @hourItems.push( ELEM.make( _lineParent, 'div',
123
+ attr:
124
+ className: 'notch'
125
+ styles:
126
+ top: (_notchTop+1)+'px'
127
+ height: Math.round(_pxPerNotch-1)+'px'
128
+ ) )
129
+ ELEM.setStyle( @markupElemIds.timeline, 'visibility', 'inherit' );
130
+
131
+ # extra hook for refreshing; updates label and hours before doing common things
132
+ refresh: ->
133
+ if @drawn
134
+ @autoLabel() if @options.autoLabel
135
+ @drawHours()
136
+ @base()
137
+
138
+ # set the timezone offset (in seconds)
139
+ setTzOffset: (_tzOffset)->
140
+ @options.tzOffset = _tzOffset
141
+ @refresh()
142
+
143
+ # set the start timestamp of the timesheet
144
+ setTimeStart: (_timeStart)->
145
+ @options.timeStart = _timeStart
146
+ @refresh()
147
+
148
+ # set the end timestamp of the timesheet
149
+ setTimeEnd: (_timeEnd)->
150
+ @options.timeEnd = _timeEnd
151
+ @refresh()
152
+
153
+ # sets the range of timestams of the timesheet
154
+ setTimeRange: (_timeRange)->
155
+ if @typeChr(_timeRange) == 'a' and _timeRange.length == 2
156
+ @setTimeStart( _timeRange[0] )
157
+ @setTimeEnd( _timeRange[1] )
158
+ else if @typeChr(_timeRange) == 'h' and _timeRange.timeStart? and _timeRange.timeEnd?
159
+ @setTimeStart( _timeRange.timeStart )
160
+ @setTimeEnd( _timeRange.timeEnd )
161
+
162
+ # sets the timestamp of the timesheet
163
+ setDate: (_date)->
164
+ @setTimeRange( [ _date, _date + @options.timeEnd - @options.timeStart ] )
165
+ @refresh()
166
+
167
+ # draw decorations
168
+ drawSubviews: ->
169
+ @drawHours()
170
+ _options = @options
171
+ _minDuration = Math.round(3600/_options.notchesPerHour)
172
+ _dummyValue = @cloneObject( @options.dummyValue )
173
+ _dummyValue.duration = _minDuration
174
+ @dragPreviewRect = @rectFromValue(
175
+ start: _options.timeStart
176
+ duration: _minDuration
177
+ )
178
+ @minDuration = _minDuration
179
+ @dragPreview = HTimeSheetItem.new( @dragPreviewRect, @,
180
+ value: _dummyValue
181
+ visible: false
182
+ iconImage: @options.iconImage
183
+ displayTime: @options.itemDisplayTime
184
+ )
185
+ @dragPreview.setStyleOfPart('state','color','#fff')
186
+
187
+ # event listener for clicks, simulates double clicks in case of not double click aware browser
188
+ click: (x,y)->
189
+ _prevClickTime = false
190
+ _notCreated = not @clickCreated and not @doubleClickCreated and not @dragCreated
191
+ if not @startDragTime and @prevClickTime
192
+ _prevClickTime = @prevClickTime
193
+ else if @startDragTime
194
+ _prevClickTime = @startDragTime
195
+ if _notCreated and @options.allowClickCreate
196
+ @clickCreate( x,y )
197
+ @clickCreated = true
198
+ @doubleClickCreated = false
199
+ @prevClickTime = false
200
+ else if _notCreated and @options.allowDoubleClickCreate
201
+ _currTime = new Date().getTime()
202
+ if _prevClickTime
203
+ _timeDiff = _currTime - _prevClickTime
204
+ else
205
+ _timeDiff = -1
206
+ if _timeDiff > 150 and _timeDiff < 500 and not @doubleClickCreated
207
+ @clickCreate( x, y )
208
+ @clickCreated = false
209
+ @doubleClickCreated = true
210
+ @doubleClickSimCreated = true
211
+ else
212
+ @doubleClickCreated = false
213
+ @prevClickTime = _currTime
214
+ else
215
+ @clickCreated = false
216
+ @doubleClickCreated = false
217
+ @prevClickTime = false
218
+
219
+ # creates an item on click
220
+ clickCreate: (x,y)->
221
+ _startTime = @pxToTime( y-@pageY(), true )
222
+ _endTime = _startTime + @minDuration
223
+ @refreshDragPreview( _startTime, _endTime )
224
+ @dragPreview.bringToFront()
225
+ @dragPreview.show()
226
+ if @activateEditor( @dragPreview )
227
+ @editor.createItem( @cloneObject( @dragPreview.value ) )
228
+ else
229
+ @dragPreview.hide()
230
+
231
+ # event listener for double clicks
232
+ doubleClick: (x,y)->
233
+ @prevClickTime = false
234
+ @doubleClickCreated = false
235
+ _notCreated = not @clickCreated and not @doubleClickCreated and not @doubleClickSimCreated and not @dragCreated
236
+ if not @options.allowDoubleClickCreate and @options.allowClickCreate and _notCreated
237
+ @click( x, y )
238
+ else if @options.allowDoubleClickCreate and not @options.allowClickCreate and _notCreated
239
+ @clickCreate( x, y )
240
+ @clickCreated = false
241
+ @doubleClickCreated = true
242
+ else
243
+ @clickCreated = false
244
+ @doubleClickSimCreated = false
245
+
246
+ # update the preview area
247
+ refreshDragPreview: (_startTime, _endTime)->
248
+ @dragPreviewRect.setTop( @timeToPx( _startTime ) )
249
+ @dragPreviewRect.setBottom( @timeToPx( _endTime ) )
250
+ @dragPreviewRect.setHeight( @options.itemMinHeight ) if @dragPreviewRect.height < @options.itemMinHeight
251
+ @dragPreview.drawRect()
252
+ @dragPreview.value.start = _startTime
253
+ @dragPreview.value.duration = _endTime - _startTime
254
+ @dragPreview.refreshValue()
255
+
256
+ # drag & drop event listeners, used for dragging new timesheet items
257
+ startDrag: (x,y)->
258
+ @_startDragY = y
259
+ @startDragTime = @pxToTime( y-@pageY(), true )
260
+ @refreshDragPreview( @startDragTime, @startDragTime + @minDuration )
261
+ @dragPreview.bringToFront()
262
+ @dragPreview.show()
263
+ true
264
+
265
+ drag: (x,y)->
266
+ _dragTime = @pxToTime( y-@pageY() )
267
+ if _dragTime < @startDragTime
268
+ _startTime = _dragTime
269
+ _endTime = @startDragTime
270
+ else
271
+ _endTime = _dragTime
272
+ _startTime = @startDragTime
273
+ @refreshDragPreview( _startTime, _endTime )
274
+ true
275
+
276
+ endDrag: (x,y)->
277
+ _dragTime = @pxToTime( y-@pageY() )
278
+ _minDistanceSatisfied = Math.abs( @_startDragY - y ) >= @options.minDragSize
279
+ @dragPreview.hide()
280
+ if _dragTime != @startDragTime
281
+ if _minDistanceSatisfied
282
+ if @activateEditor( @dragPreview )
283
+ @dragCreated = true
284
+ @editor.createItem( @cloneObject( @dragPreview.value ) )
285
+ return true
286
+ @dragCreated = false
287
+ else
288
+ @dragCreated = false
289
+ @clickCreated = false
290
+ @startDragTime = false
291
+ @click(x, y)
292
+ return true
293
+ false
294
+
295
+ # a resize triggers refresh, of which the important part is refreshValue, which triggers redraw of the time sheet items
296
+ resize: ->
297
+ @base()
298
+ @refresh()
299
+
300
+ # snaps the time to grid
301
+ snapTime: (_timeSecs,_begin)->
302
+ _options = @options
303
+ _pxDate = new Date( Math.round(_timeSecs) * 1000 )
304
+ _snapSecs = Math.round( 3600 / _options.notchesPerHour )
305
+ _halfSnapSecs = _snapSecs * 0.5
306
+ _hourSecs = (_pxDate.getUTCMinutes()*60) + _pxDate.getUTCSeconds()
307
+ _remSecs = _hourSecs % _snapSecs
308
+ if _begin
309
+ _timeSecs -= _remSecs
310
+ else
311
+ if _remSecs > _halfSnapSecs
312
+ _timeSecs += _snapSecs-_remSecs
313
+ else
314
+ _timeSecs -= _remSecs
315
+ _timeSecs
316
+
317
+ # snaps the pixel to grid
318
+ snapPx: (_px)->
319
+ _timeSecs = @pxToTime( _px )
320
+ _timeSecs = @snapTime( _timeSecs )
321
+ @timeToPx( _timeSecs )
322
+
323
+ # activates the editor; _item is the timesheet item to edit
324
+ activateEditor: (_item)->
325
+ if @editor
326
+ _editor = @editor
327
+ _editor.setTimeSheetItem( _item )
328
+ _item.bringToFront()
329
+ _editor.bringToFront()
330
+ _editor.show()
331
+ return true
332
+ false
333
+
334
+ ###
335
+ # = Description
336
+ # Sets the editor given as parameter as the editor of instance.
337
+ #
338
+ # = Parameters
339
+ # +_editor+::
340
+ ###
341
+ setEditor: (_editor)-> @editor = _editor
342
+
343
+ ###
344
+ # = Description
345
+ # Destructor; destroys the editor first and commences inherited die.
346
+ ###
347
+ die: ->
348
+ @editor.die() if @editor
349
+ @editor = null
350
+ @base()
351
+
352
+ # converts pixels to time
353
+ pxToTime: (_px, _begin)->
354
+ _options = @options
355
+ _timeStart = _options.timeStart
356
+ _timeEnd = _options.timeEnd
357
+ _timeRange = _timeEnd - _timeStart
358
+ _itemOptions = @itemOptions
359
+ _top = _itemOptions.offsetTop+1
360
+ _height = _itemOptions.height
361
+ _pxPerSec = _height / _timeRange
362
+ _px -= _top
363
+ _timeSecs = _timeStart + ( _px / _pxPerSec )
364
+ _timeSecs = @snapTime( _timeSecs, _begin ) if @options.snapToNotch
365
+ if _timeSecs > _options.timeEnd
366
+ _timeSecs = _options.timeEnd
367
+ else if _timeSecs < _options.timeStart
368
+ _timeSecs = _options.timeStart
369
+ Math.round( _timeSecs )
370
+
371
+ # converts time to pixels
372
+ timeToPx: (_time, _begin)->
373
+ _time = @snapTime( _time, _begin ) if @options.snapToNotch
374
+ _options = @options
375
+ _timeStart = _options.timeStart
376
+ _timeEnd = _options.timeEnd
377
+ _time = _timeStart if _time < _timeStart
378
+ _time = _timeEnd if _time > _timeEnd
379
+ _timeRange = _timeEnd - _timeStart
380
+ _itemOptions = @itemOptions
381
+ _top = _itemOptions.offsetTop
382
+ _height = _itemOptions.height
383
+ _pxPerSec = _height / _timeRange
384
+ _timeSecs = _time - _timeStart
385
+ _px = _top + ( _timeSecs * _pxPerSec )
386
+ Math.round( _px )
387
+
388
+ # converts time to pixels for the rect
389
+ rectFromValue: (_value)->
390
+ _topPx = @timeToPx( _value.start )
391
+ _bottomPx = @timeToPx( _value.start + _value.duration )
392
+ _leftPx = @options.itemOffsetLeft
393
+ _rightPx = @rect.width - @options.itemOffsetRight - 2
394
+ if _topPx == 'underflow'
395
+ _topPx = _itemOptions.offsetTop
396
+ else if _topPx == 'overflow'
397
+ return false
398
+ if _bottomPx == 'underflow'
399
+ return false
400
+ else if _bottomPx == 'overflow'
401
+ _bottomPx = _itemOptions.offsetTop + _itemOptions.height
402
+ _rect = HRect.new( _leftPx, _topPx, _rightPx, _bottomPx )
403
+ if _rect.height < @options.itemMinHeight
404
+ _rect.setHeight( @options.itemMinHeight )
405
+ _rect
406
+
407
+ # creates a single time sheet item component
408
+ createTimeSheetItem: (_value)->
409
+ _rect = @rectFromValue( _value )
410
+ return false if rect == false
411
+ HTimeSheetItem.new( _rect, @,
412
+ value: _value
413
+ displayTime: @options.itemDisplayTime
414
+ events:
415
+ draggable: true
416
+ doubleClick: true
417
+ )
418
+
419
+ # calls createTimeSheetItem with each value of the timesheet value array
420
+ drawTimeSheetItems: ->
421
+ _data = @value
422
+ _items = @timeSheetItems
423
+ if @typeChr(_data) == 'a' and _data.length > 0
424
+ for _value in _data
425
+ _item = @createTimeSheetItem( _value )
426
+ _items.push( _item ) if _item
427
+
428
+ ###
429
+ # =Description
430
+ # Create a new timeSheetItems if it hasn't been done already,
431
+ # otherwise destroy the items of the old one before proceeding.
432
+ ###
433
+ _initTimeSheetItems: ->
434
+ @timeSheetItems = [] unless @timeSheetItems?
435
+ if @timeSheetItems.length > 0
436
+ for _timeSheetItem in @timeSheetItems
437
+ _timeSheetItem.die()
438
+ @timeSheetItems = []
439
+
440
+ # finds the index in the array which contains most sequential items
441
+ _findLargestSequence: (_arr)->
442
+ _index = 0
443
+ _length = 1
444
+ _maxLength = 1
445
+ _bestIndex = 0
446
+ for i in [1..._arr.length] by 1
447
+ # grow:
448
+ if _arr[i] - _arr[i-1] == 1 and _index == i-_length
449
+ _length++
450
+ # reset:
451
+ else
452
+ _index = i
453
+ _length = 1
454
+ if _length > _maxLength
455
+ _bestIndex = _index
456
+ _maxLength = _length
457
+ [ _bestIndex, _maxLength ]
458
+
459
+ # find the amount of overlapping time sheet items
460
+ _findOverlapCount: (_items)->
461
+ _overlaps = []
462
+ _testRects = @_getTestRects( _items )
463
+ for i in [0..._items.length] by 1
464
+ _overlaps[i] = 0
465
+ for i in [0...(_items.length-1)] by 1
466
+ for j in [(i+1)..._items.length] by 1
467
+ if _items[i].rect.intersects( _testRects[j] )
468
+ _overlaps[i]++
469
+ _overlaps[j]++
470
+ Math.max.apply( Math, _overlaps )
471
+
472
+ _getTestRects: (_items)->
473
+ _rects = []
474
+ for i in [0..._items.length] by 1
475
+ _rects[i] = HRect.new( _items[i].rect )
476
+ _rects[i].insetBy( 1, 1 )
477
+ _rects
478
+
479
+ # returns a sorted copy of the timeSheetItems array
480
+ _sortedTimeSheetItems: (_sortFn)->
481
+ unless _sortFn?
482
+ _sortFn = (a,b)-> b.rect.height - a.rect.height
483
+ _arr = []
484
+ _items = @timeSheetItems
485
+ _arr.push( _item ) for _item in _items
486
+ _arr.sort(_sortFn)
487
+
488
+ # Optimizes the left and right position of each timesheet item to fit
489
+ # NOTE: This method will require refactoring; it's way too long and complicated
490
+ _updateTimelineRects: ->
491
+ # loop indexes:
492
+ _options = @options
493
+ _rect = @rect
494
+ _availWidth = _rect.width - _options.itemOffsetRight - _options.itemOffsetLeft
495
+ _left = _options.itemOffsetLeft
496
+ # get a list of timesheet items sorted by height (larger to smaller order)
497
+ _items = @_sortedTimeSheetItems()
498
+ _itemCount = _items.length
499
+ # amount of items ovelapping (max, actual number might be smaller after optimization)
500
+ _overlapCount = @_findOverlapCount( _items )
501
+ _width = Math.floor( _availWidth / (_overlapCount+1) )
502
+ _maxCol = 0
503
+ _origColById = []
504
+ # No overlapping; nothing to do
505
+ return false unless _overlapCount
506
+ # move all items initially to one column right of the max overlaps
507
+ _leftPos = _left+(_width*(_overlapCount+1))
508
+ for i in [0..._itemCount] by 1
509
+ _itemRect = _items[i].rect
510
+ _itemRect.setLeft( _leftPos )
511
+ _itemRect.setRight( _leftPos+_width )
512
+
513
+ # optimize gaps by traversing each combination
514
+ # and finding the first column with no gaps
515
+ # the top-level loops three times in the following modes:
516
+ # 0: place items into the first vacant column and find the actual max columns
517
+ # 1: stretch columns to final column width
518
+ # 2: stretch columns to fit multiple columns, if space is vacant
519
+ for l in [0...3] by 1
520
+ for i in [0..._itemCount] by 1
521
+ _itemRect = _items[i].rect
522
+ # in mode 1, just the column widths are changed
523
+ if l == 1
524
+ _leftPos = _left + (_origColById[i]*_width)
525
+ _itemRect.setLeft( _leftPos )
526
+ _itemRect.setRight( _leftPos + _width )
527
+ continue
528
+ _overlapCols = []
529
+ _vacantCols = []
530
+ _testRects = @_getTestRects( _items )
531
+ _testRect = HRect.new( _itemRect )
532
+ # test each column position (modes 0 and 2)
533
+ for k in [0...(_overlapCount+1)] by 1
534
+ _leftPos = _left + (k*_width)
535
+ _testRect.setLeft( _leftPos )
536
+ _testRect.setRight( _leftPos + _width )
537
+ for j in [0..._itemCount] by 1
538
+ if i != j and _testRect.intersects( _testRects[j] )
539
+ _overlapCols.push( k ) unless ~_overlapCols.indexOf( k )
540
+ if not ~_vacantCols.indexOf( k ) and not ~_overlapCols.indexOf( k )
541
+ _vacantCols.push( k )
542
+
543
+ # on the first run (mode 0) place items into the first column:
544
+ if l == 0
545
+ _origCol = _vacantCols[0]
546
+ _origColById.push( _origCol )
547
+ _leftPos = _left+(_origCol*_width)
548
+ _rightPos = _leftPos + _width
549
+ _maxCol = _origCol if _maxCol < _origCol
550
+ else
551
+ # on mode 2: stretch to fill multiple column widths,
552
+ # because no item moving is done anymore at this stage, so we know what's free and what's not
553
+ if _vacantCols.length > 0
554
+ _optimalColAndLength = @_findLargestSequence( _vacantCols )
555
+ _col = _vacantCols[ _optimalColAndLength[0] ]
556
+ _colWidth = _optimalColAndLength[1]
557
+ else
558
+ _origCol = _origColById[i]
559
+ _col = _origCol
560
+ _colWidth = 1
561
+ _leftPos = _left+(_col*_width)
562
+ _rightPos = _leftPos+(_colWidth*_width)
563
+ _itemRect.setLeft( _leftPos )
564
+ _itemRect.setRight( _rightPos )
565
+ # after the first run (mode 0) we know the actual amount of columns, so adjust column width accordingly
566
+ if l == 0
567
+ _overlapCount = _maxCol
568
+ _width = Math.floor( _availWidth / (_maxCol+1) )
569
+ true
570
+
571
+ # draws the timeline (sub-routine of refreshValue)
572
+ drawTimeline: ->
573
+ @_initTimeSheetItems()
574
+ @drawTimeSheetItems()
575
+ @_updateTimelineRects()
576
+ # use the dimensions of the views
577
+ for _timeSheetItem in @timeSheetItems
578
+ _timeSheetItem.drawRect()
579
+
580
+ _sha: SHA.new(8)
581
+
582
+ ###
583
+ # Each item looks like this, any extra attributes are allowed,
584
+ # but not used and not guaranteed to be preserved:
585
+ #
586
+ # { id: 'abcdef1234567890', # identifier, used in server to map id's
587
+ # label: 'Event title', # label of event title
588
+ # start: 1299248619, # epoch timestamp of event start
589
+ # duration: 3600, # duration of event in seconds
590
+ # locked: true, # when false, prevents editing the item
591
+ # icons: [ 1, 3, 6 ], # icon id's to display
592
+ # color: '#ffffff' # defaults to '#ffffff' if undefined
593
+ # }
594
+ #
595
+ # = Description
596
+ # Redraws and refreshes the values on timesheet.
597
+ #
598
+ ###
599
+ refreshValue: ->
600
+ return unless @itemOptions
601
+ # optimization that ensures the rect and previous value are different before redrawing
602
+ _valueStr = @encodeObject( @value )
603
+ _rectStr = @rect.toString()
604
+ _timeRangeStr = @options.timeStart+':'+@options.timeEnd
605
+ _shaSum = @_sha.strSHA1( _valueStr+_rectStr+_timeRangeStr )
606
+ if @_prevSum != _shaSum
607
+ # the preview timesheet item is hidden when new data arrives (including what it created)
608
+ @dragPreview.hide()
609
+ @_prevSum = _shaSum
610
+ @drawTimeline()