Upgrade to ExtJS 3.0.0 - Released 07/06/2009
[extjs.git] / examples / tasks / tasks.js
1 /*!
2  * Ext JS Library 3.0.0
3  * Copyright(c) 2006-2009 Ext JS, LLC
4  * licensing@extjs.com
5  * http://www.extjs.com/license
6  */
7 Ext.onReady(function(){\r
8     Ext.QuickTips.init();\r
9 \r
10     var xg = Ext.grid;\r
11     // turn off default shadows which look funky in air\r
12     xg.GridEditor.prototype.shadow = false;\r
13     \r
14     var conn = Ext.data.SqlDB.getInstance();\r
15         conn.open('tasks.db');\r
16     \r
17     // the main grid store\r
18     var taskStore = new TaskStore(conn);\r
19     \r
20     // Category store shared by category combos\r
21     var catStore = new CategoryStore();\r
22     \r
23         taskStore.load({\r
24                 callback: function(){\r
25                         // first time?\r
26                         if(taskStore.getCount() < 1){\r
27                                 Ext.Msg.confirm('Create Tasks?', 'Your database is currently empty. Would you like to insert some demo data?', \r
28                                         function(btn){\r
29                                                 if(btn == 'yes'){\r
30                                                         loadDemoTasks(taskStore);       \r
31                                                 }\r
32                                                 catStore.init(taskStore);\r
33                                         });\r
34                         }else{\r
35                                 catStore.init(taskStore);\r
36                         }\r
37                 }\r
38         });\r
39 \r
40     // custom event to notify when a new category is available\r
41     taskStore.on('newcategory', catStore.addCategory, catStore);\r
42 \r
43     // set of event handlers shared by combos to allow them to share\r
44     // the same local store\r
45     var comboEvents = {\r
46         focus: function(){\r
47             this.bindStore(catStore);\r
48         },\r
49         blur: function(c){\r
50             catStore.purgeListeners();\r
51         }\r
52     }\r
53 \r
54     var completeColumn = new CompleteColumn();\r
55 \r
56     // custom template for the grid header\r
57     var headerTpl = new Ext.Template(\r
58         '<table border="0" cellspacing="0" cellpadding="0" style="{tstyle}">',\r
59         '<thead><tr class="x-grid3-hd-row">{cells}</tr></thead>',\r
60         '<tbody><tr class="new-task-row">',\r
61             '<td><div id="new-task-icon"></div></td>',\r
62             '<td><div class="x-small-editor" id="new-task-title"></div></td>',\r
63             '<td><div class="x-small-editor" id="new-task-cat"></div></td>',\r
64             '<td><div class="x-small-editor" id="new-task-due"></div></td>',\r
65         '</tr></tbody>',\r
66         "</table>"\r
67     );\r
68 \r
69     var selections = new Ext.grid.RowSelectionModel();\r
70 \r
71     // The main grid in all its configuration option glory\r
72     var grid = new xg.EditorGridPanel({\r
73         id:'tasks-grid',\r
74         store: taskStore,\r
75         sm: selections,\r
76         clicksToEdit: 'auto',\r
77         enableColumnHide:false,\r
78         enableColumnMove:false,\r
79                 border:false,\r
80                 title:'All Tasks',\r
81                 iconCls:'icon-show-all',\r
82                 region:'center',\r
83                 \r
84         plugins: completeColumn,\r
85 \r
86         columns: [\r
87             completeColumn,\r
88             {\r
89                 header: "Task",\r
90                 width:400,\r
91                 sortable: true,\r
92                 dataIndex: 'title',\r
93                 id:'task-title',\r
94                 editor: new Ext.form.TextField({\r
95                     allowBlank: false\r
96                 })\r
97             },\r
98             {\r
99                 header: "Category",\r
100                 width:150,\r
101                 sortable: true,\r
102                 dataIndex: 'category',\r
103                 editor: new Ext.form.ComboBox({\r
104                     displayField: 'text',\r
105                     triggerAction: 'all',\r
106                     mode:'local',\r
107                     selectOnFocus:true,\r
108                     listClass:'x-combo-list-small',\r
109                     listeners: comboEvents\r
110                 })\r
111             },\r
112             {\r
113                 header: "Due Date",\r
114                 width: 150,\r
115                 sortable: true,\r
116                 renderer: Ext.util.Format.dateRenderer('D m/d/Y'),\r
117                 dataIndex: 'dueDate',\r
118                 groupRenderer: textDate(),\r
119                 groupName: 'Due',\r
120                 editor: new Ext.form.DateField({\r
121                     format : "m/d/Y"\r
122                 })\r
123             }\r
124         ],\r
125 \r
126         view: new Ext.grid.GroupingView({\r
127             forceFit:true,\r
128             ignoreAdd: true,\r
129             emptyText: 'No Tasks to display',\r
130 \r
131             templates: {\r
132                 header: headerTpl\r
133             },\r
134 \r
135             getRowClass : function(r){\r
136                 var d = r.data;\r
137                 if(d.completed){\r
138                     return 'task-completed';\r
139                 }\r
140                 if(d.dueDate && d.dueDate.getTime() < new Date().clearTime().getTime()){\r
141                     return 'task-overdue';\r
142                 }\r
143                 return '';\r
144             }\r
145         })\r
146     });\r
147 \r
148     var viewPanel = new Ext.Panel({\r
149         frame:true,\r
150         title: 'Views',\r
151         collapsible:true,\r
152         contentEl:'task-views',\r
153         titleCollapse: true\r
154     });\r
155     \r
156     var taskActions = new Ext.Panel({\r
157         frame:true,\r
158         title: 'Task Actions',\r
159         collapsible:true,\r
160         contentEl:'task-actions',\r
161         titleCollapse: true\r
162     });\r
163     \r
164     var groupActions = new Ext.Panel({\r
165         frame:true,\r
166         title: 'Task Grouping',\r
167         collapsible:true,\r
168         contentEl:'task-grouping',\r
169         titleCollapse: true\r
170     });\r
171     \r
172     var actionPanel = new Ext.Panel({\r
173         id:'action-panel',\r
174         region:'west',\r
175         split:true,\r
176         collapsible: true,\r
177         collapseMode: 'mini',\r
178         width:200,\r
179         minWidth: 150,\r
180         border: false,\r
181         baseCls:'x-plain',\r
182         items: [taskActions, viewPanel, groupActions]\r
183     });\r
184 \r
185     if(Ext.isAir){ // create AIR window\r
186         var win = new Ext.air.MainWindow({\r
187             layout:'border',\r
188             items: [actionPanel, grid],\r
189             title: 'Simple Tasks',\r
190             iconCls: 'icon-show-all'\r
191         }).render();\r
192         }else{\r
193         var viewport = new Ext.Viewport({\r
194             layout:'border',\r
195             items: [actionPanel, grid]\r
196         });\r
197     }\r
198 \r
199     var ab = actionPanel.body;\r
200     ab.on('mousedown', doAction, null, {delegate:'a'});\r
201         ab.on('click', Ext.emptyFn, null, {delegate:'a', preventDefault:true});\r
202 \r
203     grid.on('resize', syncFields);\r
204         grid.on('columnresize', syncFields);\r
205 \r
206     grid.on('afteredit', function(e){\r
207         if(e.field == 'category'){\r
208             catStore.addCategory(e.value);\r
209         }\r
210         if(e.field == taskStore.getGroupState()){\r
211             taskStore.applyGrouping();\r
212         }\r
213 \r
214     });\r
215 \r
216     grid.on('keydown', function(e){\r
217          if(e.getKey() == e.DELETE && !grid.editing){\r
218              actions['action-delete']();\r
219          }\r
220     });\r
221 \r
222     selections.on('selectionchange', function(sm){\r
223         var bd = taskActions.body, c = sm.getCount();\r
224         bd.select('li:not(#new-task)').setDisplayed(c > 0);\r
225         bd.select('span.s').setDisplayed(c > 1);\r
226     });\r
227 \r
228     // The fields in the grid's header\r
229     var ntTitle = new Ext.form.TextField({\r
230         renderTo: 'new-task-title',\r
231         emptyText: 'Add a task...'\r
232     });\r
233 \r
234     var ntCat = new Ext.form.ComboBox({\r
235         renderTo: 'new-task-cat',\r
236         disabled:true,\r
237         displayField: 'text',\r
238         triggerAction: 'all',\r
239         mode:'local',\r
240         selectOnFocus:true,\r
241         listClass:'x-combo-list-small',\r
242         listeners: comboEvents\r
243     });\r
244 \r
245     var ntDue = new Ext.form.DateField({\r
246         renderTo: 'new-task-due',\r
247         value: new Date(),\r
248         disabled:true,\r
249         format : "m/d/Y"\r
250     });\r
251 \r
252     // syncs the header fields' widths with the grid column widths\r
253     function syncFields(){\r
254         var cm = grid.getColumnModel();\r
255         ntTitle.setSize(cm.getColumnWidth(1)-2);\r
256         ntCat.setSize(cm.getColumnWidth(2)-4);\r
257         ntDue.setSize(cm.getColumnWidth(3)-4);\r
258     }\r
259     syncFields();\r
260 \r
261     var editing = false, focused = false, userTriggered = false;\r
262     var handlers = {\r
263         focus: function(){\r
264             focused = true;\r
265         },\r
266         blur: function(){\r
267             focused = false;\r
268             doBlur.defer(250);\r
269         },\r
270         specialkey: function(f, e){\r
271             if(e.getKey()==e.ENTER){\r
272                 userTriggered = true;\r
273                 e.stopEvent();\r
274                 f.el.blur();\r
275                 if(f.triggerBlur){\r
276                     f.triggerBlur();\r
277                 }\r
278             }\r
279         }\r
280     }\r
281     ntTitle.on(handlers);\r
282     ntCat.on(handlers);\r
283     ntDue.on(handlers);\r
284 \r
285     ntTitle.on('focus', function(){\r
286         focused = true;\r
287         if(!editing){\r
288             ntCat.enable();\r
289             ntDue.enable();\r
290             syncFields();\r
291             editing = true;\r
292         }\r
293     });\r
294 \r
295     // when a field in the add bar is blurred, this determines\r
296     // whether a new task should be created\r
297     function doBlur(){\r
298         if(editing && !focused){\r
299             var title = ntTitle.getValue();\r
300             if(!Ext.isEmpty(title)){\r
301                 taskStore.addTask({\r
302                     taskId: Task.nextId(),\r
303                     title: title,\r
304                     dueDate: ntDue.getValue()||'',\r
305                     description: '', // ???\r
306                     category: ntCat.getValue(),\r
307                     completed: false\r
308                 });\r
309                 ntTitle.setValue('');\r
310                 if(userTriggered){ // if the entered to add the task, then go to a new add automatically\r
311                     userTriggered = false;\r
312                     ntTitle.focus.defer(100, ntTitle);\r
313                 }\r
314             }\r
315             ntCat.disable();\r
316             ntDue.disable();\r
317             editing = false;\r
318         }\r
319     }\r
320         \r
321     var actions = {\r
322         'view-all' : function(){\r
323                 taskStore.applyFilter('all');\r
324                 grid.setTitle('All Tasks', 'icon-show-all');\r
325         },\r
326         \r
327         'view-active' : function(){\r
328                 taskStore.applyFilter(false);\r
329                 grid.setTitle('Active Tasks', 'icon-show-active');\r
330         },\r
331         \r
332         'view-complete' : function(){\r
333                 taskStore.applyFilter(true);\r
334                 grid.setTitle('Completed Tasks', 'icon-show-complete');\r
335         },\r
336         \r
337         'action-new' : function(){\r
338                 ntTitle.focus();\r
339         },\r
340         \r
341         'action-complete' : function(){\r
342                 selections.each(function(s){\r
343                         s.set('completed', true);\r
344                 });\r
345             taskStore.applyFilter();\r
346         },\r
347         \r
348         'action-active' : function(){\r
349                 selections.each(function(s){\r
350                         s.set('completed', false);\r
351                 });\r
352             taskStore.applyFilter();\r
353         },\r
354         \r
355         'action-delete' : function(){\r
356                 Ext.Msg.confirm('Confirm', 'Are you sure you want to delete the selected task(s)?', \r
357                 function(btn){\r
358                 if(btn == 'yes'){\r
359                         selections.each(function(s){\r
360                                         taskStore.remove(s);\r
361                                 });\r
362                 }\r
363             });\r
364         },\r
365         \r
366         'group-date' : function(){\r
367                 taskStore.groupBy('dueDate');\r
368         },\r
369         \r
370         'group-cat' : function(){\r
371                 taskStore.groupBy('category');\r
372         },\r
373         \r
374         'no-group' : function(){\r
375                 taskStore.clearGrouping();\r
376         }\r
377     };\r
378     \r
379     function doAction(e, t){\r
380         e.stopEvent();\r
381         actions[t.id]();\r
382     }\r
383     \r
384     \r
385     // generates a renderer function to be used for textual date groups\r
386     function textDate(){\r
387         // create the cache of ranges to be reused\r
388         var today = new Date().clearTime(true);\r
389         var year = today.getFullYear();\r
390         var todayTime = today.getTime();\r
391         var yesterday = today.add('d', -1).getTime();\r
392         var tomorrow = today.add('d', 1).getTime();\r
393         var weekDays = today.add('d', 6).getTime();\r
394         var lastWeekDays = today.add('d', -6).getTime();\r
395 \r
396         return function(date){\r
397             if(!date) {\r
398                 return '(No Date)';\r
399             }\r
400             var notime = date.clearTime(true).getTime();\r
401 \r
402             if (notime == todayTime) {\r
403                 return 'Today';\r
404             }\r
405             if(notime > todayTime){\r
406                 if (notime == tomorrow) {\r
407                     return 'Tomorrow';\r
408                 }\r
409                 if (notime <= weekDays) {\r
410                     return date.format('l');\r
411                 }\r
412             }else {\r
413                 if(notime == yesterday) {\r
414                         return 'Yesterday';\r
415                     }\r
416                     if(notime >= lastWeekDays) {\r
417                         return 'Last ' + date.format('l');\r
418                     }\r
419             }            \r
420             return date.getFullYear() == year ? date.format('D m/d') : date.format('D m/d/Y');\r
421        }\r
422     }\r
423 });\r
424 \r
425 /* This is used to laod some demo tasks if the task database is empty */\r
426 function loadDemoTasks(store){\r
427         var s = new Date();\r
428         // hardcoded demo tasks\r
429         store.addTask({taskId: Task.nextId(), title:'Start documentation of Ext 2.0', category:'Ext', description:'', dueDate: s.add('d', 21), completed: false});\r
430         store.addTask({taskId: Task.nextId(), title:'Release Ext 1.l Beta 2', category:'Ext', description:'', dueDate:s.add('d', 2), completed: false});\r
431         store.addTask({taskId: Task.nextId(), title:'Take wife to see movie', category:'Family', description:'', dueDate:s.add('d', 2), completed: false});\r
432         store.addTask({taskId: Task.nextId(), title:'Finish task list demo app', category:'Ext', description:'', dueDate:s.add('d', 2), completed: false});\r
433         store.addTask({taskId: Task.nextId(), title:'Do something other than work', category:'Family', description:'', dueDate:s.add('d', -1), completed: false});\r
434         store.addTask({taskId: Task.nextId(), title:'Go to the grocery store', category:'Family', description:'', dueDate:s.add('d', -1), completed: true});\r
435         store.addTask({taskId: Task.nextId(), title:'Reboot my computer', category:'Misc', description:'', dueDate:s, completed: false});\r
436         store.addTask({taskId: Task.nextId(), title:'Respond to emails', category:'Ext', description:'', dueDate:s, completed: true});\r
437 }\r
438 \r
439     \r
440 \r