--- /dev/null
+<html>\r
+<head>\r
+ <title>The source code</title>\r
+ <link href="../resources/prettify/prettify.css" type="text/css" rel="stylesheet" />\r
+ <script type="text/javascript" src="../resources/prettify/prettify.js"></script>\r
+</head>\r
+<body onload="prettyPrint();">\r
+ <pre class="prettyprint lang-js">Ext.onReady(function(){\r
+ Ext.QuickTips.init();\r
+\r
+ var xg = Ext.grid;\r
+ // turn off default shadows which look funky in air\r
+ xg.GridEditor.prototype.shadow = false;\r
+ \r
+ var conn = Ext.data.SqlDB.getInstance();\r
+ conn.open('tasks.db');\r
+ \r
+ // the main grid store\r
+ var taskStore = new TaskStore(conn);\r
+ \r
+ // Category store shared by category combos\r
+ var catStore = new CategoryStore();\r
+ \r
+ taskStore.load({\r
+ callback: function(){\r
+ // first time?\r
+ if(taskStore.getCount() < 1){\r
+ Ext.Msg.confirm('Create Tasks?', 'Your database is currently empty. Would you like to insert some demo data?', \r
+ function(btn){\r
+ if(btn == 'yes'){\r
+ loadDemoTasks(taskStore); \r
+ }\r
+ catStore.init(taskStore);\r
+ });\r
+ }else{\r
+ catStore.init(taskStore);\r
+ }\r
+ }\r
+ });\r
+\r
+ // custom event to notify when a new category is available\r
+ taskStore.on('newcategory', catStore.addCategory, catStore);\r
+\r
+ // set of event handlers shared by combos to allow them to share\r
+ // the same local store\r
+ var comboEvents = {\r
+ focus: function(){\r
+ this.bindStore(catStore);\r
+ },\r
+ blur: function(c){\r
+ catStore.purgeListeners();\r
+ }\r
+ }\r
+\r
+ var completeColumn = new CompleteColumn();\r
+\r
+ // custom template for the grid header\r
+ var headerTpl = new Ext.Template(\r
+ '<table border="0" cellspacing="0" cellpadding="0" style="{tstyle}">',\r
+ '<thead><tr class="x-grid3-hd-row">{cells}</tr></thead>',\r
+ '<tbody><tr class="new-task-row">',\r
+ '<td><div id="new-task-icon"></div></td>',\r
+ '<td><div class="x-small-editor" id="new-task-title"></div></td>',\r
+ '<td><div class="x-small-editor" id="new-task-cat"></div></td>',\r
+ '<td><div class="x-small-editor" id="new-task-due"></div></td>',\r
+ '</tr></tbody>',\r
+ "</table>"\r
+ );\r
+\r
+ var selections = new Ext.grid.RowSelectionModel();\r
+\r
+ // The main grid in all its configuration option glory\r
+ var grid = new xg.EditorGridPanel({\r
+ id:'tasks-grid',\r
+ store: taskStore,\r
+ sm: selections,\r
+ clicksToEdit: 'auto',\r
+ enableColumnHide:false,\r
+ enableColumnMove:false,\r
+ border:false,\r
+ title:'All Tasks',\r
+ iconCls:'icon-show-all',\r
+ region:'center',\r
+ \r
+ plugins: completeColumn,\r
+\r
+ columns: [\r
+ completeColumn,\r
+ {\r
+ header: "Task",\r
+ width:400,\r
+ sortable: true,\r
+ dataIndex: 'title',\r
+ id:'task-title',\r
+ editor: new Ext.form.TextField({\r
+ allowBlank: false\r
+ })\r
+ },\r
+ {\r
+ header: "Category",\r
+ width:150,\r
+ sortable: true,\r
+ dataIndex: 'category',\r
+ editor: new Ext.form.ComboBox({\r
+ displayField: 'text',\r
+ triggerAction: 'all',\r
+ mode:'local',\r
+ selectOnFocus:true,\r
+ listClass:'x-combo-list-small',\r
+ listeners: comboEvents\r
+ })\r
+ },\r
+ {\r
+ header: "Due Date",\r
+ width: 150,\r
+ sortable: true,\r
+ renderer: Ext.util.Format.dateRenderer('D m/d/Y'),\r
+ dataIndex: 'dueDate',\r
+ groupRenderer: textDate(),\r
+ groupName: 'Due',\r
+ editor: new Ext.form.DateField({\r
+ format : "m/d/Y"\r
+ })\r
+ }\r
+ ],\r
+\r
+ view: new Ext.grid.GroupingView({\r
+ forceFit:true,\r
+ ignoreAdd: true,\r
+ emptyText: 'No Tasks to display',\r
+\r
+ templates: {\r
+ header: headerTpl\r
+ },\r
+\r
+ getRowClass : function(r){\r
+ var d = r.data;\r
+ if(d.completed){\r
+ return 'task-completed';\r
+ }\r
+ if(d.dueDate && d.dueDate.getTime() < new Date().clearTime().getTime()){\r
+ return 'task-overdue';\r
+ }\r
+ return '';\r
+ }\r
+ })\r
+ });\r
+\r
+ var viewPanel = new Ext.Panel({\r
+ frame:true,\r
+ title: 'Views',\r
+ collapsible:true,\r
+ contentEl:'task-views',\r
+ titleCollapse: true\r
+ });\r
+ \r
+ var taskActions = new Ext.Panel({\r
+ frame:true,\r
+ title: 'Task Actions',\r
+ collapsible:true,\r
+ contentEl:'task-actions',\r
+ titleCollapse: true\r
+ });\r
+ \r
+ var groupActions = new Ext.Panel({\r
+ frame:true,\r
+ title: 'Task Grouping',\r
+ collapsible:true,\r
+ contentEl:'task-grouping',\r
+ titleCollapse: true\r
+ });\r
+ \r
+ var actionPanel = new Ext.Panel({\r
+ id:'action-panel',\r
+ region:'west',\r
+ split:true,\r
+ collapsible: true,\r
+ collapseMode: 'mini',\r
+ width:200,\r
+ minWidth: 150,\r
+ border: false,\r
+ baseCls:'x-plain',\r
+ items: [taskActions, viewPanel, groupActions]\r
+ });\r
+\r
+ if(Ext.isAir){ // create AIR window\r
+ var win = new Ext.air.MainWindow({\r
+ layout:'border',\r
+ items: [actionPanel, grid],\r
+ title: 'Simple Tasks',\r
+ iconCls: 'icon-show-all'\r
+ }).render();\r
+ }else{\r
+ var viewport = new Ext.Viewport({\r
+ layout:'border',\r
+ items: [actionPanel, grid]\r
+ });\r
+ }\r
+\r
+ var ab = actionPanel.body;\r
+ ab.on('mousedown', doAction, null, {delegate:'a'});\r
+ ab.on('click', Ext.emptyFn, null, {delegate:'a', preventDefault:true});\r
+\r
+ grid.on('resize', syncFields);\r
+ grid.on('columnresize', syncFields);\r
+\r
+ grid.on('afteredit', function(e){\r
+ if(e.field == 'category'){\r
+ catStore.addCategory(e.value);\r
+ }\r
+ if(e.field == taskStore.getGroupState()){\r
+ taskStore.applyGrouping();\r
+ }\r
+\r
+ });\r
+\r
+ grid.on('keydown', function(e){\r
+ if(e.getKey() == e.DELETE && !grid.editing){\r
+ actions['action-delete']();\r
+ }\r
+ });\r
+\r
+ selections.on('selectionchange', function(sm){\r
+ var bd = taskActions.body, c = sm.getCount();\r
+ bd.select('li:not(#new-task)').setDisplayed(c > 0);\r
+ bd.select('span.s').setDisplayed(c > 1);\r
+ });\r
+\r
+ // The fields in the grid's header\r
+ var ntTitle = new Ext.form.TextField({\r
+ renderTo: 'new-task-title',\r
+ emptyText: 'Add a task...'\r
+ });\r
+\r
+ var ntCat = new Ext.form.ComboBox({\r
+ renderTo: 'new-task-cat',\r
+ disabled:true,\r
+ displayField: 'text',\r
+ triggerAction: 'all',\r
+ mode:'local',\r
+ selectOnFocus:true,\r
+ listClass:'x-combo-list-small',\r
+ listeners: comboEvents\r
+ });\r
+\r
+ var ntDue = new Ext.form.DateField({\r
+ renderTo: 'new-task-due',\r
+ value: new Date(),\r
+ disabled:true,\r
+ format : "m/d/Y"\r
+ });\r
+\r
+ // syncs the header fields' widths with the grid column widths\r
+ function syncFields(){\r
+ var cm = grid.getColumnModel();\r
+ ntTitle.setSize(cm.getColumnWidth(1)-2);\r
+ ntCat.setSize(cm.getColumnWidth(2)-4);\r
+ ntDue.setSize(cm.getColumnWidth(3)-4);\r
+ }\r
+ syncFields();\r
+\r
+ var editing = false, focused = false, userTriggered = false;\r
+ var handlers = {\r
+ focus: function(){\r
+ focused = true;\r
+ },\r
+ blur: function(){\r
+ focused = false;\r
+ doBlur.defer(250);\r
+ },\r
+ specialkey: function(f, e){\r
+ if(e.getKey()==e.ENTER){\r
+ userTriggered = true;\r
+ e.stopEvent();\r
+ f.el.blur();\r
+ if(f.triggerBlur){\r
+ f.triggerBlur();\r
+ }\r
+ }\r
+ }\r
+ }\r
+ ntTitle.on(handlers);\r
+ ntCat.on(handlers);\r
+ ntDue.on(handlers);\r
+\r
+ ntTitle.on('focus', function(){\r
+ focused = true;\r
+ if(!editing){\r
+ ntCat.enable();\r
+ ntDue.enable();\r
+ syncFields();\r
+ editing = true;\r
+ }\r
+ });\r
+\r
+ // when a field in the add bar is blurred, this determines\r
+ // whether a new task should be created\r
+ function doBlur(){\r
+ if(editing && !focused){\r
+ var title = ntTitle.getValue();\r
+ if(!Ext.isEmpty(title)){\r
+ taskStore.addTask({\r
+ taskId: Task.nextId(),\r
+ title: title,\r
+ dueDate: ntDue.getValue()||'',\r
+ description: '', // ???\r
+ category: ntCat.getValue(),\r
+ completed: false\r
+ });\r
+ ntTitle.setValue('');\r
+ if(userTriggered){ // if the entered to add the task, then go to a new add automatically\r
+ userTriggered = false;\r
+ ntTitle.focus.defer(100, ntTitle);\r
+ }\r
+ }\r
+ ntCat.disable();\r
+ ntDue.disable();\r
+ editing = false;\r
+ }\r
+ }\r
+ \r
+ var actions = {\r
+ 'view-all' : function(){\r
+ taskStore.applyFilter('all');\r
+ grid.setTitle('All Tasks', 'icon-show-all');\r
+ },\r
+ \r
+ 'view-active' : function(){\r
+ taskStore.applyFilter(false);\r
+ grid.setTitle('Active Tasks', 'icon-show-active');\r
+ },\r
+ \r
+ 'view-complete' : function(){\r
+ taskStore.applyFilter(true);\r
+ grid.setTitle('Completed Tasks', 'icon-show-complete');\r
+ },\r
+ \r
+ 'action-new' : function(){\r
+ ntTitle.focus();\r
+ },\r
+ \r
+ 'action-complete' : function(){\r
+ selections.each(function(s){\r
+ s.set('completed', true);\r
+ });\r
+ taskStore.applyFilter();\r
+ },\r
+ \r
+ 'action-active' : function(){\r
+ selections.each(function(s){\r
+ s.set('completed', false);\r
+ });\r
+ taskStore.applyFilter();\r
+ },\r
+ \r
+ 'action-delete' : function(){\r
+ Ext.Msg.confirm('Confirm', 'Are you sure you want to delete the selected task(s)?', \r
+ function(btn){\r
+ if(btn == 'yes'){\r
+ selections.each(function(s){\r
+ taskStore.remove(s);\r
+ });\r
+ }\r
+ });\r
+ },\r
+ \r
+ 'group-date' : function(){\r
+ taskStore.groupBy('dueDate');\r
+ },\r
+ \r
+ 'group-cat' : function(){\r
+ taskStore.groupBy('category');\r
+ },\r
+ \r
+ 'no-group' : function(){\r
+ taskStore.clearGrouping();\r
+ }\r
+ };\r
+ \r
+ function doAction(e, t){\r
+ e.stopEvent();\r
+ actions[t.id]();\r
+ }\r
+ \r
+ \r
+ // generates a renderer function to be used for textual date groups\r
+ function textDate(){\r
+ // create the cache of ranges to be reused\r
+ var today = new Date().clearTime(true);\r
+ var year = today.getFullYear();\r
+ var todayTime = today.getTime();\r
+ var yesterday = today.add('d', -1).getTime();\r
+ var tomorrow = today.add('d', 1).getTime();\r
+ var weekDays = today.add('d', 6).getTime();\r
+ var lastWeekDays = today.add('d', -6).getTime();\r
+\r
+ return function(date){\r
+ if(!date) {\r
+ return '(No Date)';\r
+ }\r
+ var notime = date.clearTime(true).getTime();\r
+\r
+ if (notime == todayTime) {\r
+ return 'Today';\r
+ }\r
+ if(notime > todayTime){\r
+ if (notime == tomorrow) {\r
+ return 'Tomorrow';\r
+ }\r
+ if (notime <= weekDays) {\r
+ return date.format('l');\r
+ }\r
+ }else {\r
+ if(notime == yesterday) {\r
+ return 'Yesterday';\r
+ }\r
+ if(notime >= lastWeekDays) {\r
+ return 'Last ' + date.format('l');\r
+ }\r
+ } \r
+ return date.getFullYear() == year ? date.format('D m/d') : date.format('D m/d/Y');\r
+ }\r
+ }\r
+});\r
+\r
+/* This is used to laod some demo tasks if the task database is empty */\r
+function loadDemoTasks(store){\r
+ var s = new Date();\r
+ // hardcoded demo tasks\r
+ store.addTask({taskId: Task.nextId(), title:'Start documentation of Ext 2.0', category:'Ext', description:'', dueDate: s.add('d', 21), completed: false});\r
+ store.addTask({taskId: Task.nextId(), title:'Release Ext 1.l Beta 2', category:'Ext', description:'', dueDate:s.add('d', 2), completed: false});\r
+ store.addTask({taskId: Task.nextId(), title:'Take wife to see movie', category:'Family', description:'', dueDate:s.add('d', 2), completed: false});\r
+ store.addTask({taskId: Task.nextId(), title:'Finish task list demo app', category:'Ext', description:'', dueDate:s.add('d', 2), completed: false});\r
+ store.addTask({taskId: Task.nextId(), title:'Do something other than work', category:'Family', description:'', dueDate:s.add('d', -1), completed: false});\r
+ store.addTask({taskId: Task.nextId(), title:'Go to the grocery store', category:'Family', description:'', dueDate:s.add('d', -1), completed: true});\r
+ store.addTask({taskId: Task.nextId(), title:'Reboot my computer', category:'Misc', description:'', dueDate:s, completed: false});\r
+ store.addTask({taskId: Task.nextId(), title:'Respond to emails', category:'Ext', description:'', dueDate:s, completed: true});\r
+}\r
+\r
+ \r
+\r
+</pre> \r
+</body>\r
+</html>
\ No newline at end of file