Upgrade to ExtJS 3.0.3 - Released 10/11/2009
[extjs.git] / examples / tasks / tasks.js
1 /*!
2  * Ext JS Library 3.0.3
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         header: false,\r
179         width:200,\r
180         minWidth: 150,\r
181         border: false,\r
182         baseCls:'x-plain',\r
183         items: [taskActions, viewPanel, groupActions]\r
184     });\r
185 \r
186     if(Ext.isAir){ // create AIR window\r
187         var win = new Ext.air.MainWindow({\r
188             layout:'border',\r
189             items: [actionPanel, grid],\r
190             title: 'Simple Tasks',\r
191             iconCls: 'icon-show-all'\r
192         }).render();\r
193         }else{\r
194         var viewport = new Ext.Viewport({\r
195             layout:'border',\r
196             items: [actionPanel, grid]\r
197         });\r
198     }\r
199 \r
200     var ab = actionPanel.body;\r
201     ab.on('mousedown', doAction, null, {delegate:'a'});\r
202         ab.on('click', Ext.emptyFn, null, {delegate:'a', preventDefault:true});\r
203 \r
204     grid.on('resize', syncFields);\r
205         grid.on('columnresize', syncFields);\r
206 \r
207     grid.on('afteredit', function(e){\r
208         if(e.field == 'category'){\r
209             catStore.addCategory(e.value);\r
210         }\r
211         if(e.field == taskStore.getGroupState()){\r
212             taskStore.applyGrouping();\r
213         }\r
214 \r
215     });\r
216 \r
217     grid.on('keydown', function(e){\r
218          if(e.getKey() == e.DELETE && !grid.editing){\r
219              actions['action-delete']();\r
220          }\r
221     });\r
222 \r
223     selections.on('selectionchange', function(sm){\r
224         var bd = taskActions.body, c = sm.getCount();\r
225         bd.select('li:not(#new-task)').setDisplayed(c > 0);\r
226         bd.select('span.s').setDisplayed(c > 1);\r
227     });\r
228 \r
229     // The fields in the grid's header\r
230     var ntTitle = new Ext.form.TextField({\r
231         renderTo: 'new-task-title',\r
232         emptyText: 'Add a task...'\r
233     });\r
234 \r
235     var ntCat = new Ext.form.ComboBox({\r
236         renderTo: 'new-task-cat',\r
237         disabled:true,\r
238         displayField: 'text',\r
239         triggerAction: 'all',\r
240         mode:'local',\r
241         selectOnFocus:true,\r
242         listClass:'x-combo-list-small',\r
243         listeners: comboEvents\r
244     });\r
245 \r
246     var ntDue = new Ext.form.DateField({\r
247         renderTo: 'new-task-due',\r
248         value: new Date(),\r
249         disabled:true,\r
250         format : "m/d/Y"\r
251     });\r
252 \r
253     // syncs the header fields' widths with the grid column widths\r
254     function syncFields(){\r
255         var cm = grid.getColumnModel();\r
256         ntTitle.setSize(cm.getColumnWidth(1)-2);\r
257         ntCat.setSize(cm.getColumnWidth(2)-4);\r
258         ntDue.setSize(cm.getColumnWidth(3)-4);\r
259     }\r
260     syncFields();\r
261 \r
262     var editing = false, focused = false, userTriggered = false;\r
263     var handlers = {\r
264         focus: function(){\r
265             focused = true;\r
266         },\r
267         blur: function(){\r
268             focused = false;\r
269             doBlur.defer(250);\r
270         },\r
271         specialkey: function(f, e){\r
272             if(e.getKey()==e.ENTER){\r
273                 userTriggered = true;\r
274                 e.stopEvent();\r
275                 f.el.blur();\r
276                 if(f.triggerBlur){\r
277                     f.triggerBlur();\r
278                 }\r
279             }\r
280         }\r
281     }\r
282     ntTitle.on(handlers);\r
283     ntCat.on(handlers);\r
284     ntDue.on(handlers);\r
285 \r
286     ntTitle.on('focus', function(){\r
287         focused = true;\r
288         if(!editing){\r
289             ntCat.enable();\r
290             ntDue.enable();\r
291             syncFields();\r
292             editing = true;\r
293         }\r
294     });\r
295 \r
296     // when a field in the add bar is blurred, this determines\r
297     // whether a new task should be created\r
298     function doBlur(){\r
299         if(editing && !focused){\r
300             var title = ntTitle.getValue();\r
301             if(!Ext.isEmpty(title)){\r
302                 taskStore.addTask({\r
303                     taskId: Task.nextId(),\r
304                     title: title,\r
305                     dueDate: ntDue.getValue()||'',\r
306                     description: '', // ???\r
307                     category: ntCat.getValue(),\r
308                     completed: false\r
309                 });\r
310                 ntTitle.setValue('');\r
311                 if(userTriggered){ // if the entered to add the task, then go to a new add automatically\r
312                     userTriggered = false;\r
313                     ntTitle.focus.defer(100, ntTitle);\r
314                 }\r
315             }\r
316             ntCat.disable();\r
317             ntDue.disable();\r
318             editing = false;\r
319         }\r
320     }\r
321         \r
322     var actions = {\r
323         'view-all' : function(){\r
324                 taskStore.applyFilter('all');\r
325                 grid.setTitle('All Tasks', 'icon-show-all');\r
326         },\r
327         \r
328         'view-active' : function(){\r
329                 taskStore.applyFilter(false);\r
330                 grid.setTitle('Active Tasks', 'icon-show-active');\r
331         },\r
332         \r
333         'view-complete' : function(){\r
334                 taskStore.applyFilter(true);\r
335                 grid.setTitle('Completed Tasks', 'icon-show-complete');\r
336         },\r
337         \r
338         'action-new' : function(){\r
339                 ntTitle.focus();\r
340         },\r
341         \r
342         'action-complete' : function(){\r
343                 selections.each(function(s){\r
344                         s.set('completed', true);\r
345                 });\r
346             taskStore.applyFilter();\r
347         },\r
348         \r
349         'action-active' : function(){\r
350                 selections.each(function(s){\r
351                         s.set('completed', false);\r
352                 });\r
353             taskStore.applyFilter();\r
354         },\r
355         \r
356         'action-delete' : function(){\r
357                 Ext.Msg.confirm('Confirm', 'Are you sure you want to delete the selected task(s)?', \r
358                 function(btn){\r
359                 if(btn == 'yes'){\r
360                         selections.each(function(s){\r
361                                         taskStore.remove(s);\r
362                                 });\r
363                 }\r
364             });\r
365         },\r
366         \r
367         'group-date' : function(){\r
368                 taskStore.groupBy('dueDate');\r
369         },\r
370         \r
371         'group-cat' : function(){\r
372                 taskStore.groupBy('category');\r
373         },\r
374         \r
375         'no-group' : function(){\r
376                 taskStore.clearGrouping();\r
377         }\r
378     };\r
379     \r
380     function doAction(e, t){\r
381         e.stopEvent();\r
382         actions[t.id]();\r
383     }\r
384     \r
385     \r
386     // generates a renderer function to be used for textual date groups\r
387     function textDate(){\r
388         // create the cache of ranges to be reused\r
389         var today = new Date().clearTime(true);\r
390         var year = today.getFullYear();\r
391         var todayTime = today.getTime();\r
392         var yesterday = today.add('d', -1).getTime();\r
393         var tomorrow = today.add('d', 1).getTime();\r
394         var weekDays = today.add('d', 6).getTime();\r
395         var lastWeekDays = today.add('d', -6).getTime();\r
396 \r
397         return function(date){\r
398             if(!date) {\r
399                 return '(No Date)';\r
400             }\r
401             var notime = date.clearTime(true).getTime();\r
402 \r
403             if (notime == todayTime) {\r
404                 return 'Today';\r
405             }\r
406             if(notime > todayTime){\r
407                 if (notime == tomorrow) {\r
408                     return 'Tomorrow';\r
409                 }\r
410                 if (notime <= weekDays) {\r
411                     return date.format('l');\r
412                 }\r
413             }else {\r
414                 if(notime == yesterday) {\r
415                         return 'Yesterday';\r
416                     }\r
417                     if(notime >= lastWeekDays) {\r
418                         return 'Last ' + date.format('l');\r
419                     }\r
420             }            \r
421             return date.getFullYear() == year ? date.format('D m/d') : date.format('D m/d/Y');\r
422        }\r
423     }\r
424 });\r
425 \r
426 /* This is used to laod some demo tasks if the task database is empty */\r
427 function loadDemoTasks(store){\r
428         var s = new Date();\r
429         // hardcoded demo tasks\r
430         store.addTask({taskId: Task.nextId(), title:'Start documentation of Ext 2.0', category:'Ext', description:'', dueDate: s.add('d', 21), completed: false});\r
431         store.addTask({taskId: Task.nextId(), title:'Release Ext 1.l Beta 2', category:'Ext', description:'', dueDate:s.add('d', 2), completed: false});\r
432         store.addTask({taskId: Task.nextId(), title:'Take wife to see movie', category:'Family', description:'', dueDate:s.add('d', 2), completed: false});\r
433         store.addTask({taskId: Task.nextId(), title:'Finish task list demo app', category:'Ext', description:'', dueDate:s.add('d', 2), completed: false});\r
434         store.addTask({taskId: Task.nextId(), title:'Do something other than work', category:'Family', description:'', dueDate:s.add('d', -1), completed: false});\r
435         store.addTask({taskId: Task.nextId(), title:'Go to the grocery store', category:'Family', description:'', dueDate:s.add('d', -1), completed: true});\r
436         store.addTask({taskId: Task.nextId(), title:'Reboot my computer', category:'Misc', description:'', dueDate:s, completed: false});\r
437         store.addTask({taskId: Task.nextId(), title:'Respond to emails', category:'Ext', description:'', dueDate:s, completed: true});\r
438 }\r
439 \r
440     \r
441 \r