After formulating the general overview of the application's pieces, the next step is to create each of the components. All the components in the subsequent sections should be placed within a single Flash document. To start, create a new Flash document named scheduler.fla.
The TimeSelector component consists of a scroll pane in which 24 TimeSelectorItem instances appear. Additionally, the TimeSelector component should have the following functionality:
You should be able to make the component selectable or nonselectable. When the user is adding notes or to-do items to a daily schedule, the entire time selector should become nonselectable to signify this.
You should be able to highlight or remove a highlight from any of the time selector items by index.
You should be able to set a callback function that is automatically invoked whenever a time selector item is selected.
To create the TimeSelector component, complete the following steps:
The TimeSelector component requires you to include the ScrollPane component in your movie. Create a copy of the ScrollPane component in your movie's Library by dragging an instance from the Components panel to the Stage. You can then delete the instance on stage; a copy of the symbol remains in the Library.
Create a new movie clip symbol named TimeSelector.
Open the linkage properties for the symbol.
Select the Export for ActionScript and Export in First Frame checkboxes.
Give the symbol a linkage identifier of TimeSelectorSymbol.
Click OK to close the Linkage Properties dialog box.
Edit the TimeSelector symbol.
Add the following code to the first frame of the default layer:
#initclip 0 // The constructor creates a scroll pane instance and populates it with 24 // instances of the TimeSelectorItem component. function TimeSelector( ) { // Create the scroll pane instance. this.attachMovie("FScrollPaneSymbol", "sp", this.getNewDepth( )); this.sp.setSize(150, 380); // Create a scroll content movie clip. this.createEmptyMovieClip("sc", this.getNewDepth( )); // The times property is an array that the component uses to keep track of all // the TimeSelectorItem instances. this.times = new Array( ); var time; // Perform the same actions 24 times, once for each hour of the day. for (var i = 0; i < 24; i++) { // Create an instance of the TimeSelectorItem component and set its y // coordinate so that it appears directly below the previous // TimeSelectorItem. time = this.sc.attachMovie("TimeSelectorItemSymbol", "time" + i, this.sp.getNewDepth( ), {_y: i * 20}); // The TimeSelectorItem.setTime( ) method sets the time displayed by the // component based on an integer from 0 (12 A.M.) to 23 (11 P.M.). time.setTime(i); // The TimeSelectorItem.setOnSelect( ) method sets the callback function that // should be invoked when the item is selected by the user. time.setOnSelect("onSelectTime", this); // Add the TimeSelectorItem component to the times array. this.times.push(time); } // Set the scroll pane's scroll content. this.sp.setScrollContent(this.sc); } // TimeSelector extends MovieClip. TimeSelector.prototype = new MovieClip( ); // The setSelectable( ) method sets whether // items in the time selector are selectable. TimeSelector.prototype.setSelectable = function (isSelectable) { // If isSelectable is true, enable all TimeSelectorItems and call // TimeSelectorItem.grayOut( ) with a value of false so that the items appear // normally. Otherwise, dim and disable the TimeSelectorItems. for (var i = 0; i < this.times.length; i++) { this.times[i].enabled = isSelectable; this.times[i].grayOut(!isSelectable); } }; // The TimeSelector.setHasValue( ) method calls the // TimeSelectorItem.setHasValue( ) method of the item that corresponds to the // specified index. See TimeSelectorItem.setHasValue( ) for more information. TimeSelector.prototype.setHasValue = function (index, hasValue) { this.times[index].setHasValue(hasValue); }; // The onSelectTime( ) method is the callback function for each of the // TimeSelectorItem instances (as assigned in the class constructor). The cmpt // parameter is a reference to the TimeSelectorItem that has been selected. TimeSelector.prototype.onSelectTime = function (cmpt) { // Call TimeSelectorItems.deselect( ) for every item except the selected one. for (var i = 0; i < this.times.length; i++) { if (this.times[i] != cmpt) { this.times[i].deselect( ); } } // Invoke the onSelect callback function. this.onSelectPath[this.onSelectCB](cmpt); }; // The setOnSelect( ) method allows you to define a callback function for the // TimeSelector component. The functionName parameter is the name of the // function, and the path parameter is a reference to a timeline in which the // callback function exists. TimeSelector.prototype.setOnSelect = function (functionName, path) { if (path == undefined) { path = this._parent; } this.onSelectPath = path; this.onSelectCB = functionName; }; Object.registerClass("TimeSelectorSymbol", TimeSelector); #endinitclip
The TimeSelector component is not very complex. The component loads 24 TimeSelectorItems into a scroll pane and acts as an interface by which some commands can be sent to the items. The constructor simply creates these instances and places them in a scroll pane.
function TimeSelector( ) { this.attachMovie("FScrollPaneSymbol", "sp", this.getNewDepth( )); this.sp.setSize(150, 380); this.createEmptyMovieClip("sc", this.getNewDepth( )); this.times = new Array( ); var time; for (var i = 0; i < 24; i++) { time = this.sc.attachMovie("TimeSelectorItemSymbol", "time" + i, this.sp.getNewDepth( ), {_y: i * 20}); time.setTime(i); time.setOnSelect("onSelectTime", this); this.times.push(time); } this.sp.setScrollContent(this.sc); }
The setSelectable( ) method enables or disables all the associated TimeSelectorItems, graying them out if appropriate. This method is used when the user selects another entry type (such as notes or the to-do list), and it serves to visually indicate whether the user is making hourly entries.
TimeSelector.prototype.setSelectable = function (isSelectable) { for (var i = 0; i < this.times.length; i++) { this.times[i].enabled = isSelectable; this.times[i].grayOut(!isSelectable); } };
When the user selects a TimeSelectorItem, the TimeSelector.onSelectTime( ) method is invoked automatically (see the constructor in which you set the onSelect callback function for each item). When an item is selected, call the deselect( ) method for all the other TimeSelectorItems, and call the onSelect callback function for the TimeSelector.
TimeSelector.prototype.onSelectTime = function (cmpt) { for (var i = 0; i < this.times.length; i++) { if (this.times[i] != cmpt) { this.times[i].deselect( ); } } this.onSelectPath[this.onSelectCB](cmpt); };
The TimeSelectorItem component constitutes the content of the TimeSelector component. Each TimeSelectorItem represents one hour of the day, and it should include the following functionality:
The component should draw a filled rectangle.
You should be able to set a time label based on an integer from 0 to 23.
When the item is selected, the rectangle's outline should be bolded, and when deselected, the outline should not be bolded.
You should be able to change the rectangle's fill color to highlight the component. (This is used to indicate that an hour has a scheduled event.)
You should be able to gray out the component to indicate it is disabled.
You should be able to specify a callback function that is invoked when the item is selected.
To create the TimeSelectorItem component, complete the following steps.
Create a new movie clip symbol named TimeSelectorItem.
Open the linkage properties for the symbol.
Select the Export for ActionScript and Export in First Frame checkboxes.
Give the symbol a linkage identifier of TimeSelectorItemSymbol.
Click OK to close the Linkage Properties dialog box.
Edit the TimeSelectorItem symbol.
Add the following code to the first frame of the default layer:
#initclip 0 // In the constructor, create the rectangle with a fill and outline and create a // text field to use as the label. function TimeSelectorItem( ) { // Create a movie clip and two nested movie clips within it, one for the fill // and one for the outline. this.createEmptyMovieClip("indicator", this.getNewDepth( )); this.indicator.createEmptyMovieClip("fill", this.indicator.getNewDepth( )); this.indicator.createEmptyMovieClip("outline", this.indicator.getNewDepth( )); // Draw the fill as a white rectangle using the drawRectangle( ) method. with (this.indicator.fill) { lineStyle(0, 0x000000, 0); beginFill(0xFFFFFF, 100); drawRectangle(150, 20); endFill( ); _x += 75; _y += 10; } // Call the drawOutline( ) method to draw the outline. this.drawOutline( ); // Create color objects to control the color for both the outline and the fill. this.indicator.fill.col = new Color(this.indicator.fill); this.indicator.outline.col = new Color(this.indicator.outline); // Create a text field in which the time label should appear. this.createTextField("time", this.getNewDepth( ), 0, 0, 150, 20); } // TimeSelectorItem extends MovieClip. TimeSelectorItem.prototype = new MovieClip( ); // The drawOutline( ) method draws/redraws the rectangle's outline. If selected // is true, draw the line with a thickness of 2. Otherwise, use no outline. TimeSelectorItem.prototype.drawOutline = function (selected) { with (this.indicator.outline) { clear( ); if (selected) { lineStyle(2, 0x000000, 100); } else { lineStyle(0, 0x000000, 100); } drawRectangle(150, 20); _x = 75; _y = 10; } }; // The static getDisplayTime( ) method takes an integer from 0 to 23 and returns // a string in the format HH AM/PM (i.e., 12 AM, 3 PM, etc.). TimeSelectorItem.getDisplayTime = function (timeVal) { var time; if (timeVal == 0) { time = "12 AM"; } else if (timeVal < 11) { time = timeVal + " AM"; } else if (timeVal == 12) { time = "12 PM"; } else { time = (timeVal - 12) + " PM"; } return time; }; // The setTime( ) method takes an integer from 0 to 23 and it displays the // corresponding time label in the text field. TimeSelectorItem.prototype.setTime = function (timeVal) { this.timeVal = timeVal; this.time.text = TimeSelectorItem.getDisplayTime(timeVal); }; // The grayOut( ) method changes the colors of the outline and fill movie clips. TimeSelectorItem.prototype.grayOut = function (grayOut) { if (grayOut) { this.indicator.outline.col.setRGB(0xBCBCBC); if (this.hasValue) { this.indicator.fill.col.setRGB(0xE4E4E4); } } else { this.indicator.outline.col.setRGB(0); if (this.hasValue) { this.indicator.fill.col.setRGB(0xCCEFFD); } } }; // The setHasValue( ) method highlights or removes a highlight from the // TimeSelectorItem by setting the color of the fill. TimeSelectorItem.prototype.setHasValue = function (hasValue) { if (hasValue) { this.indicator.fill.col.setRGB(0xCCEFFD); this.hasValue = true; } else { this.indicator.fill.col.setRGB(0xFFFFFF); this.hasValue = false; } }; // When an item is selected, call drawOutline(true) to bold the outline. TimeSelectorItem.prototype.select = function ( ) { this.drawOutline(true); }; // When an item is deselected, call drawOutline( ) to unbold the outline. TimeSelectorItem.prototype.deselect = function ( ) { this.drawOutline( ); }; // Get the time (an integer from 0 to 23) that // corresponds to the TimeSelectorItem. TimeSelectorItem.prototype.getTime = function ( ) { return this.timeVal; }; // When the item is rolled over, set the color of the fill to a light gray. TimeSelectorItem.prototype.onRollOver = function ( ) { this.indicator.fill.col.setRGB(0xDFDFDF); }; // When the item is rolled out, set the color of the fill to either white (if the // user has not scheduled anything for the hour) or light blue (if the user has // scheduled something for the hour). TimeSelectorItem.prototype.onRollOut = function ( ) { if (this.hasValue) { this.indicator.fill.col.setRGB(0xCCEFFD) } else { this.indicator.fill.col.setRGB(0xFFFFFF); } }; // When the item is clicked on, call the select( ) method and // the onSelect callback function. TimeSelectorItem.prototype.onPress = function ( ) { this.select( ); this.onSelectPath[this.onSelectCB](this); }; // Set the onSelect callback function. TimeSelectorItem.prototype.setOnSelect = function (functionName, path) { if (path == undefined) { path = this._parent; } this.onSelectPath = path; this.onSelectCB = functionName; }; Object.registerClass("TimeSelectorItemSymbol", TimeSelectorItem); #endinitclip
Although the TimeSelectorItem class has a lot of code, there is nothing within the code that is unusual or complicated. The majority of the code involves either drawing shapes or changing colors.
The Schedule component acts as a container for each of the ScheduleItem components. While ScheduleItem components correspond to specific calendar dates, the Schedule component is the framework within which each of these items is stored. The Schedule component should have the following functionality:
It should allow you to add new schedule items for calendar dates.
It should keep track of all notifications that a user has set, and it should alert the user at the appropriate interval automatically.
It should save the data to a local shared object and retrieve that data whenever the application is reopened.
To create the Schedule component, complete the following steps:
Create a new movie clip symbol named Schedule.
Open the linkage properties for the symbol.
Select the Export for ActionScript and Export in First Frame checkboxes.
Give the symbol a linkage identifier of ScheduleSymbol.
Click OK to close the Linkage Properties dialog box.
Edit the Schedule symbol.
Add the following code to the first frame of the default layer:
#initclip function Schedule( ) { // Create associative arrays to keep track of schedule items and notifiers. this.items = new Object( ); this.notifiers = new Object( ); // Create a local shared object or open an existing LSO. this.so = SharedObject.getLocal("mySchedule"); // If LSO is not new, populate the schedule // application with the retrieved data. if (this.so.data.schedules != undefined) { for (var i in this.so.data.schedules) { var dt = this.so.data.schedules[i].siDate; this.addItem(dt); this.items[dt].setValues(this.so.data.schedules[i]); } this.notifiers = this.so.data.notifiers; } // Run the notifier routine now and call it once per hour. this.runNotify( ); this.interval = setInterval(this, "runNotify", 3600000); } Schedule.prototype = new MovieClip( ); // The save( ) method is called when the module is closed; // it writes the schedule data to the local shared object. Schedule.prototype.save = function ( ) { // Create a schedules associative array and fill it // with the values from every schedule item. this.so.data.schedules = new Object( ); for (var i in this.items) { this.so.data.schedules[i] = this.items[i].getValues( ); } // Create a notifiers associative array that has the // value of the notifiers property of the schedule component. this.so.data.notifiers = this.notifiers; // Invoke the flush( ) method to write the data to disk. this.so.flush( ); }; // The onCloseNotification( ) method is the callback function // for when the user closes a notifier window. It removes // the Notifier component instance from the movie. Schedule.prototype.onCloseNotification = function (cmpt) { this.noteDisp.removeMovieClip( ); }; // Get a schedule item by index (which is a date string). Schedule.prototype.getItem = function (index) { return this.items[index]; }; // Add a new schedule item by index (which is a date string). Schedule.prototype.addItem = function (index) { var uniqueVal = this.getNewDepth( ); // Create a new instance of the ScheduleItem component and assign a reference // to the new component to an element of the items associative array. this.items[index] = this.attachMovie("ScheduleItemSymbol", "item" + uniqueVal, uniqueVal); this.items[index].setDate(index); }; // The runNotify( ) method is called at hourly // intervals to display any new updates. Schedule.prototype.runNotify = function ( ) { // Create a date for tomorrow and get that value as a formatted date string. var d = new Date( ); d.setDate(d.getDate( ) + 1); index = d.format("MM-dd-yyyy"); // Check to see if a notifier exists for that date. if (this.notifiers[index] != undefined) { var notification = ""; // Loop through all the elements for that date's notifiers and add them to // the notification string. for (var i = 0; i < this.notifiers[index].length; i++) { if (this.notifiers[index][i]) { notification += index + " " + TimeSelectorItem.getDisplayTime(i) + newline + this.items[index].getSchedule(i) + "\n\n"; } } // Delete the notifier information so that // the user will not be notified again. delete this.notifiers[index]; // Call the displayNotifications( ) method // to display information to the user. this.displayNotifications(notification); } }; // Create a new NotifierDisplayer component instance and display the notification // information to the user. Schedule.prototype.displayNotifications = function (displayValue) { this.attachMovie("NotifierDisplayerSymbol", "noteDisp", this.getNewDepth( )); this.noteDisp._x = Stage.width/2 - this.noteDisp._width/2; this.noteDisp._y = Stage.height/2 - this.noteDisp._height/2; this.noteDisp.display(displayValue); }; // The setNotify( ) method adds a new notifier to the schedule, or, if doNotify // is not true, it removes any existing notifiers for the date and time. Schedule.prototype.setNotify = function (dt, hour, doNotify) { if (doNotify) { if (this.notifiers[dt] == undefined) { this.notifiers[dt] = new Array( ); } this.notifiers[dt][hour] = true; } else { delete this.notifiers[dt][hour]; } }; // Call the open( ) method for a schedule item, which makes it visible, by index. Schedule.prototype.openItem = function (index) { this.items[index].open( ); }; // The onClose( ) method is invoked automatically when a schedule item is closed. // This method invokes the schedule's onClose callback function. Schedule.prototype.onClose = function ( ) { this.onClosePath[this.onCloseCB]( ); }; // Set the onClose callback function. Schedule.prototype.setOnClose = function (functionName, path) { if (path == undefined) { path = this._parent; } this.onClosePath = path; this.onCloseCB = functionName; }; Object.registerClass("ScheduleSymbol", Schedule); #endinitclip
Let's examine the Schedule component code more closely to shed light on parts of the code that might be unclear.
When you initialize the Schedule component, it must create properties to keep track of the schedule items and the notifiers. It uses an associative array for each of these properties so that the keys are in the form of formatted date strings (instead of simply integer indexes). Additionally, it creates (or opens) the shared object in which the schedule information is stored. If the shared object already exists, the retrieved data is used to populate the schedule. Finally, it sets up an interval for notifications, which runs once an hour. Although this example auto-saves the data when the user clicks the close box, you could implement a manual save feature using a button instead.
function Schedule( ) { this.items = new Object( ); this.notifiers = new Object( ); this.so = SharedObject.getLocal("mySchedule"); if (this.so.data.schedules != undefined) { for (var i in this.so.data.schedules) { var dt = this.so.data.schedules[i].siDate; this.addItem(dt); this.items[dt].setValues(this.so.data.schedules[i]); } this.notifiers = this.so.data.notifiers; } this.runNotify( ); this.interval = setInterval(this, "runNotify", 3600000); }
The addItem( ) method adds a new schedule item. The index is a formatted date string, and the new schedule item is added to the items associative array using that date string as the key. This makes it easy to retrieve a reference to the schedule item later on.
Schedule.prototype.addItem = function (index) { var uniqueVal = this.getNewDepth( ); this.items[index] = this.attachMovie("ScheduleItemSymbol", "item" + uniqueVal, uniqueVal); this.items[index].setDate(index); };
The runNotify( ) method is invoked every 60 minutes; it checks for any notifications for the next day. To calculate the date that represents the next day, add one to the current date. From this date, you can create a formatted date string (the format used by the keys of the notifiers associative array) using the Date.format( ) method. Each element of the notifiers associative array is an array of notifications for that day. Therefore, if there is an entry in the notifiers associative array for the next day, loop through all the elements of that array and create a string of those values. Then, run the displayNotifications( ) method to actually open the notifier displayer.
Schedule.prototype.runNotify = function ( ) { var d = new Date( ); d.setDate(d.getDate( ) + 1); index = d.format("MM-dd-yyyy"); if (this.notifiers[index] != undefined) { var notification = ""; for (var i = 0; i < this.notifiers[index].length; i++) { if (this.notifiers[index][i]) { notification += index + " " + TimeSelectorItem.getDisplayTime(i) + newline + this.items[index].getSchedule(i) + "\n\n"; } } delete this.notifiers[index]; this.displayNotifications(notification); } };
The ScheduleItem component represents a single calendar date. Each schedule item consists of a time selector, an input text field, and a list box from which the user can select the type of entry he wants to make (hourly schedule, notes, or to-do list). The schedule item should have the following functionality:
A user should be able to select from different types of schedule entries, and he should be able to make an entry for that type by typing into a text field.
A user should be able to select from available hours in the time selector and add an entry for that hour. Time selector items corresponding to hours with entries should be colorized to indicate this.
A user should be able to specify whether the scheduler should notify him of hourly entries on the day before they are scheduled to occur.
A user should be able to close a schedule item window.
To create the ScheduleItem component, complete the following steps:
The ScheduleItem component requires you to include the ScrollBar and CheckBox components in your movie. Create a copy of these components in your movie's Library by dragging an instance of each component from the Components panel onto the Stage and then deleting them. A copy of the symbols remains in the Library.
Create a new movie clip symbol named ScheduleItem.
Open the linkage properties for the symbol.
Select the Export for ActionScript and Export in First Frame checkboxes.
Give the symbol a linkage identifier of ScheduleItemSymbol.
Click OK to close the Linkage Properties dialog box.
Edit the ScheduleItem symbol.
Add the following code to the first frame of the default layer:
#initclip 2 function ScheduleItem ( ) { // Draw the component. this.draw( ); // Create arrays to hold information about the hourly schedule entries and any // entries for which the user has chosen to be notified. this.hourlySchedule = new Array( ); this.notifiers = new Array( ); // Initialize the component, such that 12 A.M. is the selected hour. this.selectedHour = 0; // Initialize the component so that it is not visible. this._visible = false; } // ScheduleItem extends MovieClip. ScheduleItem.prototype = new MovieClip( ); // Retrieve the schedule item's values. ScheduleItem.prototype.getValues = function ( ) { var valuesObj = new Object( ); // siDate is the formatted date string that indicates the date to which the // schedule item corresponds. valuesObj.siDate = this.siDate; // These properties are the entries (and notifiers) for the schedule item. valuesObj.hourlySchedule = this.hourlySchedule; valuesObj.notifiers = this.notifiers; valuesObj.toDo = this.toDo; valuesObj.notes = this.notes; return valuesObj; }; // The setValues( ) method is used to populate a schedule item when the values // have been saved to disk and have been retrieved again when the schedule // application is reopened. The valuesObj is in the same form as the valuesObj // that getValues( ) returns. Therefore, setValues( ) does essentially the reverse // of what getValues( ) does. ScheduleItem.prototype.setValues = function (valuesObj) { this.hourlySchedule = valuesObj.hourlySchedule; this.notifiers = valuesObj.notifiers; this.toDo = valuesObj.toDo; this.notes = valuesObj.notes; // In addition to setting the properties of the schedule item, call the // setHasValue( ) method for the time selector for any times that have entries. for (var i = 0; i < this.hourlySchedule.length; i++) { if (this.hourlySchedule[i] != undefined) { this.times.setHasValue(i, true); } } }; // The draw( ) method creates the component on the Stage. ScheduleItem.prototype.draw = function ( ) { // Create a text field for the schedule item title. this.createTextField("title", this.getNewDepth( ), 0, 0, 550, 20); this.title.selectable = false; this.title.border = true; this.title.background = true; this.title.backgroundColor = 0xDFDFDF; // Create a TimeSelector instance and set its onSelect callback function. this.attachMovie("TimeSelectorSymbol", "times", this.getNewDepth( )); this.times.setOnSelect("onSelectTime", this); // Create an input text field so that the user can make entries. this.createInputTextField("scheduleInfo", this.getNewDepth( ), 0, 0, 234, 350); this.scheduleInfo.multiline = true; // When the user removes focus from the text field (usually by clicking // elsewhere in the movie), call the saveInfo( ) method to save the entry. this.scheduleInfo.onKillFocus = function ( ) { this._parent.saveInfo( ); }; // Create a scrollbar for the input text field. this.attachMovie("FScrollBarSymbol", "sb", this.getNewDepth( )); this.sb.setScrollTarget(this.scheduleInfo); this.sb.setSize(this.scheduleInfo._height); // Create a movie clip for the notification checkbox. this.createEmptyMovieClip("notifyBox", this.getNewDepth( )); // Draw a filled rectangle as a background. this.notifyBox.createEmptyMovieClip("background", this.notifyBox.getNewDepth( )); with (this.notifyBox.background) { lineStyle(0, 0x000000, 100); beginFill(0xFFFFFF, 100); drawRectangle(this.scheduleInfo._width + this.sb._width, 30); endFill( ); _x = _width / 2; _y = _height / 2; } // Attach a CheckBox component instance and set the callback function for it. this.notifyBox.attachMovie("FCheckBoxSymbol", "notifyCheckBox", this.notifyBox.getNewDepth( ), {_x: 10, _y: 10}); this.notifyBox.notifyCheckBox.setLabel("notify me"); this.notifyBox.notifyCheckBox.setChangeHandler("notifyChange", this); // Use a table to position all the elements that you have created thus far. midTr0 = new TableRow(0, new TableColumn(0, this.scheduleInfo), new TableColumn(0, this.sb)); midTr1 = new TableRow(0, new TableColumn(0, this.notifyBox)); midTable = new Table(0, 0, 0, midTr0, midTr1); // Add a list box so the user can select an entry type. this.attachMovie("FListBoxSymbol", "entryTypeMenu", this.getNewDepth( )); this.entryTypeMenu.setSize(150, 380); this.entryTypeMenu._height = 380; // Set the values for the list box. this.entryTypeMenu.setDataProvider(["hourly", "to do list", "notes"]); this.entryTypeMenu.setChangeHandler("onSelectEntryType", this); this.entryTypeMenu.setSelectedIndex(0); // Create a table to position all the elements, including ones from midTable. tr0 = new TableRow(0, new TableColumn(0, this.title)); tr1 = new TableRow(0, new TableColumn(0, this.times), new TableColumn(0, midTable), new TableColumn(0, this.entryTypeMenu)); t = new Table(0, 0, 0, tr0, tr1); // Create a button movie clip to let the user close the schedule item window. this.createEmptyMovieClip("closeBtn", this.getNewDepth( )); with (this.closeBtn) { lineStyle(0, 0x000000, 100); beginFill(0xFFFFFF, 100); drawRectangle(10, 10); endFill( ); _x = 540; _y = 10; } this.closeBtn.onRelease = function ( ) { this._parent.close( ); // Invoke the save( ) method of the schedule to // save the data when the item window is closed. this._parent._parent.save( ) }; }; // Return the entry for an hour. ScheduleItem.prototype.getSchedule = function (hour) { return this.hourlySchedule[hour]; }; // The notifyChange( ) method is the callback function for the notification // checkbox. This method calls the setNotify( ) method of the parent schedule and // adds to the notifiers array property. ScheduleItem.prototype.notifyChange = function (cmpt) { this._parent.setNotify(this.siDate, this.selectedHour, cmpt.getValue( )); this.notifiers[this.selectedHour] = cmpt.getValue( ); }; // The onSelectEntryType( ) method is the callback function that is invoked when // a user selects an item from the list box. ScheduleItem.prototype.onSelectEntryType = function (cmpt) { var index = cmpt.getSelectedIndex( ); switch (index) { case 0: // If the first item (hourly) is selected, make sure the time selector is // set to selectable and also enable the checkbox. this.times.setSelectable(true); this.entryType = "hourly"; this.selectTime( ); this.notifyBox.notifyCheckBox.setEnabled(true); break; case 1: // If the second item (to-do list) is selected, make sure the time selector // is not selectable and disable the checkbox. this.times.setSelectable(false); this.scheduleInfo.text = ""; this.entryType = "toDo"; this.scheduleInfo.text = this.toDo; this.notifyBox.notifyCheckBox.setEnabled(false); this.notifyBox.notifyCheckBox.setValue(false); break; case 2: // If the third item (notes) is selected, make sure the time selector is // not selectable and disable the checkbox. this.times.setSelectable(false); this.scheduleInfo.text = ""; this.entryType = "notes"; this.scheduleInfo.text = this.notes; this.notifyBox.notifyCheckBox.setEnabled(false); this.notifyBox.notifyCheckBox.setValue(false); } }; // The setDate( ) method takes a formatted date string as a parameter and sets // the title text and the siDate property for the schedule item. ScheduleItem.prototype.setDate = function (val) { this.title.text = val; var tf = new TextFormat( ); tf.bold = true; tf.align = "center"; tf.size = 15; this.title.setTextFormat(tf); this.siDate = val; }; ScheduleItem.prototype.open = function ( ) { this._visible = true; }; ScheduleItem.prototype.close = function ( ) { this._visible = false; this._parent.onClose( ); }; // The saveInfo( ) method is invoked whenever the text field loses focus. ScheduleItem.prototype.saveInfo = function ( ) { switch (this.entryType) { case "hourly": // If the user has made an hourly schedule entry, save that entry in the // hourlySchedule array and colorize the corresponding time selector item; // otherwise, remove any colorization on a time selector item. if (this.scheduleInfo.text != "") { this.hourlySchedule[this.selectedHour] = this.scheduleInfo.text; this.times.setHasValue(this.selectedHour, true); } else { this.times.setHasValue(this.selectedHour, false); } break; case "toDo": // Save the to-do information. this.toDo = this.scheduleInfo.text; break; case "notes": // Save the notes information. this.notes = this.scheduleInfo.text; } }; // The onSelectTime( ) method is the callback function // when a time selector item is chosen. ScheduleItem.prototype.onSelectTime = function (cmpt) { this.selectedHour = cmpt.getTime( ); this.selectTime( ); }; // The selectTime( ) method displays any saved entries for a selected time in the // input text field. Also, if the user has chosen to be notified for the event, // the checkbox is checked. ScheduleItem.prototype.selectTime = function ( ) { if (this.hourlySchedule[this.selectedHour] != undefined) { this.scheduleInfo.text = this.hourlySchedule[this.selectedHour]; } else { this.scheduleInfo.text = ""; } if (this.notifiers[this.selectedHour] != undefined) { this.notifyBox.notifyCheckBox.setValue(this.notifiers[this.selectedHour]); } else { this.notifyBox.notifyCheckBox.setValue(false); } }; Object.registerClass("ScheduleItemSymbol", ScheduleItem); #endinitclip
Although the ScheduleItem component code is not particularly daunting, it is still worth taking a closer look at some of what is going on here.
The getValues( ) and setValues( ) methods do the reverse of one another. The getValues( ) method is used in conjunction with the Schedule component's save( ) method. It returns an object that contains the user data, which is, in turn, saved to the shared object. Then, when the data is retrieved from the shared object, the same data object is passed back to the setValues( ) method to repopulate the schedule item.
ScheduleItem.prototype.getValues = function ( ) { var valuesObj = new Object( ); valuesObj.siDate = this.siDate; valuesObj.hourlySchedule = this.hourlySchedule; valuesObj.notifiers = this.notifiers; valuesObj.toDo = this.toDo; valuesObj.notes = this.notes; return valuesObj; }; ScheduleItem.prototype.setValues = function (valuesObj) { this.hourlySchedule = valuesObj.hourlySchedule; this.notifiers = valuesObj.notifiers; this.toDo = valuesObj.toDo; this.notes = valuesObj.notes; for (var i = 0; i < this.hourlySchedule.length; i++) { if (this.hourlySchedule[i] != undefined) { this.times.setHasValue(i, true); } } };
The onSelectEntryType( ) method is the change handler function for the entry types list box. Although there are many lines of code in the method, it is really nothing complex. The entire method is a single switch statement that sets the time selector's selectable status, the entryType property, and other minor modifications depending on the type of entry the user has selected (hourly, to-do list, or notes):
ScheduleItem.prototype.onSelectEntryType = function (cmpt) { var index = cmpt.getSelectedIndex( ); switch (index) { case 0: this.times.setSelectable(true); this.entryType = "hourly"; this.selectTime( ); this.notifyBox.notifyCheckBox.setEnabled(true); break; case 1: this.times.setSelectable(false); this.scheduleInfo.text = ""; this.entryType = "toDo"; this.scheduleInfo.text = this.toDo; this.notifyBox.notifyCheckBox.setEnabled(false); this.notifyBox.notifyCheckBox.setValue(false); break; case 2: this.times.setSelectable(false); this.scheduleInfo.text = ""; this.entryType = "notes"; this.scheduleInfo.text = this.notes; this.notifyBox.notifyCheckBox.setEnabled(false); this.notifyBox.notifyCheckBox.setValue(false); } };
The saveInfo( ) method is invoked automatically when the user removes the focus from the input text field. This technique ensures that the schedule item's data is updated to reflect the new entry (or entry changes). This method doesn't do anything particularly tricky. It just sets the appropriate schedule item property values depending on the type of entry.
ScheduleItem.prototype.saveInfo = function ( ) { switch(this.entryType) { case "hourly": if (this.scheduleInfo.text != "") { this.hourlySchedule[this.selectedHour] = this.scheduleInfo.text; this.times.setHasValue(this.selectedHour, true); } else { this.times.setHasValue(this.selectedHour, false); } break; case "toDo": this.toDo = this.scheduleInfo.text; break; case "notes": this.notes = this.scheduleInfo.text; } };
The selectTime( ) method is invoked whenever a user selects a time selector item. In this method, you should make sure that if the schedule item already has an entry for the selected hour, the text field should be populated with that data. Otherwise, remove any text from the text field. Also, if the user has chosen to be notified for an event stored for that hour, you should mark the checkbox as selected. Otherwise, deselect the checkbox.
ScheduleItem.prototype.selectTime = function ( ) { if (this.hourlySchedule[this.selectedHour] != undefined) { this.scheduleInfo.text = this.hourlySchedule[this.selectedHour]; } else { this.scheduleInfo.text = ""; } if (this.notifiers[this.selectedHour] != undefined) { this.notifyBox.notifyCheckBox.setValue(this.notifiers[this.selectedHour]); } else { this.notifyBox.notifyCheckBox.setValue(false); } };
The NotifierDisplayer component is nothing more than a scrollable text field with a rectangle background and a Close button. This simple component should do the following:
Draw a rectangle for the background
Add a text field and a scrollbar that scrolls the text field
Add a button that enables the user to close the displayer
To create the NotifierDisplayer component, complete the following steps.
The NotifierDisplayer component requires you to include the ScrollBar and PushButton components in your movie. You should already have included the ScrollBar component earlier, so you only need to include the PushButton component by dragging an instance from the Components panel to the Stage and then deleting the instance. A copy of the symbol remains in the Library.
Create a new movie clip symbol named NotifierDisplayer.
Open the linkage properties for the symbol.
Select the Export for ActionScript and Export in First Frame checkboxes.
Give the symbol a linkage identifier of NotifierDisplayerSymbol.
Click OK to close the Linkage Properties dialog box.
Edit the NotifierDisplayer symbol.
Add the following code to the first frame of the default layer:
#initclip 0 function NotifierDisplayer ( ) { // Create a movie clip and draw a filled square in it. This serves as the // background for the component. this.createEmptyMovieClip("background", this.getNewDepth( )); with (this.background) { lineStyle(0, 0x000000, 100); beginFill(0xFFFFFF, 100); drawRectangle(300, 300); endFill( ); _x = 150; _y = 150; } // Create a text field in which the notifications can be displayed. this.createTextField("notificationText", this.getNewDepth( ), 0, 0, 240, 240); this.notificationText.border = true; this.notificationText.background = true; // Create a scrollbar and set its size. this.attachMovie("FScrollBarSymbol", "sb", this.getNewDepth( )); this.sb.setSize(this.notificationText._height); // Create a push button that closes the notifier displayer. this.attachMovie("FPushButtonSymbol", "closeBtn", this.getNewDepth( )); this.closeBtn.setLabel("close"); this.closeBtn.setClickHandler("onCloseNotification", this._parent); // Use a table to position the elements. tr0 = new TableRow(0, new TableColumn(0, this.notificationText), new TableColumn(0, this.sb)); tr1 = new TableRow(0, new TableColumn(0, this.closeBtn)); t = new Table(5, 30, 30, tr0, tr1); } // NotifierDisplayer extends MovieClip. NotifierDisplayer.prototype = new MovieClip( ); // The display( ) method sets the notification text. NotifierDisplayer.prototype.display = function (value) { this.notificationText.text = value; this.sb.setScrollTarget(this.notificationText); }; Object.registerClass("NotifierDisplayerSymbol", NotifierDisplayer); #endinitclip