An application’s ability to work with the files and directories on a user’s computer is a basic necessity. Fortunately, doing so in an AIR application is generally one of the easiest things you’ll ever implement. AIR provides three simple to use classes that offer all the file and directory-related functionality you’ll ever need. In fact, there’s so much information to be covered in this area that I’ve broken it down into two separate chapters.
In this chapter you’ll learn about the fundamentals of working with files and directories. This includes allowing users to select a file or directory on their computer, accessing basic file information (its name, size, creation date, and so forth), and listing the contents of a directory. Examples will also demonstrate how to create, copy, move, delete, and trash directories and files. In Chapter 10, “Working with File Content,” you’ll learn how to write to and read from a file in different ways.
As I’ve done in some other chapters, I’ll begin by covering some of the basic information you’ll need before working with files and directories. You should read these three topics before moving on to the actual examples.
When it comes to working with files and directories, there are three defined classes of note:
File
FileStream
FileMode
Of these, File
is by far the most important. You’ll use a File
object to refer to a file or a directory on the user’s computer. Once you have a File
object, you can create new files and directories, move them, delete them, copy them, list their contents (for directories), and so forth. Everything is done with at least one File
object.
To create a File
object, you would write
var file = new air.File();
But File
objects will sometimes be created without the new
keyword, depending on the circumstance. You’ll see examples like this:
var file = air.File.desktopDirectory;
The next topic discusses what this particular line of code means, but for now just understand that when it comes to creating a File
object, you use new
when referring to just air.File()
and you omit new
when referring to air.File.
something
.
The FileStream
object is used to read from or write to a file. The FileMode
object contains definitions for four constants that will be used with the FileStream
object. Both of these classes will be demonstrated in Chapter 10.
You can reference files and directories on a user’s computer in two ways. The first is through its native path, which is an operating-system specific absolute reference. A valid native path value on Windows might be C:Documents and Settings
username
Desktop
filename
. On Mac, it might be /Users/
username
/Desktop/
filename
(replacing username with an actual username for that computer).
So if you want to explicitly state where a file is when creating a File
object, you would do this:
var file = new air.File(); file.nativePath = '/path/to/file';
That code is the same as using this:
var file = new air.File('/path/to/file'),
In both cases, /path/to/file would be replaced with the actual path: for example, C:Documents and Settings
... or /Users/
username
.... Still, the fact is you won’t often use this method of identifying a file because /path/to/file will inevitably be operating-system-specific, thereby making your application less universal. A better way of referring to a file is to use an associated shortcut and the resolvePath()
method.
When using native paths, there are five associated shortcuts for common locations that an application often references. The first three are user-related:
userDirectory (C:Documents and Settings
username
on Windows; /Users/
username
on Mac OS X)
documentsDirectory (C:Documents and Settings
username
My Documents
on Windows; /Users/
username
/Documents
on Mac OS X)
desktopDirectory (C:Documents and Settings
username
Desktop
on Windows; /Users/
username
/Desktop
on Mac OS X)
Each AIR application also has two associated shortcuts. The first is applicationStorageDirectory, which points to the application’s storage directory (naturally). Where, exactly, this is on the computer depends on many conditions, but it would likely be within the C:Documents and Settings
username
Application Data
directory on Windows and within /Users/
username
/Library/Preferences
on Mac OS X.
The second application-related reference is to the directory where the program is installed: applicationDirectory. Note that while you might read data from the application directory (i.e., data and files that were installed with the program), you likely don’t want to write new data there. That’s what the application storage directory is for, after all.
As an example of this information, to create a File
object that points to where the application was installed, use
var file = air.File.applicationDirectory;
To create a File
object that points to the user’s home directory regardless of operating system, use
var file = air.File.userDirectory;
To refer to a file located within one of these locations, use the resolvePath()
method. Provide it with the name of a file or directory that is the final destination, and it will return the full, absolute path to it. For example, to point to file.txt
, which is on the user’s desktop, you would use
var file = air.File.desktopDirectory.resolvePath('file.txt'),
That line of code will be the same as assigning C:Documents and Settings
username
Desktopfile.txt
to file.nativePath
on Windows and assigning /Users/
username
/Desktop/file.txt
to file.nativePath
on Mac OS X. But by using the shortcut and the resolvePath()
method, your application will work on both operating systems without any tweaks.
As already stated, referring to an item’s native path is the first—and probably preferred—way to refer to a file or directory. The second way is to use a URL. Chapter 4 discusses the important URL schemes. For files and directories, there are three important schemes: file, app-storage, and app. The URL app-storage:/prefs.xml
provides the same reference as air.File.applicationStorageDirectory.resolvePath('prefs.xml')
; similarly app:/data.xml
equates to air.File.applicationDirectory.resolvePath('data.xml')
.
The file.txt
document on the user’s desktop could also be found using file:///Documents and Settings/
username
/Desktop/file.txt
and file:///Users/
username
/Desktop/file.txt
. (The three slashes after file: are actually two slashes—file://
—followed by an implied host value of localhost, followed by the next slash; this is the same as file://localhost/Documents and Settings
.)
Keep in mind that because of the differences among the various operating systems, a file reference will only be valid on a single platform (e.g., file:///Documents and Settings/
username
/...
only works for Windows). Conversely, app and app-storage will work on all operating systems.
For the most part, you’ll use a File
object and its native path value when working with files and directories. You will find the occasional situation in which some other function expects to receive a URL value for a file or directory, in which case you’ll make use of its URL scheme value.
It’s not a problem to have spaces in your file and path names. AIR will convert those to the URL-encoded equivalent—%20—as needed.
Having a File
object represent a file or directory that does not yet exist won’t generate errors. In fact, this is a common thing to do for applications that create files and directories. You will, however, see errors if an application attempts to read from, move, or delete files or directories that don’t exist.
Windows uses the backslash () to separate folders in a path, whereas Mac OS X uses the forward slash (
/
). You can refer to air.File.separator
in an application to fetch the current operating system’s correct slash.
Since the backslash character has a special role in JavaScript (it’s used to escape characters to create other meanings, like
which equates to a newline), you must use two backslashes to define a path on Windows:
var file = air.File.applicationDirectory.resolvePath('resources\data.xml'),
The getRelativePath()
method of the File
object will return the relative path between two files.
The third concept that you must comprehend for working with files and directories is synchronicity. Transactions, like the writing of data to a file, can be done in either synchronous or asynchronous modes. In synchronous mode, the application performs the transaction all in one burst, meaning that other tasks the application may do have to wait until the transaction is complete. In asynchronous mode, the transaction goes on behind the scenes, and the program or the user can continue to do other tasks. As an example, most Ajax processes in Web pages (or AIR applications) are asynchronous: The user can continue to scroll through the page, enter text into a form, and so on while the JavaScript is making a behind the scenes request.
In your AIR programming, you’ll need to choose between these two modes. Doing so normally involves using a different method for a task. For example, the moveToTrash()
method sends the referenced file to the user’s trash. That’s the synchronous version of the method; there’s also the asynchronous moveToTrashAsync()
. The most important distinction between these two modes from a programming standpoint is that the asynchronous methods require the use of event listeners that do whatever needs to be done when the transaction is occurring or completed.
So, in short, synchronous transactions are easier to program for and are perfectly fine for tasks that will take but a moment (like reading a small amount of text in from a text file). Asynchronous transactions require more programming but will result in a more polished experience for the end user when it comes to more intensive tasks. To most easily demonstrate these concepts, this chapter primarily uses the synchronous methods. Asynchronous alternatives will be discussed here and there, and in more detail in the next chapter.
Commonplace in most applications is the ability for users to select a file or directory from their computer. For example, users might choose a file to edit or select a directory where a file should be saved. Creating a browse for file (Figure 9.1) or browse for directory (Figure 9.2) prompt in AIR is easy. Start by creating an object of File
type (in either case):
var selection = new air.File();
If you want to start the user off in a given location, use the appropriate shortcut reference without the new
keyword:
var selection = air.File.userDirectory;
To create the browse for file prompt, call that method:
file.browseForFile('Prompt text'),
To create the browse for directory prompt, call that method:
file.browseForDirectory('Prompt text'),
In both cases the argument provided is the text that appears in title bar of the dialog window (see Figures 9.1 and 9.2).
Both of these methods are asynchronous (there are no synchronous alternatives). This means that event handlers must be assigned to them prior to calling browseForFile()
or browseForDirectory()
. The event to be watched for is SELECT
:
file.addEventListener(air.Event.SELECT, selectFunction); function selectFunction(e) { // Do whatever. }
To practice using these methods, let’s create an application that starts by letting users select a file or a directory, and then reiterates to them what they selected.
In your project’s primary HTML file, create a new File
object (Script 9.1):
var file = new air.File();
That’s all there is to it unless you’d rather start the user off in a set directory, in which case you might use
var file = air.File.userDirectory;
or
var file = air.File.documentsDirectory;
or
var file = air.File.desktopDirectory;
Just to be clear, this example is being created from scratch. My assumption is that you already know how to create the application.xml
file and the primary HTML page (if not, see Chapter 2, “Creating an HTML Application,” and Chapter 3, “AIR Development Tools”).
Example 9.1. This simple program reports back to users what file or directory they selected.
1 <html><!-- Script 9.1 --> 2 <head> 3 <title>User Browsing</title> 4 <script type="text/javascript" src="AIRAliases.js"></script> 5 <script type="text/javascript"> 6 7 // Create an object of File type: 8 var file = new air.File(); 9 10 // Add the event listener: 11 file.addEventListener(air.Event.SELECT, itemWasSelected); 12 13 // Function that will be called 14 // when the event occurs. 15 function itemWasSelected(e) { 16 17 // Use an alert to print the selected item's name: 18 alert ('You selected: ' + file.nativePath); 19 20 } // End of itemWasSelected() function. 21 22 // Function called when the user clicks the 23 // 'Select a File' button. 24 function selectFile() { 25 26 // Create the Open prompt: 27 file.browseForOpen('Choose a file:'), 28 29 } 30 31 // Function called when the user clicks the 32 // 'Select a Directory' button. 33 function selectDirectory() { 34 35 // Create the Open prompt: 36 file.browseForDirectory('Choose a directory:'), 37 38 } 39 40 </script> 41 </head> 42 43 <body> 44 45 <button onclick="selectFile();">Select a File</button> 46 47 <button onclick="selectDirectory();">Select a Directory</button> 48 49 </body> 50 </html>
Add the event listener.
file.addEventListener(air.Event.SELECT, itemWasSelected);
The event to be listened for is air.Event.SELECT
. When that event occurs for this object, the itemWasSelected()
function will be called.
Define the itemWasSelected()
function:
function itemWasSelected(e) { alert ('You selected: ' + file.nativePath); }
This function will use an alert to report the selected item’s native path value.
Define a function that prompts the user to select a file:
function selectFile() { file.browseForOpen('Choose a file:'), }
The application will have just two buttons (Figure 9.3). Clicking the Select a File button will call this function, which in turn calls the browseForOpen()
method.
Define a function that prompts the user to select a directory:
function selectDirectory() { file.browseForDirectory('Choose a directory:'), }
The other application button calls this function, which calls the browseForDirectory()
method, using a different prompt as well (Figure 9.4). Other than that, allowing the user to select a file or a directory is the same process.
<button onclick="selectFile();">Select a File</button> <button onclick="selectDirectory();">Select a Directory</button>
In a real program, you’d likely call the selectFile()
function using a File > Open command, but for this example, a simple button is fine.
Save, test, debug, and run the completed application (Figure 9.5).
The browseForOpenMultiple()
method allows the user to select multiple files. The event to be listened for when using this method is air.FileListEvent.SELECT_MULTIPLE
. In the event handling function, the file
variable (or whatever you name it) would actually be an array of File
objects.
The canonicalize()
method of the File
object formats the nativePath
value using the correct case. For example, you can likely get away with referring to the file C:Documents and SettingsUsernameFileName
as C:documents and settingsusernameFILENAME
, but this method will correct the reference. It also expands shortened names on Windows to their full length.
The File
object has more than two dozen properties, some of which are listed in Table 9.1. Each property provides information about the referenced file or directory. For example, as Script 9.1 shows, you can get a file’s native path (i.e., absolute path) using file.nativePath
. Let’s use the information in Table 9.1 to update the last example so that it tells a little bit more about the selected item.
Table 9.1. These file and directory attributes provide common information about the item. Note that packages only exist on Mac OS X (but can be treated like directories), symbolic links are not the same as aliases or shortcuts (they’re primarily used on Unix, Linux, and Mac OS X), and the parent
attribute returns a File
object representing the parent folder.
File Object Properties | |
---|---|
| When it was created |
| If it exists |
| What a file’s extension is |
| If it is a directory |
| If it is hidden |
| If it is a package (Mac OS X) |
| If it is a symbolic link |
| When it was last modified |
| Its name |
| Its full name and path |
| Its parent folder |
| Its size in bytes |
| Its URL scheme value |
Open Script 9.1 in your text editor or IDE, if it is not already.
Remove the alert()
line from the itemWasSelected()
function (Script 9.2).
Example 9.2. Some of the selected file or directory’s information will be printed by this updated version of Script 9.1.
1 <html><!-- Script 9.2 --> 2 <head> 3 <title>File Information</title> 4 <script type="text/javascript" src="AIRAliases.js"></script> 5 <script type="text/javascript"> 6 7 // Create an object of File type: 8 var file = new air.File(); 9 10 // Add the event listener: 11 file.addEventListener(air.Event.SELECT, itemWasSelected); 12 13 // Function that will be called 14 // when the event occurs. 15 function itemWasSelected(e) { 16 17 // Add the information to a variable: 18 var message = 'You selected ' + file.name; 19 message += ', located within the ' + file.parent.name + ' folder. '; 20 message += 'It was created on ' + file.creationDate; 21 message += '. It was last modified on ' + file.modificationDate + '.'; 22 23 // If it's not a directory, add the file's size: 24 if (!file.isDirectory) { 25 message += ' It is ' + Math.ceil(file.size/1024) + 'KB in size.'; 26 } 27 28 // Place the message on the page: 29 var p = document.createElement('p'), 30 p.innerText = message; 31 document.body.appendChild(p); 32 33 } // End of itemWasSelected() function. 34 35 // Function called when the user clicks the 36 // 'Select a File' button. 37 function selectFile() { 38 39 // Create the Open prompt: 40 file.browseForOpen('Choose a file:'), 41 42 } 43 44 // Function called when the user clicks the 45 // 'Select a Directory' button. 46 function selectDirectory() { 47 48 // Create the Open prompt: 49 file.browseForDirectory('Choose a directory:'), 50 51 } 52 53 </script> 54 </head> 55 56 <body> 57 58 <button onclick="selectFile();">Select a File</button> 59 60 <button onclick="selectDirectory();">Select a Directory</button> 61 62 </body> 63 </html>
Instead of using an alert dialog, this version of the application will print out a bunch of information by adding a paragraph to the Document Object Model (Figure 9.6).
Create a message variable, starting with the item’s name:
var message = 'You selected ' + file.name;
Unlike the nativePath
property, which stores the absolute path to a file, the name
property contains just the file or directory name. It’ll be assigned to a string variable, along with some literal text, that will later be added to the DOM.
Add the item’s parent folder, creation date, and modification date to the message:
message += ', located within the ' + file.parent.name + ' folder. '; message += 'It was created on ' + file.creationDate; message += '. It was last modified on ' + file.modificationDate + '.';
Three more strings—combinations of literal text and file attributes—are concatenated to the message
variable. Because the parent
property returns a File
object that points to the parent directory, its name
attribute is what should be added to the string (if you add just file.parent
to the message string, the value object File will be displayed).
Add the file’s size to the message:
if (!file.isDirectory) { message += ' It is ' + Math.ceil (file.size/1024) + 'KB in size.'; }
The size
attribute only has a value for nondirectories (getting a directory’s size requires adding up all of its file sizes). For this reason, a conditional first checks that the file isn’t a directory. If that is true, the file’s size in kilobytes is determined by dividing its size
value (which is in bytes) by 1024, and then rounding this up to the next highest integer (so that a file smaller than one kilobyte doesn’t appear as being 0KB in size).
Add the message to a paragraph on the page:
var p = document.createElement('p'), p.innerText = message; document.body.appendChild(p);
First a new element of type p
is created. Next, its innerText
value is assigned the message variable (innerText
is what goes between the opening and closing tags). Then this paragraph is appended to the document body.
Save, test, debug, and run the completed application (Figure 9.7).
If a file or folder is in the root directory, its parent attribute will be that root directory. The name of that parent folder will therefore be / on Mac OS X and C: (or whatever drive) on Windows.
If the user selects his or her root directory, file.parent.name
will not have a value. To account for this possibility, you could use a conditional that prints the parent folder’s name only if it is not null.
Although you can provide your AIR application with a specific directory or filename to use, or you can allow the user to select one, sometimes it’s necessary for an application to find a file on its own. For example, iTunes has the ability to look for music files on your computer.
The File
object’s getDirectoryListing()
and getDirectoryListingAsync()
methods can be used to retrieve every file and folder within a given directory. This function returns an array of File
objects, one for each item in the directory. To use it, start by creating a File
object and provide it with the directory to read. You could use
var dir = new air.File('/path/to/dir');
Or, more likely, start with a common reference:
var dir = air.File.userDirectory;
Of course, you can also use code as in the previous example that allows the user to select a directory.
Next, call the getDirectoryListing()
(or getDirectoryListingAsync()
) method and assign the result to another variable:
var stuff = dir.getDirectoryListing();
Now stuff
is an array; you can iterate through it as you would any other array:
for (i = 0; i < stuff.length; i++) { // Do whatever with stuff[i]. }
For this next example, the contents of a directory will be displayed in an unordered list (Figure 9.8).
In your project’s primary HTML file, begin a new JavaScript function (Script 9.3):
window.onload = function() { }
I’ll start by creating a new project with its own root HTML page. Within it an anonymous function will be defined that is automatically called once the window has loaded. This is necessary because the page will add elements to the DOM, and the document’s body will only exist after the page has loaded.
Within the anonymous function, get the contents of a directory:
var dir = air.File.desktopDirectory; var contents = dir.getDirectoryListing();
These two lines, already explained, are all that’s required. For this example, the target directory will be the user’s desktop. Now the contents
variable is an array of File
objects.
If the directory isn’t empty, create an unordered list:
if (contents.length > 0) { var ul = document. createElement('ul'), var li = Array();
This conditional checks that there is at least one item in the array (i.e., in the directory). If so, a new element of type ul
is created, along with an array called li
. This array will be used to add each item in the directory to the unordered list.
Add all visible items to the list:
for (i = 0; i < contents.length; i++) { if (!contents[i].isHidden) { li[i] = document.createElement('li'), li[i].innerText = contents[i].name; ul.appendChild(li[i]); } // End of isHidden IF. } // End of FOR loop.
Example 9.3. This program lists the visible files and folders found on the user’s desktop.
1 <html><!-- Script 9.3 --> 2 <head> 3 <title>Directory Listing</title> 4 <script type="text/javascript" src="AIRAliases.js"></script> 5 <script type="text/javascript"> 6 7 // Create an object of File type, 8 // pointing to the user's desktop. 9 var dir = air.File.desktopDirectory; 10 11 // Once the page has loaded, call this function: 12 window.onload = makeList; 13 14 // Function for creating the directory listing. 15 function makeList() { 16 17 // Get the directory's contents: 18 var contents = dir.getDirectoryListing(); 19 20 // Check that it's not empty: 21 if (contents.length > 0) { 22 23 // Make an unordered list: 24 var ul = document.createElement('ul'), 25 var li = Array(); 26 27 // Loop through the contents: 28 for (i = 0; i < contents.length; i++) { 29 30 // Print only visible items: 31 if (!contents[i].isHidden) { 32 33 // Create a list item: 34 li[i] = document.createElement('li'), 35 li[i].innerText = contents[i].name; 36 ul.appendChild(li[i]); 37 38 } // End of isHidden IF. 39 40 } // End of FOR loop. 41 42 // Add the UL to the page: 43 document.body.appendChild(ul); 44 45 } else { // Nothing there! 46 47 // Make a paragraph: 48 var p = document.createElement('p'), 49 p.innerHTML = 'The directory is empty.'; 50 document.body.appendChild(p); 51 52 } 53 54 } // End of makeList() function. 55 56 </script> 57 </head> 58 59 <body> 60 61 <h3>What's on your Desktop?</h3> 62 63 </body> 64 </html>
This basic for
loop construct will access every element within the contents
array. Within the loop, a conditional checks that the current item—accessed via contents[i]
—isn’t hidden. This is an optional step, but because the getDirectoryListing()
method returns hidden files, showing them here could be confusing to the viewer.
To add the item to the list, a new element of type li
is added to the li
array. Its innerText
value will be the name of the file. This element is then appended to the unordered list. Using an array to create and add list items is necessary because reusing the same li
element over and over won’t work.
Add the list to the page:
document.body.appendChild(ul);
Complete the main conditional:
} else { var p = document.createElement('p'), p.innerHTML = 'The directory is empty.'; document.body.appendChild(p); }
If the conditional in step 3 is false, the directory is empty. In that case, a paragraph is added to the page saying as much.
If you want, create a heading within the page’s body:
<h3>What's on your Desktop?</h3>
Save, test, debug, and run the completed application.
If you’d like, change the directory (to documentsDirectory
or something else) and rerun the program (Figure 9.9).
One way you could expand this example would be to add a check box indicating whether or not hidden files should be revealed. Then the conditional in the loop would read something like
if (!contents[i].isHidden && (document.getElementById('showHidden').checked == false)) {
You would also need to add a function that removes the unordered list and redraws it when the box is checked or unchecked. The next example shows how to recreate a list of items.
To use the asynchronous version of the getDirectoryListing()
method, you’ll need to add an event listener to the File
object. The event to be watched is FileListEvent.DIRECTORY_LISTING
. Sample code for this would be:
var dir = air.File.userDirectory; dir.getDirectoryListingAsync(); dir.addEventListener(air.FileListEvent.DIRECTORY_LISTING, doList); function doList(e) { var contents = e.files; // FOR loop... }
Files and directories can be deleted using the deleteFile()
and deleteDirectory()
methods (or the deleteFileAsync()
and deleteDirectoryAsync()
versions).
var file = air.File.documentsDirectory.resolvePath('somefile'), file.deleteFile(); var dir = air.File.userDirectory.resolvePath('Some Dir'), dir.deleteDirectory();
By default, the delete directory methods will only get rid of empty directories. To delete a directory that may have some files or subdirectories in it, pass the method an argument with the value true
:
dir.deleteDirectory(true);
If you’d rather take less drastic steps, you can move the file or directory to the trash. That way the item won’t be officially removed until the user empties the trash (or Recycle Bin on Windows). The moveToTrash()
and moveToTrashAsync()
functions do this:
var file = air.File.documentsDirectory.resolvePath('somefile'), file.moveToTrash(); var dir = air.File.userDirectory.resolvePath('Some Dir'), dir.moveToTrash();
To test this new knowledge, let’s add links to the previous application that give the user the option of deleting a file or folder.
Open Script 9.3 in your text editor or IDE, if it is not already.
Within the makeList()
function, after creating the ul
object, add an id
attribute to it (Script 9.4):
ul.setAttribute('id', 'contents'),
To have the application reflect any changes, the program will need to be able to remove and re-create the unordered list. One way of removing a document element is to refer to its ID, so one is being added to the list here.
Within the for
loop, after creating the li
element, add an onclick
event handler to it:
li[i].setAttribute('onclick', 'deleteItem("' + contents[i].nativePath + '");'),
The user will be able to delete an item simple by clicking on its name. To add that functionality, onclick
event handlers are added to each list item. When the item is clicked on, the deleteItem()
function will be called. This function will be passed the nativePath
value of the item to be deleted (so that the function knows what to get rid of).
Begin a new function called deleteItem:
function deleteItem(which) {
This is the function that will be called when the user clicks on an item. It’s passed the item’s nativePath
value, which is an absolute reference to the item on the user’s computer.
Example 9.4. This update of Script 9.3 adds onclick
event handlers to the list of items so that clicking on one will move it to the trash.
1 <html><!-- Script 9.4 --> 2 <head> 3 <title>Directory Listing</title> 4 <script type="text/javascript" src="AIRAliases.js"></script> 5 <script type="text/javascript"> 6 7 // Create an object of File type, 8 // pointing to the user's desktop. 9 var dir = air.File.desktopDirectory; 10 11 // Once the page has loaded, call this function: 12 window.onload = makeList; 13 14 // Function for creating the directory listing. 15 function makeList() { 16 17 // Get the directory's contents: 18 var contents = dir.getDirectoryListing(); 19 20 // Check that it's not empty: 21 if (contents.length > 0) { 22 23 // Make an unordered list: 24 var ul = document.createElement('ul'), 25 26 // Give the ul an ID: 27 ul.setAttribute('id', 'contents'), 28 29 var li = Array(); 30 31 // Loop through the contents: 32 for (i = 0; i < contents.length; i++) { 33 34 // Print only visible items: 35 if (!contents[i].isHidden) { 36 37 // Create a list item: 38 li[i] = document.createElement('li'), 39 li[i].innerText = contents[i].name; 40 41 // Add an onclick event handler: 42 li[i].setAttribute('onclick', 'deleteItem("' + contents[i].nativePath + '");'), 43 44 ul.appendChild(li[i]); 45 46 } // End of isHidden IF. 47 48 } // End of FOR loop. 49 50 // Add the UL to the page: 51 document.body.appendChild(ul); 52 53 } else { // Nothing there! 54 55 // Make a paragraph: 56 var p = document.createElement('p'), 57 p.innerHTML = 'The directory is empty.'; 58 document.body.appendChild(p); 59 60 } 61 62 } // End of makeList() function. 63 64 65 // Function for deleting an item. 66 // Takes one argument: the native path of the item. 67 function deleteItem(which) { 68 69 // Confirm with the user prior to deleting: 70 if (confirm('Delete ' + which + '?')) { 71 72 // Get a reference to the item: 73 var trash = new air.File(); 74 trash.nativePath = which; 75 76 // Delete it: 77 trash.moveToTrash(); 78 79 // Remake the list: 80 document.body.removeChild(document.getElementById('contents')); 81 makeList(); 82 83 } // End of confirm IF. 84 85 } // End of deleteItem() function. 86 87 </script> 88 </head> 89 90 <body> 91 92 <h3>What's on your Desktop?</h3> 93 <p>Click on an item's name to delete it.</p> 94 95 </body> 96 </html>
If the user definitely wants to delete the file, get rid of it:
if (confirm('Delete ' + which + '?')) { var trash = new air.File(); trash.nativePath = which; trash.moveToTrash();
Rather than just immediately deleting the item, the user must click OK in a confirmation dialog (Figure 9.10). If the user clicks OK, a reference to the item is created by declaring a new File
object and assigning it the provided nativePath
value. Finally, the item is moved to the trash. Although I’ve been saying this script demonstrates deleting items, I’m playing it safe and just trashing each item.
In a real application, I might be inclined to add a check that the item exists, prior to attempting to delete it:
if (trash.exists) {...
Re-create the unordered list:
document.body.removeChild(document.getElementById('contents')); makeList();
If you don’t re-create the unordered list, it will continue to show the removed items. To rebuild it, start by deleting the existing list, which is accomplished by using the removeChild()
method, and then passing it the element to be removed, which is returned by getElementById()
. Then call the makeList()
function.
Complete the deleteItem()
function:
} // End of confirm IF. } // End of deleteItem() function.
Add instructions for the user to the body of the page:
<p>Click on an item's name to delete it.</p>
Save, test, debug, and run the completed application (Figures 9.11 and 9.12).
If you’d rather not play it safe by moving an item to the trash, you can use this code instead:
if (trash.isDirectory) { trash.deleteDirectory(true); } else { trash.deleteFile(); }
If you’re using the asynchronous versions of the delete functions—deleteDirectoryAsync()
and deleteFileAsync()
—the event to be watched for is air.Event.COMPLETE
. If you add an event listener for that event, when the directory has been completely deleted, your handling function will be called.
Directories and files can be copied on the user’s computer or moved from one location to another. Copying uses the copyTo()
method and two valid File
objects: One represents the original name and location, the second is the destination name and location. Even if you want to create a copy of a file or directory in the same directory as the original, you’ll still need two File
objects (and the second will need a new name, or else you won’t get a copy).
var source = air.File.userDirectory('file.txt'), var dest = air.File.desktopDirectory('file.txt'), source.copyTo(dest);
The above code will copy file.txt
, located in the user’s home directory, to the user’s desktop (keeping the name file.txt
).
The copyTo()
method takes an optional second argument, indicating whether or not an overwrite should be allowed. Assuming you have the same source
and dest
objects from above, this line will replace an existing file.txt
on the user’s desktop:
source.copyTo(dest, true);
Moving a file is exactly the same as copying—requiring two objects with different native paths—but involves the moveTo()
method:
var source = air.File.userDirectory('file.txt'), var dest = air.File.desktopDirectory('file.txt'), source.moveTo(dest);
That code moves file.txt
from the user’s home directory to the user’s desktop. The moveTo()
method also takes an optional second argument. If provided with a value of true
, it will overwrite any existing item with the same name.
As a demonstration of this, the next example allows the user to select a file, select a directory, and then either copy or move the file to that directory (Figure 9.13).
In your project’s primary HTML file, create two File objects (Script 9.5).
var file = new air.File(); var dir = new air.File();
Again, I’m starting from scratch with a new project.
Create two flag variables:
var fileSelected = false; var dirSelected = false;
To ensure that the copying or moving isn’t attempted until both a file and a directory have been selected, these two flag variables will track their statuses.
Add event listeners to the two File
objects:
file.addEventListener(air.Event.SELECT, fileWasSelected); dir.addEventListener(air.Event.SELECT, dirWasSelected);
For both objects, the event to be watched for is air.Event.SELECT
. Each object has its own associated function to be called when that event happens.
Define the fileWasSelected()
function:
function fileWasSelected(e) { document.getElementById ('fileName').innerText = file.nativePath; fileSelected = true; }
This function will be called when the user selects a file from the hard drive. It then shows the selected file next to the corresponding button on the page (Figure 9.14). The associated flag variable is also set to true
.
Define the dirWasSelected()
function:
function dirWasSelected(e) { document.getElementById ('dirName').innerText = dir.nativePath; dirSelected = true; }
This is just a slight variation on the dirWasSelected()
function. The directory’s native path value will be displayed next to that button, and the matching flag variable gets set to true
.
Example 9.5. This application will move or copy a selected file to a new directory.
1 <html><!-- Script 9.5 --> 2 <head> 3 <title>Copying and Moving</title> 4 <script type="text/javascript" src="AIRAliases.js"></script> 5 <script type="text/javascript"> 6 7 // Create two File objects: 8 var file = new air.File(); 9 var dir = new air.File(); 10 11 // Flag variables: 12 var fileSelected = false; 13 var dirSelected = false; 14 15 // Add the event listeners: 16 file.addEventListener(air.Event.SELECT, fileWasSelected); 17 dir.addEventListener(air.Event.SELECT, dirWasSelected); 18 19 // Function called when the file is selected. 20 function fileWasSelected(e) { 21 22 document.getElementById('fileName').innerText = file.nativePath; 23 fileSelected = true; 24 25 } // End of fileWasSelected() function. 26 27 // Function called when the directory is selected. 28 function dirWasSelected(e) { 29 30 document.getElementById('dirName').innerText = dir.nativePath; 31 dirSelected = true; 32 33 } // End of dirWasSelected() function. 34 35 // Function called when the user clicks the 36 // 'Select a File' button. 37 function selectFile() { 38 39 // Create the Open prompt: 40 file.browseForOpen('Choose a file:'), 41 42 } 43 44 // Function called when the user clicks the 45 // 'Select a Directory' button. 46 function selectDirectory() { 47 48 // Create the Open prompt: 49 dir.browseForDirectory('Choose a directory:'), 50 51 } 52 53 // Function called when the user clicks Go! 54 function doCopyMove() { 55 56 // Make sure that both items have been selected: 57 if (fileSelected && dirSelected) { 58 59 // Determine the destination: 60 var dest = new air.File(); 61 dest.nativePath = dir.nativePath + air.File.separator + file. 62 63 // Copy or move: 64 if (document.getElementById('copyMove').value == 'copy') { 65 file.copyTo(dest); 66 alert('The file has been copied!'), 67 } else { 68 file.moveTo(dest); 69 alert('The file has been moved!'), 70 fileSelected = false; 71 } 72 73 } else { // Missing something! 74 alert('You must select a file and a directory first!'), 75 } 76 77 } // End of doCopyMove() function. 78 79 </script> 80 </head> 81 82 <body> 83 84 <h3>Copy or Move a File</h3> 85 86 <p><button onclick="selectFile();">Select a File</button> <span id="filename"></span></p> 87 88 <p><button onclick="selectDirectory();">Select a Directory</button> <span id="dirname"> </span></p> 89 90 <p><select id="copyMove"><option value="copy">Copy</option><option value="move">Move </option></select><button onclick="doCopyMove();">Go!</button></p> 91 92 </body> 93 </html>
Define a function for selecting a file:
function selectFile() { file.browseForOpen('Choose a file:'), }
This function will be called when the user clicks the Select a File button. It generates the browse for open dialog (see Figure 9.1).
Define a function for selecting a directory:
function selectDirectory() { dir.browseForDirectory('Choose a directory:'), }
This function will be called when the user clicks this Select a Directory button. It generates the browse for directory dialog (as in Figure 9.2).
Begin the doCopyMove()
function:
function doCopyMove() { if (fileSelected && dirSelected) {
This is the most important function, because it performs the actual copying or moving. The function will be called when the user clicks the Go! button (see Figures 9.13 and 9.14). Within the function, a conditional confirms that both the file and directory have been selected (by referring to the flag variables).
Identify the destination for the file:
var dest = new air.File(); dest.nativePath = dir.nativePath + air.File.separator + file.name;
One more File
object is necessary to identify the destination. That native path value will be the nativePath
of the selected directory, plus the operating-system-specific separator (a forward or backward slash), plus the selected file’s name.
If appropriate, copy the file:
if (document.getElementById('copyMove').value == 'copy') { file.copyTo(dest); alert('The file has been copied!'),
The user can indicate the action to be performed—copy or move—using the select menu. If the user chooses copy (the default option), the copyTo()
method is called and an alert informs the user of the action (Figure 9.15).
Otherwise, move the file:
} else { file.moveTo(dest); alert('The file has been moved!'), fileSelected = false; }
Besides using the moveTo()
function, instead of copyTo()
, the other significant alteration here is the resetting of the fileSelected
variable’s value to false
. This is necessary because the file no longer exists in the same location after a move, so its reference is inaccurate. By setting fileSelected
to false
, the user will need to select a new file (even if that means the same file in its new location) prior to copying or moving again.
Complete the doCopyMove()
function:
} else { // Missing something! alert('You must select a file and a directory first!'), } } // End of doCopyMove() function.
If the user clicks Go! before selecting both a file and a directory, the use will see this alert (Figure 9.16).
Within the body of the page, create the necessary buttons:
<p><button onclick="selectFile();">Select a File</button> <span id="fileName"></span></p> <p><button onclick="selectDirectory();">Select a Directory</button> <span id="dirName"></span></p>
The first button invokes the selectFile()
function; the second, the selectDirectory()
function.
Add the select menu and the Go! button:
<p><select id="copyMove"><option value="copy">Copy</option><option value="move">Move</option></select><button onclick="doCopyMove();">Go!</button></p>
Save, test, debug, and run the completed application.
Note that this application will not perform the move if a file with the same name already exists in that destination. To account for that possibility, you could add an event listener that watches for an IOError
or call the moveTo()
method within a try...catch
block.
The clone()
method duplicates the existing File
object, giving you two references to the same file:
var file = air.File.userDirectory('file.txt'), var file2 = file.clone();
To rename a file, use the moveTo()
method, but leave the directory the same and change the name value:
var source = air.File.userDirectory('file.txt'), var dest = air.File.userDirectory('newname.txt'), source.moveTo(dest);
98.82.120.188