Case Study: Creating a Wiki Application Control

Controls can be used to apply JavaScript behavior and a rich, event-based API to DOM elements. However, in typical applications you want to develop controls that provide application functionality that is bound to server-side data sources. After all, without a Web service back end, AJAX components are only fancy JavaScript controls. To provide a rich AJAX user experience, you need to provide real-time data integration with back-end services.

An application control is usually a composite control or a DIV-based control that provides much of the functionality for a page. In the following example, we’ll build an application control for the knowledge base application we’ve been developing throughout the book. First we’ll develop a simple parsing library of static text-parsing functions that can be extended to provide full wiki capability. Then we’ll develop a control for rendering, navigating, creating, and editing wiki content.

Figure 8-2 illustrates the wiki application. The SOAjax.Application component is used by the page to control the login elements as well as the wiki application control. The main content of the page is implemented by SOAjax.Controls.WikiControl, which we’ll build in the following code samples. This control is browsable and editable using standard wiki markup.

The SOAjax.Controls.WikiControl control is used to implement the main user interface for a wiki application.

Figure 8-2. The SOAjax.Controls.WikiControl control is used to implement the main user interface for a wiki application.

The wiki application in the following example is based on a wiki control that provides wiki rendering, editing, and navigation; the application runtime component from Chapter 7 that provides authorization and profile information; and a page class that creates the components and binds them to HTML elements. The application runtime that we developed and tested previously doesn’t need to be rewritten for this application because we can add event handlers and respond to authentication events and properties from the page class, binding these properties to the wiki control. For example, we’ll display a subset of the application for an anonymous user and provide editing capabilities when the user is authenticated.

Example 8-11 displays the ASPX page for the wiki application (found in the ASPX file wiki.aspx in this chapter’s sample code). The _WikiContent element is used to render the wiki, and the editing controls are used to switch editing modes. Because we support an anonymous user for read access, the editing controls are conditionally displayed by the page’s JavaScript code. Notice that I included a reference to the Windows Communication Foundation (WCF) endpoint dataservice.svc in the ScriptManager. We use these Web service methods in the wiki control. In this sample we also use JSON-formatted data and the ASP.NET AJAX generated JavaScript proxies, incorporating much of the logic from earlier chapters but wrapping it in a Control class implementation.

Example 8-11. The wiki page elements are defined in the ASPX page and controls are defined in JavaScript libraries (Web/Wiki.aspx).

<%@ Page Language="C#" %>
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
    <title>Service-Oriented AJAX</title>
    <link href="Style/StyleSheet.css" rel="stylesheet" type="text/css" />
</head>
<body>
  <form id="form1" runat="server">
    <asp:ScriptManager ID="AjaxScriptManager" runat="server"
            RoleService-LoadRoles="true"
            ProfileService-LoadProperties="startTopic">
        <Scripts>
            <asp:ScriptReference Path="~/Script/WikiLibrary.js" />
            <asp:ScriptReference Path="~/Script/SOAjax.js" />
            <asp:ScriptReference Path="~/Script/Tooltip.js" />
            <asp:ScriptReference Path="~/Script/SmartInputControl.js" />
            <asp:ScriptReference Path="~/Script/ApplicationRuntime.js" />
            <asp:ScriptReference Path="~/Script/WikiControl.js" />
            <asp:ScriptReference Path="~/Script/WikiPage.js" />
        </Scripts>
        <Services>
            <asp:ServiceReference Path="services/dataservice.svc" />
        </Services>
    </asp:ScriptManager>
    <div id="pageHeader">
        Service-Oriented AJAX on the Microsoft Platform

        <div id="logoutControl" class="loginControl" style="display: none;" >
            <button id="ButtonLogout" type="button" value="Logout">Logout</button>
        </div>

        <div style="display: none;" id="loginControl" class="loginControl">
            <div class="widgetTitle" id="loginLink">Login</div>
            <div class="lightPadding" id="loginUI" style="display: none;">
                Username <br />
                <input type="text" id="usernameinput" /><br /><br />

                Password <br />
                <input type="text" id="passwordinput" /><br /><br />

                <button id="ButtonLogin" value="Login" type="button"
                title="Login">Login</button> &nbsp;
                <button id="ButtonCancelLogin" value="Cancel" type="button"
                title="Cancel">Cancel</button>
                <div id="loginFeedbackDiv">
                </div>
            </div>
        </div>
    </div>

    <div id="pageBody">
        <table style="width:100%; height:100%" cellpadding="0" cellspacing="0">
            <tr valign="top">
                <td width="150px" style="background-color:#EFEFEF; padding:15px;">

                    <div id="_EditControls" style="display:none;">
                        <span id="_editButton" class="Button">Edit</span>
                        <span id="_saveButton"
                            style="display:none;" class="Button">Save</span>
                        <span id="_cancelButton"
                            style="display:none;" class="Button">Cancel</span>

                        <div style="padding:10px;">
                            Tag: <input type="text" id="tagInput" maxlength="50" />
                        </div>

                    </div>
                    <div id="NavigationPlaceholder"
                        style="width:150px; background-color:#EFEFEF">
                        <!-- Navigation goes here -->
                    </div>
                </td>

                <td width="100%">
                    <div id="MainContent" style="overflow:auto;" >
                        <div id="_WikiContent"/>
                    </div>
                </td>
            </tr>
        </table>
    </div>
  </form>
</body>
</html>

Usually in an AJAX application you use XSLT or JavaScript templates for rendering. In the next chapter we’ll look more closely at AJAX rendering techniques and at the XmlControl implementation that binds a control to an XML data source. For the sample application, however, we need to develop a wiki parser for rendering HTML from wiki markup. We can use regular expressions in JavaScript to parse the wiki text and create HTML and add event handlers to handle navigation. Example 8-12 contains the text-parsing methods to convert wiki syntax into HTML. To avoid writing more script to change HTML into wiki text, we store and edit the wiki text and only use the HTML conversion code in the rendering method.

Tip

Tip

The wiki parsing in this example is very basic. For more information on wiki markup, search for "wiki creole". Additional parsing methods are included in the sample code downloads.

Example 8-12. JavaScript can be used to create simple wiki parsing methods (Web/Scripts/WikiLibrary.js).

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

// Simple Wiki parsing library:
// converts the HTML format to WIKI text
SOAjax.Wiki.convertToWiki = function(element){
  var links = element.getElementsByTagName('span'),
  var placeholders = new Array();
  for(var i=0;i<links.length;i++){
     if (links[i].className=='WIKILINK')
        Array.add(placeholders, links[i]);
  }
  for(var ix=0;ix<placeholders.length;ix++){
     var wik = document.createTextNode(String.format('[[{0}]]',
         SOAjax.getText(placeholders[ix])) );
     var old = placeholders[ix].parentNode.replaceChild(wik, placeholders[ix]);
     placeholders[ix] = null;
  }
}

SOAjax.getText = function(node) {
    if (node == null) return '';
    if (node.innerText) return node.innerText;
    else if (node.textContent) return node.textContent;
    else return '';
}

// Converts WIKI text to HTML format
SOAjax.Wiki.wikiToHtml = function(wikiText){
  var rex = new RegExp('\[\[([^\]]+)\]\]',"mg");
  var match = rex.exec(wikiText);
  while (match){
     var linked = String.format("<span class="WIKILINK">{0}</span>",match[1]);
     wikiText = wikiText.replace(match[0],linked);
     match = rex.exec(wikiText);
  }
  return wikiText;
}

// Adds click handlers to the WIKILINK spans
SOAjax.Wiki.addWikiHandlers = function(element){
  var links = element.getElementsByTagName('span'),
  for(var i=0;i<links.length;i++){
     if (links[i].className=='WIKILINK')
         $addHandler(links[i], 'click', SOAjax.Wiki.Link);
  }
}

SOAjax.Wiki.Link = function(clickEvent) {
    if (clickEvent == null) { clickEvent = new Sys.UI.DomEvent(); }
    var wiki = SOAjax.FindParentControl(clickEvent.target, SOAjax.Wiki.WikiControl);
    if (wiki == null) return;
    var link = null;
    if (clickEvent.target.innerText)
        link = clickEvent.target.innerText;
    else if (clickEvent.target.textContent)
        link = clickEvent.target.textContent;
    if (link != null && link != '')
        wiki.load(link);
}

After creating a simple parsing library with JavaScript, we’re ready to create a wiki control. The wiki control implements both the rendering and editing elements of the page and references button controls that are used to switch into editing mode or save content. Because we don’t have special needs for buttons at the moment, we can use simple DOM elements for the buttons. Example 8-13 shows the starter code for the wiki control, with the properties for DOM elements defined and placeholders for Web service event handlers.

Example 8-13. A Control class can be implemented to provide content rendering and editing capabilities (Web/Script/WikiControl.js).

/// <reference name="MicrosoftAjax.js"/>
/// <reference path="SOAjax.js"/>
/// <reference path="DataService.js"/>
/// <reference path="WikiLibrary.js"/>

SOAjax.Wiki.WikiControl = function(element) {
    SOAjax.Wiki.WikiControl.initializeBase(this, [element]);
}

SOAjax.Wiki.WikiControl.prototype = {
    _renderDiv: null,
    _titleDiv: null,
    _editControl: null,
    _editMode: false,
    _topic: null,
    _isLoading: false,
    _catalog: 'Default',
    _rawWiki: null,
    _tagInput: null,
    _editButton: null,
    _cancelButton: null,
    _saveButton: null,

    initialize: function() {
        Sys.Debug.trace('WikiControl:initialize'),
        SOAjax.Wiki.WikiControl.callBaseMethod(this, 'initialize'),
        // custom initialization
        var control = this.get_element();

        this._titleDiv = document.createElement('DIV'),
        this._titleDiv.margin = '7px';
        this._titleDiv.style.padding = '7px';
        Sys.UI.DomElement.addCssClass(this._titleDiv, 'TitleHead'),
        control.appendChild(this._titleDiv);

        this._renderDiv = document.createElement('DIV'),
        control.appendChild(this._renderDiv);
        this._editControl = document.createElement('TEXTAREA'),
        this._editControl.style.display = 'none';
        this._editControl.style.width = '100%';
        this._editControl.style.height = '100%';
        this._editControl.style.border = 'solid 0px';
        this._editControl.style.margin = '0px';
        this._editControl.style.padding = '0px';
        control.appendChild(this._editControl);
    },


    // child controls:
    get_editButton: function() { return (this._editButton); },
    set_editButton: function(button) {
        this._editButton = button;
        $addHandler(this._editButton,
            'click', Function.createDelegate(this, this.enterEditMode));
    },

    get_cancelButton: function() { return (this._cancelButton); },
    set_cancelButton: function(button) {
        this._cancelButton = button;
        $addHandler(this._cancelButton,
            'click', Function.createDelegate(this, this.cancelEdits));
    },
    get_saveButton: function() { return (this._saveButton); },
    set_saveButton: function(button) {
        this._saveButton = button;
        $addHandler(this._saveButton,
            'click', Function.createDelegate(this, this.save));
    },

    get_tagInput: function() { },
    set_tagInput: function(input) {
        this._tagInput = input;
        this._tagInput.onkeydown =
            Function.createDelegate(this, this._onTagInputKeyDown);
    },

    get_isLoading: function() {
        return this._isLoading;
    },

    set_isLoading: function(isLoading) {
        this.raisePropertyChanged('loading'),
        this._isLoading = isLoading;
    },

    enterEditMode: function(event) {
        if (event == null) event = new Sys.UI.DomEvent();
        event.stopPropagation();
        this.set_editMode(true);
    },

    load: function(topic) {/*Placeholder*/ },
    save: function() { /*Placeholder*/ },
    get_topic: function() { return this._topic; },
    set_topic: function(topic) {
        if (this._topic !== topic) {
            this._topic = topic;
            this.raisePropertyChanged('topic'),
        }
    },

    get_editMode: function() {
        /// <value type="Boolean">True if we're in edit mode,
        /// otherwise false.</value>
        return this._editMode;
    },
    set_editMode: function(value) {
        Sys.Debug.trace('WikiControl:switching EditMode:' + value);
        if (value !== this.get_editMode()) {
            this._editMode = value;
            this.raisePropertyChanged('editMode'),
            if (!this.get_isUpdating()) {
                // switch to edit if true, otherwise render
                Sys.UI.DomElement.setVisible(this._editControl, value);
                Sys.UI.DomElement.setVisible(this._cancelButton, value);
                Sys.UI.DomElement.setVisible(this._saveButton, value);
                Sys.UI.DomElement.setVisible(this._editButton, !value);
                Sys.UI.DomElement.setVisible(this._renderDiv, !value);
            }
        }
    },

    cancelEdits: function() {
        SOAjax.ClearHandlers(this._renderDiv);
        this._renderDiv.innerHTML = '';
        this.set_editMode(false);
        this._renderDiv.innerHTML = SOAjax.Wiki.wikiToHtml(this._rawWiki);
        SOAjax.Wiki.addWikiHandlers(this._renderDiv);
        this._editControl.value = this._rawWiki;
    },


    dispose: function() {
        Sys.Debug.trace('WikiControl:dispose'),

        $clearHandlers(this._editButton);
        $clearHandlers(this._saveButton);
        $clearHandlers(this._cancelButton);

        SOAjax.Purge(this._renderDiv);
        SOAjax.Purge(this._editControl);
        var control = this.get_element();
        if (control) {
            control.removeChild(this._renderDiv);
            control.removeChild(this._editControl);
        }
        SOAjax.Wiki.WikiControl.callBaseMethod(this, 'dispose'),
    }
}
SOAjax.Wiki.WikiControl.registerClass('SOAjax.Wiki.WikiControl', Sys.UI.Control);
Sys.Application.notifyScriptLoaded();

After defining the basic control shell, we can tie into the Web service methods we defined in previous chapters and add the wiki text-parsing and rendering methods in the custom wiki JavaScript library. In Example 8-14, the load placeholder method is replaced with the Web service load implementation, including the asynchronous callback handlers. After loading is completed, the wiki library (defined earlier in Example 8-12) is used to generate HTML from the wiki text and is rendered in the control with wiki links.

Example 8-14. Controls can be implemented utilizing a Web service back end (Web/Script/WikiControl.js).

load: function(topic) {
    this.set_isLoading(true);
    this._topic = topic;
    this._titleDiv.innerHTML = topic;
    SOAjax.ClearHandlers(this._renderDiv);
    this._renderDiv.innerHTML = '';
    this._editControl.value = '';

    Sys.Debug.trace(String.format('WikiControl:load("{0}")', this._topic));
    //var control = this.get_element();

    knowledgebase.DataService.GetData(this._catalog, this._topic,
        Function.createDelegate(this, this.onLoadComplete),
        Function.createDelegate(this, this.onFailure),
        this);
},

onLoadComplete: function(wikiData, userContext, methodName) {
    var html = SOAjax.Wiki.wikiToHtml(wikiData.Body);
    this._editControl.value = wikiData.Body;
    this._rawWiki = wikiData.Body;
    SOAjax.ClearHandlers(this._renderDiv);
    this._renderDiv.innerHTML = html;
    SOAjax.Wiki.addWikiHandlers(this._renderDiv);
    this.set_isLoading(false);
}

onFailure: function(exception, userContext, methodName) {
    Sys.Debug.traceDump(exception, 'Failed callback!'),
    Sys.Debug.fail(String.format("The method {0} failed miserably! ({1})",
        methodName, exception.get_message()));
    Sys.Debug.traceDump(this, 'Failed callback!'),
}

Likewise, the save placeholder method is replaced with the Web service save operation and its callback handlers. When the user clicks the Save button (shown only in editing mode), the save method is called as the event handler. Example 8-15 demonstrates the save method integrated into the wiki control.

Example 8-15. Controls can persist data against Web services using the Microsoft AJAX Library (Web/Script/WikiControl.js).

save: function() {
    Sys.Debug.trace(String.format('WikiControl:save("{0}")', this._topic));

    this._saveButton.style.display = 'none';
    this._cancelButton.style.display = 'none';
    this._editButton.style.display = '';
    var wikiObject = new Object();
    wikiObject.Body = this._editControl.value;
    wikiObject.Title = this._topic;

    knowledgebase.DataService.SaveData(wikiObject,
        Function.createDelegate(this, this.onSaveCallback),
        Function.createDelegate(this, this.onFailure),
        null); //usercontext

    SOAjax.ClearHandlers(this._renderDiv);
    this._renderDiv.innerHTML = SOAjax.Wiki.wikiToHtml(wikiObject.Body);
    this._rawWiki = wikiObject.Body;
    SOAjax.Wiki.addWikiHandlers(this._renderDiv);
    this.set_editMode(false);
},

onSaveCallback: function() {
    Sys.Debug.trace('Save success! '),
}

With the load and save AJAX methods integrated into the control, it’s now fully functional. To sum up, the wiki control loads wiki text from a Web service in response to loading and navigation events. The wiki control switches from rendering to an edit control when the editMode property is set to true, and saves the data when the save event is fired from the DOM element that implements the Save button. The full code for the wiki control is shown in Example 8-16.

Example 8-16. The final WikiControl control (Web/Script/WikiControl.js).

/// <reference name="MicrosoftAjax.js"/>
/// <reference path="SOAjax.js"/>
/// <reference path="DataService.js"/>

Type.registerNamespace('SOAjax.Wiki'),

SOAjax.Wiki.WikiControl = function(element) {
    SOAjax.Wiki.WikiControl.initializeBase(this, [element]);
}

SOAjax.Wiki.WikiControl.prototype = {
    _renderDiv: null,
    _titleDiv: null,
    _editControl: null,
    _editMode: false,
    _topic: null,
    _isLoading: false,
    _catalog: 'Default',
    _rawWiki: null,
    _tagInput: null,
    _editButton: null,
    _cancelButton: null,
    _saveButton: null,

    initialize: function() {
        Sys.Debug.trace('WikiControl:initialize'),
        SOAjax.Wiki.WikiControl.callBaseMethod(this, 'initialize'),
        // custom initialization
        var control = this.get_element();

        this._titleDiv = document.createElement('DIV'),
        this._titleDiv.margin = '7px';
        this._titleDiv.style.padding = '7px';
        Sys.UI.DomElement.addCssClass(this._titleDiv, 'TitleHead'),
        control.appendChild(this._titleDiv);

        this._renderDiv = document.createElement('DIV'),
        control.appendChild(this._renderDiv);
        this._editControl = document.createElement('TEXTAREA'),
        this._editControl.style.display = 'none';
        this._editControl.style.width = '100%';
        this._editControl.style.height = '100%';
        this._editControl.style.border = 'solid 0px';
        this._editControl.style.margin = '0px';
        this._editControl.style.padding = '0px';
        control.appendChild(this._editControl);
    },


    // child controls:
    get_editButton: function() { return (this._editButton); },
    set_editButton: function(button) {
        this._editButton = button;
        $addHandler(this._editButton,
            'click', Function.createDelegate(this, this.enterEditMode));
    },

    get_cancelButton: function() { return (this._cancelButton); },
    set_cancelButton: function(button) {
        this._cancelButton = button;
        $addHandler(this._cancelButton,
            'click', Function.createDelegate(this, this.cancelEdits));
    },
    get_saveButton: function() { return (this._saveButton); },
    set_saveButton: function(button) {
        this._saveButton = button;
        $addHandler(this._saveButton,
            'click', Function.createDelegate(this, this.save));
    },

    get_tagInput: function() { },
    set_tagInput: function(input) {
        this._tagInput = input;
        this._tagInput.onkeydown =
            Function.createDelegate(this, this._onTagInputKeyDown);
    },
    get_isLoading: function() {
        return this._isLoading;
    },

    set_isLoading: function(isLoading) {
        this.raisePropertyChanged('loading'),
        this._isLoading = isLoading;
    },

    enterEditMode: function(event) {
        if (event == null) event = new Sys.UI.DomEvent();
        event.stopPropagation();
        // this._editControl.value = SOAjax.Wiki.convertToWiki(this._renderDiv);
        this.set_editMode(true);
    },

    load: function(topic) {
        this.set_isLoading(true);
        this._topic = topic;
        this._titleDiv.innerHTML = topic;
        SOAjax.ClearHandlers(this._renderDiv);
        this._renderDiv.innerHTML = '';
        this._editControl.value = '';

        Sys.Debug.trace(String.format('WikiControl:load("{0}")', this._topic));
        //var control = this.get_element();

        knowledgebase.DataService.GetData(this._catalog, this._topic,
            Function.createDelegate(this, this.onLoadComplete),
            Function.createDelegate(this, this.onFailure),
            this);
    },

    onLoadComplete: function(wikiData, userContext, methodName) {
        var html = SOAjax.Wiki.wikiToHtml(wikiData.Body);
        this._editControl.value = wikiData.Body;
        this._rawWiki = wikiData.Body;
        SOAjax.ClearHandlers(this._renderDiv);
        this._renderDiv.innerHTML = html;
        SOAjax.Wiki.addWikiHandlers(this._renderDiv);
        this.set_isLoading(false);
    },

    onFailure: function(exception, userContext, methodName) {
        Sys.Debug.traceDump(exception, 'Failed callback!'),

        Sys.Debug.fail(String.format("The method {0} failed miserably! ({1})",
            methodName, exception.get_message()));
        Sys.Debug.traceDump(this, 'Failed callback!'),

    },

    save: function() {
        Sys.Debug.trace(String.format('WikiControl:save("{0}")', this._topic));
        this._saveButton.style.display = 'none';
        this._cancelButton.style.display = 'none';
        this._editButton.style.display = '';

        var wikiObject = new Object();
        wikiObject.Body = this._editControl.value;
        wikiObject.Title = this._topic;

        knowledgebase.DataService.SaveData(wikiObject,
            Function.createDelegate(this, this.onSaveCallback),
            Function.createDelegate(this, this.onFailure),
            null); //usercontext

        SOAjax.ClearHandlers(this._renderDiv);
        this._renderDiv.innerHTML = SOAjax.Wiki.wikiToHtml(wikiObject.Body);
        this._rawWiki = wikiObject.Body;
        SOAjax.Wiki.addWikiHandlers(this._renderDiv);
        this.set_editMode(false);
    },

    onSaveCallback: function() {
        Sys.Debug.trace('Save success! '),
    },

    get_topic: function() { return this._topic; },
    set_topic: function(topic) {
        if (this._topic !== topic) {
            this._topic = topic;
            this.raisePropertyChanged('topic'),
        }
    },

    get_editMode: function() {
        /// <value type="Boolean">True if we're in edit mode,
        /// otherwise false.</value>
        return this._editMode;
    },
    set_editMode: function(value) {
        Sys.Debug.trace('WikiControl:switching EditMode:' + value);
        if (value !== this.get_editMode()) {
            this._editMode = value;
            this.raisePropertyChanged('editMode'),
            if (!this.get_isUpdating()) {
                // switch to edit if true, otherwise render
                Sys.UI.DomElement.setVisible(this._editControl, value);
                Sys.UI.DomElement.setVisible(this._cancelButton, value);
                Sys.UI.DomElement.setVisible(this._saveButton, value);
                Sys.UI.DomElement.setVisible(this._editButton, !value);
                Sys.UI.DomElement.setVisible(this._renderDiv, !value);
            }
        }
    },
    cancelEdits: function() {
        SOAjax.ClearHandlers(this._renderDiv);
        this._renderDiv.innerHTML = '';
        this.set_editMode(false);
        this._renderDiv.innerHTML = SOAjax.Wiki.wikiToHtml(this._rawWiki);
        SOAjax.Wiki.addWikiHandlers(this._renderDiv);
        this._editControl.value = this._rawWiki;
    },

    // events
    add_editModeChange: function(handler) {
        /// <summary>Adds a event handler for the editModeChange event.</summary>
        /// <param name="handler" type="Function">The handler to add to the event.
        /// </param>
        this.get_events().addHandler("editModeChange", handler);
    },
    remove_editModeChange: function(handler) {
        /// <summary>Removes a event handler for the editModeChange event.</summary>
        /// <param name="handler" type="Function">The handler to remove.</param>
        this.get_events().removeHandler("editModeChange", handler);
    },

    _editModeCallback: function() {
        var handler = this.get_events().getHandler("editModeChange");
        if (handler) {
            handler(this, Sys.EventArgs.Empty);
        }
    },

    dispose: function() {
        Sys.Debug.trace('WikiControl:dispose'),

        $clearHandlers(this._editButton);
        $clearHandlers(this._saveButton);
        $clearHandlers(this._cancelButton);

        SOAjax.Purge(this._renderDiv);
        SOAjax.Purge(this._editControl);
        var control = this.get_element();
        if (control) {
            control.removeChild(this._renderDiv);
            control.removeChild(this._editControl);
        }
        SOAjax.Wiki.WikiControl.callBaseMethod(this, 'dispose'),
    }
}
SOAjax.Wiki.WikiControl.registerClass('SOAjax.Wiki.WikiControl', Sys.UI.Control);
Sys.Application.notifyScriptLoaded();

With load, edit, and save functionality integrated in the wiki control, it’s ready to be dropped onto the page and integrated with the application. In the application, the JavaScript page code instantiates the environment with components and creates controls and behaviors from DOM elements. Depending on the complexity of the script, this code may be inline or in a separate file (which can be more maintainable). In most cases, I prefer to keep the page JavaScript code in a separate file, typically in a pages subdirectory of scripts. In the sample code, I’ve created a page file named wikipage.js, which is like a JavaScript code-behind file for the wiki ASPX page. This script is shown in Example 8-17.

Example 8-17. The wiki page code-behind JavaScript is used to instantiate and integrate the page, components, and control instances (Web/Script/WikiPage.js).

/// <reference name="MicrosoftAjax.js"/>
/// <reference path="ApplicationRuntime.js"/>
/// <reference path="CommonControls.js"/>
/// <reference path="SOAjax.js"/>
/// <reference path="WikiControl.js"/>

Type.registerNamespace('WikiPage'),

WikiPage.load = function() {

    var isAuthenticated = Sys.Services.AuthenticationService.get_isLoggedIn();
    var startTopic = Sys.Services.ProfileService.properties.startTopic;
    if (startTopic == null || startTopic == '') startTopic = 'Default';

    var wikiElement = $get('_WikiContent'),
    Sys.Debug.assert(wikiElement != null,
        '_WikiContent div must be present on page.'),

    var wiki = $create(SOAjax.Wiki.WikiControl,
        { editButton: $get('_editButton'),
            saveButton: $get('_saveButton'),
            cancelButton: $get('_cancelButton'),
            tagInput: $get('tagInput')
        },
        null, null, wikiElement);

    wiki.load(startTopic);
    WikiPage.wiki = wiki;

    var appProperties = {
        usernameInput: $get('usernameInput'),
        passwordInput: $get('passwordInput'),
        loginButton: $get('ButtonLogin'),
        cancelLoginButton: $get('ButtonCancelLogin'),
        logoutButton: $get('ButtonLogout'),
        loginUI: $get('loginControl'),
        loggedinUI: $get('logoutControl'),
        loginFeedbackDiv : $get('loginFeedbackDiv')
    };

    var appEvents = { 'authenticated': WikiPage.OnAuthenticated };
    WikiPage.App = $create(SOAjax.Application, appProperties, appEvents);
    WikiPage.App.updateControls();
    $addHandler($get('loginLink'), 'mouseover', WikiPage.ShowLoginUI);
    $addHandler(window, 'resize', WikiPage.resizeElements);
    WikiPage.resizeElements();
}

WikiPage.OnAuthenticated = function(sender, eventArgs) {
    Sys.Debug.trace('WikiPage.OnAuthenticated'),

    if (Sys.Services.RoleService.isUserInRole('contributor')) {
        //show contributor controls on page
        $get('_EditControls').style.display = '';
    }
    if (Sys.Services.RoleService.isUserInRole('moderator')) {
        // instantiate moderator controls on page
    }
}

WikiPage.unload = function() {
    wiki = WikiPage.wiki;
    WikiPage.wiki = null;

    if (wiki != null)
        wiki.dispose();
    if (WikiPage.App != null)
        WikiPage.App.dispose();
}

Sys.Application.add_load(WikiPage.load);
Sys.Application.add_unload(WikiPage.unload);

// code from chapter 5, resize page elements to fill UI.
WikiPage.resizeElements = function() {
    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'),
    if (pageHeader) {
        var headerBounds = Sys.UI.DomElement.getBounds(pageHeader);
        var contentDivs = ['MainContent', 'NavigationPlaceholder'];
        for (var i = 0; i < contentDivs.length; i++) {
            var content = $get(contentDivs[i]);
            if (content)
                content.style.height = height - headerBounds.height + 'px';
        }
    }
}
Sys.Application.notifyScriptLoaded();
..................Content has been hidden....................

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