http://ext.ensible.com
https://github.com/bmoeskau/Extensible
https://github.com/TeamupCom/extensible
http://www.rahulsingla.com/sites/default/files/content/blog/extjs-calendar/dynamic-calendars.html
http://ext.ensible.com/deploy/dev/examples/
http://ext.ensible.com/products/calendar/download/choose.php
dynamic-calendars.js:
Ext.ns(‘Ext.ensible.sample‘); Ext.ensible.sample.CalendarData = { "calendars":[{ "id":1, "title":"Home", "color":2 },{ "id":2, "title":"Work", "color":22 },{ "id":3, "title":"School", "color":7 },{ "id":4, "title":"Sports", //"hidden":true, // optionally init this calendar as hidden by default "color":26 }] }; var today = new Date().clearTime(); Ext.ensible.sample.EventData = { "evts":[{ "id":1001, "cid":1, "title":"Vacation", "start":today.add(Date.DAY, -20).add(Date.HOUR, 10), "end":today.add(Date.DAY, -10).add(Date.HOUR, 15), "notes":"Have fun" },{ "id":1002, "cid":2, "title":"Lunch with Matt", "start":today.add(Date.HOUR, 11).add(Date.MINUTE, 30), "end":today.add(Date.HOUR, 13), "loc":"Chuy‘s!", "url":"http://chuys.com", "notes":"Order the queso", "rem":"15" },{ "id":1003, "cid":3, "title":"Project due", "start":today.add(Date.HOUR, 15), "end":today.add(Date.HOUR, 15) },{ "id":1004, "cid":1, "title":"Sarah‘s birthday", "start":today, "end":today, "notes":"Need to get a gift", "ad":true },{ "id":1005, "cid":2, "title":"A long one...", "start":today.add(Date.DAY, -12), "end":today.add(Date.DAY, 10).add(Date.SECOND, -1), "ad":true },{ "id":1006, "cid":3, "title":"School holiday", "start":today.add(Date.DAY, 5), "end":today.add(Date.DAY, 7).add(Date.SECOND, -1), "ad":true, "rem":"2880" },{ "id":1007, "cid":1, "title":"Haircut", "start":today.add(Date.HOUR, 9), "end":today.add(Date.HOUR, 9).add(Date.MINUTE, 30), "notes":"Get cash on the way" },{ "id":1008, "cid":3, "title":"An old event", "start":today.add(Date.DAY, -30), "end":today.add(Date.DAY, -28), "ad":true },{ "id":1009, "cid":2, "title":"Board meeting", "start":today.add(Date.DAY, -2).add(Date.HOUR, 13), "end":today.add(Date.DAY, -2).add(Date.HOUR, 18), "loc":"ABC Inc.", "rem":"60" },{ "id":1010, "cid":3, "title":"Jenny‘s final exams", "start":today.add(Date.DAY, -2), "end":today.add(Date.DAY, 3).add(Date.SECOND, -1), "ad":true },{ "id":1011, "cid":1, "title":"Movie night", "start":today.add(Date.DAY, 2).add(Date.HOUR, 19), "end":today.add(Date.DAY, 2).add(Date.HOUR, 23), "notes":"Don‘t forget the tickets!", "rem":"60" },{ "id":1012, "cid":4, "title":"Gina‘s basketball tournament", "start":today.add(Date.DAY, 8).add(Date.HOUR, 8), "end":today.add(Date.DAY, 10).add(Date.HOUR, 17) },{ "id":1013, "cid":4, "title":"Toby‘s soccer game", "start":today.add(Date.DAY, 5).add(Date.HOUR, 10), "end":today.add(Date.DAY, 5).add(Date.HOUR, 12) }] }; /* * A simple reusable store that loads static calendar field definitions into memory * and can be bound to the CalendarCombo widget and used for calendar color selection. */ Ext.ensible.sample.CalendarStore = Ext.extend(Ext.data.Store, { constructor: function(config){ config = Ext.applyIf(config || {}, { storeId: ‘calendarStore‘, root: ‘calendars‘, idProperty: Ext.ensible.cal.CalendarMappings.CalendarId.mapping || ‘id‘, proxy: new Ext.data.MemoryProxy(), autoLoad: true, fields: Ext.ensible.cal.CalendarRecord.prototype.fields.getRange(), sortInfo: { field: Ext.ensible.cal.CalendarMappings.Title.name, direction: ‘ASC‘ } }); this.reader = new Ext.data.JsonReader(config); Ext.ensible.sample.CalendarStore.superclass.constructor.call(this, config); } }); /* * This is a simple in-memory store implementation that is ONLY intended for use with * calendar samples running locally in the browser with no external data source. Under * normal circumstances, stores that use a MemoryProxy are read-only and intended only * for displaying data read from memory. In the case of the calendar, it‘s still quite * useful to be able to deal with in-memory data for sample purposes (as many people * may not have PHP set up to run locally), but by default, updates will not work since the * calendar fully expects all CRUD operations to be supported by the store (and in fact * will break, for example, if phantom records are not removed properly). This simple * class gives us a convenient way of loading and updating calendar event data in memory, * but should NOT be used outside of the local samples. * * For a real-world store implementation see the remote sample (remote.js). */ Ext.ensible.sample.MemoryEventStore = Ext.extend(Ext.data.Store, { constructor: function(config){ config = Ext.applyIf(config || {}, { storeId: ‘eventStore‘, root: ‘evts‘, proxy: new Ext.data.MemoryProxy(), writer: new Ext.data.DataWriter(), fields: Ext.ensible.cal.EventRecord.prototype.fields.getRange(), idProperty: Ext.ensible.cal.EventMappings.EventId.mapping || ‘id‘ }); this.reader = new Ext.data.JsonReader(config); Ext.ensible.sample.MemoryEventStore.superclass.constructor.call(this, config); }, // In real implementations the store is responsible for committing records // after a remote transaction has returned success = true. Since we never do // a real transaction, we never get any of the normal store callbacks telling // us that an edit occurred. This simple hack works around that for the purposes // of the local samples, but should NEVER actually be done in real code. afterEdit : function(rec){ rec.commit(); }, listeners: { // Since MemoeryProxy has no "create" implementation, added events // get stuck as phantoms without an EventId. The calendar does not support // batching transactions and expects records to be non-phantoms, so for // the purpose of local samples we can hack that into place. In real remote // scenarios this is handled automatically by the store, and so you should // NEVER actually do something like this. ‘add‘: function(store, rec){ var r = rec[0]; r.data[Ext.ensible.cal.EventMappings.EventId.name] = r.id; r.phantom = false; r.commit(); } } }); App = function() { return { init: function() { Ext.BLANK_IMAGE_URL = ‘http://extjs.cachefly.net/ext-3.1.0/resources/images/default/s.gif‘; // This is an example calendar store that enables event color-coding this.calendarStore = new Ext.ensible.sample.CalendarStore({ // defined in data-calendars.js data: Ext.ensible.sample.CalendarData }); // A sample event store that loads static JSON from a local file. Obviously a real // implementation would likely be loading remote data via an HttpProxy, but the // underlying store functionality is the same. this.eventStore = new Ext.ensible.sample.MemoryEventStore({ // defined in data-events.js data: Ext.ensible.sample.EventData }); // This is the app UI layout code. All of the calendar views are subcomponents of // CalendarPanel, but the app title bar and sidebar/navigation calendar are separate // pieces that are composed in app-specific layout code since they could be omitted // or placed elsewhere within the application. new Ext.Viewport({ layout: ‘border‘, renderTo: ‘calendar-ct‘, items: [{ id: ‘app-header‘, region: ‘north‘, height: 35, border: false, contentEl: ‘app-header-content‘ }, { id: ‘app-center‘, title: ‘...‘, // will be updated to the current view‘s date range region: ‘center‘, layout: ‘border‘, listeners: { ‘afterrender‘: function() { Ext.getCmp(‘app-center‘).header.addClass(‘app-center-header‘); } }, items: [{ id: ‘app-west‘, region: ‘west‘, width: 176, border: false, items: [{ xtype: ‘datepicker‘, id: ‘app-nav-picker‘, cls: ‘ext-cal-nav-picker‘, listeners: { ‘select‘: { fn: function(dp, dt) { App.calendarPanel.setStartDate(dt); }, scope: this } } }, { xtype: ‘extensible.calendarlist‘, store: this.calendarStore, border: false, width: 175 }, { xtype: ‘button‘, text: ‘Manage Calendars‘, handler: function() { App.calendarWindow.show(); } }] }, { xtype: ‘extensible.calendarpanel‘, eventStore: this.eventStore, calendarStore: this.calendarStore, border: false, id: ‘app-calendar‘, region: ‘center‘, activeItem: 3, // month view // Any generic view options that should be applied to all sub views: viewConfig: { //enableFx: false }, // View options specific to a certain view (if the same options exist in viewConfig // they will be overridden by the view-specific config): monthViewCfg: { showHeader: true, showWeekLinks: true, showWeekNumbers: true }, multiWeekViewCfg: { //weekCount: 3 }, // Some optional CalendarPanel configs to experiment with: //readOnly: true, //showDayView: false, //showMultiDayView: true, //showWeekView: false, //showMultiWeekView: false, //showMonthView: false, //showNavBar: false, //showTodayText: false, //showTime: false, //editModal: true, //title: ‘My Calendar‘, // the header of the calendar, could be a subtitle for the app // Once this component inits it will set a reference to itself as an application // member property for easy reference in other functions within App. initComponent: function() { App.calendarPanel = this; this.constructor.prototype.initComponent.apply(this, arguments); }, // plugins: [{ // ptype: ‘ext.ensible.cal.contextmenu‘ // }], listeners: { ‘eventclick‘: { fn: function(vw, rec, el) { this.clearMsg(); }, scope: this }, ‘eventover‘: function(vw, rec, el) { //console.log(‘Entered evt rec=‘+rec.data[Ext.ensible.cal.EventMappings.Title.name]‘, view=‘+ vw.id +‘, el=‘+el.id); }, ‘eventout‘: function(vw, rec, el) { //console.log(‘Leaving evt rec=‘+rec.data[Ext.ensible.cal.EventMappings.Title.name]+‘, view=‘+ vw.id +‘, el=‘+el.id); }, ‘eventadd‘: { fn: function(cp, rec) { this.showMsg(‘Event ‘ + rec.data[Ext.ensible.cal.EventMappings.Title.name] + ‘ was added‘); }, scope: this }, ‘eventupdate‘: { fn: function(cp, rec) { this.showMsg(‘Event ‘ + rec.data[Ext.ensible.cal.EventMappings.Title.name] + ‘ was updated‘); }, scope: this }, ‘eventdelete‘: { fn: function(cp, rec) { //this.eventStore.remove(rec); this.showMsg(‘Event ‘ + rec.data[Ext.ensible.cal.EventMappings.Title.name] + ‘ was deleted‘); }, scope: this }, ‘eventcancel‘: { fn: function(cp, rec) { // edit canceled }, scope: this }, ‘viewchange‘: { fn: function(p, vw, dateInfo) { if (this.editWin) { this.editWin.hide(); }; if (dateInfo !== null) { // will be null when switching to the event edit form so ignore Ext.getCmp(‘app-nav-picker‘).setValue(dateInfo.activeDate); this.updateTitle(dateInfo.viewStart, dateInfo.viewEnd); } }, scope: this }, ‘dayclick‘: { fn: function(vw, dt, ad, el) { this.clearMsg(); }, scope: this }, ‘rangeselect‘: { fn: function(vw, dates, onComplete) { this.clearMsg(); }, scope: this }, ‘eventmove‘: { fn: function(vw, rec) { rec.commit(); var time = rec.data[Ext.ensible.cal.EventMappings.IsAllDay.name] ? ‘‘ : ‘ \\a\\t g:i a‘; this.showMsg(‘Event ‘ + rec.data[Ext.ensible.cal.EventMappings.Title.name] + ‘ was moved to ‘ + rec.data[Ext.ensible.cal.EventMappings.StartDate.name].format(‘F jS‘ + time)); }, scope: this }, ‘eventresize‘: { fn: function(vw, rec) { rec.commit(); this.showMsg(‘Event ‘ + rec.data[Ext.ensible.cal.EventMappings.Title.name] + ‘ was updated‘); }, scope: this }, ‘eventdelete‘: { fn: function(win, rec) { this.eventStore.remove(rec); this.showMsg(‘Event ‘ + rec.data[Ext.ensible.cal.EventMappings.Title.name] + ‘ was deleted‘); }, scope: this }, ‘initdrag‘: { fn: function(vw) { if (this.editWin && this.editWin.isVisible()) { this.editWin.hide(); } }, scope: this } } }] }] }); }, // The CalendarPanel itself supports the standard Panel title config, but that title // only spans the calendar views. For a title that spans the entire width of the app // we added a title to the layout‘s outer center region that is app-specific. This code // updates that outer title based on the currently-selected view range anytime the view changes. updateTitle: function(startDt, endDt) { var p = Ext.getCmp(‘app-center‘); if (startDt.clearTime().getTime() == endDt.clearTime().getTime()) { p.setTitle(startDt.format(‘F j, Y‘)); } else if (startDt.getFullYear() == endDt.getFullYear()) { if (startDt.getMonth() == endDt.getMonth()) { p.setTitle(startDt.format(‘F j‘) + ‘ - ‘ + endDt.format(‘j, Y‘)); } else { p.setTitle(startDt.format(‘F j‘) + ‘ - ‘ + endDt.format(‘F j, Y‘)); } } else { p.setTitle(startDt.format(‘F j, Y‘) + ‘ - ‘ + endDt.format(‘F j, Y‘)); } }, // This is an application-specific way to communicate CalendarPanel event messages back to the user. // This could be replaced with a function to do "toast" style messages, growl messages, etc. This will // vary based on application requirements, which is why it‘s not baked into the CalendarPanel. showMsg: function(msg) { Ext.fly(‘app-msg‘).update(msg).removeClass(‘x-hidden‘); }, clearMsg: function() { Ext.fly(‘app-msg‘).update(‘‘).addClass(‘x-hidden‘); } } } (); Ext.onReady(App.init, App); /////////////////////////////////////////////////////////////////////////////////////////////////////////// //Dynamic Calendars support. Ext.onReady(function() { App.calendarWindowMenu = new Ext.menu.Menu({ cls: ‘x-calendar-list-menu‘, items: [new Ext.ensible.cal.ColorPalette({ handler: function(palette, colorId) { var record = App.calendarPanel.calendarStore.getAt(App.calendarWindowMenu.recordIndex); record.set(‘ColorId‘, colorId); App.calendarWindowMenu.hide(); App.calendarWindowDataView.refreshNode(App.calendarWindowMenu.recordIndex); } }), new Ext.menu.Item({ text: ‘Delete‘, iconCls: ‘no-icon‘, handler: function() { Ext.Msg.confirm(‘Action confirmation‘, ‘Please ensure no Event is associated to this Calendar. Continue with deletion?‘, function(btn) { if (btn == ‘yes‘) { App.calendarPanel.calendarStore.removeAt(App.calendarWindowMenu.recordIndex); } }); } })] }); App.calendarWindow = new Ext.Window({ title: ‘Manage Calendars‘, width: 325, height: 400, modal: true, layout: ‘fit‘, closeAction: ‘hide‘, bodyStyle: ‘background-color: white‘, items: [new Ext.DataView({ store: App.calendarPanel.calendarStore, cls: ‘x-combo-list‘, style: ‘background-color: white; padding: 5px‘, itemSelector: ‘.x-combo-list-item‘, selectedClass: ‘x-combo-selected‘, overClass: ‘x-combo-selected‘, autoScroll: true, tpl: new Ext.XTemplate( ‘<div>Click on a Calendar to change its color or remove it.</div>‘, ‘<tpl for=".">‘, ‘<div class="x-combo-list-item" style="vertical-align: middle">‘, ‘<div class="x-cal-{ColorId}">‘, ‘<div class="mail-calendar-cat-color ext-cal-picker-icon" onmouseover="Ext.get(this).addClass(\‘mail-calendar-cat-color-over\‘);" onmouseout="Ext.get(this).removeClass(\‘mail-calendar-cat-color-over\‘);"> </div>‘, ‘</div>‘, ‘<div>{Title}</div>‘, ‘</div>‘, ‘</tpl>‘, ‘<div class="x-clear"></div>‘ ), multiSelect: false, listeners: { click: function(view, index, node, e) { App.calendarWindowMenu.recordIndex = index; App.calendarWindowMenu.show(Ext.get(node)); } } })], buttons: [{ text: ‘Add Calendar‘, handler: function() { Ext.Msg.prompt(‘Enter name‘, ‘Enter calendar name:‘, function(btn, text) { if (btn != ‘ok‘) return; if (App.calendarPanel.calendarStore.findExact(‘Title‘, text) != -1) { Ext.Msg.alert(‘Invalid input‘, ‘A Calendar with the same name already exists.‘); } else { App.calendarPanel.calendarStore.loadData({ calendars: [{ title: text, color: 1}] }, true); } }); } }, { text: ‘Save Changes‘, handler: function() { Ext.Msg.alert(‘Save‘, ‘Save changes to the Calendars on the server here.‘); App.calendarPanel.calendarStore.commitChanges(); App.calendarWindow.hide(); } }, { text: ‘Cancel‘, handler: function() { App.calendarPanel.calendarStore.rejectAllChanges(); App.calendarWindowDataView.refresh(); App.calendarWindow.hide(); } } ] }); App.calendarWindowDataView = App.calendarWindow.items.items[0]; }); Ext.data.Store.prototype.rejectAllChanges = function() { this.rejectChanges(); for (var i = this.data.length - 1; i >= 0; i--) { var rec = this.data.items[i]; if (rec.phantom) { this.remove(rec); if (Ext.isArray(this.deleted)) { this.deleted.remove(rec); } if (Ext.isArray(this.removed)) { this.removed.remove(rec); } } } }
dynamic-calendars.html:
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> <html xmlns="http://www.w3.org/1999/xhtml"> <title>Extensible Calendar - Dynamic Calendars</title> <!-- Ext includes http://extjs.cachefly.net/--> <link rel="stylesheet" type="text/css" href="extjs3.2/resources/css/ext-all.css" /> <script type="text/javascript" src="extjs3.2/adapter/ext/ext-base-debug.js"></script> <script type="text/javascript" src="extjs3.2/ext-all-debug.js"></script> <!-- Extensible includes --> <link rel="stylesheet" type="text/css" href="calendar/css/extensible-all.css" /> <script type="text/javascript" src="calendar/extensible-all.js"></script> <!-- Page-specific includes --> <link rel="stylesheet" type="text/css" href="test-app.css" /> <script type="text/javascript" src="dynamic-calendars.js"></script> <style type="text/css"> .mail-calendar-cat-color { float: left; width: 10px; height: 10px; margin-right: 10px; } .mail-calendar-cat-color-over { width: 12px; height: 12px; } </style> </head> <body> <div style="display:none;"> <div id="app-header-content"> <div id="app-logo"> <div class="logo-top"> </div> <div id="logo-body"> </div> <div class="logo-bottom"> </div> </div> <h1>Ext Calendar Pro <span>BETA</span></h1> <span id="app-msg" class="x-hidden"></span> </div> </div> </body> <script type="text/javascript"> var updateLogoDt = function () { document.getElementById(‘logo-body‘).innerHTML = new Date().getDate(); } updateLogoDt(); setInterval(updateLogoDt, 1000); </script> </html>
时间: 2024-12-22 03:18:09