JavaScript Base Type Extensions

The AJAX library contains base type extensions that add functionality to the string, array, number, Boolean error, and object types. These extensions are useful when you are working with JavaScript primitive data types and when you are working with text in the browser. The base type extensions also provide support for the browser’s current culture when used in a localized application.

To enable automatic localization of the Web application, add the following globalization node to system.web in web.config, and set the ScriptManager’s EnableScriptGlobalization property to true.

<globalization culture="auto" />

The base type extensions described in the following sections can be used to simplify the JavaScript programming model and are also useful in localizing applications. The base type extensions appear in Microsoft Visual Studio IntelliSense when the JavaScript file contains the following script reference at the top of the file:

/// <reference name="MicrosoftAjax.js"/>

Array Extensions

The Microsoft AJAX Library adds static methods that simplify the programming model for working with JavaScript arrays through the Array static type. The JavaScript array is not static in length, as is a .NET array. It can change length, and it contains both stack and queue functionality. Table 5-2 lists extensions to the Array type. (These extensions are all static methods.)

Table 5-2. Array Extensions

Method

Description

Array.add(array, item)

Adds an item to the end of the array.

Array.addRange(array, items)

Copies all the elements of the additional array to the end of the array.

Array.clear(array)

Removes all items from the array.

Array.clone(array)

Returns a new array with copied elements from the existing array.

Array.contains(array, item)

Returns true if the item is found in the array, otherwise false.

Array.dequeue(array, item)

Removes and returns the first element from the array.

Array.forEach(array, delegate)

Runs the delegate code on each member of the array.

Array.indexOf(array, item)

Returns the index of the item in the array.

Array.insert(array, index, item)

Inserts the item into the specified location of the array.

Array.parse(string)

Returns an array from a string.

Array.remove(array, item)

Removes the item from the array.

Array.removeAt(array, index)

Removes the item from the array at the specified index.

More Information

More Information

For more information on the native JavaScript Array object, see http://msdn.microsoft.com/yyadtt0s.aspx.

Boolean Extensions

The Boolean parse method returns true or false when parsing a string. Boolean.parse will only parse the case insensitive "true" or "false" strings, it will not parse 0 or 1. If the string cannot be parsed, an Error.argumentOutOfRange is thrown. Table 5-3 lists the Boolean.parse extension to the Boolean type.

Table 5-3. Boolean Extensions

Method

Description

Boolean.parse(boolString)

Returns a Boolean true or false by parsing a case insensitive "true" or "false" string.

Date Extensions

The Date extensions in the AJAX library offer localization support for date formats and include serialization support. Table 5-4 lists the Date extensions in the Microsoft AJAX Library. Where date is listed, the method is an instance method in which date is a date type. Date.parseLocale and Date.parseInvariant are static functions.

Table 5-4. Date Extensions

Method

Description

date.format(formatString)

Returns a date using the date format of the formatString parameter using the invariant culture.

date.localeformat(formatString)

Returns a date using the date format of the formatString parameter using the current culture.

Date.parseLocale(dateString, formats)

Parses the date string and returns a date using the optional array of formats. If the date string cannot be parsed, null is returned.

Date.parseInvariant(dateString)

Parses the date string and returns a date using the invariant culture. If the date string cannot be parsed, null is returned.

Date format strings can be obtained from the culture object’s dateTimeFormat field, part of Sys.CultureInfo. Using culture-sensitive format strings ensures that the date rendered is appropriate for the culture. For example, 12/11/2008 means December 11 in the United States, but it means November 12 in the United Kingdom and elsewhere.

Sys.CultureInfo.CurrentCulture.dateTimeFormat.ShortDatePattern returns the string "M/d/yyyy" in the U.S. English culture. In the UK English culture, ShortDatePattern returns "dd/MM/yyyy".

More Information

More Information

For more information on the native JavaScript Date object, see http://msdn.microsoft.com/t5580e8h.aspx. For additional information on globalizing dates through script, see http://msdn.microsoft.com/bb386581.aspx.

Error Extensions

The Error type extensions of the AJAX library extend the native Error type by providing a richer API around exception details. Just as specific exceptions can be thrown in the .NET Framework, specific errors can be thrown in the AJAX Library, providing more meaningful errors than the general purpose Error. The Error type provides one factory method, nine methods to create specialized errors, and one utility method that adds exception details.

Creating Errors with Error.create

Error.create is a factory method that simplifies the process of creating an error. It is used by all the specialized error types in the AJAX library. The message of the error is used as the main parameter. Additional information passed into the errorInfo dictionary is added to the error object.

To raise an exception using Error.create, pass a display message and error details as parameters. The following code sample creates a generic error, passing a second object containing additional error information that could be used in debug code:

throw Error.create("Unexpected error",
    { name: "SOAjax.Examples.Foo" , argument : "bar"});

The methods listed in Table 5-5 create specific errors. They are used within the AJAX library and are also useful for custom code. For each error, the method that creates the error is listed along with usage guidelines.

Table 5-5. Error Extensions

Method

Description

Error.argument (paramName, message)

Use when an argument is passed that is the wrong type, such as when a string is passed when a Boolean value is expected.

Error.argumentNull (paramName, message)

Use when a required parameter is null.

Error.argumentOutOfRange (paramName, message)

Use when an argument is passed that is outside the expected value range. For example, in a specialized method you might accept a string parameter with only known strings, or you might accept a parameter within a specific numeric range.

Error.argumentType (paramName, message)

Use when an argument is passed that is the wrong type, such as when a string is passed when a Boolean value is expected.

Error.argumentUndefined (paramName, message)

Use when a required argument is not passed. Similar to the argument Null method, although the undefined exception is not thrown when a parameter is null, only when its argument is not passed.

Error.format()

Use when a formatting related exception occurs.

Error.invalidOperation()

Use when an operation is called that is invalid.

Error.notImplemented()

Use in a method that is stubbed out for future use but is not implemented. You can also use this in a base class method when you are expecting a concrete class to provide the implementation.

Error.parameterCount()

Use when there’s an expected number of parameters that is not met.

More Information

More Information

For more information on the native JavaScript Error object, see http://msdn.microsoft.com/t9zk6eay.aspx.

Number Extensions

The number extensions in the AJAX library are used to support formatting and parsing, specifically with culture-specific formats. Table 5-6 lists the format extensions included in the Microsoft AJAX Library and describes when to use them.

Table 5-6. Number Extensions

Method

Description

Number.format(number)

Use to format numbers with the invariant culture.

Number.localeFormat(number)

Use to format numbers with the current culture.

Number.parseInvariant(numericString)

Use to parse numbers with the invariant culture.

Number.parseLocale(numericString)

Use to format numbers with the current culture.

Object Extensions

The Object extensions in the Microsoft AJAX Library are included to extend the library’s type system and are used for classes registered with Type.registerClass. The extensions are instance methods and are listed in Table 5-7.

Table 5-7. Object Extensions

Method

Description

object.GetType()

Use to get the type of an object that was registered using the Microsoft AJAX Library’s type system. Returns the type’s constructor.

object.GetTypeName()

Use to get the type name of an object that was registered using the Microsoft AJAX Library’s type system.

String Extensions

The AJAX library includes extensions to the String type that simplify string programming tasks. String.format and String.localeFormat are static methods of String; the other extensions are instance methods of String that can be used on any String object.

Table 5-8. String Extensions

Method

Description

String.format(format,args)

Creates a string from the format argument using the invariant culture, replacing each token with an item from the argument’s array.

String.localeFormat(format,args)

Creates a string from the format argument using the current culture, replacing each token with an item from the argument’s array. The current culture is used to format dates and numbers.

string.endsWith(suffix)

Determines whether the string instance ends with the given suffix. The comparison is made after trimming the end of the string and setting both strings to lowercase.

string.startsWith(prefix)

Determines whether the string instance starts with the given prefix. The comparison is made after trimming the start of the string and setting both strings to lowercase.

string.trim()

Removes leading and trailing white space from the string.

string.trimEnd()

Removes trailing white space from the end of the string.

string.trimStart()

Removes leading white space from the beginning of the string.

One of the most useful extensions is String.format, first introduced in .NET 1.0. With String.format you can define formatted text with replacement arguments, just as you can in server-side code with the .NET Framework’s string.Format method. For example, the following script produces a string containing information about the current browser context. The first argument contains the format string, while the arguments passed in replace the numbered token parameters.

String.format('Browser: {0}; version {1}; supports debugger: {2}',
    Sys.Browser.name, Sys.Browser.version, Sys.Browser.hasDebuggerStatement)

In addition to the string extensions, the type Sys.StringBuilder is a core object for working with strings in the Microsoft AJAX Library and is logically an extension to String. Whenever you are building a complex string, consider using the StringBuilder class.

Sys.StringBuilder

The Sys.StringBuilder type uses an array of strings to implement a client-side JavaScript version of the System.Text.StringBuilder class. Because strings are immutable in JavaScript, just as they are in managed code languages such as C#, you are creating a new instance of the string each time it is modified. Working with an array of immutable strings is more efficient in JavaScript than is manipulating a single string variable at run time. This approach can be useful when a program is building HTML, XML, or JSON manually by looping through a result set. Example 5-1 demonstrates the use of Sys.StringBuilder in the Microsoft AJAX Library’s Sys$Net$WebRequest$_createQueryString function.

Tip

Tip

Code for this book is available online at http://www.microsoft.com/mspress/companion/9780735625914. The code for this chapter is in the file Chapter 5.zip.

Example 5-1. Sys.StringBuilder is the ideal way to build strings when looping through a result set. The code sample is from MicrosoftAjax.debug.js.

function Sys$Net$WebRequest$_createQueryString(queryString, encodeMethod) {
    if (!encodeMethod)
        encodeMethod = encodeURIComponent;
    var sb = new Sys.StringBuilder();
    var i = 0;
    for (var arg in queryString) {
        var obj = queryString[arg];
        if (typeof(obj) === "function") continue;
        var val = Sys.Serialization.JavaScriptSerializer.serialize(obj);
        if (i !== 0) {
            sb.append('&'),
        }
        sb.append(arg);
        sb.append('='),
        sb.append(encodeMethod(val));
        i++;
    }
    return sb.toString();
}

More Information

More Information

For full documentation of the global base type extensions, see the online documentation at http://msdn.microsoft.com/bb397506.aspx.

Global Types and Objects in the Sys Namespace

The Sys namespace is analogous to the System namespace in the .NET Framework. It contains the Sys.Application object that controls the AJAX library runtime and is the root namespace for the Microsoft AJAX Library. The types Sys.Application, Sys.Debug, and Sys.Browser are the principal global objects in the Sys namespace and are used in the basic application infrastructure.

Sys.Application

In Chapter 1, I introduced the Sys.Application object, which is instantiated at the beginning of client execution and then used to initialize the client application. (If you are not familiar with Sys.Application and the basic client life cycle, review Chapter 1 to learn how this object works at the center of the client-side application runtime.) Sys.Application is an instance of the Microsoft AJAX Sys._Application class defined in MicrosoftAjax.debug.js. Its main task is to manage page components, history, and the current state of the JavaScript application. It is also a key JavaScript component for applications using the Update panel (a pseudo–AJAX ASP.NET technology).

Sys.Application has four events to which you can add handlers: init, load, navigate, and unload. You can use Sys.Application to control the order of these events when a page is loaded. To add page initialization or page loading functionality to a client, attach a function to the events as shown in the following examples, where handler is a custom function:

Sys.Application.add_init(handler);
Sys.Application.add_load(handler);
Sys.Application.add_navigate(handler);
Sys.Application.add_unload(handler);

Upon creation, Sys.Application fires the init event and then calls any event handlers (functions) added with the add_init method. After initialization, the load event fires and calls any event handlers added with the add_load method. Next, if a pageLoad function is defined on the page, Sys.Application calls the pageLoad function.

Tip

Tip

If you include JavaScript code in the ASPX page that references the Microsoft AJAX Library, it must be located after the page’s form element, which is where the AJAX library runtime is included on the page. If code referencing the library is included in the page header, the library will not have been loaded when the code attempts to execute. Ideally, all significant JavaScript code will be external to the page in a JavaScript file.

A pageLoad function should be used to initialize the page if there is logic specific to the page, whereas discreet components should add handlers to the init and load events, typically in external scripts. Remember that both the init and load events fire before the pageLoad function is called and have the advantage of being able to call multiple event handlers. The pageLoad function should only be defined on the page itself, not in code libraries that can be deployed to multiple pages. Example 5-2 and Example 5-3 demonstrate the order of execution: initialization, the load event, and finally the pageLoad function.

Example 5-2. The pageLoad function is used to handle page loading (Web/Sys.Application.aspx).

<%@ Page Language="C#" %>
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
  <title>Sys.Application Runtime Demo</title>
  <script type="text/javascript">
    function pageLoad() {
        Sys.Debug.trace('Page Load: pageLoad function on page.'),
    }
  </script>
</head>
<body>
    <form id="form1" runat="server">
        <asp:ScriptManager ID="ScriptManager1" runat="server" >
            <Scripts>
                <asp:ScriptReference Path="SysApplication.js" />
            </Scripts>
        </asp:ScriptManager>
        <div style="width:100%; height:200px">
            <textarea cols="80" rows="10" id="TraceConsole"></textarea>
        </div>
    </form>

</body>
</html>

Example 5-3. Sys.Application is used to initialize the page runtime and library components through the events init and load (Web/SysApplication.js).

Type.registerNamespace('SysAppDemo'),

SysAppDemo.Init = function() {
    Sys.Debug.clearTrace();
    Sys.Debug.trace(
        'SysAppDemo.Init: used to initialize SysAppDemo library component(s)'),
}

SysAppDemo.Load = function() {
    Sys.Debug.trace(
        'SysAppDemo.Load: used to load SysAppDemo library component(s)'),
}

// An init handler is used to initialize library components
Sys.Application.add_init(SysAppDemo.Init);

// A load handler is used to handle data loading for library components
Sys.Application.add_load(SysAppDemo.Load);

The output of Example 5-2 and Example 5-3 is shown in Figure 5-1, demonstrating the order of execution.

The application fires the init and load events before calling pageLoad.

Figure 5-1. The application fires the init and load events before calling pageLoad.

Example 5-2 and Example 5-3 demonstrate the usual way to include and instantiate script libraries. When the ScriptManager control loads scripts, it injects script elements into the DOM sequentially and waits for each script to be loaded before it continues. The Sys.Application.notifyScriptLoaded method is used to notify Sys.Application that a script has been loaded successfully. This method is the only reliable way across browsers for an application to know that a script was loaded. In compiled JavaScript resources, the ASP.NET AJAX runtime injects the following line, but you should include it in all file-based scripts.

Sys.Application.notifyScriptLoaded();

Important

Important

In each JavaScript file that is to be added with the ScriptManager or through the Sys.Application object, include the JavaScript call Sys.Application.notifyScriptLoaded();.

As an alternative to loading scripts through the ASP.NET ScriptReference property of the ScriptManager control, you can load scripts using the Sys._ScriptManager class. The Sys._ScriptManager class can be used to load scripts after the page is loaded and can also be used to load scripts as needed throughout the page’s life cycle.

Because ScriptManager blocks page rendering until all script resources are loaded, queuing up script references after the page loads can dramatically improve page-loading performance. This method is preferred for script libraries that enhance page functionality but aren’t required for the initial page rendering. For example, consider a wiki page with a content area, tag cloud, and navigation controls. The navigation controls and initial content should be rendered as soon as possible as the page is loaded. The page itself will be very lightweight and render immediately, and the content will be rendered as soon as it is retrieved from a client cache or a back-end system. In this design, the scripts for the navigation controls and initial content should be included as ScriptReference elements of the ScriptManager. Secondary functionality, such as the tag cloud, user status, and social data, can be deferred into script libraries that are placed in a queue for loading after the initial rendering of the page.

To load scripts after a page loads, you need to get a reference to the Sys._ScriptLoader object through its getInstance method, queue up script references by using the queueScriptReference method, and then call the loadScripts method. You can queue up multiple scripts and pass a callback handler, or you can let the scripts handle their own initialization through the Sys.Application init and load events, which are fired again after the scripts are loaded. Keep in mind that event handlers that were called previously are not called again. The following example demonstrates how to use the ScriptLoader object to load secondary scripts after the core runtime is loaded.

function pageLoad() {
    var scriptloader = Sys._ScriptLoader.getInstance();
    scriptloader.queueScriptReference('DynamicLoadedScript.js'),
    scriptloader.loadScripts(1000, null, null, null);
}

Another purpose of ScriptLoader is to load scripts in response to user actions during the lifetime of the page. With this technique, you can defer loading seldomly used functionality until a user needs it. Example 5-4 demonstrates using ScriptLoader to load functionality as it is needed by the runtime.

Example 5-4. Sys._ScriptLoader can be used to load scripts after the page is loaded (Web/ScriptLoader.aspx).

<%@ Page Language="C#"%>
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
    <title>Sys._ScriptLoader Demo</title>
    <script type="text/javascript">

      function pageLoad() {
        Sys.Debug.trace('Page Load: pageLoad function on page.'),

        // To load scripts after the page load, uncomment the following lines:
//        var scriptloader = Sys._ScriptLoader.getInstance();
//        scriptloader.queueScriptReference('ScriptLoader_Demo.js'),
//        scriptloader.loadScripts(1000, null, null, null);

        // To load scripts dynamically you can initiate the script load
        // through a handler:
        var target = $get('TestNode'),
        $addHandler(target, 'click', loadScriptsDynamically);
      }
    </script>
</head>
<body>
    <form id="form1" runat="server">
        <asp:ScriptManager ID="ScriptManager" runat="server" />
        <div style="width:100%; height:600px">
            <textarea cols="80" rows="10" id="TraceConsole"></textarea>

            <div id="TestNode" style="cursor:pointer;">
                Click me to load scripts.
            </div>

        </div>
    </form>
    <script type="text/javascript">
        function loadScriptsDynamically(){
            var scriptloader = Sys._ScriptLoader.getInstance();
            scriptloader.queueScriptReference('ScriptLoader_Demo.js'),
            scriptloader.loadScripts(1000, scriptLoadedCallback,
                scriptFailedCallback, scriptTimedOutCallback);
        }
        function scriptLoadedCallback(scriptLoader){
            // scriptLoader is of type Sys._ScriptLoader
            Sys.Debug.trace(
                'Scripts have been loaded successfully through the script loader.'),
            Sys.Debug.assert(
                typeof(ScriptLoaderDemo.DynamicScriptType) == 'function',
                'Script did not load succesfully!'),
        }
        function scriptFailedCallback(scriptLoader){
            Sys.Debug.traceDump(scriptLoader);
            Sys.Debug.fail('Script load failed.'),
        }
        function scriptTimedOutCallback(scriptLoader){
            Sys.Debug.traceDump(scriptLoader);
            Sys.Debug.fail('Script load timed out.'),
        }
    </script>
</body>
</html>

When script is loaded dynamically, the init and load events are raised once more when the script components, as shown in Example 5-5.

Example 5-5. load and init can be used to initiate code in scripts after the page is loaded (Web/ScriptLoader_Demo.js).

/// <reference name="MicrosoftAjax.js"/>
Type.registerNamespace('ScriptLoaderDemo'),

Sys.Debug.trace('Dynamically loaded script.'),

ScriptLoaderDemo.Init = function() {
    Sys.Debug.trace(
        'ScriptLoaderDemo.Init: used to initialize library component(s)'),
}

ScriptLoaderDemo.Load = function() {
    Sys.Debug.trace(
        'ScriptLoaderDemo.Load: used to load library component(s)'),
}

ScriptLoaderDemo.DynamicScriptType = function() { }

// The init event will be raised to initialize dynamically loaded script
Sys.Application.add_init(ScriptLoaderDemo.Init);
// The load event will be raised to load dynamically loaded script components
Sys.Application.add_load(ScriptLoaderDemo.Load);

// Lets Sys.Application know that the script has loaded.
Sys.Application.notifyScriptLoaded();

Loading scripts dynamically is an advanced technique, but can add significant new capabilities to your AJAX application by loading components when needed rather than predetermining the JavaScript runtime at page load.

Sys.Debug

Sys.Debug provides debugging and tracing capabilities to a JavaScript application. This object is similar to System.Diagnostics.Debug in the .NET Framework. You can use this object to log events to the JavaScript console or the Visual Studio output window.

Note

Note

In addition to Visual Studio’s output window, the JavaScript console is available as a tab in FireBug (FireFox’s free developer extension, available at www.getfirebug.com) or as the Script Console in the Internet Explorer Web Developer Extensions (available at http://projects.nikhilk.net).

The Sys.Debug object is instantiated by the Microsoft AJAX Library as an object of type Sys._Debug. This object is available in both debug and release scripts and can be set in script by the field Sys.Debug.isDebug. The value of isDebug is set to true in the debug script and to false in the release script. However, you can set isDebug to true or false through script. If you are using the Microsoft AJAX Library as a resource from the System.Web.Extensions assembly, debug scripts are enabled through web.config.

Debug scripts are enabled the same way that you enable debugging in an ASP.NET application—through the /configuration/system.web/compilation node. To enable debug mode through web.config, find the compilation node under system.web and then set debug="true". The following excerpt shows how to enable debug mode:

<configuration>
  <system.web>
    <compilation debug="true"/>
  </system.web>
<configuration>

Diagnostics with Sys.Debug.trace

You can write a trace message to the script console through the statement Sys.Debug.trace(message). This is perhaps the most useful way to quickly diagnose what is happening in the AJAX application. The script console is implemented in several ways. In FireFox, the script console is implemented in browser extensions such as Firebug (http://www.getfirebug.com), and in Internet Explorer the script console is implemented in browser extensions such as Web Development Helper (http://projects.nikhilk.net). When the Visual Studio debugger is attached to Internet Explorer, the trace is written to the output window, similarly to the .NET method System.Diagnostics.WriteLine(text). You can also implement the script console as a text area in the page. Figure 5-2 demonstrates the trace output in Firebug’s console, and Figure 5-3 demonstrates trace output written to the HTML script console as well as the Visual Studio output window in Internet Explorer and Visual Studio.

The Firebug debug console in Mozilla Firefox displays data from the Sys.Debug.trace method.

Figure 5-2. The Firebug debug console in Mozilla Firefox displays data from the Sys.Debug.trace method.

The Internet Explorer integrated debug environment with Visual Studio and the Debug Console.

Figure 5-3. The Internet Explorer integrated debug environment with Visual Studio and the Debug Console.

To write an HTML script console that runs entirely in the browser, create a text area in the page with the ID "TraceConsole". This could also be created through debug scripts as demonstrated in Example 5-6. This code sample shows the implementation of an HTML script console that runs in the browser for Internet Explorer only. To clear the TextBox trace console, use the method Sys.Debug.clearTrace().

Example 5-6. A text area with the ID "TraceConsole" can be used with Sys.Debug (Web/DebugConsole.aspx).

function initDebug(){
    if (Sys.Debug.isDebug){
        if (Sys.Browser.agent == Sys.Browser.InternetExplorer){
            var console = $get('TraceConsole'),
            if (console == null){
                console = document.createElement('TEXTAREA'),
                console.id = 'TraceConsole';
                document.body.appendChild(console);
            }
        }
    }
}

function pageLoad() {
    initDebug();
    Sys.Debug.trace('Hello, World!'),
}

Diagnostics with Sys.Debug.traceDump(object, name)

Sys.Debug.traceDump dumps object fields to the script console. The name parameter is used only as a label in the trace output. This statement can be helpful in dumping out object state to diagnose errors. You can call this method from the browser’s script console (Firebug for FireFox, or Web Development Helper for Microsoft Internet Explorer) or from the immediate window in Visual Studio. For example, to dump the Sys.Application object to the console, use the following code:

Sys.Debug.traceDump(Sys.Application);

Diagnostics with Sys.Debug.assert(condition, message, displayCaller)

Sys.Debug.assert is used to assert that the condition argument evaluates to true. If the condition is false, a dialog box displays the message through the JavaScript confirm function, giving you an opportunity to launch the debugger. If displayCaller is true, the dialog displays the full function body of the calling method.

For example, the following code is used to assert that the Sys.Application instance is initialized. If it is not initialized, it prompts the user to break into the debugger:

Sys.Debug.assert(Sys.Application.get_isInitialized(),
    'Expected an initialized Sys.Application!', true);

Diagnostics with Sys.Debug.fail()

Sys.Debug.fail is used to break into the script debugger after writing the message to the console. This is a simple wrapper for the debugger JavaScript statement, as the debugger statement does not exist in all browsers, although it is integrated in both Internet Explorer and FireFox browsers.

To break into the debugger, include the following code:

Sys.Debug.fail();

Sys.Browser

The Sys.Browser object is used to reference the current browser application instance, such as Internet Explorer or Firefox. Sys.Browser.agent is an object instance that is only useful to compare against Sys.Browser.InternetExplorer, Sys.Browser.FireFox, Sys.Browser.Safari, or Sys.Browser.Opera (the four major browsers). Example 5-7 demonstrates a simple switch based on the current browser version.

Example 5-7. Sys.Browser can be used to implement browser-specific code.

function pageLoad() {

    switch(Sys.Browser.agent){
        case Sys.Browser.InternetExplorer:
            Sys.Debug.trace('We Love Redmond'),
            break;
        case Sys.Browser.Firefox:
            Sys.Debug.trace('We Love Mountain View'),
            break;
        case Sys.Browser.Safari:
            Sys.Debug.trace('We Love Cupertino'),
            break;
        case Sys.Browser.Opera:
            Sys.Debug.trace('We Love Oslo'),
            break;
    }
    Sys.Debug.trace(
        String.format('Browser: {0}; version {1}; supports debugger: {2}',
            Sys.Browser.name,
            Sys.Browser.version,
            Sys.Browser.hasDebuggerStatement) );
}

Sys.Browser can be used in your own applications, but it is mainly used in the AJAX library to determine browser capabilities. To see exactly how Sys.Browser is constructed, it’s best to reference the source code, which is shown in Example 5-8.

Example 5-8. Sys.Browser is used to determine the current browser application. The code sample is from the framework, defined in MicrosoftAjax.debug.js.

Sys.Browser = {};
Sys.Browser.InternetExplorer = {};
Sys.Browser.Firefox = {};
Sys.Browser.Safari = {};
Sys.Browser.Opera = {};
Sys.Browser.agent = null;
Sys.Browser.hasDebuggerStatement = false;
Sys.Browser.name = navigator.appName;
Sys.Browser.version = parseFloat(navigator.appVersion);
if (navigator.userAgent.indexOf(' MSIE ') > -1) {
    Sys.Browser.agent = Sys.Browser.InternetExplorer;
    Sys.Browser.version = parseFloat(navigator.userAgent.match(
        /MSIE (d+.d+)/)[1]);
    Sys.Browser.hasDebuggerStatement = true;
}
else if (navigator.userAgent.indexOf(' Firefox/') > -1) {
    Sys.Browser.agent = Sys.Browser.Firefox;
    Sys.Browser.version = parseFloat(navigator.userAgent.match(
        / Firefox/(d+.d+)/)[1]);
    Sys.Browser.name = 'Firefox';
    Sys.Browser.hasDebuggerStatement = true;
}
else if (navigator.userAgent.indexOf(' AppleWebKit/') > -1) {
    Sys.Browser.agent = Sys.Browser.Safari;
    Sys.Browser.version = parseFloat(navigator.userAgent.match(
        / AppleWebKit/(d+(.d+)?)/)[1]);
    Sys.Browser.name = 'Safari';
}
else if (navigator.userAgent.indexOf('Opera/') > -1) {
    Sys.Browser.agent = Sys.Browser.Opera;
}

The Network Library in Sys.Net

The Microsoft AJAX Library provides a network stack for handling AJAX calls from the browser to local Web services. Sys.Net.WebRequest is the main class you’ll use to implement AJAX functionality of this sort. It is a JavaScript version of the C# class System.Net.HttpWebRequest and is used to make network calls for which you need full control over the request object and response callback.

Note

Note

Because of browser security, you can make network requests only to the same domain as the Web server that is hosting the AJAX page. This means that you cannot make remote Web requests—you can only access services on the Web server that is local to the Web application. Limiting network requests to the local Web server is a security implementation of the browser, not a limitation of the Microsoft AJAX Library. If scripts were able to make cross-domain calls, they would also be able to send data to remote resources. However, both images and scripts can be used to get and send data to remote servers. You can access remote script resources to load data by using a wrapped JSON message format known as JSONP, but this is a workaround and not part of the core library. A proposal for a cross-site XMLHttpRequest exists, but at the time of this writing it is only a working document.

To create a Web request, instantiate a new Sys.Net.WebRequest and set the URL and callback handler. The HTTP verb, request body, and HTTP headers can also be set, giving you full control over the network call. The invoke method initiates the call, and after the call is invoked you can get a WebRequestExecutor object by accessing the executor property. Sys.Net.WebRequestExecutor is the class that executes the actual request, and it is instantiated only after the request is invoked. The following code sample demonstrates a basic request and obtains a reference to the Web request executor after invoking the request:

var net = new Sys.Net.WebRequest();
net.set_url('example.WCF'),
net.add_completed(callback);
net.invoke();
var networkCall = net.get_executor();

The WebRequestExecutor object is useful for canceling a network call. For example, you might be loading data in an AJAX call when a user changes his mind and decides to navigate to another area of data entry. In this case, you can call the abort method of the executor object, canceling the call. The callback is not processed on an aborted operation, although it is processed on an operation that times out. To cancel the network call, simply call abort on the executor as follows:

networkCall.abort();

A full code sample demonstrating a simple network call, a cancel operation, and the callback is shown in Example 5-9

Example 5-9. Network requests are created using Sys.Net.WebRequest and executed with Sys.Net.WebRequestExecutor(SysNet.aspx).

// Processes the main page logic, demonstrating the network request.
function pageLoad() {
    // Create a new object to represent the application
    window.MyApplication = new Object();

    var context = {Method : 'pageLoad'};
    var net = new Sys.Net.WebRequest();
    net.set_url('timeout.aspx'),
    net.set_timeout(1000);
    net.set_userContext(context);
    net.add_completed(callback);

    net.invoke();
    // Set our application's active network call to the current request,
    // so the user can cancel the request
    window.MyApplication.networkCall = net.get_executor();

    // Add a cancel handler to the CancelButton and display it.
    var cancelButton = $get('CancelButton'),
    cancelButton.style.display='';
    Sys.UI.DomEvent.addHandler(cancelButton, 'click', abortRequest);
}
// Cancels the current web request. Could be called from a cancel button or by an
// impatient user
function abortRequest(){
    if (window.MyApplication.networkCall)
        window.MyApplication.networkCall.abort();
    window.MyApplication.networkCall = null;
}

// Processes the web request
function callback(response, method, context){
    // Adds intellisense to the method body:
    if (response == null){
        response = new Sys.Net.WebRequestExecutor(); throw 'Error';
    }

    // Clear the cancel button and the cancel handler.
    var cancelButton = $get('CancelButton'),
    cancelButton.style.display='none';
    Sys.UI.DomEvent.removeHandler(cancelButton, 'click', abortRequest);

    // Clear the application's networkCall reference
    window.MyApplication.networkCall = null;

    // Get the response, if it's available:
    if (response.get_responseAvailable()){
        var xml = response.get_xml();
    }
    var timdOut = response.get_timedOut();
    var aborted = response.get_aborted();
    if (aborted){
        Sys.Debug.trace('aborted!!!'),
    }
}

Additionally, each page includes an instance of Sys.Net.WebRequestManager, which is used to define global settings for Web requests, such as the timeout and callback handlers. If you need to define global settings or handlers on AJAX methods, use the Sys.Net.WebRequestManager object (which is actually an instance of the private class Sys.Net._WebRequestManager). To set the default timeout for Web requests, use the defaultTimeout property as follows:

Sys.Net.WebRequestManager.set_defaultTimeout(1000);

Finally, the network library also includes the Sys.Net.WebServiceProxy class, which is used as a base class for Web service proxies generated by script-enabled Windows Communication Foundation (WCF) or ASMX Web services. You will generally not use the WebServiceProxy class directly, but you’ll use an implementation that calls your Web service through the auto-generated proxy. You can, however, use this class to call an arbitrary Web service, although the REST architecture is preferred if you are not generating a script proxy.

JavaScript Serialization with Sys.Serialization

The Sys.Serialization.JavaScriptSerializer class is used to serialize and deserialize JavaScript types into JSON objects. It is also used specifically to process date values serialized by ASP.NET AJAX server code. To serialize your JavaScript object into a JSON-formatted string, use the serialize method as follows:

var json = Sys.Serialization.JavaScriptSerializer.serialize(value);

Conversely, if you have a JSON string returned from a WCF service, you can use the deserialize method to get a JavaScript object from the JSON string, as the following code sample demonstrates:

var myObject = Sys.Serialization.JavaScriptSerializer.deserialize(value);

The deserialize method is not needed for general purpose deserialization but is used when the JSON string is generated from ASP.NET AJAX server serialization methods.

Application Services with Sys.Services

The Sys.Services namespace is used to integrate ASP.NET services such as authentication, profile, and roles with the client runtime. These services are integrated into the Sys.Application object and expose application services through integrated Web service proxies and script injection during the server-side page load. I’ll discuss the Sys.Services namespace in depth in Chapter 6.

Browser Extensions with Sys.UI

The Sys.UI namespace provides a framework for programming with DOM objects in the browser. It consists of base classes for control developers to implement as well as utility methods you can use to work with the DOM and DOM events.

Tip

Tip

The Control and Behavior classes are base classes used to build reusable components. I’ll talk about both classes in further detail in Chapter 7, and Chapter 8.

Programming DOM Elements with Sys.UI.DomElement

Sys.UI.DomElement is the main class that you’ll use to work with DOM objects. It is useful for attaching event handlers and CSS style classes and for otherwise accessing or manipulating a DOM object. DomElement is a static class that contains utility methods for working with DOM elements.

Sys.UI.DomElement.getElementById(id, parent)

Sys.UI.DomElement.getElementById extends the basic JavaScript method document.getElementById with a second parameter: the parent element in which to search. Passing a second parameter limits the document’s search scope and is more efficient than searching the entire document. Because this method is one of the most common JavaScript methods you’ll use, it also has a shortcut method, $get.

For example, to get the document object "editButton" use the following code:

var editButton = $get('editButton'),

If you know the scope of the search and already have a reference to the parent element, limit the scope of the search with the parent parameter, as the following code demonstrates:

var userStatusControl = $get('UserStatusControl'),
if (Sys.Services.AuthenticationService.get_isLoggedIn()){
    var loginButton = $get('loginButton', userStatusControl);
    if (loginButton) Sys.UI.DomElement.setVisible(loginButton, false);
}

Tip

Tip

It’s a best practice to check that the object exists before using it! Otherwise, you’ll get the famous error message, "Null is null or not an object."

Sys.UI.DomElement.getLocation(element)

The getLocation method is used to get a Sys.UI.Point object that contains the element’s x and y coordinates. The following code gets the target element’s location:

var loc = Sys.UI.DomElement.getLocation(target);

The return object is of the type Sys.UI.Point, which is a simple data structure defined by the following code in the Microsoft AJAX Library:

Sys.UI.Point = function Sys$UI$Point(x, y) {
    this.x = x;
    this.y = y;
}
Sys.UI.Point.registerClass('Sys.UI.Point'),

Sys.UI.DomElement.setLocation(element, Sys.UI.Point)

The setLocation method is used to set an element’s x and y coordinates. The following code gets the target element’s location by using getLocation and then sets the location to be 10 pixels offset in each direction:

var loc = Sys.UI.DomElement.getLocation(target);
Sys.UI.DomElement.setLocation(target, loc.x + 10, loc.y + 10);

Sys.UI.DomElement.getBounds(element)

The getBounds method is used to get the element’s location, width, and height. This information can be useful when you want to position elements in relation to each other. For example, you might want to resize or reposition an element at run time in response to user actions or the size of the user’s browser window. The following example shows how to get the bounds of an object using getBounds:

var bounds = Sys.UI.DomElement.getBounds(target);

The getBounds method returns an object of type Sys.UI.Bounds, a simple data structure similar to Sys.UI.Point. The following Microsoft AJAX Library source code defines Sys.UI.Bounds:

Sys.UI.Bounds = function Sys$UI$Bounds(x, y, width, height) {
    this.x = x;
    this.y = y;
    this.height = height;
    this.width = width;
}
Sys.UI.Bounds.registerClass('Sys.UI.Bounds'),

To demonstrate the usefulness of getBounds, you can build a browser application that maintains UI elements such as navigation and header controls but that implements a scrolling region for the content. To implement this, you can get the window’s height and set the content area to the height of the window minus the height of the header. Because you might be drawing the header and other controls dynamically, you can use the bounds of the header to determine the height and then set the height of the content area each time the user resizes the page. Figure 5-4 shows a simple user interface in which the content of the page is scrollable but the header and navigation placeholder are not.

The DomElement class can be used to size and manage scrolling elements on the page.

Figure 5-4. The DomElement class can be used to size and manage scrolling elements on the page.

To implement this functionality, the code in Example 5-10 gets the height of the header and then calculates the height that the content div needs to be to allow scrolling.

Tip

Tip

Determining the window’s height is straightforward in FireFox but not in Internet Explorer. In Internet Explorer, you must get the document.body.parentNode’s height. The following code can be used to determine the height of the Web browser in both Internet Explorer and Firefox. Because each browser uses a slightly different version of the DOM model, you can simply determine whether the window implements the innerHeight property and use that if it does.

var height;
if (window.innerHeight){
    height=window.innerHeight;
}
else if (document.body.parentNode.clientHeight){
    height = document.body.parentNode.clientHeight;
}
else if (document.body.offsetHeight){
    height = document.body.offsetHeight;
}

Example 5-10. You can use Sys.UI.DomElement methods to maintain a scrolling region on the page (Web/SysUI.Sizing.aspx).

<%@ Page Language="C#" %>
<html xmlns="http://www.w3.org/1999/xhtml">
<head runat="server">
    <title>Sizing example with Sys.UI</title>
<script type="text/javascript">

function pageLoad() {
    $addHandler(window, 'resize', resizeElements);
    resizeElements();
}

function resizeElements(){
    var height;
    if (window.innerHeight){
        height=window.innerHeight;
    }
    else if (document.body.parentNode.clientHeight){
        height = document.body.parentNode.clientHeight;
    }
    else if (document.body.offsetHeight){
        height = document.body.offsetHeight;
    }
    var pageHeader = $get('pageHeader'),
    var headerBounds = Sys.UI.DomElement.getBounds(pageHeader);
    var content = $get('MainContent'),
    content.style.height = height - headerBounds.height + 'px';
}

</script>
    <style type="text/css">
        #pageHeader {padding:10px; background-color:Silver; font-weight:bold;
    </style>
</head>
<body style="padding:0px; margin:0px; overflow:hidden;" scroll="no" >
    <div id="pageHeader">
        Service-Based AJAX on the Microsoft Platform
    </div>
    <table style="width:100%; height:100%" cellpadding="0" cellspacing="0">
        <tr valign="top">
            <td>
                <div id="NavigationPlaceholder" style="width:250px;"/>
            </td>

            <td>
                <div id="MainContent" style="overflow:auto;" >
                    <p>
                        <!-- Content shortened. In practice, this would be the main
                             content region, containing the main text of the wiki
                             in our example application. -->
                        Lorem ipsum dolor sit amet, consectetuer adipiscing elit.
                    </p>
                </div>
            </td>
        </tr>
    </table>
    <form id="form1" runat="server">
        <asp:ScriptManager ID="ScriptManager1" runat="server" />
    </form>
</body>
</html>

Programming DOM Events with Sys.UI.DomEvent

Generally speaking, DOM events are anything that a user does while interacting with the browser’s document. However, in the Microsoft AJAX Library the Sys.UI.DomEvent class wraps the browser event with a rich event object that is compatible across browsers. The Sys.UI.DomEvent can be used to handle mouse events, keyboard events, and browser events. To handle these events, use the Sys.UI.DomEvent class to add event handlers to DOM elements. Event handlers are functions that are called with a parameter of type Sys.UI.DomEvent. To add an event handler, use the addHandler method as follows:

Sys.UI.DomEvent.addHandler(target, 'click', domEventHandler);

Sys.UI.DomEvent.addHandler is also available through the shortcut reference $addHandler. The sample code above can be shortened to the following:

$addHandler(target, 'click', domEventHandler);

To demonstrate a simple event handler, you can dump the output of a DOM event to the trace log. Example 5-11 demonstrates a simple event handler that logs the result of a DOM event to the trace console.

Example 5-11. Sys.UI.DomEvent can be used to add handlers and process DOM events (Web/Sys.UI.DomEvent.aspx).

<%@ Page Language="C#" %>
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
    <title>Sys.UI.DomEvent Sample</title>
<script type="text/javascript">
function pageLoad() {
    Sys.Debug.clearTrace();
    var target = $get('TestNode'),
    Sys.UI.DomEvent.addHandler(target, 'click', domEventHandler);
}

function domEventHandler(domEvent){
    if (domEvent == null){ domEvent = new Sys.UI.DomEvent();
        throw 'InvalidOperation';}
    Sys.Debug.traceDump(domEvent);
}
</script>

</head>
<body>
    <form id="form1" runat="server">
        <asp:ScriptManager ID="ScriptManager" runat="server"/>
    </form>
    <div id="TestNode" style="cursor:pointer;">Click me to test </div>
    <textarea cols="80" rows="10" id="TraceConsole"></textarea>
</body>
</html>

In practice, you’ll use a DOM event to initiate AJAX calls such as loading data, saving data, or manipulating screen elements. Example 5-12 demonstrates the DomEvent class and its properties in the event handler. The code also uses methods in Sys.UI.DomElement to manipulate the browser objects.

Example 5-12. The DomEvent class is used to process user actions (Web/Sys.UI.DomEvent2.aspx).

<%@ Page Language="C#" %>
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
    <title>Sys.UI.DomEvent Sample 2</title>
    <style type="text/css">
        .superfly{ background-color:Yellow; }
        .superflied{  background-color:Lime; }
    </style>
    <script type="text/javascript">

function pageLoad() {
    var target = $get('TestNode'),
    Sys.UI.DomEvent.addHandler(target, 'click', testMethod);
}
function testMethod(event){
    if (event == null){ event = new Sys.UI.DomEvent(); throw 'Invalid Operation';}
    var target = event.target;
    Sys.Debug.traceDump(event);

    Sys.UI.DomElement.addCssClass(target, 'superfly'),
    Sys.UI.DomElement.toggleCssClass(target,'superflied'),
    var bounds = Sys.UI.DomElement.getBounds(target);
    Sys.UI.DomElement.setLocation(target, bounds.x + 10, bounds.y + 10);

    var target2 = Sys.UI.DomElement.getElementById('TestNode2', target);
    Sys.UI.DomElement.setVisible(target2, !Sys.UI.DomElement.getVisible(target2));
}

    </script>
</head>
<body>
    <form id="form1" runat="server">
        <asp:ScriptManager ID="ScriptManager1" runat="server" />

        <div id="TestNode" style="cursor:pointer;">Click me to test
            <span id="TestNode2" style="display:none;">...no, really!</span>
        </div>
    </form>
</body>
</html>

With browser functionality implemented in Sys.UI, network operations implemented in Sys.Net, application functionality implemented in Sys.Application, debugging support implemented in Sys.Debug, and native and global JavaScript types enhanced and defined in the Sys namespace, the Microsoft AJAX Library makes client-side application programming a viable model. On top of this library you can develop components based on Sys.Component, behaviors based on Sys.UI.Behavior, and controls based on Sys.UI.Control, as you’ll see in the following chapters. By using the library’s type system and the ScriptManager control, you can package your application into discrete libraries and page runtime applications, and you can deliver your components to multiple applications as components.

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

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