Ext.onReady(function(){
    Ext.QuickTips.init();

    var xg = Ext.grid;
    // turn off default shadows which look funky in air
    xg.GridEditor.prototype.shadow = false;
    
    var conn = Ext.data.SqlDB.getInstance();
	conn.open('tasks.db');
    
    // the main grid store
    var taskStore = new TaskStore(conn);
    
    // Category store shared by category combos
    var catStore = new CategoryStore();
    
	taskStore.load({
		callback: function(){
			// first time?
			if(taskStore.getCount() < 1){
				Ext.Msg.confirm('Create Tasks?', 'Your database is currently empty. Would you like to insert some demo data?', 
					function(btn){
						if(btn == 'yes'){
							loadDemoTasks(taskStore);	
						}
						catStore.init(taskStore);
					});
			}else{
				catStore.init(taskStore);
			}
		}
	});

    // custom event to notify when a new category is available
    taskStore.on('newcategory', catStore.addCategory, catStore);

    // set of event handlers shared by combos to allow them to share
    // the same local store
    var comboEvents = {
        focus: function(){
            this.bindStore(catStore);
        },
        blur: function(c){
            catStore.purgeListeners();
        }
    }

    var completeColumn = new CompleteColumn();

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