1.8. Ext JS's High-Level Organizational Structure

One of the things that sets Ext JS apart from most other libraries is the clean structure of its API. You can tell that Jack and Co. believe in the object-oriented paradigm quite strongly because Ext JS follows a highly object-oriented design. Almost everything is within a class (and they even differentiate between regular classes and singletons!). Classes are within namespaces (analogous to packages in languages like Java), and classes extend other classes to form logical hierarchies.

For example, take a CheckBox widget. It extends from the Field class, which extends from the BoxComponent class, which extends from the Component class, which extends from the Observable class, which is the base of the inheritance tree (technically, Observable extends from the basic JavaScript Object class, but that's true of any class in JavaScript and therefore generally isn't something to be concerned with). As is the case in object-oriented design, the final CheckBox class takes behavior and characteristics from each of its ancestors. This allows a CheckBox to be treated like a Component, for instance, because it too is a Component by virtue of its inheritance, which means it has all the methods and fields that any Component has. This all means that (a) there's a logical structure to the entire Ext JS API, and (b) learning about something usually helps you learn about something else because that new knowledge will apply to it as well (or stated another way, once you learn what you can do with a Component, for example, you'll know at least some of what a CheckBox can do automatically).

At the highest level, there are a number of classes in the global scope, and then there is the Ext namespace. The contents of these classes and this namespace form the core of Ext JS, and provide many of the utility-type functionality you'd want from a good JavaScript library.

NOTE

The following section is not meant to be an exhaustive look at everything in Ext JS. It isn't my intent to duplicate the Ext JS API documentation, which is rather good. Instead, I will touch on the things that I see as most useful and of special interest in the context of the projects to come. The bottom line is Ext JS already has great reference documentation, and I'm not trying to re-create that merely to highlight cool stuff!

1.8.1. Global-Scope Classes

As of Ext JS version 2.2, there are six classes in global scope, one of which is a singleton.

NOTE

The code snippets shown next are each part of an HTML file that you can load and see in action. An HTML file is available for each of these classes.

1.8.1.1. Array

This class extends the built-in Array class, adding some new methods to it. One such method is indexOf():

var a = [ "Frank", "Bill", "Mike", "Peter" ];
alert(a.indexOf("Mike"));

This will display an alert message with the text "2" because that's the index where Mike is found.

The remove() method is also added, so you can do this:

a.remove("Bill");
alert(a);

You can see the alert() message in Figure 1-11. Note that Bill was removed, which is reflected in the alert() message.

Figure 1.11. Contents of the array after Bill is removed

1.8.1.2. Date

The Date class both extends the JavaScript intrinsic Date class and provides some static utility methods itself. To begin, let's take a look at a couple of methods that allow us to get values from a Date object:

var d = new Date("02/17/1973");
var sOut = "";
sOut += "Starting date: " +
  Date.getShortDayName(d.getDay()) + " " +
  Date.getShortMonthName(d.getMonth()) + " " +
  d.getDate() + ", " + d.getFullYear() + "<br>";
Ext.getDom("divOutput").innerHTML = sOut;

Here you can see the getShortDayName() and getShortMonthName() methods. The former will return "Sat" for this Date, and the later returns "Feb". getDate() and getFullYear() are methods of the standard Date class. This generates some HTML and writes to a <div> with the ID divOutput.

The format() method is another handy method. It uses a subset of PHP's date() function, which is helpful only if you know PHP! For the rest of us, the Ext JS docs detail the format specification very well. Here's an example of it in action:

var dFormatted = d.format("D M j, Y");

This results in the Date object seen previously being formatted into a string "Sat Feb 17, 1973" (which just so happens to be the output of the code seen earlier, which was constructed manually).

Next up is a handy function for doing basic date math. For example, let's say we want to add four days to the Date object we've been looking at so far. Here's all you need to do:

var d1 = d.add(Date.DAY, 4);

Note that calling the add() method doesn't alter the Date object pointed to by the variable d here; it returns a new instance, in this case Wednesday February 21, 1973. You can use negative numbers as well to effectively subtract from a date. In addition, here you can see one of a number of properties present on the Date class. This one is a simple constant that specifies what we're adding to the Date object, the day in this case, so Date.DAY. If you wanted to add four years to the date instead, you could use Date.YEAR. In addition to the rest that you'd expect, like Date.MONTH and Date.MINUTE and so on, there are a couple of arrays that provide useful information, such as Date.dayNames and Date.monthNames, which I suspect are self-explanatory.

You can determine whether the year of a given Date object is a leap year by calling the isLeapYear() method on it, which returns true or false. You can determine what day of the year the Date represents by calling getDayOfYear(), which returns a number between 1 and 365 (or 366 in a leap year). The getWeekOfYear() method tells you what week of the year the Date object falls in. The getDaysInMonth() method tells you how many days are in the month of the Date object. You can even get the suffix (like "th," "nd," "rd," or "st" for a day. So, you can do this:

sOut += "The day of " + d.format("D M j, Y") + " is the " +
 d.getDate() + d.getSuffix() + " (used getSuffix() to get th)<br>";

This will result in the text "The day of Sat Feb 17, 1973 is the 17th (used getSuffix() to get th)". If you've ever written the typical if block of code to do this yourself, you'll very much appreciate this method!

Another handy edition to the Date class is the between() method. This lets you determine if the Date object you call between() on falls between two specified Date objects. Here's an example:

var d3 = new Date("02/19/1973");
sOut += d3.format("D M j, Y") + ".between(" +
  d.format("D M j, Y") + ", " + d1.format("D M j, Y") + "): " +
  d3.between(d, d1) + "<br>";

This will result in the text "Mon Feb 19, 1973.between(Sat Feb 17, 1973, Wed Feb 21, 1973): true." As you can see, between() returns a simple true or false.

Along the same lines is getElapsed(), which tells you how much time, in milliseconds, has elapsed between the Date object you call it on and a given Date object.

Finally we have the parseDate() method, which parses a string into a Date object using a format specifier. Here it is in action:

sOut += "Date.parseDate('1973-02-17 12:14:06AM', "Y-m-d h:i:sA")" +
  ": " + Date.parseDate("1973-02-17 12:14:06AM", "Y-m-d h:i:sA");

This results in the text "Date.parseDate('1973-02-17 12:14:06AM', 'Y-m-d h:i:sA'): Sat Feb 17 1973 00:14:06 GMT-0500 (Eastern Standard Time)." As you can see, the string has been properly parsed and the standard toString() of the Date class is the proof of that.

The result of the execution of all these functions is shown in Figure 1-12.

NOTE

The last line may look different depending on the browser you run it in because JavaScript implementations can implement toString() of the Date class as they see fit (the screenshot is running in Firefox). You should, however, see the same date represented.

Figure 1.12. Output of the Date class examples

1.8.1.3. Ext

The Ext class is that one singleton I mentioned earlier. You are not meant to instantiate this class. More to the point, you cannot instantiate it—you'll see "Ext is not a constructor error" (in Firefox, at least) if you try.

The Ext class has a number of useful members, starting with a batch of public properties. These are summarized in Table 1-2 (which I copied directly from the Ext JS documentation on the grounds that I'm lazy!).

Table 1.2. Public Properties of the Ext Class
Property NameDescription
BLANK_IMAGE_URLContains a URL to a 1 × 1 transparent GIF image used by Ext to create inline icons with CSS background images. (Defaults to http://extjs.com/s.gif; you should change this to a URL on your server.)
SSL_SECURE_URLContains a URL to a blank file used by Ext when in secure mode for iframe src and onReady src to prevent the Internet Explorer insecure content warning (defaults to javascript:false).
emptyFnA reusable empty function.
enableGarbageCollectorSet to true to automatically uncache orphaned Ext.Elements periodically (defaults to true).
enableListenerCollectionSet to true to automatically purge event listeners after uncaching an element (defaults to false). Note: This only happens if enableGarbageCollector is true.
isAirContains true if the detected platform is Adobe AIR.
isBorderBoxContains true if the detected browser is Internet Explorer running in nonstrict mode.
isChromeContains true if the detected browser is Google's Chrome.
isGeckoContains true if the detected browser uses the Gecko layout engine (e.g., Mozilla or Firefox).
isGecko2Contains true if the detected browser uses a pre–Gecko 1.9 layout engine (e.g., Firefox 2.x).
isGecko3Contains true if the detected browser uses a Gecko 1.9+ layout engine (e.g., Firefox 3.x).
isIEContains true if the detected browser is Internet Explorer.
isIE6Contains true if the detected browser is Internet Explorer 6.x.
isIE7Contains true if the detected browser is Internet Explorer 7.x.
isIE8Contains true if the detected browser is Internet Explorer 8.x.
isLinuxContains true if the detected platform is Linux.
isMacContains true if the detected platform is Mac OS.
isOperaContains true if the detected browser is Opera.
isReadyContains true when the document is fully initialized and ready for action.
isSafariContains true if the detected browser is Safari.
isSafari2Contains true if the detected browser is Safari 2.x.
isSafari3Contains true if the detected browser is Safari 3.x.
isSecureContains true if the page is running over SSL.
isStrictTrue if the browser is in strict (standards-compliant) mode, as opposed to quirks mode.
isWindowsContains true if the detected platform is Windows.
useShimsBy default, Ext intelligently decides whether floating elements should be shimmed. Shimming is a trick used specifically to deal with an Internet Explorer issue where <select> elements will "poke through" elements placed over them with z-index style settings. So, let's say you have a <div> that you want to float over a <select>. By default, the <select> will be seen through the <div>, or at least some portion of the <div>. Shimming means that you place an iFrame, which can float over a <select>, behind the <div> and adjust the z-index values of the iFrame and <div> in such a way that the iFrame blocks the <select>, and then the <div>, which can float on top of the iFrame, is positioned in exactly the same location. So, the iFrame blocks the <select>, but the <div> displays on top of the iFrame, so the user sees the <div> floating over the <select> as expected and nothing more. Thankfully, this Ext JS setting allows you to not have to know any of that and simply let Ext JS deal with it! If you are using Flash, or Java applets, you may want to set this to true.

Here's an example of these properties in action:

var sOut = "";
sOut += "isAir: " + Ext.isAir + "<br>";
sOut += "isBorderBox: " + Ext.isBorderBox + "<br>";
sOut += "isStrict: " + Ext.isStrict + "<br>";
sOut += "isGecko: " + Ext.isGecko + "<br>";
sOut += "isGecko2: " + Ext.isGecko2 + "<br>";
sOut += "isGecko3: " + Ext.isGecko3 + "<br>";
sOut += "isIE: " + Ext.isIE + "<br>";
sOut += "isIE6: " + Ext.isIE6 + "<br>";
sOut += "isIE7: " + Ext.isIE7 + "<br>";
sOut += "isLinux: " + Ext.isLinux + "<br>";
sOut += "isWindows: " + Ext.isWindows + "<br>";
sOut += "isMac: " + Ext.isMac + "<br>";
sOut += "isOpera: " + Ext.isOpera + "<br>";
sOut += "isSafari: " + Ext.isSafari + "<br>";
sOut += "isSafari2: " + Ext.isSafari2 + "<br>";
sOut += "isSafari3: " + Ext.isSafari3 + "<br>";
sOut += "isReady: " + Ext.isReady + "<br>";
sOut += "isSecure: " + Ext.isSecure + "<br>";
Ext.getDom("divOutput").innerHTML = sOut;

Assuming you ran this code in Firefox on Windows, and further assuming that there is a <div> with the ID divOutput on the page, you'd see the output in Figure 1-13.

There are also a number of methods available via the Ext class. First up is addBehaviors():

Ext.addBehaviors({
  "div@click" : function(e, t) {
    alert("You clicked a div");
  }
});

This method basically allows you to add event handlers to elements. You call it and pass to it an object that contains a number of key/value pairs. The key portion is a CSS selector. Any element on the page with that selector will have applied to it the function defined as the value of the key/value pair. The name of the event follows the at sign (@)in the key supplied. So here, any <div> on the page will react to click events by executing the inline function. If you've run the example from the source bundle that shows everything that is being discussed now (and if you haven't yet, now would be a real good time!), you can click on the <div> where the text is located to see the alert() appear. Note that the function arguments e and t are browser Event objects that describe the event and a reference to the DOM node, respectively.

Figure 1.13. Values of Ext class public properties

The apply() method is next, and this allows you to apply properties from one object to another:

var sOut = "";
function Class1() {
  this.field1 = "liberate me";
}
function Class2() {
  this.field2 = "liberate tutame ex inferis";
}
var obj1 = new Class1();
var obj2 = new Class2();
Ext.apply(obj1, obj2);
sOut += "obj1.field1: " + obj1.field1 + "<br>";
sOut += "obj1.field2: " + obj1.field2 + "<br>";
Ext.getDom("divOutput").innerHTML = sOut;

The output to divOutput will be "liberate me," which is defined in Class1, and then you will see "liberate tutame ex inferis,"[] which is defined in Class2, but which has been applied to obj1 (an instance of Class1).

[] SPOILER ALERT... The phrase "liberate me" is Latin for "save me" (maybe... keep reading!). This was the message received from the long-lost ship Event Horizon in the movie of the same name. Unfortunately for the rescue crew that found the Event Horizon, the message was misheard and was not "liberate me" but was actually "liberate tutame ex inferis," which translated means "save yourself from hell." It doesn't get much more ominous than that! My technical reviewer pointed out that "liberate me" should actually have been translated as "free me," and therefore "liberate tutame ex inferis" would be "free me from hell." Now, I'm no Latin expert, and even though I found some possibly contradictory information on the Web, I tend to trust my tech reviewer here! Either way, that's one scary phrase in the context of the movie's story!

Having an array and needing to do something with each member of it is a common enough requirement, and Ext JS is there to help with the Ext.each() method:

var sOut = "";
var a = [ 2, 4, 6, 8];
Ext.each(a, function(elem) {
  sOut += elem + " * 2: " + elem * 2 + "<br>";
});
Ext.getDom("divOutput").innerHTML = sOut;

Each element is multiplied by two and the output added to the sOut string, which when displayed shows this:

2 * 2: 4
4 * 2: 8
6 * 2: 12
8 * 2: 16

You can of course do whatever you want in the function, as simple or as complex as you need.

In the Ext class you'll find a couple of methods for determining the identity of a variable. There's isArray(), which returns true if the argument passed to it is an array and false otherwise. Likewise, isDate() returns true if the argument is a Date and false if not. There is also isEmpty(), which returns true if the argument is null, undefined, or an empty string.

The num() method is next, and it is used to validate that a given value is numeric. Further, if it isn't, a default value can be returned. For example:

var sOut = "";
sOut += "Ext.num(12, 5): " + Ext.num(12) + "<br>";
sOut += "Ext.num("Frank", 123): " + Ext.num("Frank", 123) + "<br>";
Ext.getDom("divOutput").innerHTML = sOut;

This results in the following output:

Ext.num(12, 5): 12
Ext.num("Frank", 123): 123

The first call returns 12 because 12 is obviously a numeric value. The second parameter, 5, would have been returned if it wasn't, as is seen in the second call, where Frank isn't numeric and so 123 is returned.

The Ext.getDom() method is one of the methods you'll probably wind up using most. It's a shorthand version of the ubiquitous document.getElementById() method. Simply pass it an ID and you'll get back a reference to the node, or null if it isn't found.

Say you have a reference to a node, and you want to remove it. Ext JS has you covered with the Ext.removeNode() method:

Ext.removeNode(Ext.getDom("removeMe"));

The Ext.type() method is another general-purpose function that is extremely useful. Pass it basically anything you want and it'll return its type. For example:

var sOut = "";
sOut += "Ext.type(new Function()): " +
  Ext.type(new Function()) + "<br>";
sOut += "Ext.type(123): " + Ext.type(123) + "<br>";
sOut += "Ext.type("test"): " + Ext.type("test") + "<br>";
Ext.getDom("divOutput").innerHTML = sOut;

When you run this code, you"ll see this output:

Ext.type(new Function()): function
Ext.type(123): number
Ext.type("test"): string

The next two methods I want to discuss go hand in hand: Ext.urlEncode() and Ext.urlDecode(). The Ext.urlEncode() method takes in an object and creates a URL-encoded string (what you'd append to a URL for a GET request that accepts parameters). With it you can do this:

var s = Ext.urlEncode({first:"Archie",last:"Bunker"}) ;

Now you have a string "first=Archie&last=Bunker" sitting in the variable s. Now, if you want to take that string and get an object out of it, you use Ext.urlDecode():

var o = Ext.urlDecode(s);

With that you could use alert(o.first); to get an alert() message with the text "Archie" in it.

The final method to discuss is Ext.onReady(). You'll see this in most of the examples in this chapter and throughout the project. Simply, Ext.onReady() tells Ext JS what function you want called when the DOM is loaded. This is before the typical onLoad event fires but also before images are loaded. This function is handy because it allows you to execute code without waiting for the entire page, and all dependent resources, to load. This helps your application load faster and makes it more responsive for the user.

1.8.1.4. Function

The Function class adds a number of methods to every single Function object in JavaScript. Let's begin with a look at the createSequence() method:

function add(num1, num2) {
 alert(num1 + num2);
}
function subtract(num1, num2) {
 alert(num1 - num2);
}
add(2, 2);
subtract(5, 3);
var doBoth = add.createSequence(subtract);
doBoth(10, 8);

The createSequence() method allows you to create a single function call that is actually two (or more) function calls executed in sequence. When this code is executed, four alert() messages appear in turn. The first says 4, since 2 + 2 = 4. The second says 2, since 5 – 3 = 2. The third and fourth are both a result of calling doBoth(). First, add() is called, passing it the parameters 10 and 8, so the alert() says 18. Then, subtract() is called, and the alert() message shows 2, since 10 – 8 = 2. The function passed to createSequence() is called with the same arguments as the Function createSequence() is called with.

Another interesting method is createInterceptor(). This provides a rudimentary form of aspect-oriented programming (AOP) whereby you can have a given function called before another is. For example:

var addMult = add.createInterceptor(function(num1, num2) {
  alert(num1 * num2);
});
addMult(6, 7);

Now, when addMult() is called, first the function defined inline in the call to createInterceptor() is executed, multiplying the two arguments and showing the result via alert(), 42 in this case. Then, add() is called, and we see 13 in a second alert() message. This is nice because you're tying two functions together in a loose way. The alternative would be to have add() call the inline function (which would be defined like any other function is in that case) before doing its own work, which makes them tightly coupled. The sort of loose coupling that createInterceptor() allows for is much cleaner, though.

NOTE

The createSequence() and createInterceptor() at first glance look quite similar, but there is one key distinction: with createInterceptor(), if the function passed to createInterceptor() returns false, then the function that createInterceptor() is called on will not be called. In this case, if the inline function returns false, then add() will not be called.

Next we'll talk about the defer() method, which allows you to execute the function you call it on after some period of time. This is a nice abstraction of the usual timeout() mechanism in JavaScript. In practice you would do something like this:

add.defer(3000, this, [8, 9]);

After three seconds (1,000 milliseconds per second, so 3,000 = 1,000 * 3), the add() function will be called, and the parameters 8 and 9 will be passed to it, so we'll see an alert() message saying 17. The argument this defines the object for which the scope is set. Also note that the call to defer() returns a number that is the ID of the timeout() created. This allows you to do a clearTimeout() before the function executes if you wish.

A BRIEF ASIDE ON ASPECT-ORIENTED PROGRAMMING

Aspect-oriented programming (AOP), sometimes called aspect-oriented software development (AOSD), is the technique whereby you identify so-called cross-cutting concerns and externalize them from the code in question.

A commonly used example is that of logging. Frequently, you want to output a log statement every time a given function is called. Typically, you would include some sort of log statement directly in the function. This works well enough, but the problem you quickly see is that you have logging code strewn all over the code because in all likelihood you want to do this in many functions.

AOP enables you to do the equivalent of telling your runtime environment, "Hey, do me a favor, buddy; output a log statement every time function A is called," without you having to specifically include the code to do so in the function. This is also an example of separation of concerns because what your function actually does is separated from the logging concern.

How this AOP approach is accomplished depends on the AOP implementation you use. Some work by modifying your code at compile time; others do so at runtime. Some truly work at the environment level, meaning your code is not modified and the function calls are instead intercepted somehow. The implementation isn't terribly important; the underlying concept is.


1.8.1.5. Number

The Number class extends the intrinsic Number JavaScript class and provides a single addition: the constrain() method. This method allows you to determine if the current value of the Number object falls within a given range by specifying a minimum and maximum value. If it does not fall within the range, constrain() will tell you which side of the range was exceeded by returning to you the minimum or maximum value as appropriate. Here's how it works:

var n = new Number(22);
alert(n.constrain(10, 25));
alert(n.constrain(1, 14));

This will result in two alert() messages, the first saying 22, because 22 falls within the range 10–25, and in that case constrain() returns the value of the Number object. The second alert() will say 14 because 22 is obviously outside the range 1–14, and it's higher than the maximum of 14, so that's the side of the range it exceeds.

1.8.1.6. String

The String class adds a couple of static methods to the intrinsic JavaScript String class, as well as two instance methods. For starters, there's the escape() method:

alert(String.escape("This\is"a test"));

This results in the alert() pop-up seen in Figure 1-14.

Figure 1.14. The output of String.escape()

The double backslash in the original string is itself escaped, so the content of the string would in fact be a single backslash. Then when the escape() method gets a hold of it, it's escaped, resulting in the double backslash you see in the output. The single quote is escaped as well.

The format() function is perhaps the handiest of all:

alert(String.format("Hello {0}, my name is {1}", "Barack", "Michelle"));

As you can see, it allows you to insert values into a string containing tokens. It's a simple positional insert, which means that subsequent arguments will be inserted sequentially into the target string, which is the first argument. So, "Barack" is inserted in place of token {0}, and "Michelle" into token {1}. The text in the alert() pop-up after that work is what you see in Figure 1-15.

Figure 1.15. The output of String.format()

Next up is the leftPad() method, which gives you a convenient way to pad out values (most usually numbers, but not necessarily):

alert(String.leftPad("1234", 8, "0"));

The first argument is the value to pad out, and the second is the final length we want it to be. The final argument is the character to pad the first argument with, if its length initially is less than the second argument. So here the alert() message says "00001234," and if you don't believe me take a look at Figure 1-16!

Figure 1.16. The output of String.leftPad()

The toggle() method is next, and it's a deceptively simple little function:

var s = "Republican";
alert(s.toggle("Republican", "Democrat"));

Here, the message in the alert() is as shown in Figure 1-17.

Figure 1.17. The output of String.toggle()

toggle() has compared the value of the string s to the literal string "Republican". If it matches, then it toggles the value and returns the second argument, "Democrat" in this case. If it was any other value it would have simply returned the current value of s. Note that the string s isn't altered by this call.

The final method in the String class is something I'm still surprised isn't built into JavaScript: the trim() method.

s = " Trimmed String ";
alert(""" + s.trim() + """);

It's very simple but supremely helpful: given a string, trim whitespace from the start and end of it, leaving any spaces in the middle alone. You would imagine the next revision of JavaScript would finally eliminate the need for libraries to provide this function! Figure 1-18 shows the outcome of the example.

Figure 1.18. The output of String.trim()

NOTE

There are a number of other classes in the global scope, including CategoryAxis, NumericAxis, PieSeries, Series, and TimeAxis. All of these are related to Ext JS's chart-generation capabilities. We'll touch on charting in Chapter 2 and then see it in action in Chapter 9, but since that is a somewhat more advanced topic I felt it better to not go into that stuff here. We're still getting our Ext JS "sea legs,"[] so to speak, under us!

[] Sea legs is a term used to describe the ability of a person to walk steadily on the deck of a moving ship at sea. More informally, the term is often used to describe when you are in the process of learning something to mean that you aren't fully knowledgeable on the topic just yet, but you're working on it!

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
18.191.42.205