]>
Commit | Line | Data |
---|---|---|
6527f429 DM |
1 | /**\r |
2 | * @class SimpleTasks.controller.Tasks\r | |
3 | * @extends Ext.app.Controller\r | |
4 | */\r | |
5 | Ext.define('SimpleTasks.controller.Tasks', {\r | |
6 | extend: 'Ext.app.Controller',\r | |
7 | \r | |
8 | models: ['Task'],\r | |
9 | stores: ['Tasks'],\r | |
10 | \r | |
11 | views: [\r | |
12 | 'tasks.Grid',\r | |
13 | 'tasks.Form',\r | |
14 | 'tasks.EditWindow',\r | |
15 | 'tasks.DefaultTimeWindow',\r | |
16 | 'tasks.ReminderWindow',\r | |
17 | 'tasks.ContextMenu'\r | |
18 | ],\r | |
19 | \r | |
20 | refs: [\r | |
21 | {\r | |
22 | ref: 'listTree',\r | |
23 | selector: 'listTree'\r | |
24 | },\r | |
25 | {\r | |
26 | ref: 'taskForm',\r | |
27 | selector: 'taskForm'\r | |
28 | },\r | |
29 | {\r | |
30 | ref: 'taskGrid',\r | |
31 | selector: 'taskGrid'\r | |
32 | },\r | |
33 | {\r | |
34 | ref: 'tasksToolbar',\r | |
35 | selector: 'tasksToolbar'\r | |
36 | },\r | |
37 | {\r | |
38 | ref: 'taskEditWindow',\r | |
39 | selector: 'taskEditWindow',\r | |
40 | xtype: 'taskEditWindow',\r | |
41 | autoCreate: true\r | |
42 | },\r | |
43 | {\r | |
44 | ref: 'defaultTimeWindow',\r | |
45 | selector: 'defaultTimeWindow',\r | |
46 | xtype: 'defaultTimeWindow',\r | |
47 | autoCreate: true\r | |
48 | },\r | |
49 | {\r | |
50 | ref: 'reminderWindow',\r | |
51 | selector: 'reminderWindow',\r | |
52 | xtype: 'reminderWindow',\r | |
53 | forceCreate: true\r | |
54 | },\r | |
55 | {\r | |
56 | ref: 'contextMenu',\r | |
57 | selector: 'tasksContextMenu',\r | |
58 | xtype: 'tasksContextMenu',\r | |
59 | autoCreate: true\r | |
60 | }\r | |
61 | ],\r | |
62 | \r | |
63 | init: function() {\r | |
64 | var me = this;\r | |
65 | me.control(\r | |
66 | {\r | |
67 | 'taskForm textfield': {\r | |
68 | specialkey: me.handleSpecialKey\r | |
69 | },\r | |
70 | '[iconCls=tasks-new]': {\r | |
71 | click: me.focusTaskForm\r | |
72 | },\r | |
73 | '#delete-task-btn': {\r | |
74 | click: me.handleDeleteClick\r | |
75 | },\r | |
76 | '#delete-task-item': {\r | |
77 | click: me.handleDeleteClick\r | |
78 | },\r | |
79 | '#mark-complete-item': {\r | |
80 | click: me.markComplete\r | |
81 | },\r | |
82 | '#mark-complete-btn': {\r | |
83 | click: me.markComplete\r | |
84 | },\r | |
85 | '#mark-active-item': {\r | |
86 | click: me.markActive\r | |
87 | },\r | |
88 | '#mark-active-btn': {\r | |
89 | click: me.markActive\r | |
90 | },\r | |
91 | '#show-all-btn': {\r | |
92 | click: me.filterAll\r | |
93 | },\r | |
94 | '#show-active-btn': {\r | |
95 | click: me.filterActive\r | |
96 | },\r | |
97 | '#show-complete-btn': {\r | |
98 | click: me.filterComplete\r | |
99 | },\r | |
100 | '#edit-task-item': {\r | |
101 | click: me.handleEditItemClick\r | |
102 | },\r | |
103 | 'taskGrid': {\r | |
104 | recordedit: me.updateTask,\r | |
105 | deleteclick: me.handleDeleteIconClick,\r | |
106 | editclick: me.handleEditIconClick,\r | |
107 | reminderselect: me.setReminder,\r | |
108 | itemmouseenter: me.showActions,\r | |
109 | itemmouseleave: me.hideActions,\r | |
110 | selectionchange: me.toggleButtons,\r | |
111 | columnresize: me.syncTaskFormFieldWidth,\r | |
112 | itemcontextmenu: me.showContextMenu\r | |
113 | },\r | |
114 | 'tasksToolbar': {\r | |
115 | afterrender: me.initShowAll\r | |
116 | },\r | |
117 | 'taskEditWindow [name=has_reminder]': {\r | |
118 | change: me.toggleReminderFields\r | |
119 | },\r | |
120 | '#cancel-task-edit-btn': {\r | |
121 | click: me.hideEditWindow\r | |
122 | },\r | |
123 | '#save-task-edit-btn': {\r | |
124 | click: me.handleSaveTaskClick\r | |
125 | },\r | |
126 | 'taskEditWindow [name=reminder_date]': {\r | |
127 | change: me.syncReminderField\r | |
128 | },\r | |
129 | 'taskEditWindow [name=reminder_time]': {\r | |
130 | change: me.syncReminderField\r | |
131 | },\r | |
132 | '#toggle-complete-btn': {\r | |
133 | click: me.toggleCompleteField\r | |
134 | },\r | |
135 | '#delete-task-window-btn': {\r | |
136 | click: me.deleteTaskAndCloseEditWindow\r | |
137 | },\r | |
138 | 'defaultTimeWindow [name=default_time]': {\r | |
139 | \r | |
140 | },\r | |
141 | '#cancel-default-time-edit-btn': {\r | |
142 | click: me.hideDefaultTimeWindow\r | |
143 | },\r | |
144 | '#save-default-time-btn': {\r | |
145 | click: me.saveDefaultTime\r | |
146 | },\r | |
147 | '[cls=snooze-btn]': {\r | |
148 | click: me.snooze\r | |
149 | },\r | |
150 | '[cls=dismiss-reminder-btn]': {\r | |
151 | click: me.dismissReminder\r | |
152 | }\r | |
153 | }\r | |
154 | );\r | |
155 | \r | |
156 | me.initReminderInterval();\r | |
157 | },\r | |
158 | \r | |
159 | /**\r | |
160 | * Handles a "specialkey" event on an field on the task form.\r | |
161 | * Creates a new task if the enter key is pressed.\r | |
162 | * @param {Ext.form.field.Text} field\r | |
163 | * @param {Ext.EventObject} e\r | |
164 | */\r | |
165 | handleSpecialKey: function(field, e) {\r | |
166 | if(e.getKey() === e.ENTER) {\r | |
167 | this.newTask();\r | |
168 | }\r | |
169 | },\r | |
170 | \r | |
171 | /**\r | |
172 | * Creates a new task based on the data currently contained in the task form.\r | |
173 | * Saves the new task to the server and adds it to the task list view.\r | |
174 | */\r | |
175 | newTask: function() {\r | |
176 | var me = this,\r | |
177 | form = me.getTaskForm(),\r | |
178 | basicForm = form.getForm(),\r | |
179 | formEl = form.getEl(),\r | |
180 | titleField = form.getForm().findField('title'),\r | |
181 | task = Ext.create('SimpleTasks.model.Task');\r | |
182 | \r | |
183 | // require title field to have a value\r | |
184 | if(!titleField.getValue()) {\r | |
185 | return;\r | |
186 | }\r | |
187 | \r | |
188 | // update the new task record with the data from the form.\r | |
189 | basicForm.updateRecord(task);\r | |
190 | \r | |
191 | // try to blur all of this form's items to make sure that the user can't type into a field while saving\r | |
192 | form.items.each(function(item) {\r | |
193 | var inputEl = item.getEl().down('input');\r | |
194 | if(inputEl) {\r | |
195 | inputEl.blur();\r | |
196 | }\r | |
197 | });\r | |
198 | \r | |
199 | // mask the form element while saving\r | |
200 | formEl.mask('saving . . .');\r | |
201 | // save the task to the server\r | |
202 | task.save({\r | |
203 | success: function(task, operation) {\r | |
204 | me.getTasksStore().add(task);\r | |
205 | me.refreshListTree();\r | |
206 | me.getTasksStore().sort();\r | |
207 | titleField.reset();\r | |
208 | titleField.focus();\r | |
209 | formEl.unmask();\r | |
210 | },\r | |
211 | failure: function(task, operation) {\r | |
212 | var error = operation.getError(),\r | |
213 | msg = Ext.isObject(error) ? error.status + ' ' + error.statusText : error;\r | |
214 | \r | |
215 | Ext.MessageBox.show({\r | |
216 | title: 'Add Task Failed',\r | |
217 | msg: msg,\r | |
218 | icon: Ext.Msg.ERROR,\r | |
219 | buttons: Ext.Msg.OK\r | |
220 | });\r | |
221 | formEl.unmask();\r | |
222 | }\r | |
223 | });\r | |
224 | },\r | |
225 | \r | |
226 | /**\r | |
227 | * Handles the task list's "recordedit" event.\r | |
228 | * Updates the task on the server whenever a task is updated using the task grid's cell editor\r | |
229 | * @param {SimpleTasks.model.Task} task The task record that was edited\r | |
230 | */\r | |
231 | updateTask: function(task) {\r | |
232 | var me = this;\r | |
233 | \r | |
234 | if (task.modified && task.modified.done === false) {\r | |
235 | task.set('reminder', null);\r | |
236 | }\r | |
237 | task.save({\r | |
238 | success: function(task, operation) {\r | |
239 | me.refreshListTree();\r | |
240 | me.getTasksStore().sort();\r | |
241 | },\r | |
242 | failure: function(task, operation) {\r | |
243 | var error = operation.getError(),\r | |
244 | msg = Ext.isObject(error) ? error.status + ' ' + error.statusText : error;\r | |
245 | \r | |
246 | Ext.MessageBox.show({\r | |
247 | title: 'Update Task Failed',\r | |
248 | msg: msg,\r | |
249 | icon: Ext.Msg.ERROR,\r | |
250 | buttons: Ext.Msg.OK\r | |
251 | });\r | |
252 | }\r | |
253 | });\r | |
254 | },\r | |
255 | \r | |
256 | /**\r | |
257 | * Handles a click on a delete icon in the task grid.\r | |
258 | * @param {Ext.grid.View} view\r | |
259 | * @param {Number} rowIndex\r | |
260 | * @param {Number} colIndex\r | |
261 | * @param {Ext.grid.column.Action} column\r | |
262 | * @param {EventObject} e\r | |
263 | */\r | |
264 | handleDeleteIconClick: function(view, rowIndex, colIndex, column, e) {\r | |
265 | this.deleteTask(this.getTasksStore().getAt(rowIndex));\r | |
266 | },\r | |
267 | \r | |
268 | /**\r | |
269 | * Handles a click on the "delete task" button or context menu item\r | |
270 | * @param {Ext.button.Button} button\r | |
271 | * @param {Ext.EventObject} e\r | |
272 | */\r | |
273 | handleDeleteClick: function(button, e) {\r | |
274 | this.deleteTask(this.getTaskGrid().getSelectionModel().getSelection()[0]);\r | |
275 | },\r | |
276 | \r | |
277 | /**\r | |
278 | * Deletes the task from the server and updates the view.\r | |
279 | * @param {SimpleTasks.model.Task} task\r | |
280 | * @param {Function} successCallback A function to call after the task has been deleted successfully\r | |
281 | */\r | |
282 | deleteTask: function(task, successCallback) {\r | |
283 | var me = this;\r | |
284 | \r | |
285 | Ext.Msg.show({\r | |
286 | title: 'Delete Task?',\r | |
287 | msg: 'Are you sure you want to delete this task?',\r | |
288 | buttons: Ext.Msg.YESNO,\r | |
289 | fn: function(response) {\r | |
290 | if(response === 'yes') {\r | |
291 | task.erase({\r | |
292 | success: function(task, operation) {\r | |
293 | me.getTasksStore().remove(task);\r | |
294 | me.refreshListTree();\r | |
295 | if(successCallback) {\r | |
296 | successCallback();\r | |
297 | }\r | |
298 | },\r | |
299 | failure: function(task, operation) {\r | |
300 | var error = operation.getError(),\r | |
301 | msg = Ext.isObject(error) ? error.status + ' ' + error.statusText : error;\r | |
302 | \r | |
303 | Ext.MessageBox.show({\r | |
304 | title: 'Delete Task Failed',\r | |
305 | msg: msg,\r | |
306 | icon: Ext.Msg.ERROR,\r | |
307 | buttons: Ext.Msg.OK\r | |
308 | });\r | |
309 | }\r | |
310 | });\r | |
311 | }\r | |
312 | }\r | |
313 | });\r | |
314 | },\r | |
315 | \r | |
316 | /**\r | |
317 | * Refreshes the task grid's list filter, and the task counts in the list tree\r | |
318 | */\r | |
319 | refreshListTree: function() {\r | |
320 | // refresh the lists list view so that the task counts will be correct\r | |
321 | this.getListTree().refreshView();\r | |
322 | },\r | |
323 | \r | |
324 | /**\r | |
325 | * Handles a click on the "Edit" context menu item\r | |
326 | * @param {Ext.menu.Item} item\r | |
327 | * @param {EventObject} e\r | |
328 | */\r | |
329 | handleEditItemClick: function(item, e) {\r | |
330 | this.showEditWindow(this.getContextMenu().getTask());\r | |
331 | },\r | |
332 | \r | |
333 | /**\r | |
334 | * Handles a click on the "Edit Task" action column\r | |
335 | * @param {Ext.grid.View} view\r | |
336 | * @param {Number} rowIndex\r | |
337 | * @param {Number} colIndex\r | |
338 | * @param {Ext.grid.column.Action} column\r | |
339 | * @param {EventObject} e\r | |
340 | */\r | |
341 | handleEditIconClick: function(view, rowIndex, colIndex, column, e) {\r | |
342 | this.showEditWindow(view.getRecord(view.findTargetByEvent(e)));\r | |
343 | },\r | |
344 | \r | |
345 | /**\r | |
346 | * Handles the task grid's "selectionchange" event.\r | |
347 | * Disables or enables the task-related toolbar buttons depending on whether or not there is a selection.\r | |
348 | * @param {Ext.selection.RowModel} selModel\r | |
349 | * @param {SimpleTasks.model.Task[]} tasks\r | |
350 | */\r | |
351 | toggleButtons: function(selModel, tasks) {\r | |
352 | var deleteTaskBtn = Ext.getCmp('delete-task-btn'),\r | |
353 | markCompleteBtn = Ext.getCmp('mark-complete-btn'),\r | |
354 | markActiveBtn = Ext.getCmp('mark-active-btn');\r | |
355 | \r | |
356 | if(tasks.length === 0) {\r | |
357 | deleteTaskBtn.disable();\r | |
358 | markCompleteBtn.disable();\r | |
359 | markActiveBtn.disable();\r | |
360 | } else {\r | |
361 | deleteTaskBtn.enable();\r | |
362 | markCompleteBtn.enable();\r | |
363 | markActiveBtn.enable();\r | |
364 | }\r | |
365 | },\r | |
366 | \r | |
367 | /**\r | |
368 | * Handles a click on the "New Task" button or context menu item\r | |
369 | * focuses the title field on the new task form\r | |
370 | * @param {Ext.Component} component\r | |
371 | * @param {Ext.EventObject} e\r | |
372 | */\r | |
373 | focusTaskForm: function(component, e) {\r | |
374 | this.getTaskForm().query('[name=title]')[0].focus();\r | |
375 | },\r | |
376 | \r | |
377 | /**\r | |
378 | * Handles a click on the "Mark Complete" button or menu item\r | |
379 | * Sets the selected task's "done" field to true\r | |
380 | * @param {Ext.Component} component\r | |
381 | * @param {Ext.EventObject} e\r | |
382 | */\r | |
383 | markComplete: function(component, e) {\r | |
384 | var contextMenu = this.getContextMenu(),\r | |
385 | task = contextMenu.isVisible() ? contextMenu.getTask() : this.getTaskGrid().getSelectionModel().getSelection()[0];\r | |
386 | \r | |
387 | task.set('done', true);\r | |
388 | task.set('reminder', null);\r | |
389 | task.save({\r | |
390 | failure: function(task, operation) {\r | |
391 | var error = operation.getError(),\r | |
392 | msg = Ext.isObject(error) ? error.status + ' ' + error.statusText : error;\r | |
393 | \r | |
394 | Ext.MessageBox.show({\r | |
395 | title: 'Mark Complete Failed',\r | |
396 | msg: msg,\r | |
397 | icon: Ext.Msg.ERROR,\r | |
398 | buttons: Ext.Msg.OK\r | |
399 | });\r | |
400 | }\r | |
401 | });\r | |
402 | this.refreshListTree();\r | |
403 | },\r | |
404 | \r | |
405 | /**\r | |
406 | * Handles a click on the "Mark Active" button\r | |
407 | * Sets the selected task's "done" field to false\r | |
408 | * @param {Ext.button.Button} button\r | |
409 | * @param {Ext.EventObject} e\r | |
410 | */\r | |
411 | markActive: function(button, e) {\r | |
412 | var contextMenu = this.getContextMenu(),\r | |
413 | task = contextMenu.isVisible() ? contextMenu.getTask() : this.getTaskGrid().getSelectionModel().getSelection()[0];\r | |
414 | \r | |
415 | task.set('done', false);\r | |
416 | task.save({\r | |
417 | failure: function(task, operation) {\r | |
418 | var error = operation.getError(),\r | |
419 | msg = Ext.isObject(error) ? error.status + ' ' + error.statusText : error;\r | |
420 | \r | |
421 | Ext.MessageBox.show({\r | |
422 | title: 'Mark Active Failed',\r | |
423 | msg: msg,\r | |
424 | icon: Ext.Msg.ERROR,\r | |
425 | buttons: Ext.Msg.OK\r | |
426 | });\r | |
427 | }\r | |
428 | });\r | |
429 | this.refreshListTree();\r | |
430 | },\r | |
431 | \r | |
432 | /**\r | |
433 | * Handles the task grid columnresize event.\r | |
434 | * Synchronizes the width the column's associated form field with the width of the column\r | |
435 | * @param {Ext.grid.header.Container} headerContainer\r | |
436 | * @param {Ext.column.Column} column\r | |
437 | * @param {Number} width The new column width\r | |
438 | */\r | |
439 | syncTaskFormFieldWidth: function(headerContainer, column, width) {\r | |
440 | var field = this.getTaskForm().query('[name=' + column.dataIndex + ']')[0];\r | |
441 | if (field) {\r | |
442 | field.setWidth(width - 5);\r | |
443 | }\r | |
444 | },\r | |
445 | \r | |
446 | /**\r | |
447 | * Handles a click on the "Show All" button. Removes any filter on the done field so that all tasks will be displayed\r | |
448 | * @param {Ext.button.Button} button\r | |
449 | * @param {Ext.EventObject} e\r | |
450 | */\r | |
451 | filterAll: function(button, e) {\r | |
452 | this.getTasksStore().clearFilter();\r | |
453 | this.refreshListTree();\r | |
454 | },\r | |
455 | \r | |
456 | /**\r | |
457 | * Handles a click on the "Show Active" button. Filters tasks by done = false\r | |
458 | * @param {Ext.button.Button} button\r | |
459 | * @param {Ext.EventObject} e\r | |
460 | */\r | |
461 | filterActive: function(button, e) {\r | |
462 | this.getTasksStore().addFilter({\r | |
463 | property: 'done',\r | |
464 | value: false\r | |
465 | });\r | |
466 | this.refreshListTree();\r | |
467 | },\r | |
468 | \r | |
469 | /**\r | |
470 | * Handles a click on the "Show Complete" button. Filters tasks by done = true.\r | |
471 | * @param {Ext.button.Button} button\r | |
472 | * @param {Ext.EventObject} e\r | |
473 | */\r | |
474 | filterComplete: function(button, e) {\r | |
475 | this.getTasksStore().addFilter({\r | |
476 | property: 'done',\r | |
477 | value: true\r | |
478 | });\r | |
479 | this.refreshListTree();\r | |
480 | },\r | |
481 | \r | |
482 | /**\r | |
483 | * Handles the tasks toolbar's render event\r | |
484 | * Initializes the "Show All" Button to the pressed state\r | |
485 | * @param {SimpleTasks.view.Toolbar} toolbar\r | |
486 | */\r | |
487 | initShowAll: function(toolbar) {\r | |
488 | toolbar.getComponent('show-all-btn').toggle();\r | |
489 | },\r | |
490 | \r | |
491 | /**\r | |
492 | * Handles a mouseenter event on a task grid item.\r | |
493 | * Shows the item's action icons.\r | |
494 | * @param {Ext.grid.View} view\r | |
495 | * @param {SimpleTasks.model.Task} task\r | |
496 | * @param {HTMLElement} node\r | |
497 | * @param {Number} rowIndex\r | |
498 | * @param {Ext.EventObject} e\r | |
499 | */\r | |
500 | showActions: function(view, task, node, rowIndex, e) {\r | |
501 | var icons = Ext.fly(node).query('.x-action-col-icon');\r | |
502 | Ext.each(icons, function(icon){\r | |
503 | Ext.get(icon).removeCls('x-hidden');\r | |
504 | });\r | |
505 | },\r | |
506 | \r | |
507 | /**\r | |
508 | * Handles a mouseleave event on a task grid item.\r | |
509 | * Hides the item's action icons.\r | |
510 | * @param {Ext.grid.View} view\r | |
511 | * @param {SimpleTasks.model.Task} task\r | |
512 | * @param {HTMLElement} node\r | |
513 | * @param {Number} rowIndex\r | |
514 | * @param {Ext.EventObject} e\r | |
515 | */\r | |
516 | hideActions: function(view, task, node, rowIndex, e) {\r | |
517 | var icons = Ext.fly(node).query('.x-action-col-icon');\r | |
518 | Ext.each(icons, function(icon){\r | |
519 | Ext.get(icon).addCls('x-hidden');\r | |
520 | });\r | |
521 | },\r | |
522 | \r | |
523 | /**\r | |
524 | * Handles the task grid's itemcontextmenu event\r | |
525 | * Shows the task context menu.\r | |
526 | * @param {Ext.grid.View} view\r | |
527 | * @param {SimpleTasks.model.Task} task\r | |
528 | * @param {HTMLElement} node\r | |
529 | * @param {Number} rowIndex\r | |
530 | * @param {Ext.EventObject} e\r | |
531 | */\r | |
532 | showContextMenu: function(view, task, node, rowIndex, e) {\r | |
533 | var contextMenu = this.getContextMenu(),\r | |
534 | markCompleteItem = Ext.getCmp('mark-complete-item'),\r | |
535 | markActiveItem = Ext.getCmp('mark-active-item');\r | |
536 | \r | |
537 | if(task.get('done')) {\r | |
538 | markCompleteItem.hide();\r | |
539 | markActiveItem.show();\r | |
540 | } else {\r | |
541 | markCompleteItem.show();\r | |
542 | markActiveItem.hide();\r | |
543 | }\r | |
544 | contextMenu.setTask(task);\r | |
545 | contextMenu.showAt(e.getX(), e.getY());\r | |
546 | e.preventDefault();\r | |
547 | },\r | |
548 | \r | |
549 | /**\r | |
550 | * Shows the "Edit Task" window\r | |
551 | * @param {SimpleTasks.model.Task} task the task to edit\r | |
552 | */\r | |
553 | showEditWindow: function(task) {\r | |
554 | var me = this,\r | |
555 | taskEditWindow = me.getTaskEditWindow(),\r | |
556 | form = taskEditWindow.down('form').getForm(),\r | |
557 | reminderCheckbox = form.findField('has_reminder'),\r | |
558 | dateField = form.findField('reminder_date'),\r | |
559 | timeField = form.findField('reminder_time'),\r | |
560 | reminder = task.get('reminder');\r | |
561 | \r | |
562 | // Set the tasks title as the title of the edit window\r | |
563 | taskEditWindow.setTitle('Edit Task - ' + task.get('title'));\r | |
564 | // load the task data into the form\r | |
565 | taskEditWindow.down('form').loadRecord(task);\r | |
566 | // set the text of the toggle-complete button depending on the tasks "done" value\r | |
567 | Ext.getCmp('toggle-complete-btn').setText(task.get('done') ? 'Mark Active' : 'Mark Complete');\r | |
568 | taskEditWindow.show();\r | |
569 | \r | |
570 | if(task.get('reminder')) {\r | |
571 | // if the task already has a reminder set check the reminder checkbox and populate the reminder date and reminder time fields\r | |
572 | reminderCheckbox.setValue(true);\r | |
573 | dateField.setValue(Ext.Date.clearTime(reminder, true));\r | |
574 | timeField.setValue(Ext.Date.format(reminder, timeField.format)); \r | |
575 | } else {\r | |
576 | // if the task does not have a reminder set uncheck the reminder checkbox and set the reminder date and time fields to null\r | |
577 | reminderCheckbox.setValue(false);\r | |
578 | dateField.setValue(null);\r | |
579 | timeField.setValue(null); \r | |
580 | }\r | |
581 | \r | |
582 | if(task.get('done')) {\r | |
583 | // if the task is done disable the reminder checkbox (reminders cannot be set on completed tasks)\r | |
584 | reminderCheckbox.disable();\r | |
585 | } else {\r | |
586 | reminderCheckbox.enable();\r | |
587 | }\r | |
588 | \r | |
589 | },\r | |
590 | \r | |
591 | /**\r | |
592 | * Handles a click on the "Edit Task" window's cancel button\r | |
593 | * Hides the "Edit Task" window\r | |
594 | * @param {Ext.Button} button\r | |
595 | * @param {Ext.EventObject} e\r | |
596 | */\r | |
597 | hideEditWindow: function(button, e) {\r | |
598 | this.getTaskEditWindow().close();\r | |
599 | },\r | |
600 | \r | |
601 | /**\r | |
602 | * Handles the change event on the task edit window's "has_reminder" checkbox\r | |
603 | * Toggles the visibility of the reminder date and time fields\r | |
604 | * @param {Ext.form.field.Checkbox} checkbox\r | |
605 | * @param {Boolean} newValue\r | |
606 | * @param {Boolean} oldValue\r | |
607 | */\r | |
608 | toggleReminderFields: function(checkbox, newValue, oldValue) {\r | |
609 | var taskEditWindow = this.getTaskEditWindow(),\r | |
610 | windowEl = taskEditWindow.getEl(),\r | |
611 | form = taskEditWindow.down('form').getForm(),\r | |
612 | task = form.getRecord(),\r | |
613 | dateField = form.findField('reminder_date'),\r | |
614 | timeField = form.findField('reminder_time'),\r | |
615 | defaultTimeDate, defaultTimeMilliseconds;\r | |
616 | \r | |
617 | if(newValue) { // if the "has reminder" checkbox was checked\r | |
618 | windowEl.mask('loading');\r | |
619 | // get the default reminder time from the server or cache\r | |
620 | this.getDefaultReminderTime(function(defaultTime) {\r | |
621 | // enable the date and time fields\r | |
622 | dateField.enable();\r | |
623 | timeField.enable();\r | |
624 | if(!dateField.getValue()) {\r | |
625 | // if the reminder date has not already been set, default the reminder date to the task's due date\r | |
626 | // or the current date if the task does not have a due date\r | |
627 | dateField.setValue(task.get('due') || Ext.Date.clearTime(new Date()));\r | |
628 | timeField.setValue(defaultTime);\r | |
629 | }\r | |
630 | // set the form's hidden reminder field by combining the reminder date and time fields\r | |
631 | defaultTimeDate = timeField.getValue();\r | |
632 | defaultTimeMilliseconds = defaultTimeDate - Ext.Date.clearTime(defaultTimeDate, true);\r | |
633 | form.findField('reminder').setValue(new Date(dateField.getValue().getTime() + defaultTimeMilliseconds));\r | |
634 | windowEl.unmask();\r | |
635 | }, timeField.format);\r | |
636 | } else { // if the "has reminder" checkbox was unchecked\r | |
637 | // nullify the form's hidden reminder field and disable the reminder date and time fields\r | |
638 | form.findField('reminder').setValue(null);\r | |
639 | dateField.disable();\r | |
640 | timeField.disable();\r | |
641 | }\r | |
642 | },\r | |
643 | \r | |
644 | /**\r | |
645 | * Handles a click on the "Task Edit" window's save button.\r | |
646 | * @param {Ext.button.Button} button\r | |
647 | * @param {Ext.EventObject} e\r | |
648 | */\r | |
649 | handleSaveTaskClick: function(button, e) {\r | |
650 | this.saveEditWindow();\r | |
651 | },\r | |
652 | \r | |
653 | /**\r | |
654 | * Updates the task record with the form data from the edit window and saves the task to the server.\r | |
655 | */\r | |
656 | saveEditWindow: function() {\r | |
657 | var taskEditWindow = this.getTaskEditWindow(),\r | |
658 | listTree = this.getListTree(),\r | |
659 | windowEl = taskEditWindow.getEl(),\r | |
660 | form = taskEditWindow.down('form').getForm(),\r | |
661 | task = form.getRecord();\r | |
662 | \r | |
663 | if (form.isValid()) {\r | |
664 | windowEl.mask('saving');\r | |
665 | form.updateRecord(task);\r | |
666 | if (task.modified && task.modified.done === false) {\r | |
667 | task.set('reminder', null);\r | |
668 | }\r | |
669 | task.save({\r | |
670 | success: function(task, operation) {\r | |
671 | windowEl.unmask();\r | |
672 | taskEditWindow.close();\r | |
673 | listTree.view.refresh();\r | |
674 | },\r | |
675 | failure: function(task, operation) {\r | |
676 | var error = operation.getError(),\r | |
677 | msg = Ext.isObject(error) ? error.status + ' ' + error.statusText : error;\r | |
678 | \r | |
679 | Ext.MessageBox.show({\r | |
680 | title: 'Edit Task Failed',\r | |
681 | msg: msg,\r | |
682 | icon: Ext.Msg.ERROR,\r | |
683 | buttons: Ext.Msg.OK\r | |
684 | });\r | |
685 | windowEl.unmask();\r | |
686 | }\r | |
687 | });\r | |
688 | } else {\r | |
689 | Ext.Msg.alert('Invalid Data', 'Please correct form errors');\r | |
690 | }\r | |
691 | },\r | |
692 | \r | |
693 | /**\r | |
694 | * Syncronizes the value of the edit window's hidden reminder field whenever "reminder_date", or "reminder_time" is changed\r | |
695 | * @param {Ext.form.field.Picker} field the date or time picker\r | |
696 | * @param {Date} oldValue\r | |
697 | * @param {Date} newValue\r | |
698 | */\r | |
699 | syncReminderField: function(field, oldValue, newValue) {\r | |
700 | var form = this.getTaskEditWindow().down('form').getForm(),\r | |
701 | reminderField = form.findField('reminder'),\r | |
702 | date = form.findField('reminder_date').getValue(),\r | |
703 | timeDate = form.findField('reminder_time').getValue(),\r | |
704 | time, reminderDate;\r | |
705 | \r | |
706 | if(date && timeDate) {\r | |
707 | time = timeDate - Ext.Date.clearTime(timeDate, true);\r | |
708 | reminderDate = new Date(date.getTime() + time);\r | |
709 | reminderField.setValue(reminderDate); \r | |
710 | }\r | |
711 | },\r | |
712 | \r | |
713 | /**\r | |
714 | * Toggles the edit window's "done" field to true when the "Mark Complete" or "Mark Active" button on the edit window is clicked\r | |
715 | * @param {Ext.button.Button} button\r | |
716 | * @param {Ext.EventObject} e\r | |
717 | */\r | |
718 | toggleCompleteField: function(button, e) {\r | |
719 | var taskEditWindow = this.getTaskEditWindow(),\r | |
720 | doneField = taskEditWindow.down('form').getForm().findField('done');\r | |
721 | \r | |
722 | if(doneField.getValue() === 'true') {\r | |
723 | doneField.setValue(false);\r | |
724 | } else {\r | |
725 | doneField.setValue(true);\r | |
726 | }\r | |
727 | this.saveEditWindow();\r | |
728 | },\r | |
729 | \r | |
730 | /**\r | |
731 | * Handles a click on the "Delete" button on the edit window.\r | |
732 | * Deletes the task and closes the edit window\r | |
733 | * @param {Ext.button.Button} button\r | |
734 | * @param {Ext.EventObject} e\r | |
735 | */\r | |
736 | deleteTaskAndCloseEditWindow: function(button, e) {\r | |
737 | var me = this,\r | |
738 | taskEditWindow = me.getTaskEditWindow(),\r | |
739 | task = taskEditWindow.down('form').getRecord();\r | |
740 | \r | |
741 | me.deleteTask(task, function() {\r | |
742 | me.getTaskEditWindow().close();\r | |
743 | });\r | |
744 | },\r | |
745 | \r | |
746 | /**\r | |
747 | * Handles the Task Grid's `reminderselect` event\r | |
748 | * Sets a task's reminder\r | |
749 | * @param {SimpleTasks.model.Task} task the underlying record of the row that was clicked to show the reminder menu\r | |
750 | * @param {String|Number} value The value that was selected\r | |
751 | */\r | |
752 | setReminder: function(task, value) {\r | |
753 | var me = this,\r | |
754 | defaultTimeWindow = me.getDefaultTimeWindow(),\r | |
755 | defaultTimeField = defaultTimeWindow.down('form').getForm().findField('default_time'),\r | |
756 | defaultTimeDate, defaultTimeMilliseconds;\r | |
757 | \r | |
758 | me.getDefaultReminderTime(function(defaultTime) {\r | |
759 | if(value === 'set') {\r | |
760 | // if the user selected "Set Default Time", show the default time window.\r | |
761 | defaultTimeField.setValue(defaultTime);\r | |
762 | defaultTimeWindow.show();\r | |
763 | } else {\r | |
764 | if(Ext.isNumber(value)) {\r | |
765 | // if the user selected a reminder time, set the reminder by adding the user selected value to the due date\r | |
766 | defaultTimeDate = Ext.Date.parse(defaultTime, defaultTimeField.format);\r | |
767 | defaultTimeMilliseconds = defaultTimeDate - Ext.Date.clearTime(defaultTimeDate, true);\r | |
768 | task.set('reminder', new Date(task.get('due').getTime() - (value * 86400000) + defaultTimeMilliseconds));\r | |
769 | } else {\r | |
770 | // if the user selected "No Reminder" set the reminder field to null\r | |
771 | task.set('reminder', null);\r | |
772 | }\r | |
773 | task.save({\r | |
774 | failure: function(task, operation) {\r | |
775 | var error = operation.getError(),\r | |
776 | msg = Ext.isObject(error) ? error.status + ' ' + error.statusText : error;\r | |
777 | \r | |
778 | Ext.MessageBox.show({\r | |
779 | title: 'Set Reminder Failed',\r | |
780 | msg: msg,\r | |
781 | icon: Ext.Msg.ERROR,\r | |
782 | buttons: Ext.Msg.OK\r | |
783 | });\r | |
784 | }\r | |
785 | });\r | |
786 | }\r | |
787 | }, defaultTimeField.format);\r | |
788 | },\r | |
789 | \r | |
790 | /**\r | |
791 | * Gets the default reminder time and passes it to the callback function.\r | |
792 | * Retrieves default reminder time from the server on the first call, then caches it for future calls.\r | |
793 | * @param {Function} callback\r | |
794 | * @param {String} timeFormat, the time format used to encode the time: the time format of the destination TimeField\r | |
795 | */\r | |
796 | getDefaultReminderTime: function(callback, timeFormat) {\r | |
797 | var me = this,\r | |
798 | defaultReminderTime;\r | |
799 | \r | |
800 | if(me.defaultReminderTime) {\r | |
801 | callback(me.defaultReminderTime);\r | |
802 | } else {\r | |
803 | me.defaultReminderTime = Ext.Date.format(Ext.Date.parse('8', 'g'), timeFormat || "g:i A"); // the default time if no value can be retrieved from storage\r | |
804 | if (SimpleTasks.Settings.useLocalStorage) {\r | |
805 | defaultReminderTime = localStorage.getItem('SimpleTasks-defaultReminderTime');\r | |
806 | if (defaultReminderTime && Ext.Date.parse(defaultReminderTime, timeFormat)) {\r | |
807 | me.defaultReminderTime = defaultReminderTime;\r | |
808 | }\r | |
809 | callback(me.defaultReminderTime);\r | |
810 | } else {\r | |
811 | Ext.Ajax.request({\r | |
812 | url: 'php/config/read.php',\r | |
813 | params: {\r | |
814 | key: 'default.reminder.time'\r | |
815 | },\r | |
816 | success: function(response, options) {\r | |
817 | var responseData = Ext.decode(response.responseText);\r | |
818 | if(responseData.success && responseData.value && Ext.Date.parse(responseData.value, timeFormat)) {\r | |
819 | me.defaultReminderTime = responseData.value;\r | |
820 | }\r | |
821 | callback(me.defaultReminderTime);\r | |
822 | },\r | |
823 | failure: function(response, options) {\r | |
824 | callback(me.defaultReminderTime);\r | |
825 | }\r | |
826 | });\r | |
827 | }\r | |
828 | }\r | |
829 | },\r | |
830 | \r | |
831 | /**\r | |
832 | * Hides the default reminder time window when the cancel button is clicked\r | |
833 | * @param {Ext.button.Button} button\r | |
834 | * @param {Ext.EventObject} e\r | |
835 | */\r | |
836 | hideDefaultTimeWindow: function(button, e) {\r | |
837 | this.getDefaultTimeWindow().close();\r | |
838 | },\r | |
839 | \r | |
840 | /**\r | |
841 | * Saves the default reminder time to the server when the OK button is clicked\r | |
842 | * @param {Ext.button.Button} button\r | |
843 | * @param {Ext.EventObject} e\r | |
844 | */\r | |
845 | saveDefaultTime: function(button, e) {\r | |
846 | var me = this,\r | |
847 | defaultTimeWindow = me.getDefaultTimeWindow(),\r | |
848 | windowEl = defaultTimeWindow.getEl(),\r | |
849 | field = defaultTimeWindow.down('form').getForm().findField('default_time'),\r | |
850 | time = field.getRawValue();\r | |
851 | \r | |
852 | if (!field.isValid()) {\r | |
853 | return;\r | |
854 | }\r | |
855 | \r | |
856 | if (SimpleTasks.Settings.useLocalStorage) {\r | |
857 | localStorage.setItem('SimpleTasks-defaultReminderTime', time);\r | |
858 | me.defaultReminderTime = time;\r | |
859 | defaultTimeWindow.close();\r | |
860 | } else {\r | |
861 | windowEl.mask('saving');\r | |
862 | Ext.Ajax.request({\r | |
863 | url: 'php/config/update.php',\r | |
864 | params: {\r | |
865 | key: 'default.reminder.time',\r | |
866 | value: time\r | |
867 | },\r | |
868 | success: function(response, options) {\r | |
869 | var responseData = Ext.decode(response.responseText);\r | |
870 | \r | |
871 | if(responseData.success) {\r | |
872 | me.defaultReminderTime = time;\r | |
873 | defaultTimeWindow.close();\r | |
874 | } else {\r | |
875 | Ext.MessageBox.show({\r | |
876 | title: 'Set Default Time Failed',\r | |
877 | msg: responseData.message,\r | |
878 | icon: Ext.Msg.ERROR,\r | |
879 | buttons: Ext.Msg.OK\r | |
880 | });\r | |
881 | }\r | |
882 | windowEl.unmask();\r | |
883 | },\r | |
884 | failure: function(response, options) {\r | |
885 | Ext.MessageBox.show({\r | |
886 | title: 'Set Default Time Failed',\r | |
887 | msg: response.status + ' ' + response.statusText,\r | |
888 | icon: Ext.Msg.ERROR,\r | |
889 | buttons: Ext.Msg.OK\r | |
890 | });\r | |
891 | windowEl.unmask();\r | |
892 | }\r | |
893 | });\r | |
894 | }\r | |
895 | },\r | |
896 | \r | |
897 | /**\r | |
898 | * Initializes checking for tasks that have passed their reminder date at 10 second intervals.\r | |
899 | */\r | |
900 | initReminderInterval: function() {\r | |
901 | var me = this,\r | |
902 | now, reminderDate;\r | |
903 | \r | |
904 | setInterval(function() {\r | |
905 | now = new Date();\r | |
906 | me.getTasksStore().each(function(task) {\r | |
907 | reminderDate = task.get('reminder');\r | |
908 | if(reminderDate && reminderDate < now && !task.get('done')) {\r | |
909 | me.showReminderWindow(task);\r | |
910 | }\r | |
911 | });\r | |
912 | }, 10000);\r | |
913 | },\r | |
914 | \r | |
915 | /**\r | |
916 | * Shows the reminder window for a given task\r | |
917 | * @param {SimpleTasks.model.Task} task\r | |
918 | */\r | |
919 | showReminderWindow: function(task) {\r | |
920 | var reminderWindow = this.getReminderWindow(),\r | |
921 | reminderDetailsBox = reminderWindow.down('[cls=tasks-reminder-details]'),\r | |
922 | title = task.get('title');\r | |
923 | \r | |
924 | task.set('reminder', null);\r | |
925 | task.save({\r | |
926 | failure: function(task, operation) {\r | |
927 | var error = operation.getError(),\r | |
928 | msg = Ext.isObject(error) ? error.status + ' ' + error.statusText : error;\r | |
929 | \r | |
930 | Ext.MessageBox.show({\r | |
931 | title: 'Clear Reminder Failed',\r | |
932 | msg: msg,\r | |
933 | icon: Ext.Msg.ERROR,\r | |
934 | buttons: Ext.Msg.OK\r | |
935 | });\r | |
936 | }\r | |
937 | });\r | |
938 | reminderWindow.setTask(task);\r | |
939 | reminderWindow.setTitle('Reminder - ' + title);\r | |
940 | reminderDetailsBox.update({\r | |
941 | title: title,\r | |
942 | due: task.get('due')\r | |
943 | });\r | |
944 | reminderWindow.show();\r | |
945 | },\r | |
946 | \r | |
947 | \r | |
948 | /**\r | |
949 | * Handles a click on the snooze button on the reminder window.\r | |
950 | * Sets the task's reminder date to the current date plus snooze time selected\r | |
951 | * @param {Ext.button.Button} button\r | |
952 | * @param {Ext.EventObject} e\r | |
953 | */\r | |
954 | snooze: function(button, e) {\r | |
955 | var reminderWindow = button.findParentByType('window'),\r | |
956 | task = reminderWindow.getTask(),\r | |
957 | snoozeMilliseconds = reminderWindow.down('[name=snooze_time]').getValue() * 60000,\r | |
958 | reminderDate = new Date(new Date().getTime() + snoozeMilliseconds);\r | |
959 | \r | |
960 | task.set('reminder', reminderDate);\r | |
961 | task.save({\r | |
962 | failure: function(task, operation) {\r | |
963 | var error = operation.getError(),\r | |
964 | msg = Ext.isObject(error) ? error.status + ' ' + error.statusText : error;\r | |
965 | \r | |
966 | Ext.MessageBox.show({\r | |
967 | title: 'Set Reminder Failed',\r | |
968 | msg: msg,\r | |
969 | icon: Ext.Msg.ERROR,\r | |
970 | buttons: Ext.Msg.OK\r | |
971 | });\r | |
972 | }\r | |
973 | });\r | |
974 | reminderWindow.close();\r | |
975 | },\r | |
976 | \r | |
977 | /**\r | |
978 | * Handle's a click on the reminder window's dismiss button.\r | |
979 | * Hides the reminder window.\r | |
980 | * @param {Ext.button.Button} button\r | |
981 | * @param {Ext.EventObject} e\r | |
982 | */\r | |
983 | dismissReminder: function(button, e) {\r | |
984 | button.findParentByType('window').close();\r | |
985 | }\r | |
986 | \r | |
987 | });\r |