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.
@@ -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()