rsence 2.1.11 → 2.2.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (216) hide show
  1. data/INSTALL.rdoc +5 -3
  2. data/README.rdoc +23 -22
  3. data/VERSION +1 -1
  4. data/conf/default_conf.yaml +65 -29
  5. data/conf/rsence_command_strings.yaml +101 -71
  6. data/js/comm/autosync/autosync.js +3 -3
  7. data/js/comm/jsloader/jsloader.js +16 -3
  8. data/js/comm/queue/queue.js +1 -1
  9. data/js/comm/transporter/transporter.js +106 -83
  10. data/js/comm/values/values.js +8 -2
  11. data/js/controls/button/button.js +9 -4
  12. data/js/controls/button/themes/default/button.css +1 -1
  13. data/js/controls/checkbox/themes/default/checkbox.css +1 -1
  14. data/js/controls/dialogs/sheet/sheet.js +27 -18
  15. data/js/controls/radiobutton/themes/default/radiobutton.css +1 -1
  16. data/js/controls/searchfield/searchfield.coffee +2 -0
  17. data/js/controls/searchfield/themes/default/searchfield.css +96 -0
  18. data/js/controls/searchfield/themes/default/searchfield.html +12 -0
  19. data/js/controls/searchfield/themes/default/searchfield_parts1-ie6.gif +0 -0
  20. data/js/controls/searchfield/themes/default/searchfield_parts1.png +0 -0
  21. data/js/controls/sliders/slider/slider.js +7 -1
  22. data/js/controls/stepper/stepper.js +6 -1
  23. data/js/controls/stringview/stringview.js +50 -39
  24. data/js/controls/tab/tab.js +103 -7
  25. data/js/controls/tab/themes/default/tab.css +2 -0
  26. data/js/controls/textarea/themes/default/textarea.css +4 -0
  27. data/js/controls/textcontrol/textcontrol.js +32 -2
  28. data/js/controls/textcontrol/themes/default/textcontrol.css +17 -2
  29. data/js/controls/uploader/themes/default/uploader.css +3 -3
  30. data/js/controls/uploader/themes/default/uploader.html +1 -1
  31. data/js/controls/uploader/uploader.coffee +110 -0
  32. data/js/controls/window/themes/default/window.css +28 -5
  33. data/js/controls/window/themes/default/window.html +2 -0
  34. data/js/controls/window/window.js +5 -1
  35. data/js/core/class/class.js +22 -4
  36. data/js/core/elem/elem.coffee +816 -0
  37. data/js/core/event/event.js +2 -2
  38. data/js/core/rsence_ns/rsence_ns.coffee +31 -0
  39. data/js/core/rsence_ns/rsence_ns.js +6 -6
  40. data/js/datetime/calendar/calendar.coffee +307 -0
  41. data/js/datetime/calendar/themes/default/calendar.css +90 -9
  42. data/js/datetime/calendar/themes/default/calendar.html +11 -0
  43. data/js/datetime/calendar/themes/default/calendar_bg-ie6.gif +0 -0
  44. data/js/datetime/calendar/themes/default/calendar_bg.png +0 -0
  45. data/js/datetime/calendar/themes/default/calendar_parts1-ie6.gif +0 -0
  46. data/js/datetime/calendar/themes/default/calendar_parts1.png +0 -0
  47. data/js/datetime/calendar/themes/default/calendar_parts2-ie6.gif +0 -0
  48. data/js/datetime/calendar/themes/default/calendar_parts2.png +0 -0
  49. data/js/datetime/datetimepicker/datetimepicker.js +217 -0
  50. data/js/datetime/datetimevalue/datetimevalue.js +22 -5
  51. data/js/datetime/timesheet/themes/default/timesheet.css +51 -22
  52. data/js/datetime/timesheet/themes/default/timesheet.html +4 -2
  53. data/js/datetime/timesheet/timesheet.js +782 -192
  54. data/js/datetime/timesheet_item/themes/default/timesheet_item.css +42 -11
  55. data/js/datetime/timesheet_item/themes/default/timesheet_item.html +4 -2
  56. data/js/datetime/timesheet_item/themes/default/timesheet_item_icons.png +0 -0
  57. data/js/datetime/timesheet_item/timesheet_item.js +158 -254
  58. data/js/datetime/timesheet_item_edit/timesheet_item_edit.js +0 -274
  59. data/js/foundation/application/application.js +52 -9
  60. data/js/foundation/control/eventresponder/eventresponder.js +7 -4
  61. data/js/foundation/control/valueaction/valueaction.js +71 -0
  62. data/js/foundation/eventmanager/eventmanager.js +71 -33
  63. data/js/foundation/geom/rect/rect.js +39 -7
  64. data/js/foundation/json_renderer/json_renderer.js +278 -62
  65. data/js/foundation/locale_settings/locale_settings.js +131 -0
  66. data/js/foundation/system/system.js +40 -13
  67. data/js/foundation/thememanager/thememanager.js +21 -0
  68. data/js/foundation/view/markupview/markupview.js +12 -12
  69. data/js/foundation/view/view.js +221 -27
  70. data/js/graphics/svgcontrol/svgcontrol.js +400 -0
  71. data/js/lists/checkboxlist/checkboxlist.js +18 -7
  72. data/js/lists/listitems/listitems.js +52 -38
  73. data/js/lists/radiobuttonlist/radiobuttonlist.js +23 -7
  74. data/js/menus/menuitem/menuitem.js +5 -0
  75. data/js/menus/menuitem/themes/default/menuitem.css +45 -0
  76. data/js/menus/menuitem/themes/default/menuitem.html +4 -0
  77. data/js/menus/minimenu/minimenu.js +47 -16
  78. data/js/menus/minimenuitem/minimenuitem.js +62 -0
  79. data/js/menus/{minimenu/minimenuitem → minimenuitem}/themes/default/minimenuitem.css +2 -2
  80. data/js/menus/{minimenu/minimenuitem → minimenuitem}/themes/default/minimenuitem.html +0 -0
  81. data/js/menus/{minimenu/minimenuitem → minimenuitem}/themes/default/minimenuitem_checkmark.png +0 -0
  82. data/js/menus/popupmenu/popupmenu.js +14 -0
  83. data/js/menus/popupmenu/themes/default/popupmenu.css +65 -0
  84. data/js/menus/popupmenu/themes/default/popupmenu.html +7 -0
  85. data/js/menus/popupmenu/themes/default/popupmenu.png +0 -0
  86. data/js/no_pkg/no_pkg.js +2 -0
  87. data/js/util/reloadapp/reloadapp.js +1 -1
  88. data/js/views/scrollview/scrollview.js +6 -0
  89. data/lib/rsence.rb +136 -3
  90. data/lib/rsence/argv.rb +218 -0
  91. data/lib/rsence/argv/argv_util.rb +58 -0
  92. data/lib/rsence/argv/env_check.rb +58 -0
  93. data/lib/rsence/argv/help_argv.rb +15 -0
  94. data/lib/rsence/argv/initenv_argv.rb +218 -0
  95. data/lib/rsence/argv/save_argv.rb +92 -0
  96. data/lib/rsence/argv/startup_argv.rb +118 -0
  97. data/lib/rsence/argv/status_argv.rb +132 -0
  98. data/lib/rsence/argv/test_port.rb +32 -0
  99. data/lib/{daemon → rsence}/daemon.rb +67 -23
  100. data/lib/{conf/default.rb → rsence/default_config.rb} +18 -10
  101. data/lib/{plugins → rsence}/dependencies.rb +0 -0
  102. data/lib/{util → rsence}/gzstring.rb +0 -0
  103. data/lib/rsence/http.rb +3 -0
  104. data/lib/{http → rsence/http}/broker.rb +106 -19
  105. data/lib/{http → rsence/http}/rackup.rb +0 -0
  106. data/lib/{http → rsence/http}/request.rb +0 -4
  107. data/lib/{http → rsence/http}/response.rb +0 -1
  108. data/lib/{session → rsence}/msg.rb +17 -1
  109. data/lib/{plugins → rsence}/pluginmanager.rb +29 -12
  110. data/lib/{plugins → rsence}/plugins.rb +7 -7
  111. data/lib/{plugins → rsence/plugins}/gui_plugin.rb +8 -3
  112. data/lib/{plugins → rsence/plugins}/guiparser.rb +9 -6
  113. data/lib/{plugins → rsence/plugins}/plugin.rb +23 -4
  114. data/lib/{plugins → rsence/plugins}/plugin_base.rb +11 -1
  115. data/lib/{plugins → rsence/plugins}/plugin_plugins.rb +2 -2
  116. data/lib/{plugins → rsence/plugins}/plugin_sqlite_db.rb +0 -0
  117. data/lib/{plugins → rsence/plugins}/servlet.rb +0 -0
  118. data/lib/{session → rsence}/sessionmanager.rb +101 -39
  119. data/lib/{session → rsence}/sessionstorage.rb +30 -16
  120. data/lib/{daemon → rsence}/sigcomm.rb +0 -0
  121. data/lib/{transporter → rsence}/transporter.rb +13 -11
  122. data/lib/{values/hvalue.rb → rsence/value.rb} +6 -1
  123. data/lib/{values → rsence}/valuemanager.rb +1 -1
  124. data/plugins/client_pkg/client_pkg.rb +14 -4
  125. data/plugins/client_pkg/info.yaml +2 -2
  126. data/plugins/client_pkg/lib/client_pkg_build.rb +145 -45
  127. data/plugins/client_pkg/lib/client_pkg_cache.rb +1 -1
  128. data/plugins/client_pkg/lib/client_pkg_serve.rb +1 -1
  129. data/plugins/main/main.rb +43 -3
  130. data/plugins/main/tmpl/index.html +2 -10
  131. data/plugins/main/values.yaml +3 -1
  132. data/plugins/ticket/lib/common.rb +6 -3
  133. data/plugins/ticket/ticket.rb +11 -3
  134. metadata +144 -174
  135. data/js/comm/autosync/js.inc +0 -0
  136. data/js/comm/js.inc +0 -0
  137. data/js/comm/jsloader/js.inc +0 -0
  138. data/js/comm/queue/js.inc +0 -0
  139. data/js/comm/session/js.inc +0 -0
  140. data/js/comm/sessionwatcher/js.inc +0 -0
  141. data/js/comm/transporter/js.inc +0 -0
  142. data/js/comm/urlresponder/js.inc +0 -0
  143. data/js/comm/values/js.inc +0 -0
  144. data/js/controls/button/js.inc +0 -0
  145. data/js/controls/checkbox/js.inc +0 -0
  146. data/js/controls/dialogs/alert_sheet/js.inc +0 -0
  147. data/js/controls/dialogs/confirm_sheet/js.inc +0 -0
  148. data/js/controls/dialogs/sheet/js.inc +0 -0
  149. data/js/controls/imageview/js.inc +0 -0
  150. data/js/controls/passwordcontrol/js.inc +0 -0
  151. data/js/controls/progress/progressbar/js.inc +0 -0
  152. data/js/controls/progress/progressindicator/js.inc +0 -0
  153. data/js/controls/radiobutton/js.inc +0 -0
  154. data/js/controls/sliders/slider/js.inc +0 -0
  155. data/js/controls/sliders/vslider/js.inc +0 -0
  156. data/js/controls/stepper/js.inc +0 -0
  157. data/js/controls/stringview/js.inc +0 -0
  158. data/js/controls/tab/js.inc +0 -0
  159. data/js/controls/textarea/js.inc +0 -0
  160. data/js/controls/textcontrol/js.inc +0 -0
  161. data/js/controls/uploader/js.inc +0 -0
  162. data/js/controls/uploader/uploader.js +0 -154
  163. data/js/controls/validatorview/js.inc +0 -0
  164. data/js/controls/window/js.inc +0 -0
  165. data/js/core/class/js.inc +0 -0
  166. data/js/core/elem/elem.js +0 -1325
  167. data/js/core/elem/js.inc +0 -0
  168. data/js/core/event/js.inc +0 -0
  169. data/js/core/iefix/js.inc +0 -0
  170. data/js/core/rsence_ns/js.inc +0 -0
  171. data/js/datetime/calendar/calendar.js +0 -198
  172. data/js/datetime/calendar/js.inc +0 -0
  173. data/js/datetime/datetimevalue/js.inc +0 -0
  174. data/js/datetime/timesheet/js.inc +0 -0
  175. data/js/datetime/timesheet/old_timesheet.js +0 -292
  176. data/js/datetime/timesheet/themes/default/old_timesheet.css +0 -30
  177. data/js/datetime/timesheet/themes/default/old_timesheet.html +0 -2
  178. data/js/datetime/timesheet_item/js.inc +0 -0
  179. data/js/datetime/timesheet_item/old_timesheet_item.js +0 -308
  180. data/js/datetime/timesheet_item/themes/default/old_timesheet_item.css +0 -42
  181. data/js/datetime/timesheet_item/themes/default/old_timesheet_item.html +0 -8
  182. data/js/datetime/timesheet_item_edit/js.inc +0 -0
  183. data/js/datetime/timesheet_item_edit/old_timesheet_item_edit.js +0 -274
  184. data/js/foundation/application/js.inc +0 -0
  185. data/js/foundation/control/controldefaults/js.inc +0 -0
  186. data/js/foundation/control/dummyvalue/js.inc +0 -0
  187. data/js/foundation/control/dyncontrol/js.inc +0 -0
  188. data/js/foundation/control/eventresponder/js.inc +0 -0
  189. data/js/foundation/control/js.inc +0 -0
  190. data/js/foundation/control/valuematrix/js.inc +0 -0
  191. data/js/foundation/control/valueresponder/js.inc +0 -0
  192. data/js/foundation/eventmanager/js.inc +0 -0
  193. data/js/foundation/geom/point/js.inc +0 -0
  194. data/js/foundation/geom/rect/js.inc +0 -0
  195. data/js/foundation/json_renderer/js.inc +0 -0
  196. data/js/foundation/system/js.inc +0 -0
  197. data/js/foundation/thememanager/js.inc +0 -0
  198. data/js/foundation/value/js.inc +0 -0
  199. data/js/foundation/view/js.inc +0 -0
  200. data/js/foundation/view/markupview/js.inc +0 -0
  201. data/js/foundation/view/morphanimation/js.inc +0 -0
  202. data/js/foundation/view/viewdefaults/js.inc +0 -0
  203. data/js/lists/checkboxlist/js.inc +0 -0
  204. data/js/lists/listitems/js.inc +0 -0
  205. data/js/lists/propertylist/js.inc +0 -0
  206. data/js/lists/propertylist/propertylisteditor/js.inc +0 -0
  207. data/js/lists/radiobuttonlist/js.inc +0 -0
  208. data/js/menus/minimenu/js.inc +0 -0
  209. data/js/menus/minimenu/minimenuitem/js.inc +0 -0
  210. data/js/menus/minimenu/minimenuitem/minimenuitem.js +0 -33
  211. data/js/util/reloadapp/js.inc +0 -0
  212. data/js/util/sha/js.inc +0 -0
  213. data/js/views/centerview/js.inc +0 -0
  214. data/js/views/inlineview/js.inc +0 -0
  215. data/js/views/scrollview/js.inc +0 -0
  216. data/lib/conf/argv.rb +0 -880
@@ -1,3 +1,14 @@
1
+ <div class="calendar_bg">
2
+ <div class="calendar_nw"></div>
3
+ <div class="calendar_n"></div>
4
+ <div class="calendar_ne"></div>
5
+ <div class="calendar_w"></div>
6
+ <div class="calendar_c"></div>
7
+ <div class="calendar_e"></div>
8
+ <div class="calendar_sw"></div>
9
+ <div class="calendar_s"></div>
10
+ <div class="calendar_se"></div>
11
+ </div>
1
12
  <div class="calendar_control" id="control#{_ID}">
2
13
  <div class="calendar_head">
3
14
  <div class="calendar_head_prev_month" onclick="HSystem.views[#{this.viewId}].prevMonth();"></div>
@@ -0,0 +1,217 @@
1
+ /* RSence
2
+ * Copyright 2010 Riassence Inc.
3
+ * http://riassence.com/
4
+ *
5
+ * You should have received a copy of the GNU General Public License along
6
+ * with this software package. If not, contact licensing@riassence.com
7
+ */
8
+
9
+ var
10
+ HDateTimePicker = HControl.extend({
11
+ setLocked: function(_lockedState){
12
+ var
13
+ _enabledState = !_lockedState;
14
+ this.yyyy.setEnabled(_enabledState);
15
+ this.mm.setEnabled(_enabledState);
16
+ this.dd.setEnabled(_enabledState);
17
+ this.h.setEnabled(_enabledState);
18
+ this.m.setEnabled(_enabledState);
19
+ },
20
+ refreshValue: function(_readMode){
21
+ if(!this.drawn){
22
+ return;
23
+ }
24
+ if(!this.m){
25
+ return;
26
+ }
27
+ this.setLocked( HVM.values[ this.options.lockedId ].value );
28
+ var
29
+ date = new Date( this.value*1000 ),
30
+ yyyy = date.getUTCFullYear(),
31
+ mm = date.getUTCMonth()+1,
32
+ dd = date.getUTCDate(),
33
+ h = date.getUTCHours(),
34
+ m = date.getUTCMinutes();
35
+ if(_readMode){
36
+ var
37
+ nuDate = new Date( this.value*1000 ),
38
+ doSet = false;
39
+ if(this.yyyyValue.value !== yyyy){
40
+ nuDate.setUTCFullYear( this.yyyyValue.value ); doSet = true;
41
+ }
42
+ if(this.mmValue.value !== mm){
43
+ nuDate.setUTCMonth( this.mmValue.value-1 ); doSet = true;
44
+ }
45
+ if(this.ddValue.value !== dd){
46
+ nuDate.setUTCDate( this.ddValue.value ); doSet = true;
47
+ }
48
+ if(this.hValue.value !== h){
49
+ nuDate.setUTCHours( this.hValue.value ); doSet = true;
50
+ }
51
+ if(this.mValue.value !== m){
52
+ nuDate.setUTCMinutes( this.mValue.value ); doSet = true;
53
+ }
54
+ if(doSet){
55
+ this.setValue( nuDate.getTime()/1000 );
56
+ this.setMonthMax( nuDate );
57
+ }
58
+ }
59
+ else{
60
+ this.yyyyValue.set( yyyy );
61
+ this.mmValue.set( mm );
62
+ this.ddValue.set( dd );
63
+ this.hValue.set( h );
64
+ this.mValue.set( m );
65
+ }
66
+ },
67
+ setMonthMax: function( nuDate ){
68
+ nuDate.setUTCSeconds( 0 );
69
+ nuDate.setUTCMinutes( 0 );
70
+ nuDate.setUTCHours( 0 );
71
+ nuDate.setUTCDate( 1 );
72
+ var
73
+ mm = nuDate.getUTCMonth();
74
+ if(mm === 11){
75
+ nuDate.setUTCMonth( 0 );
76
+ nuDate.setUTCFullYear( nuDate.getUTCFullYear()+1 );
77
+ }
78
+ else {
79
+ nuDate.setUTCMonth( mm+1 );
80
+ }
81
+ var
82
+ ms = nuDate.getTime() - 1000,
83
+ maxDaysDate = new Date(ms),
84
+ maxDays = maxDaysDate.getUTCDate();
85
+ // console.log('maxDaysDate:',maxDaysDate.getUTCFullYear(),'-',maxDaysDate.getUTCMonth(),'-',maxDaysDate.getUTCDate());
86
+ // console.log('maxDays:',maxDays);
87
+ if(maxDays !== this.dd.numField.options.maxValue){
88
+ // console.log('reset maxValue..');
89
+ this.dd.numField.options.maxValue = maxDays;
90
+ (this.dd.numField.options.maxValue < this.dd.numField.value) && this.ddValue.set( maxDays );
91
+ this.dd.stepper.options.maxValue = maxDays;
92
+ }
93
+ },
94
+ die: function(){
95
+ this.yyyyValue.die();
96
+ this.yyyy.die();
97
+ this.mmValue.die();
98
+ this.mm.die();
99
+ this.ddValue.die();
100
+ this.dd.die();
101
+ this.hValue.die();
102
+ this.h.die();
103
+ this.mValue.die();
104
+ this.m.die();
105
+ this.base();
106
+ },
107
+ drawSubviews: function(){
108
+ ELEM.setStyle( this.elemId, 'overflow', 'visible' );
109
+ var
110
+ _NumStepperField = HView.extend({
111
+ setEnabled: function(_state){
112
+ this.numField.setEnabled(_state);
113
+ this.stepper.setEnabled(_state);
114
+ if(_state){
115
+ this.stepper.show();
116
+ }
117
+ else{
118
+ this.stepper.hide();
119
+ }
120
+ },
121
+ drawSubviews: function(){
122
+ ELEM.setStyle( this.elemId, 'overflow', 'visible' );
123
+ this.numField = HNumericTextControl.extend({
124
+ refreshValue: function(){
125
+ this.base();
126
+ this.parent.parent.refreshValue(true);
127
+ },
128
+ textBlur: function(){
129
+ this.setValue(
130
+ this.validateText(
131
+ this.getTextFieldValue()
132
+ )
133
+ );
134
+ }
135
+ }).nu(
136
+ [ 0, 0, this.rect.width-10, 21 ],
137
+ this, {
138
+ events: {
139
+ textEnter: false
140
+ },
141
+ minValue: this.options.minValue,
142
+ maxValue: this.options.maxValue,
143
+ valueObj: this.options.valueObj
144
+ }
145
+ );
146
+ this.stepper = HStepper.nu(
147
+ [ this.rect.width-15, 0, 15, 21 ],
148
+ this, {
149
+ minValue: this.options.minValue,
150
+ maxValue: this.options.maxValue,
151
+ valueObj: this.options.valueObj
152
+ }
153
+ );
154
+ }
155
+ }),
156
+ _numStepperRect = HRect.nu( 0, 0, 50, 21 );
157
+ this.yyyyValue = HValue.nu( false, 2010 );
158
+ this.yyyy = _NumStepperField.nu(
159
+ HRect.nu( _numStepperRect ),
160
+ this, {
161
+ minValue: 2010,
162
+ maxValue: 2020,
163
+ valueObj: this.yyyyValue
164
+ }
165
+ );
166
+ _numStepperRect.setWidth( 35 );
167
+ _numStepperRect.offsetBy( 55, 0 );
168
+ this.mmValue = HValue.nu( false, 12 );
169
+ this.mm = _NumStepperField.nu(
170
+ HRect.nu( _numStepperRect ),
171
+ this, {
172
+ minValue: 1,
173
+ maxValue: 12,
174
+ valueObj: this.mmValue
175
+ }
176
+ );
177
+ _numStepperRect.offsetBy( 40, 0 );
178
+ this.ddValue = HValue.nu( false, 24 );
179
+ this.dd = _NumStepperField.nu(
180
+ HRect.nu( _numStepperRect ),
181
+ this, {
182
+ minValue: 1,
183
+ maxValue: 31,
184
+ valueObj: this.ddValue
185
+ }
186
+ );
187
+ _numStepperRect.offsetBy( 50, 0 );
188
+ this.hValue = HValue.nu( false, 22 );
189
+ this.h = _NumStepperField.nu(
190
+ HRect.nu( _numStepperRect ),
191
+ this, {
192
+ minValue: 0,
193
+ maxValue: 23,
194
+ valueObj: this.hValue
195
+ }
196
+ );
197
+ _numStepperRect.offsetBy( 40, 0 );
198
+ this.mValue = HValue.nu( false, 45 );
199
+ this.m = _NumStepperField.nu(
200
+ HRect.nu( _numStepperRect ),
201
+ this, {
202
+ minValue: 0,
203
+ maxValue: 59,
204
+ valueObj: this.mValue
205
+ }
206
+ );
207
+ _numStepperRect.offsetBy( 37, 2 );
208
+ _numStepperRect.setWidth( 60 );
209
+ HStringView.nu(
210
+ HRect.nu( _numStepperRect ),
211
+ this, {
212
+ valueObj: HVM.values[this.options.tzValueId]
213
+ }
214
+ );
215
+ this.refreshValue();
216
+ }
217
+ });
@@ -16,10 +16,6 @@ HDateTime = HClass.extend({
16
16
  msDay: 86400000,
17
17
  msHour: 3600000,
18
18
  msMinute: 60000,
19
- months_localized: [
20
- 'January', 'February', 'March', 'April', 'May', 'June', 'July',
21
- 'August', 'September', 'October', 'November', 'December'
22
- ],
23
19
 
24
20
  /** = Description
25
21
  * Returns month name of the given date.
@@ -33,7 +29,7 @@ HDateTime = HClass.extend({
33
29
  **/
34
30
  monthName: function(_date){
35
31
  _date = (_date instanceof Date)?_date:this.date();
36
- return this.months_localized[_date.getUTCMonth()];
32
+ return HLocale.dateTime.strings.monthsLong[_date.getUTCMonth()];
37
33
  },
38
34
 
39
35
  /** = Description
@@ -77,6 +73,13 @@ HDateTime = HClass.extend({
77
73
  return _date.getUTCDate();
78
74
  },
79
75
 
76
+ setMday: function( _mday ){
77
+ var
78
+ _date = this.date();
79
+ _date.setUTCDate( _mday );
80
+ return Math.round( _date.getTime()/1000 );
81
+ },
82
+
80
83
  /** = Description
81
84
  * Returns month number for the date given as input.
82
85
  * Note that months are numbered from 0 to 11.
@@ -93,6 +96,13 @@ HDateTime = HClass.extend({
93
96
  return _date.getUTCMonth();
94
97
  },
95
98
 
99
+ setMonth: function( _month ){
100
+ var
101
+ _date = this.date();
102
+ _date.setUTCMonth( _month );
103
+ return Math.round( _date.getTime()/1000 );
104
+ },
105
+
96
106
  /** = Description
97
107
  * Returns year for given date.
98
108
  *
@@ -108,6 +118,13 @@ HDateTime = HClass.extend({
108
118
  return _date.getUTCFullYear();
109
119
  },
110
120
 
121
+ setYear: function( _year ){
122
+ var
123
+ _date = this.date();
124
+ _date.setUTCFullYear( _year );
125
+ return Math.round( _date.getTime()/1000 );
126
+ },
127
+
111
128
  /** = Description
112
129
  * Returns the timezone offset in milliseconds.
113
130
  *
@@ -1,30 +1,59 @@
1
- .timesheet_hours_col {
1
+ .timesheet_label,
2
+ .timesheet_items,
3
+ .timesheet_timeline,
4
+ .timesheet_timeline_hour,
5
+ .timesheet_timeline_line,
6
+ .timesheet_timeline_notch {
2
7
  position: absolute;
3
- top: 0px; left: 0px; width: 32px;
4
- font-family: Arial, sans-serif;
8
+ }
9
+
10
+ .timesheet_label,
11
+ .timesheet_items,
12
+ .timesheet_timeline {
13
+ font-family: Helvetica, Arial, sans-serif;
5
14
  font-size: 11px;
6
- color: #666;
15
+ color: #000;
7
16
  vertical-align: middle;
8
- text-align: right;
17
+ text-align: left;
9
18
  }
10
- .timesheet_hours_row {
11
- position: absolute;
12
- left: 0px; width: 32px;
19
+
20
+ .timesheet_label {
21
+ left: 33px; top: 0px; right: 0px; height: 16px;
22
+ font-size: 15px;
13
23
  }
14
- .timesheet_lines_col {
15
- position: absolute;
16
- top: 0px; left: 36px; right: 0px;
17
- border-left: 1px solid #aaa;
18
- border-bottom: 1px solid #aaa;
19
- border-right: 1px solid #aaa;
24
+
25
+ .timesheet_timeline {
26
+ left: 0px; top: 0px; right: 0px; bottom: 0px;
27
+ cursor: move;
20
28
  }
21
- .timesheet_lines_row0,
22
- .timesheet_lines_row1 {
23
- position: absolute;
24
- left: 0px; right: 0px; height: 1px;
25
- line-height: 1px; font-size: 0px;
26
- background-color: #ccc;
29
+
30
+ .timesheet_timeline_hour {
31
+ left: 0px; width: 26px; text-align: right; height: 14px;
32
+ }
33
+
34
+ .timesheet_timeline_line {
35
+ left: 31px; right: 3px; height: 1px; background-color: #bbb;
36
+ }
37
+ .timesheet_timeline_notch {
38
+ left: 31px; right: 3px; height: 1px; background-color: #ddd;
39
+ }
40
+
41
+ .timesheet_items {
42
+ left: 29px; top: 19px; right: 1px; bottom: 11px;
43
+ border: 1px solid #ccc;
44
+ border-radius: 3px;
45
+ }
46
+
47
+ .nohours .timesheet_timeline_notch,
48
+ .nohours .timesheet_timeline_line {
49
+ left: 2px;
50
+ }
51
+ .nohours .timesheet_timeline_hour {
52
+ display: none;
53
+ }
54
+ .nohours .timesheet_label {
55
+ left: 4px;
27
56
  }
28
- .timesheet_lines_row0 {
29
- background-color: #aaa;
57
+ .nohours .timesheet_items {
58
+ left: 0px;
30
59
  }
@@ -1,2 +1,4 @@
1
- <div class="timesheet_hours_col" id="label#{_ID}"></div>
2
- <div class="timesheet_lines_col" id="state#{_ID}"></div>
1
+ <div id="label#{_ID}" class="timesheet_label"></div>
2
+ <div id="timeline#{_ID}" class="timesheet_timeline"></div>
3
+ <div id="value#{_ID}" class="timesheet_items"></div>
4
+ ${this.themeSettings(30, 20, 0, 10, -6);}
@@ -6,149 +6,442 @@
6
6
  * with this software package. If not, contact licensing@riassence.com
7
7
  */
8
8
 
9
- /*** = Description
10
- ** HTimesheet is a simple timesheet control.
11
- ***/
9
+ HLocale.components.HTimeSheet = {
10
+ strings: {
11
+ newItemLabel: 'New item'
12
+ }
13
+ };
14
+
15
+
12
16
  var//RSence.DateTime
13
17
  HTimeSheet = HControl.extend({
14
18
 
15
- componentName: 'timesheet',
19
+ debug: false,
16
20
 
17
- /* Default amount of pixels per hour. Override with options when necessary. */
18
- pxPerHour: 24,
19
-
20
- /* Default amount of pixels from left. Override with options when necessary. */
21
- itemOffsetLeft: 36,
22
-
23
- /* Default amount of pixels from right. Override with options when necessary. */
24
- itemOffsetRight: 0,
21
+ localeStrings: HLocale.components.HTimeSheet.strings,
22
+ componentName: 'timesheet',
23
+ markupElemNames: [
24
+ 'label', 'value', 'timeline'
25
+ ],
25
26
 
26
27
  defaultEvents: {
27
- draggable: true
28
+ draggable: true,
29
+ click: true,
30
+ doubleClick: true,
31
+ resize: true
28
32
  },
29
33
 
30
34
  controlDefaults: HControlDefaults.extend({
31
- pxPerHour: null,
32
- itemOffsetLeft: null,
33
- itemOffsetRight: null,
34
- itemMinHeight: 24,
35
- newItemLabel: 'New Item',
36
- constructor: function(_ctrl){
37
- if(this.pxPerHour !== null){
38
- _ctrl.pxPerHour = this.pxPerHour;
35
+ timeStart: 0, // 1970-01-01 00:00:00
36
+ timeEnd: 86399, // 1970-01-01 23:59:59
37
+ tzOffset: 0, // timezone offset in seconds; eg: 7200 => UTC+2
38
+ itemMinHeight: 16,
39
+ hideHours: false,
40
+ autoLabel: false, // automatically set the label to the date
41
+ autoLabelFn: 'formatDate',
42
+ autoLabelFnOptions: { longWeekDay: true },
43
+ notchesPerHour: 4, // by default 1/4 of an hour precision (15 minutes)
44
+ itemOffsetLeft: 64, // Theme settings; don't enter in options
45
+ itemOffsetRight: 0, // Theme settings; don't enter in options
46
+ itemOffsetTop: 20, // Theme settings; don't enter in options
47
+ itemOffsetBottom: 0, // Theme settings; don't enter in options
48
+ itemDisplayTime: true,
49
+ allowClickCreate: false,
50
+ allowDoubleClickCreate: true,
51
+ minDragSize: 5, // minimum amount of pixels dragged required for accepting a drag
52
+ hourOffsetTop: -4, // Theme settings; don't enter in options
53
+ constructor: function( _ctrl ){
54
+ if( this.defaultLabel === undefined ){
55
+ this.defaultLabel = _ctrl.localeStrings.newItemLabel;
39
56
  }
40
- if(this.itemOffsetLeft !== null){
41
- _ctrl.itemOffsetLeft = this.itemOffsetLeft;
57
+ }
58
+ }),
59
+
60
+ themeSettings: function( _itemOffsetLeft, _itemOffsetTop, _itemOffsetRight, _itemOffsetBottom, _hourOffsetTop ){
61
+ if( this.options.hideHours ){
62
+ ELEM.addClassName( this.elemId, 'nohours' );
63
+ this.options.itemOffsetLeft = 0;
64
+ }
65
+ else if( _itemOffsetLeft !== undefined ) {
66
+ this.options.itemOffsetLeft = _itemOffsetLeft;
67
+ }
68
+ if( _itemOffsetTop !== undefined ) {
69
+ this.options.itemOffsetTop = _itemOffsetTop;
70
+ }
71
+ if( _itemOffsetRight !== undefined ) {
72
+ this.options.itemOffsetRight = _itemOffsetRight;
73
+ }
74
+ if( _itemOffsetBottom !== undefined ) {
75
+ this.options.itemOffsetBottom = _itemOffsetBottom;
76
+ }
77
+ if( _hourOffsetTop !== undefined ) {
78
+ this.options.hourOffsetTop = _hourOffsetTop;
79
+ }
80
+ },
81
+
82
+ autoLabel: function(){
83
+ var
84
+ _locale = HLocale.dateTime,
85
+ _label = _locale[this.options.autoLabelFn]( this.options.timeStart, this.options.autoLabelFnOptions );
86
+ if( this.label !== _label ){
87
+ this.label = _label;
88
+ this.refreshLabel();
89
+ }
90
+ },
91
+
92
+ clearHours: function(){
93
+ for( var i = 0; i < this.hourItems.length; i++ ){
94
+ ELEM.del( this.hourItems[i] );
95
+ }
96
+ },
97
+
98
+ drawHours: function(){
99
+
100
+ var
101
+ _parentElemId = this.markupElemIds.timeline,
102
+ _dateStart = new Date( this.options.timeStart * 1000 ),
103
+ _dateEnd = new Date( this.options.timeEnd * 1000 ),
104
+ _hourStart = _dateStart.getUTCHours(),
105
+ _hourEnd = _dateEnd.getUTCHours(),
106
+ _hours = (_hourEnd - _hourStart) + 1,
107
+ _rectHeight = ELEM.getSize( _parentElemId )[1],
108
+ _topOffset = this.options.itemOffsetTop,
109
+ _height = _rectHeight - _topOffset - this.options.itemOffsetBottom,
110
+ _pxPerHour = _height / _hours,
111
+ _notchesPerHour = this.options.notchesPerHour,
112
+ _pxPerLine = _pxPerHour / _notchesPerHour,
113
+ _hour = _hourStart,
114
+ _bottomPos = _rectHeight-_height-_topOffset-2,
115
+ _hourItem,
116
+ _lineItem,
117
+ _hourTop,
118
+ _lineTop,
119
+ i,
120
+ _pxPerNotch = _pxPerHour / _notchesPerHour,
121
+ _notchItem,
122
+ _notchTop;
123
+
124
+ ELEM.setStyle( _parentElemId, 'visibility', 'hidden', true );
125
+
126
+ ELEM.setStyle( this.markupElemIds.value, 'bottom', _bottomPos+'px' );
127
+
128
+ if( this['hourItems'] !== undefined ){
129
+ this.clearHours();
130
+ }
131
+ this.itemOptions = {
132
+ notchHeight: _pxPerNotch,
133
+ notches: (_hours * _notchesPerHour),
134
+ offsetTop: _topOffset,
135
+ offsetBottom: _bottomPos,
136
+ height: _height
137
+ };
138
+ this.hourItems = [];
139
+ for( ; _hour < (_hourEnd+1); _hour++ ){
140
+ _hourItem = ELEM.make( this.markupElemIds.timeline, 'div' );
141
+ _lineTop = Math.round(_topOffset + (_hour*_pxPerHour));
142
+ if( _hour !== _hourStart ){
143
+ _hourTop = _lineTop + this.options.hourOffsetTop;
144
+ ELEM.setStyle( _hourItem, 'top', _hourTop+'px' );
145
+ ELEM.addClassName( _hourItem, 'timesheet_timeline_hour' );
146
+ ELEM.setHTML( _hourItem, _hour+':00' );
147
+ this.hourItems.push( _hourItem );
148
+ _lineItem = ELEM.make( _parentElemId, 'div' );
149
+ ELEM.addClassName( _lineItem, 'timesheet_timeline_line' );
150
+ ELEM.setStyle( _lineItem, 'top', _lineTop + 'px' );
151
+ this.hourItems.push( _lineItem );
42
152
  }
43
- if(this.itemOffsetRight !== null){
44
- _ctrl.itemOffsetRight = this.itemOffsetRight;
153
+ for( i=1; i < _notchesPerHour; i++ ){
154
+ _notchItem = ELEM.make( _parentElemId, 'div' );
155
+ ELEM.addClassName( _notchItem, 'timesheet_timeline_notch' );
156
+ _notchTop = Math.round(_lineTop + (_pxPerNotch*i));
157
+ ELEM.setStyle( _notchItem, 'top', _notchTop+'px' );
158
+ this.hourItems.push( _notchItem );
45
159
  }
46
160
  }
47
- }),
161
+
162
+ ELEM.setStyle( this.markupElemIds.timeline, 'visibility', 'inherit' );
163
+
164
+ },
48
165
 
49
- /** = Description
50
- * Redraws the timesheet.
51
- *
52
- **/
166
+ // extra hook for refreshing; updates label and hours before doing common things
53
167
  refresh: function(){
54
- if(this.drawn){
55
- var _areaHeight = this.rect.height;
56
- this.pxPerHour = (_areaHeight-(_areaHeight%48)) / 24;
57
- if(this.options['hideLabel']){
58
- this.setStyleOfPart( 'label', 'display', 'none' );
59
- this.setStyleOfPart( 'state', 'left', '0px' );
60
- this.itemOffsetLeft = 0;
168
+ if( this.drawn ){
169
+ if( this.options.autoLabel ){
170
+ this.autoLabel();
61
171
  }
62
- else{
63
- this.setStyleOfPart( 'label', 'height', (this.pxPerHour*24)+'px' );
64
- }
65
- this.setStyleOfPart( 'state', 'height', (this.pxPerHour*24)+'px' );
172
+ this.drawHours();
66
173
  }
67
174
  this.base();
68
175
  },
69
176
 
70
- /** = Description
71
- * Refreshes the hour labels.
72
- *
73
- **/
74
- refreshLabel: function(){
75
- var hour = 1,
76
- hours = [],
77
- rowHeight = this.pxPerHour;
78
- lineHeight = Math.round(this.pxPerHour/2);
79
- for(; hour < 24; hour++){
80
- hours.push('<div style="line-height:'+rowHeight+'px;height:'+rowHeight+'px;top:'+Math.round((hour*rowHeight)-lineHeight)+'px" class="timesheet_hours_row">'+hour+':00</div>');
177
+ // set the timezone offset (in seconds)
178
+ setTzOffset: function( _tzOffset ){
179
+ this.options.tzOffset = _tzOffset;
180
+ this.refresh();
181
+ },
182
+
183
+ // set the start timestamp of the timesheet
184
+ setTimeStart: function( _timeStart ){
185
+ this.options.timeStart = _timeStart;
186
+ this.refresh();
187
+ },
188
+
189
+ // set the end timestamp of the timesheet
190
+ setTimeEnd: function( _timeEnd ){
191
+ this.options.timeEnd = _timeEnd;
192
+ this.refresh();
193
+ },
194
+
195
+ // sets the range of timestams of the timesheet
196
+ setTimeRange: function( _timeRange ){
197
+ if( (_timeRange instanceof Array) && (_timeRange.length === 2) ){
198
+ this.setTimeStart( _timeRange[0] );
199
+ this.setTimeEnd( _timeRange[1] );
200
+ }
201
+ else if(
202
+ (_timeRange instanceof Object) &&
203
+ (_timeRange['timeStart'] !== undefined) &&
204
+ (_timeRange['timeEnd'] !== undefined)
205
+ ){
206
+ this.setTimeStart( _timeRange.timeStart );
207
+ this.setTimeEnd( _timeRange.timeEnd );
81
208
  }
82
- this.markupElemIds && this.markupElemIds.label && ELEM.setHTML(this.markupElemIds.label,hours.join(''));
83
- this.refreshState();
84
209
  },
85
210
 
86
- /** = Description
87
- * Refreshes the lines which mark hours and half-hours.
88
- *
89
- **/
90
- refreshState: function(){
91
- var line = 0,
92
- lines = [],
93
- lineHeight = Math.round(this.pxPerHour/2);
94
- for(; line < 48; line++){
95
- lines.push('<div style="top:'+(line*lineHeight)+'px" class="timesheet_lines_row'+(line%2)+'"></div>');
211
+ // sets the timestamp of the timesheet
212
+ setDate: function( _date ){
213
+ var
214
+ _range = (this.options.timeEnd - this.options.timeStart),
215
+ _newTimeRange = [
216
+ _date,
217
+ _date + _range
218
+ ];
219
+ this.setTimeRange( _newTimeRange );
220
+ this.refresh();
221
+ },
222
+
223
+ // draw decorations
224
+ drawSubviews: function(){
225
+ this.drawHours();
226
+ var
227
+ _options = this.options,
228
+ _minDuration = Math.round(3600/_options.notchesPerHour),
229
+ _dummyValue = {
230
+ // id: 'new',
231
+ label: '',
232
+ start: 0,
233
+ duration: _minDuration,
234
+ // locked: false,
235
+ color: '#cc0000'
236
+ };
237
+ this.dragPreviewRect = this.rectFromValue({start:_options.timeStart,duration:_minDuration});
238
+ this.minDuration = _minDuration;
239
+ this.dragPreview = HTimeSheetItem.nu(
240
+ this.dragPreviewRect,
241
+ this, {
242
+ value: _dummyValue,
243
+ visible: false,
244
+ displayTime: this.options.itemDisplayTime
245
+ }
246
+ );
247
+ this.dragPreview.setStyleOfPart('state','color','#fff');
248
+ },
249
+
250
+ // event listener for clicks, simulates double clicks in case of not double click aware browser
251
+ click: function( x, y, b ){
252
+ var
253
+ prevClickTime = false,
254
+ notCreated = !this.clickCreated && !this.doubleClickCreated && !this.dragCreated;
255
+ // this.doubleClickSimCreated = false;
256
+ if( !this.startDragTime && this.prevClickTime ){
257
+ prevClickTime = this.prevClickTime;
258
+ }
259
+ else if (this.startDragTime){
260
+ prevClickTime = this.startDragTime;
261
+ }
262
+ if( notCreated && this.options.allowClickCreate ){
263
+ // console.log('click create');
264
+ this.clickCreate( x,y );
265
+ this.clickCreated = true;
266
+ this.doubleClickCreated = false;
267
+ this.prevClickTime = false;
268
+ }
269
+ else if( notCreated && this.options.allowDoubleClickCreate ){
270
+ var
271
+ currTime = new Date().getTime(),
272
+ timeDiff = prevClickTime?(currTime - prevClickTime):-1;
273
+ if( timeDiff > 150 && timeDiff < 500 && !this.doubleClickCreated ){
274
+ // console.log('click double create');
275
+ this.clickCreate( x, y );
276
+ this.clickCreated = false;
277
+ this.doubleClickCreated = true;
278
+ this.doubleClickSimCreated = true;
279
+ }
280
+ else {
281
+ this.doubleClickCreated = false;
282
+ }
283
+ this.prevClickTime = currTime;
284
+ }
285
+ else {
286
+ this.clickCreated = false;
287
+ this.doubleClickCreated = false;
288
+ this.prevClickTime = false;
96
289
  }
97
- this.markupElemIds && this.markupElemIds.label && ELEM.setHTML(this.markupElemIds.state,lines.join(''));
98
290
  },
99
- dragItem: false,
100
291
 
101
- /** = Description
102
- * Creates an item into timesheet with default label 'New Item'.
103
- *
104
- * = Parameters
105
- * +origY+:: Y coordinate of the new item.
106
- *
107
- **/
108
- createItem: function(origY){
109
- var _lineHeight = Math.round(this.pxPerHour/2);
110
- origY = Math.floor( origY / _lineHeight )*_lineHeight;
111
- var maxY = _lineHeight*48,
112
- lineHeight = Math.round(this.pxPerHour/2);
113
- if(origY>maxY){
114
- origY = maxY;
292
+ // creates an item on click
293
+ clickCreate: function(x,y){
294
+ var
295
+ _startTime = this.pxToTime( y-this.pageY() ),
296
+ _endTime = _startTime + this.minDuration;
297
+ this.refreshDragPreview( _startTime, _endTime );
298
+ this.dragPreview.bringToFront();
299
+ this.dragPreview.show();
300
+ if( this.activateEditor( this.dragPreview ) ){
301
+ this.editor.createItem( HVM.clone( this.dragPreview.value ) );
302
+ }
303
+ else {
304
+ this.dragPreview.hide();
115
305
  }
116
- this.dragItem = this.createTimeSheetItem( { label: this.options.newItemLabel }, origY, lineHeight, 'create' );
117
306
  },
118
307
 
119
- /** = Description
120
- * Dragging is used to mark items on the timesheet.
121
- *
122
- * = Parameters
123
- * +x+:: x coordinate of the origin of drag
124
- * +y+:: y coordinate of the origin of drag
125
- *
126
- **/
127
- startDrag: function(x,y){
128
- this._startDragTime = new Date().getTime();
129
- this._startDragCoords = [x,y];
308
+ // event listener for double clicks
309
+ doubleClick: function(x,y){
310
+ this.prevClickTime = false;
311
+ this.doubleClickCreated = false;
312
+ var notCreated = !this.clickCreated && !this.doubleClickCreated && !this.doubleClickSimCreated && !this.dragCreated;
313
+ if( !this.options.allowDoubleClickCreate && this.options.allowClickCreate && notCreated ){
314
+ this.click(x,y);
315
+ }
316
+ else if ( this.options.allowDoubleClickCreate && !this.options.allowClickCreate && notCreated ){
317
+ // console.log('double click create');
318
+ this.clickCreate( x, y );
319
+ this.clickCreated = false;
320
+ this.doubleClickCreated = true;
321
+ }
322
+ else {
323
+ // console.log('no double click create');
324
+ this.clickCreated = false;
325
+ }
326
+ this.doubleClickSimCreated = false;
327
+ },
328
+
329
+ // update the preview area
330
+ refreshDragPreview: function( _startTime, _endTime ){
331
+ this.dragPreviewRect.setTop( this.timeToPx( _startTime, true ) );
332
+ this.dragPreviewRect.setBottom( this.timeToPx( _endTime, true ) );
333
+ if( this.dragPreviewRect.height < this.options.itemMinHeight ){
334
+ this.dragPreviewRect.setHeight( this.options.itemMinHeight );
335
+ }
336
+ this.dragPreview.drawRect();
337
+ this.dragPreview.value.start = _startTime;
338
+ this.dragPreview.value.duration = _endTime - _startTime;
339
+ this.dragPreview.refreshValue();
340
+ },
341
+
342
+ // drag & drop event listeners, used for dragging new timesheet items
343
+ startDrag: function( x, y, b ){
344
+ // console.log('st');
345
+ this._startDragY = y;
346
+ this.startDragTime = this.pxToTime( y-this.pageY() );
347
+ this.refreshDragPreview( this.startDragTime, this.startDragTime + this.minDuration );
348
+ this.dragPreview.bringToFront();
349
+ this.dragPreview.show();
130
350
  return true;
131
351
  },
132
352
 
133
- drag: function(x,y){
134
- if(((new Date().getTime()) - this._startDragTime) < 200){
135
- // only create when 200 ms has elapsed to enable clicking
136
- return true;
353
+ drag: function( x, y, b ){
354
+ // console.log('dr');
355
+ var
356
+ _dragTime = this.pxToTime( y-this.pageY() ),
357
+ _startTime,
358
+ _endTime;
359
+ if( _dragTime < this.startDragTime ){
360
+ _startTime = _dragTime;
361
+ _endTime = this.startDragTime;
137
362
  }
138
- if(this._startDragCoords[0]!==x && this._startDragCoords[1]!==y){
139
- this.createItem(this._startDragCoords[1]-this.pageY());
140
- EVENT.startDragging( this.dragItem );
141
- return true;
363
+ else {
364
+ _endTime = _dragTime;
365
+ _startTime = this.startDragTime;
142
366
  }
367
+ this.refreshDragPreview( _startTime, _endTime );
368
+ return true;
143
369
  },
144
370
 
145
- click: function(x,y){
146
- // deactivate all items
147
- EVENT.changeActiveControl(this);
148
- return true;
371
+ endDrag: function( x, y, b ){
372
+ // console.log('ed');
373
+ var
374
+ _dragTime = this.pxToTime( y-this.pageY() ),
375
+ _minDistanceSatisfied = Math.abs( this._startDragY - y ) >= this.options.minDragSize;
376
+ // if( this.options.allowClickCreate ){
377
+ // _minDistanceSatisfied = true;
378
+ // }
379
+ this.dragPreview.hide();
380
+ if( _dragTime !== this.startDragTime ){
381
+ if( _minDistanceSatisfied ){
382
+ if( this.activateEditor( this.dragPreview ) ){
383
+ // console.log('drag create');
384
+ this.dragCreated = true;
385
+ this.editor.createItem( HVM.clone( this.dragPreview.value ) );
386
+ return true;
387
+ }
388
+ }
389
+ this.dragCreated = false;
390
+ }
391
+ else {
392
+ this.dragCreated = false;
393
+ this.clickCreated = false;
394
+ this.startDragTime = false;
395
+ this.click( x, y, b );
396
+ return true;
397
+ }
398
+ return false;
149
399
  },
150
400
 
151
- listItemViews: false,
401
+ // a resize triggers refresh, of which the important part is refreshValue, which triggers redraw of the time sheet items
402
+ resize: function(){
403
+ this.base();
404
+ this.refresh();
405
+ },
406
+
407
+ // snaps the time to grid
408
+ snapTime: function( _timeSecs ){
409
+ var
410
+ _options = this.options,
411
+ _pxDate = new Date( Math.round(_timeSecs) * 1000 ),
412
+ _snapSecs = Math.round( 3600 / _options.notchesPerHour ),
413
+ _halfSnapSecs = _snapSecs * 0.5,
414
+ _hourSecs = (_pxDate.getUTCMinutes()*60) + _pxDate.getUTCSeconds(),
415
+ _remSecs = _hourSecs % _snapSecs;
416
+ if( _remSecs > _halfSnapSecs ){
417
+ _timeSecs += _snapSecs-_remSecs;
418
+ }
419
+ else {
420
+ _timeSecs -= _remSecs;
421
+ }
422
+ return _timeSecs;
423
+ },
424
+
425
+ // snaps the pixel to grid
426
+ snapPx: function( _px ){
427
+ var
428
+ _timeSecs = this.pxToTime( _px );
429
+ _timeSecs = this.snapTime( _timeSecs );
430
+ return this.timeToPx( _timeSecs );
431
+ },
432
+
433
+ // activates the editor; _item is the timesheet item to edit
434
+ activateEditor: function( _item ){
435
+ if( this['editor'] ){
436
+ var _editor = this.editor;
437
+ _editor.setTimeSheetItem( _item );
438
+ _item.bringToFront();
439
+ _editor.bringToFront();
440
+ _editor.show();
441
+ return true;
442
+ }
443
+ return false;
444
+ },
152
445
 
153
446
  /** = Description
154
447
  * Sets the editor given as parameter as the editor of instance.
@@ -160,133 +453,430 @@ HTimeSheet = HControl.extend({
160
453
  setEditor: function( _editor ){
161
454
  this.editor = _editor;
162
455
  },
163
-
164
- /** = Description
165
- * Returns HRect the size of given parameters and suitable for timesheet.
166
- *
167
- * = Parameters
168
- * +_origY+:: Y coordinate.
169
- * +_lineHeight+:: The height of item on time sheet.
170
- *
171
- **/
172
- createItemRect: function(_origY, _lineHeight){
173
- var _left = this.itemOffsetLeft+1,
174
- _top = _origY+1,
175
- _right = this.rect.width - this.itemOffsetRight - 1,
176
- _bottom = _origY + _lineHeight - 2;
177
- if( (_top - _bottom) < this.options.itemMinHeight ){
178
- _bottom = _top + this.options.itemMinHeight;
179
- }
180
- return HRect.nu( _left, _top, _right, _bottom );
181
- },
182
456
 
183
457
  /** = Description
184
458
  * Destructor; destroys the editor first and commences inherited die.
185
459
  *
186
460
  **/
187
461
  die: function(){
188
- this.editor.die();
462
+ this.editor && this.editor.die();
463
+ this.editor = null;
189
464
  this.base();
190
465
  },
191
466
 
192
- /** = Description
193
- * Returns a new time sheet item control. By default returns an instance
194
- * of HTimeSheetItem. Extend to use custom time sheet controls customized
195
- * for application specific purposes.
196
- *
197
- * = Parameters
198
- * *_value*:: A value object for the item.
199
- * *_top*:: Top position, 0 by default.
200
- * *_height*:: Height, same as half of +#pxPerHour+ by default.
201
- * *_drogMode*:: Dragging mode. 'normal' or 'create'. Is 'normal' by default.
202
- *
203
- **/
204
- createTimeSheetItem: function( _value, _top, _height, _dragMode ) {
205
- if(_dragMode===undefined){
206
- _dragMode = 'normal';
467
+ // converts pixels to time
468
+ pxToTime: function( _px, _noSnap ){
469
+ var
470
+ _options = this.options,
471
+ _timeStart = _options.timeStart,
472
+ _timeEnd = _options.timeEnd,
473
+ _timeRange = _timeEnd - _timeStart,
474
+ _itemOptions = this.itemOptions,
475
+ _top = _itemOptions.offsetTop+1,
476
+ _height = _itemOptions.height,
477
+ _pxPerSec = _height / _timeRange,
478
+ _timeSecs;
479
+ _px -= _top;
480
+ _timeSecs = _timeStart + ( _px / _pxPerSec );
481
+ if( !_noSnap ){
482
+ _timeSecs = this.snapTime( _timeSecs );
483
+ }
484
+ if( _timeSecs > _options.timeEnd ){
485
+ _timeSecs = _options.timeEnd;
486
+ }
487
+ else if ( _timeSecs < _options.timeStart ) {
488
+ _timeSecs = _options.timeStart;
489
+ }
490
+ return Math.round( _timeSecs );
491
+ },
492
+
493
+ // converts time to pixels
494
+ timeToPx: function( _time, _snap ){
495
+
496
+ if( _snap ){
497
+ _time = this.snapTime( _time );
498
+ }
499
+
500
+ var
501
+ _options = this.options,
502
+ _timeStart = _options.timeStart,
503
+ _timeEnd = _options.timeEnd;
504
+
505
+ if( _time < _timeStart ){
506
+ _time = _timeStart;
507
+ }
508
+ if( _time > _timeEnd ){
509
+ _time = _timeEnd;
510
+ }
511
+
512
+ var
513
+ _timeRange = _timeEnd - _timeStart,
514
+ _itemOptions = this.itemOptions,
515
+ _top = _itemOptions.offsetTop,
516
+ _height = _itemOptions.height,
517
+ _pxPerSec = _height / _timeRange,
518
+ _timeSecs = _time - _timeStart,
519
+ _px = _top + ( _timeSecs * _pxPerSec );
520
+ return Math.round( _px );
521
+ },
522
+
523
+ // converts time to pixels for the rect
524
+ rectFromValue: function( _value ){
525
+ var
526
+ _topPx = this.timeToPx( _value.start ),
527
+ _bottomPx = this.timeToPx( _value.start + _value.duration ),
528
+ _leftPx = this.options.itemOffsetLeft,
529
+ _rightPx = this.rect.width - this.options.itemOffsetRight - 2,
530
+ _rect;
531
+ if( _topPx === 'underflow' ){
532
+ _topPx = _itemOptions.offsetTop;
533
+ }
534
+ else if( _topPx === 'overflow' ){
535
+ return false;
536
+ }
537
+ if( _bottomPx === 'underflow' ){
538
+ return false;
207
539
  }
208
- if(_top===undefined){
209
- _top = 0;
540
+ else if( _bottomPx === 'overflow' ){
541
+ _bottomPx = _itemOptions.offsetTop + _itemOptions.height;
210
542
  }
211
- if(_height===undefined){
212
- _height = Math.round(this.pxPerHour/2);
543
+ _rect = HRect.nu( _leftPx, _topPx, _rightPx, _bottomPx );
544
+ if( _rect.height < this.options.itemMinHeight ){
545
+ _rect.setHeight( this.options.itemMinHeight );
213
546
  }
547
+ return _rect;
548
+ },
549
+
550
+ // creates a single time sheet item component
551
+ createTimeSheetItem: function( _value ){
214
552
  var
215
- _label = _value['label'],
553
+ _rect = this.rectFromValue( _value ),
554
+ _item;
555
+ if( _rect === false ){
556
+ return false;
557
+ }
216
558
  _item = HTimeSheetItem.nu(
217
- this.createItemRect( _top, _height ),
559
+ _rect,
218
560
  this, {
219
- label: _label,
220
561
  value: _value,
562
+ displayTime: this.options.itemDisplayTime,
221
563
  events: {
222
- draggable: true
564
+ draggable: true,
565
+ // click: true,
566
+ doubleClick: true
223
567
  }
224
568
  }
225
569
  );
226
- _item.dragMode = _dragMode;
227
570
  return _item;
228
571
  },
229
-
230
- /** = Description
231
- * Redraws and refreshes the values on timesheet.
232
- *
233
- **/
234
- refreshValue: function(){
572
+
573
+ // calls createTimeSheetItem with each value of the timesheet value array
574
+ drawTimeSheetItems: function(){
575
+
235
576
  var
236
577
  _data = this.value,
237
- i;
238
- if(this.listItemViews === false){
239
- this.listItemViews = [];
578
+ i = 0,
579
+ _value,
580
+ _item,
581
+ _items = this.timeSheetItems;
582
+
583
+ if((_data instanceof Array) && (_data.length > 0)){
584
+ for( ; i < _data.length; i++){
585
+ _value = _data[i];
586
+ _item = this.createTimeSheetItem( _value );
587
+ if(_item){
588
+ _items.push( _item );
589
+ }
590
+ }
591
+ }
592
+ },
593
+
594
+
595
+ /** =Description
596
+ * Create a new timeSheetItems if it hasn't been done already,
597
+ * otherwise destroy the items of the old one before proceeding.
598
+ **/
599
+ _initTimeSheetItems: function(){
600
+ if(this.timeSheetItems === undefined){
601
+ this.timeSheetItems = [];
240
602
  }
241
- else if(this.listItemViews.length > 0){
242
- for( i=0; i<this.listItemViews.length; i++){
243
- this.listItemViews[i].die();
603
+ else if(this.timeSheetItems.length > 0){
604
+ for( var i=0; i<this.timeSheetItems.length; i++){
605
+ this.timeSheetItems[i].die();
244
606
  }
245
- this.listItemViews = [];
607
+ this.timeSheetItems = [];
246
608
  }
247
- if(_data instanceof Array && _data.length > 0){
248
- var
249
- _value,
250
- _item;
251
- for( i=0; i<_data.length; i++){
252
- _value = _data[i];
253
- _item = this.createTimeSheetItem( _value );
254
- this.listItemViews.push( _item );
609
+ },
610
+
611
+ // checks if i overlaps j
612
+ // todo: refactor, it's a bit messy
613
+ _doesOverlap: function( i, j, _overlaps, _rectSelf, _items ){
614
+ if( _rectSelf === undefined ) {
615
+ _rectSelf = this.timeSheetItems[i].rect;
616
+ }
617
+ if( _items === undefined ){
618
+ _items = this.timeSheetItems;
619
+ }
620
+ var
621
+ _isntSame = (i !== j),
622
+ _isntListedSelf = (_overlaps.indexOf(i)===-1),
623
+ _isntListedOther = (_overlaps.indexOf(j)===-1),
624
+ _rectOther = _items[j].rect;
625
+ if( !_isntSame ){ return false; }
626
+ if( !_isntListedSelf ){ return false; }
627
+ if( _isntListedOther ){
628
+ if( _rectOther.intersects( _rectSelf, 1, 1 ) || _rectSelf.intersects( _rectOther, 1, 1 ) ){
629
+ return true;
255
630
  }
256
631
  }
632
+ return false;
633
+ },
634
+
635
+
636
+ // finds the index in the array which contains most sequential items
637
+ _findLargestSequence: function( _arr ){
257
638
  var
639
+ i = 1,
640
+ _index = 0,
641
+ _length = 1,
642
+ _maxLength = 1,
643
+ _bestIndex = 0;
644
+ for( ; i < _arr.length; i++ ){
645
+ // grow:
646
+ if( ( _arr[i] - _arr[i-1] === 1 ) && ( _index === i-_length ) ){
647
+ _length += 1;
648
+ }
649
+ // reset:
650
+ else {
651
+ _index = i;
652
+ _length = 1;
653
+ }
654
+ if( _length > _maxLength ){
655
+ _bestIndex = _index;
656
+ _maxLength = _length;
657
+ }
658
+ }
659
+ return [ _bestIndex, _maxLength ];
660
+ },
661
+
662
+ // find the amount of overlapping time sheet items
663
+ _findOverlapCount: function(){
664
+ var
665
+ _overlapCount,
666
+ _items = this._sortedTimeSheetItems(),
258
667
  _overlaps = [],
668
+ _maxOverlapCount = 0,
669
+ i = 0,
259
670
  j;
260
- for(i=0;i<this.listItemViews.length;i++){
261
- for(j=0;j<this.listItemViews.length;j++){
262
- if((i !== j) && (_overlaps.indexOf(i)===-1) && (_overlaps.indexOf(j)===-1)){
263
- if(this.listItemViews[i].rect.intersects(this.listItemViews[j].rect)){
264
- _overlaps.push(i);
265
- }
671
+ for( ; i < _items.length; i++ ){
672
+ _overlapCount = 0;
673
+ for( j = 0; j < _items.length; j++ ){
674
+ if( this._doesOverlap( i, j, _overlaps, _items[i].rect, _items ) ){
675
+ _overlapCount++;
676
+ _overlaps.push( j );
677
+ }
678
+ }
679
+ if( _overlapCount !== 0 ){
680
+ _overlaps.push( i );
681
+ if( _overlapCount > _maxOverlapCount ){
682
+ _maxOverlapCount = _overlapCount;
266
683
  }
267
684
  }
268
685
  }
686
+ return _maxOverlapCount;
687
+ },
688
+
689
+ // returns a sorted copy of the timeSheetItems array
690
+ _sortedTimeSheetItems: function( _sortFn ){
691
+ if( _sortFn === undefined ){
692
+ _sortFn = function(a,b){
693
+ return ( b.rect.height - a.rect.height);
694
+ };
695
+ }
269
696
  var
270
- _overlapCount = _overlaps.length+1,
271
- _overlapLefts = {},
272
- _itemWidth = ( this.rect.width - this.itemOffsetRight - this.itemOffsetLeft ),
273
- _width = Math.floor( _itemWidth / _overlapCount),
274
- _left = this.itemOffsetLeft;
275
- for(j=0;j<_overlapCount;j++){
276
- _overlapLefts[_overlaps[j]] = _left + (j*_width) + _width;
277
- }
278
- for(i=0;i<this.listItemViews.length;i++){
279
- if(_overlaps.indexOf(i)===-1){
280
- this.listItemViews[i].rect.setLeft(_left);
697
+ i = 0,
698
+ _arr = [],
699
+ _items = this.timeSheetItems;
700
+ for( ; i < _items.length; i++ ){
701
+ _arr.push( _items[i] );
702
+ }
703
+ _arr = _arr.sort(_sortFn);
704
+ return _arr;
705
+ },
706
+
707
+
708
+ // Optimizes the left and right position of each timesheet item to fit
709
+ _updateTimelineRects: function(){
710
+ var
711
+ // loop indexes:
712
+ i, j, k, l,
713
+ // amount of items ovelapping (max, actual number might be smaller after optimization)
714
+ _options = this.options,
715
+ _rect = this.rect,
716
+ _overlapCount = this._findOverlapCount(),
717
+ _availWidth = ( _rect.width - _options.itemOffsetRight - _options.itemOffsetLeft ),
718
+ _left = _options.itemOffsetLeft,
719
+ _width = Math.floor( _availWidth / (_overlapCount+1) ),
720
+ // get a list of timesheet items sorted by height (larger to smaller order)
721
+ _items = this._sortedTimeSheetItems(),
722
+ _itemCount = _items.length,
723
+ _itemRect,
724
+ _testRect,
725
+ _leftPos,
726
+ _rightPos,
727
+ _maxCol = 0,
728
+ _origCol,
729
+ _origColById = [],
730
+ _overlapCols,
731
+ _vacantCols,
732
+ _optimalColAndLength,
733
+ _col,
734
+ _colWidth,
735
+ _overlaps;
736
+
737
+ // No overlapping; nothing to do
738
+ if(_overlapCount === 0 ){
739
+ return false;
740
+ }
741
+
742
+ // move all items initially to one column right of the max overlaps
743
+ _leftPos = _left+(_width*(_overlapCount+1));
744
+ for( i = 0; i < _itemCount; i++ ){
745
+ _itemRect = _items[i].rect;
746
+ _itemRect.setLeft( _leftPos );
747
+ _itemRect.setRight( _leftPos+_width );
748
+ }
749
+
750
+ // optimize gaps by traversing each combination
751
+ // and finding the first column with no gaps
752
+ // the top-level loops three times in the following modes:
753
+ // 0: place items into the first vacant column and find the actual max columns
754
+ // 1: stretch columns to final column width
755
+ // 2: stretch columns to fit multiple columns, if space is vacant
756
+ for( l = 0; l < 3; l++ ){
757
+ for( i = 0; i < _itemCount; i++){
758
+ _itemRect = _items[i].rect;
759
+ // in mode 1, just the column widths are changed
760
+ if( l === 1 ){
761
+ _leftPos = _left + (_origColById[i]*_width);
762
+ _itemRect.setLeft( _leftPos );
763
+ _itemRect.setRight( _leftPos + _width );
764
+ continue;
765
+ }
766
+ _testRect = HRect.nu( _itemRect );
767
+
768
+ _overlapCols = [];
769
+ _vacantCols = [];
770
+
771
+ // test each column position (modes 0 and 2)
772
+ for( k = 0; k < _overlapCount+1; k++ ){
773
+ _leftPos = _left + (k*_width);
774
+ _testRect.setLeft( _leftPos );
775
+ _testRect.setRight( _leftPos + _width );
776
+ _overlaps = [];
777
+ for( j = 0; j < _itemCount; j++){
778
+ if( this._doesOverlap( i, j, _overlaps, _testRect, _items ) ){
779
+ if( _overlapCols.indexOf( k ) === -1 ){
780
+ _overlapCols.push( k );
781
+ }
782
+ }
783
+ }
784
+ if( _vacantCols.indexOf( k ) === -1 && _overlapCols.indexOf( k ) === -1 ){
785
+ _vacantCols.push( k );
786
+ }
787
+ }
788
+
789
+ // on the first run (mode 0) place items into the first column:
790
+ if( l === 0 ){
791
+ _origCol = _vacantCols[0];
792
+ _origColById.push( _origCol );
793
+ _leftPos = _left+(_origCol*_width);
794
+ _rightPos = _leftPos + _width;
795
+ if( _maxCol < _origCol ){
796
+ _maxCol = _origCol;
797
+ }
798
+ }
799
+ else {
800
+ // on mode 2: stretch to fill multiple column widths,
801
+ // because no item moving is done anymore at this stage, so we know what's free and what's not
802
+ if( _vacantCols.length > 0 ){
803
+ _optimalColAndLength = this._findLargestSequence( _vacantCols );
804
+ _col = _vacantCols[ _optimalColAndLength[0] ];
805
+ _colWidth = _optimalColAndLength[1];
806
+ }
807
+ else {
808
+ _origCol = _origColById[i];
809
+ _col = _origCol;
810
+ _colWidth = 1;
811
+ }
812
+ _leftPos = _left+(_col*_width);
813
+ _rightPos = _leftPos+(_colWidth*_width);
814
+ }
815
+ _itemRect.setLeft( _leftPos );
816
+ _itemRect.setRight( _rightPos );
281
817
  }
282
- else {
283
- this.listItemViews[i].rect.setLeft(_overlapLefts[i]);
818
+ // afther the first run (mode 0) we know the actual amount of columns, so adjust column width accordingly
819
+ if( l === 0 ){
820
+ _overlapCount = _maxCol;
821
+ _width = Math.floor( _availWidth / (_maxCol+1) );
284
822
  }
285
- this.listItemViews[i].rect.setWidth(_width);
286
823
  }
287
- for(i=0;i<this.listItemViews.length;i++){
288
- this.listItemViews[i].drawRect();
824
+ return true;
825
+ },
826
+
827
+ // draws the timeline (sub-routine of refreshValue)
828
+ drawTimeline: function(){
829
+ this._initTimeSheetItems();
830
+ this.drawTimeSheetItems();
831
+ this._updateTimelineRects();
832
+ // use the dimensions of the views
833
+ for( var i = 0; i < this.timeSheetItems.length; i++){
834
+ this.timeSheetItems[i].drawRect();
835
+ }
836
+ },
837
+
838
+ _sha: SHA.nu(8),
839
+
840
+ /*
841
+
842
+ Each item looks like this, any extra attributes are allowed,
843
+ but not used and not guaranteed to be preserved:
844
+
845
+ {
846
+ id: 'abcdef1234567890', // identifier, used in server to map id's
847
+ label: 'Event title', // label of event title
848
+ start: 1299248619, // epoch timestamp of event start
849
+ duration: 3600, // duration of event in seconds
850
+ locked: true, // when false, prevents editing the item
851
+ icons: [ 1, 3, 6 ], // icon id's to display
852
+ color: '#ffffff' // defaults to '#ffffff' if undefined
853
+ }
854
+
855
+
856
+ */
857
+ /** = Description
858
+ * Redraws and refreshes the values on timesheet.
859
+ *
860
+ **/
861
+ refreshValue: function(){
862
+
863
+ if(!this.itemOptions){
864
+ return;
865
+ }
866
+
867
+ // optimization that ensures the rect and previous value are different before redrawing
868
+ var
869
+ _valueStr = COMM.Values.encode( this.value ),
870
+ _rectStr = this.rect.toString(),
871
+ _timeRangeStr = this.options.timeStart+':'+this.options.timeEnd,
872
+ _shaSum = this._sha.strSHA1( _valueStr+_rectStr+_timeRangeStr );
873
+ if( this._prevSum !== _shaSum ){
874
+ // the preview timesheet item is hidden when new data arrives (including what it created)
875
+ this.dragPreview.hide();
876
+ this._prevSum = _shaSum;
877
+ this.drawTimeline();
289
878
  }
290
879
  }
880
+
291
881
  });
292
882