Common_TaskResults_VBNet\OnDemandTaskResults\JavaScript\OnDemandTaskResults.js
Copyright 2010 ESRI All rights reserved under the copyright laws of the United States and applicable international laws, treaties, and conventions. You may freely redistribute and use this sample code, with or without modification, provided you include the original copyright notice and use restrictions. See <a href="http://help.arcgis.com/en/sdk/10.0/usageRestrictions.htm">the use restrictions</a>. // *************************** // MapTips Extensions // *************************** // === Public Methods === // Initializes MapTips so that attributes for a feature are retrieved only when a MapTip // is expanded ESRI.ADF.UI.MapTips.prototype.setupOnDemandMapTips = function(properties) { // Retrieve the GraphicFeatureGroup (i.e. layer) corresponding to the passed-in ID var graphicFeatureGroup = $find(properties.graphicFeatureGroupID); // Loop through the features in the GraphicFeatureGroup, setting the hasAttributes // property for each to false and adding a handler to each's mouseOver event. var graphicFeature; for (var i = 0; i < graphicFeatureGroup.getFeatureCount(); i++) { graphicFeature = graphicFeatureGroup.get(i); graphicFeature.set_hasAttributes(false); // Add a handler to the feature's mouseOver event to update the MapTip's currentFeature // property with the moused-over feature. Note that createDelegate is used to specify // the handler so that the MapTips control can be referenced in the handler via the // "this" keyword. graphicFeature.add_mouseOver(Function.createDelegate(this, this._updateCurrentFeature)); } // Set the NodeID of the TreeViewPlusNode corresponding to the GraphicFeatureGroup this.set_graphicsLayerNodeID(properties.graphicsLayerNodeID); // Initialize the activity indicator and attributes templates this.set_activityIndicatorTemplate(properties.activityIndicatorTemplate); this.set_attributesTemplate(this.get_contentTemplate()); this.set_contentTemplate(properties.activityIndicatorTemplate); // Set the syntax to issue a callback to the server tier TaskResults control this.set_onDemandCallbackFunctionString(properties.callbackFunctionString); // Wire a handler for the MapTips expanded event that will apply either the activity // indicator or attributes content and, if necessary, issue a callback to retrieve // attributes. Note we use createDelegate to specify the handler so the "this" // keyword can be used within the handler to reference the MapTips control. this.add_expanded(Function.createDelegate(this, this._setOnDemandContent)); } // Updates the GraphicFeature specified by the passed-in ID with the passed-in attributes and // also updates the MapTips display, if a MapTip for the GraphicFeature is currently shown ESRI.ADF.UI.MapTips.prototype.updateAttributes = function(graphicFeatureID, attributes) { // Get the GraphicFeature with the specified ID and update it with the passed-in attributes var graphicFeature = $find(graphicFeatureID); graphicFeature.set_attributes(attributes); // Set the flag indicating that attributes have been retrieved for the GraphicFeature graphicFeature.set_hasAttributes(true); // Check whether a MapTip is currently shown and, if so, whether it is for the graphic feature // with the passed-in ID. If so, call the method to update the MapTip's content. if (this.get_callout().get_isOpen() && (graphicFeatureID == this.get_currentFeature().get_id())) this._setOnDemandContent(graphicFeature); } // === Private Methods === ESRI.ADF.UI.MapTips.prototype._updateCurrentFeature = function(graphicFeature) { this.set_currentFeature(graphicFeature); } // Updates the MapTips content based on whether or not attributes have been retrieved from the // server. Fires when a MapTip is expanded and when attributes are returned from the server. ESRI.ADF.UI.MapTips.prototype._setOnDemandContent = function(graphicFeature) { // If the graphic feature was not passed in, use the MapTips' current feature if (graphicFeature && !ESRI.ADF.Graphics.GraphicFeature.isInstanceOfType(graphicFeature)) graphicFeature = this.get_currentFeature(); // Get the attributes of the passed-in feature var attributes = graphicFeature.get_attributes(); // Create the HTML for the mapTips title by binding the attributes to the title template var titleMarkup = ESRI.ADF.System.templateBinder(attributes, this.get_hoverTemplate()); // If attributes have been retrieved for the feature, create the mapTips content HTML by binding // those attributes to the attributes template. Otherwise, bind to the activity indicator template. var contentMarkup; if (graphicFeature.get_hasAttributes()) contentMarkup = ESRI.ADF.System.templateBinder(attributes, this.get_attributesTemplate()); else contentMarkup = ESRI.ADF.System.templateBinder(attributes, this.get_activityIndicatorTemplate()); // Apply the markup to the MapTips control this.setContent(contentMarkup, titleMarkup); // Check whether attributes have been retrieved for the current feature. If not, issue a callback to // retrieve them. if (!graphicFeature.get_hasAttributes()) { // Format the information to be sent in the callback var context; var argument = 'EventArg=getAttributes&GraphicID={0}&MapTipsID={1}&LayerNodeID={2}'; argument = String.format(argument, graphicFeature.get_id(), this.get_id(), this.get_graphicsLayerNodeID()); // Issue the callback eval(this.get_onDemandCallbackFunctionString()); } } // === Public Properties === // NodeID of the TreeViewPlusNode corresponding to the MapTip's associated GraphicFeatureGroup ESRI.ADF.UI.MapTips.prototype.get_graphicsLayerNodeID = function() { return this._graphicsLayerNodeID; } ESRI.ADF.UI.MapTips.prototype.set_graphicsLayerNodeID = function(value) { this._graphicsLayerNodeID = value; } // HTML markup to use when displaying a MapTip on a feature for which attributes have been // retrieved ESRI.ADF.UI.MapTips.prototype.get_attributesTemplate = function() { return this._attributesTemplate; } ESRI.ADF.UI.MapTips.prototype.set_attributesTemplate = function(value) { this._attributesTemplate = value; } // HTML markup to use when displaying a MapTip on a feature for which attributes have been // retrieved ESRI.ADF.UI.MapTips.prototype.get_currentFeature = function() { return this._currentFeature; } ESRI.ADF.UI.MapTips.prototype.set_currentFeature = function(value) { this._currentFeature = value; } // HTML markup to use when displaying a MapTip on a feature for which attributes have not // yet been retrieved ESRI.ADF.UI.MapTips.prototype.get_activityIndicatorTemplate = function() { return this._activityIndicatorTemplate; } ESRI.ADF.UI.MapTips.prototype.set_activityIndicatorTemplate = function(value) { this._activityIndicatorTemplate = value; } // Syntax to issue a callback to the TaskResults control referencing the GraphicsLayer on // which MapTips are displayed ESRI.ADF.UI.MapTips.prototype.get_onDemandCallbackFunctionString = function() { return this._onDemandCallbackFunctionString; } ESRI.ADF.UI.MapTips.prototype.set_onDemandCallbackFunctionString = function(value) { this._onDemandCallbackFunctionString = value; } // ********************************* // GraphicFeature Extensions // ********************************* // === Public Properties === // Indicates whether attributes for the feature have been retrieved from the server ESRI.ADF.Graphics.GraphicFeature.prototype.get_hasAttributes = function() { return this._hasAttributes; } ESRI.ADF.Graphics.GraphicFeature.prototype.set_hasAttributes = function(value) { this._hasAttributes = value; } // ********************************* // TaskResults Extensions // ********************************* // === Public Methods === // Creates client tier TreeViewPlusNode objects given node data in JSON format ESRI.ADF.UI.TaskResults.prototype.initClientNodes = function(taskResultNodeData) { // Get the DOM element for the task result node var taskResultNodeElement = $get(taskResultNodeData.nodeID); // Create a client tier TaskResultNode AJAX component var taskResultNode = $create(ESRI.ADF.Samples.TaskResultNode, { 'id': taskResultNodeElement.id }, null, null, taskResultNodeElement); // Iterate through the data passed-in for graphics layer nodes that are children of the task // results node, creating a client tier node object for each. for (var i = 0; i < taskResultNodeData.graphicsLayerNodes.length; i++) { // Get the DOM element for the graphics layer node var graphicFeatureGroupNodeElement = $get(taskResultNodeData.graphicsLayerNodes[i].nodeID); // Declare a variable to store the node's initialization properties and initialize it with // the node's id. var properties = { 'id': graphicFeatureGroupNodeElement.id }; // If the node data includes a pagingTextFormatString, then the node contains more than one page // of child (feature) nodes. Add properties to manage paging accordingly. if (taskResultNodeData.graphicsLayerNodes[i].pagingTextFormatString) { properties.pagingTextFormatString = taskResultNodeData.graphicsLayerNodes[i].pagingTextFormatString; properties.pageCount = taskResultNodeData.graphicsLayerNodes[i].pageCount; properties.pageSize = taskResultNodeData.graphicsLayerNodes[i].pageSize; properties.nodeCount = taskResultNodeData.graphicsLayerNodes[i].nodeCount; } // Create the graphics layer node object. Note that, on the client, this is a GraphicFeatureGroup // node, following the correspondence between web tier GraphicsLayers and client tier GraphicFeatureGroups var graphicFeatureGroupNode = $create(ESRI.ADF.Samples.GraphicFeatureGroupNode, properties, null, null, graphicFeatureGroupNodeElement); // Add the graphic feature group node to the task result node taskResultNode.addNode(graphicFeatureGroupNode); // Iterate through data for feature nodes that are children of the current graphic feature group node for (var j = 0; j < taskResultNodeData.graphicsLayerNodes[i].featureNodes.length; j++) { // Get the DOM element for the feature node var featureNodeElement = $get(taskResultNodeData.graphicsLayerNodes[i].featureNodes[j]); // Create the client tier feature node object and add it to the current graphic feature group node var featureNode = $create(ESRI.ADF.Samples.GraphicFeatureNode, { 'id': featureNodeElement.id }, null, null, featureNodeElement); graphicFeatureGroupNode.addNode(featureNode); } } // Add the task result node to the client tier task results control this.addNode(taskResultNode); } // Makes the passed-in node a child node of the task results control ESRI.ADF.UI.TaskResults.prototype.addNode = function(node) { // Create the private array of child nodes if necessary if (!this._nodes) this._nodes = new Array(); // Make sure the argument is a TreeViewPlusNode instance if (ESRI.ADF.Samples.TreeViewPlusNode.isInstanceOfType(node)) { // Add the node to the control's child nodes array and set the // nodes parent property to point to the control. this._nodes.push(node); node.set_parent(this); } } // Disassociates the passed-in node with the task results control. Note that // this does not alter the node itself, just the association. ESRI.ADF.UI.TaskResults.prototype.removeNode = function(node) { // Make sure the control has child nodes if (!this._nodes) return; // Iterate through the control's child nodes array. Remove the passed-in // node from the array if found. for (var i = 0; i < this._nodes.length; i++) { if (this._nodes[i] === node) { this._nodes.splice(i, 1); break; } } } // Adds an array of nodes to the control's child nodes collection ESRI.ADF.UI.TaskResults.prototype.addNodes = function(nodes) { for (var i = 0; i < nodes.length; i++) this.addNode(nodes[i]); } // Retrieves a descendant node by node ID ESRI.ADF.UI.TaskResults.prototype.findNodeByID = function(nodeID) { var node = null; // Iterate through the control's child nodes, checking the id of each to // see whether it matches the passed-in value for (var i = 0; i < this._nodes.length; i++) { // If the current node's ID matches that passed-in, initialize the return // value with the node. Otherwise, recursively call findNodeByID on the // child node. if (this._nodes[i].get_id() == nodeID) node = this._nodes[i]; else node = this._nodes[i].findNodeByID(nodeID); // Exit the loop if a matching node was found if (node) break; } return node; } // ***************************************************** // TreeViewPlusNode AJAX control implementation // ***************************************************** Type.registerNamespace('ESRI.ADF.Samples'); ESRI.ADF.Samples.TreeViewPlusNode = function(element) { ESRI.ADF.Samples.TreeViewPlusNode.initializeBase(this, [element]); // Parent TreeViewPlusNode or TaskResults control this._parent = null; // Child nodes this._nodes = new Array(); // DOM checkbox element associated with the node this._checkBox = null; // State of the node's checkbox this._isChecked = null; // Div that contains the node's children this._childNodeDiv = null; // Number of child nodes per page this._pageSize = null; // Current child node page shown this._pageNumber = 1; // Number of child node pages this._pageCount = null; // Child nodes pending initialization this._newNodes = new Object(); // HTML of the page display for each child node page this._pagingTableMarkup = new Object(); // Used to format the page display. Equivalent to the web tier TaskResults // property of the same name. this._pagingTextFormatString = null; // HTML for indenting the page display so it is aligned with the child nodes this._pagingTablePadding = new Array(); // Total number of child nodes this._nodeCount = null; // Node page of the parent TreeViewPlusNode on which this node is displayed this._containingPage = 1; // This node's HTML representation this._content = null; }; ESRI.ADF.Samples.TreeViewPlusNode.prototype = { // Fires when the object is instantiated via the AJAX $create function initialize: function() { ESRI.ADF.Samples.TreeViewPlusNode.callBaseMethod(this, 'initialize'); // Get the DOM checkbox associated with the node this._checkBox = $get(String.format('{0}_CheckBox', this.get_id())); if (this._checkBox) { // Attach the _checked function to the checkbox's click event. We use createDelegate // for this so that the "this" keyword in the event handler refers to the // TreeViewPlusNode instance. this._checkBox.onclick = Function.createDelegate(this, this._checked); // Initialize the isChecked property with the checkbox's checked state this._isChecked = this._checkBox.checked; } // Get the div that holds the node's child nodes this._childNodeDiv = $get(String.format('{0}_childrenContainer', this.get_id())); // Try to get the DOM element containing the link for the next node page. This will only exist // if the number of child nodes exceeds the node's page size var nextPageElement = $get(String.format('{0}_NextPage', this.get_id())); // If the element was found, then the node has more than one page. So we initialize paging properties. if (nextPageElement) { // Change the next page link's href to invoke the paging functionality of the client tier // TreeViewPlusNode. When this element is initially created by the web tier TaskResults control, // it invokes web tier functionality when clicked. nextPageElement.href = String.format( "javascript:$find('{0}').set_page($find('{0}').get_pageNumber() + 1)", this.get_element().id); // Find the DOM table containing the link. This table contains the paging display. var pagingTable = nextPageElement.parentNode; while (pagingTable.parentNode && pagingTable.tagName != 'TABLE') pagingTable = pagingTable.parentNode; // The paging display is formatted within one row. Interrogate the cells of this row to extract the // HTML that is used to pad (indent) the table. This padding aligns the display with the child nodes. for (var i = 0; i < pagingTable.rows[0].cells.length; i++) { // Get the number of child elements in the current cell var childNodeCount = pagingTable.rows[0].cells[i].childNodes.length; // Get the inner text of the cell's first child element var innerText = pagingTable.rows[0].cells[i].childNodes[0].innerText; // If the cell only contains one element that has no inner text, then the cell is used for padding if (childNodeCount == 1 && (innerText == '' || innerText == 'undefined' || innerText == null)) { // Store the padding markup for use in updating the paging display var paddingHtml = pagingTable.rows[0].cells[i].innerHTML; this._pagingTablePadding.push(paddingHtml); } } } }, // Destroys the node instance and those of any child nodes dispose: function() { // Invoke the base control's dispose method to destroy the current instance ESRI.ADF.Samples.TreeViewPlusNode.callBaseMethod(this, 'dispose'); // Call dispose on child nodes for (var i = 0; i < this._nodes.length; i++) this._nodes[i].dispose(); }, // AJAX component ID get_id: function() { return this._id; }, set_id: function(value) { this._id = value; }, // TreeViewPlusNode or TaskResults control that contains the node set_parent: function(value) { // Make sure the argument is a TreeViewPlusNode or TaskResults control if (ESRI.ADF.Samples.TreeViewPlusNode.isInstanceOfType(value) || ESRI.ADF.UI.TaskResults.isInstanceOfType(value)) this._parent = value; }, get_parent: function() { return this._parent; }, // Child nodes set_nodes: function(value) { this._nodes = new Array(); this.addNodes(value); }, get_nodes: function() { return this._nodes; }, // Checked state of the node's checkbox get_isChecked: function() { return this._isChecked; }, get_checkBox: function() { return this._checkBox; }, // Makes the passed-in node a child node addNode: function(node) { // Make sure the argument is a TreeViewPlusNode instance if (ESRI.ADF.Samples.TreeViewPlusNode.isInstanceOfType(node)) { // Add the node to the control's child nodes array and set the // nodes parent property to point to the control. this._nodes.push(node); node.set_parent(this); } }, // Adds an array of nodes to the child nodes collection addNodes: function(nodes) { for (var i = 0; i < nodes.length; i++) this.addNode(nodes[i]); }, // Retrieves a descendant node by node ID findNodeByID: function(nodeID) { var node = null; // Iterate through the node's child nodes, checking the id of each to see whether // it matches the passed-in value for (var i = 0; i < this._nodes.length; i++) { // If the current node's ID matches that passed-in, initialize the return // value with the node. Otherwise, recursively call findNodeByID on the // child node. if (this._nodes[i].get_id() == nodeID) node = this._nodes[i]; else node = this._nodes[i].findNodeByID(nodeID); // Exit the loop if a matching node was found if (node) break; } return node; }, // Removes the passed-in node from the child nodes collection removeNode: function(node) { for (var i = 0; i < this._nodes.length; i++) { if (this._nodes[i] === node) { this._nodes.splice(i, 1); break; } } }, // Removes all child nodes clearNodes: function() { this._nodes = new Array(); }, // Adds a handler for the node checkbox's click event add_checked: function(handler) { this.get_events().addHandler('checked', handler); }, // Removes the passed-in handler from the node checkbox's click event remove_checked: function(handler) { this.get_events().removeHandler('checked', handler); }, // Fires when the node's checkbox is clicked _checked: function() { // Update the node's isChecked property this._isChecked = this._checkBox.checked; // Add or remove the checked attribute from the checkbox's HTML. Needed because // the node's html representation is used for paging. For the HTML to be correct // in FireFox, this attribute must be explicitly added or removed. if (this._checkBox.checked) this._checkBox.setAttribute('checked', 'checked'); else this._checkBox.removeAttribute('checked'); // Raise the node's checked event to fire any event handlers this._raiseEvent('checked'); // Issue an asynchronous request to the server to update the node's status in the web tier var argument = String.format('EventArg=nodeChecked&NodeID={0}&Checked={1}', this.get_id(), this._isChecked); var context; eval(this.get_callbackFunctionString()); }, // The current page of child nodes get_pageNumber: function() { return this._pageNumber; }, // Page number of the parent node that contains this node get_containingPage: function() { return this._containingPage; }, set_containingPage: function(value) { this._containingPage = value; }, // Retrieves all the nodes on the passed-in page getChildNodesByPage: function(pageNumber) { var nodes = new Array(); for (var i = 0; i < this._nodes.length; i++) { if (this._nodes[i].get_containingPage() == pageNumber) nodes.push(this._nodes[i]); } return nodes; }, // HTML representation of the node. Setting this property updates the node's appearance // with the passed-in markup. get_content: function() { return this._content; }, set_content: function(value) { // Update the content property this._content = value; // Get the node's DOM element var nodeElement = $get(this.get_id()); if (nodeElement) { // Apply the markup to the node nodeElement.outerHTML = value; // Get the node's checkbox var checkBox = $get(String.format('{0}_CheckBox', this.get_id())); if (checkBox) { // Re-wire the _checked function to the checkbox's clicked event. Necessary because // updating the node's markup disconnects this handler. this._checkBox = checkBox; this._checkBox.onclick = Function.createDelegate(this, this._checked); this._checkBox.checked = this._isChecked; } } }, // Changes the page shown set_page: function(pageNumber, createNodeObjects) { // Try getting the child nodes for the passed-in page from the node's collection var nodes = this.getChildNodesByPage(pageNumber); // If the node doesn't have any child nodes defined for the page, check if there are // nodes pending initialization. These are stored in the _newNodes private member if (nodes.length == 0) nodes = this._newNodes[pageNumber]; // Check whether nodes were found for the requested page if (nodes && nodes.length > 0) { // Save the markup for the current page's nodes var currentNodes = this.getChildNodesByPage(this._pageNumber); for (var i = 0; i < currentNodes.length; i++) { // Store the markup for each node in the content property and remove the node element // from the document var nodeElement = $get(currentNodes[i].get_id()); currentNodes[i]._content = nodeElement.outerHTML; nodeElement.parentNode.removeChild(nodeElement); } // Add the paging table to the node's content div if (!this._pagingTableMarkup[pageNumber]) this._pagingTableMarkup[pageNumber] = this._createPagingMarkup(pageNumber); this._childNodeDiv.innerHTML = this._pagingTableMarkup[pageNumber]; // Add the page's nodes to the content div for (var i = 0; i < nodes.length; i++) { // Get the current node and update the div's markup with it var node = nodes[i]; this._childNodeDiv.innerHTML += node._content; // If the nodes have yet to be initialized (i.e. instantiated as TreeViewPlusNodes), do // so here if (this._newNodes[pageNumber] && createNodeObjects) { var nodeElement = $get(node.nodeID); node = $create(ESRI.ADF.Samples.TreeViewPlusNode, { 'id': nodeElement.id, 'containingPage': pageNumber }, null, null, nodeElement); this.addNode(node); } } // If nodes for the requested page have already been created, re-wire the _checked function // to the node checkbox's checked event if (!this._newNodes[pageNumber]) { nodes = this.getChildNodesByPage(pageNumber); for (var i = 0; i < nodes.length; i++) { node = nodes[i]; var checkBox = $get(String.format('{0}_CheckBox', node.get_id())); if (checkBox) { node._checkBox = checkBox; node._checkBox.onclick = Function.createDelegate(node, node._checked); } } } // Update the current page number this._pageNumber = pageNumber; // Clear the array of nodes pending initialization this._newNodes[pageNumber] = null; } else { // Data for the requested page was not found on the client, so issue an asynchronous // request to the server to retrieve it var argument = 'EventArg=getPage&PageNumber={0}&NodeID={1}'; argument = String.format(argument, pageNumber, this.get_id()); var context; eval(this.get_callbackFunctionString()); } }, // Specifies the format of the paging display. The values substituted into the // string are as follows: // {0} - Page starting record // {1} - Page ending record // {2} - Total record count // {3} - Current page number // {4} - Number of pages // So setting this property to "Features {0} to {1} of {2}" could, for instance, // evaluate at run-time to "Features 1 to 25 of 352" get_pagingTextFormatString: function() { return this._pagingTextFormatString; }, set_pagingTextFormatString: function(value) { this._pagingTextFormatString = value; }, // Number of nodes displayed on a page. Used solely for display in the paging table. get_pageSize: function() { return this._pageSize; }, set_pageSize: function(value) { this._pageSize = value; }, // Number of pages of child nodes get_pageCount: function() { return this._pageCount; }, set_pageCount: function(value) { this._pageCount = value; }, // Number of child nodes. Used solely for the paging display. get_nodeCount: function() { return this._nodeCount; }, set_nodeCount: function(value) { this._nodeCount = value; }, // Fires handlers for the specified event _raiseEvent: function(name, e) { // Get the handlers for the event. ASP.NET AJAX automatically consolidates all // handlers into one function. var handler = this.get_events().getHandler(name); // Make sure a function was returned if (handler) { // Initialize the event arguments if none were passed in if (!e) { e = Sys.EventArgs.Empty; } // Fire the handlers handler(this, e); } }, // Generates paging display HTML for the specified page _createPagingMarkup: function(pageNumber) { // Create a DOM table element var table = document.createElement('table'); table.cellPadding = '0px'; table.cellSpacing = '0px'; // Create a row and add padding to it to align the paging display with the child nodes var row = table.insertRow(0); for (var i = 0; i < this._pagingTablePadding.length; i++) { var cell = row.insertCell(row.cells.length); cell.innerHTML = this._pagingTablePadding[i]; } cell = row.insertCell(row.cells.length); // If the page requested is 2 or above, create a previous page button if (pageNumber > 1) { var previousPageLink = document.createElement('a'); previousPageLink.href = String.format("javascript:$find('{0}').set_page($find('{0}').get_pageNumber() - 1)", this.get_element().id); previousPageLink.innerHTML = "<"; cell.appendChild(previousPageLink); cell = row.insertCell(row.cells.length); } // Create a div for the paging text var div = document.createElement('div'); // Calculate the indexes of the first and last nodes on the page var startRecord = ((pageNumber - 1) * this._pageSize) + 1; var endRecord = pageNumber * this._pageSize; if (endRecord > this._nodeCount) endRecord = this._nodeCount; // Create the paging text and add it to the table div.innerHTML = String.format(this._pagingTextFormatString, startRecord, endRecord, this._nodeCount, pageNumber, this._pageCount); cell.appendChild(div); // If the requested page is not the last page, add a next page button if (pageNumber < this._pageCount) { cell = row.insertCell(row.cells.length); var nextPageLink = document.createElement('a'); nextPageLink.href = String.format("javascript:$find('{0}').set_page($find('{0}').get_pageNumber() + 1)", this.get_element().id); nextPageLink.innerHTML = ">"; cell.appendChild(nextPageLink); } // Return the table's markup return table.outerHTML; }, // Returns the syntax for invoking an asynchronous request to the server. Retrieved from the // containing TreeViewPlus control get_callbackFunctionString: function() { // Make sure the node has a parent var parent = this.get_parent(); if (!parent) return; var callbackFunctionString; // If the node's parent is a TreeViewPlus control, return its callbackFunctionString. // Otherwise, recursively call the method on the parent node. if (ESRI.ADF.UI.TreeViewPlus.isInstanceOfType(parent)) callbackFunctionString = parent.get_callbackFunctionString(); else if (ESRI.ADF.Samples.TreeViewPlusNode.isInstanceOfType(parent)) callbackFunctionString = parent.get_callbackFunctionString(); return callbackFunctionString; } } // Register the TreeViewPlusNode class as an ASP.NET AJAX client control ESRI.ADF.Samples.TreeViewPlusNode.registerClass('ESRI.ADF.Samples.TreeViewPlusNode', Sys.UI.Control); // ********************************************************** // TaskResultNode client control implementation // ********************************************************** ESRI.ADF.Samples.TaskResultNode = function(element) { ESRI.ADF.Samples.TaskResultNode.initializeBase(this, [element]); }; ESRI.ADF.Samples.TaskResultNode.prototype = { // Fires when a TaskResultNode is instantiated via $create initialize: function() { ESRI.ADF.Samples.TaskResultNode.callBaseMethod(this, 'initialize'); // Wire updateChildVisibility to the node's checked event. We do this using createDelegate so // that the "this" keyword in the handler refers to the TaskResultNode instance. this.add_checked(Function.createDelegate(this, this.updateChildVisibility)); }, // Overrides TreeViewPlusNode's addNode method to ensure the passed-in node is a GraphicFeatureGroupNode addNode: function(node) { if (ESRI.ADF.Samples.GraphicFeatureGroupNode.isInstanceOfType(node)) ESRI.ADF.Samples.TaskResultNode.callBaseMethod(this, 'addNode', [node]); }, // Fires when the node checkbox is checked. Updates the visibility of graphics associated with // child nodes updateChildVisibility: function() { // Check whether the checkbox is checked if (this.get_isChecked()) { // Synchronize the visibility of child node graphics with their checked states for (var i = 0; i < this._nodes.length; i++) { this._nodes[i].updateChildVisibility(); } } else { // Hide graphic feature groups associated with child nodes for (var i = 0; i < this._nodes.length; i++) { var node = this._nodes[i]; var graphicFeatureGroup = $find(node.get_graphicFeatureGroupID()); graphicFeatureGroup.set_visible(false); } } } } // Register the TaskResultNode class as an ASP.NET AJAX client control ESRI.ADF.Samples.TaskResultNode.registerClass('ESRI.ADF.Samples.TaskResultNode', ESRI.ADF.Samples.TreeViewPlusNode); // ****************************************************************** // GraphicFeatureGroupNode client control implementation // ****************************************************************** ESRI.ADF.Samples.GraphicFeatureGroupNode = function(element) { // AJAX component ID of the graphic feature group associated with the node this._graphicFeatureGroupID = null; // AJAX component ID of the maptips control associated with the node's graphic feature group this._mapTipsID = null; // Child feature nodes that were checked while this node was unchecked this._childNodesPendingUpdate = new Array(); ESRI.ADF.Samples.GraphicFeatureGroupNode.initializeBase(this, [element]); }; ESRI.ADF.Samples.GraphicFeatureGroupNode.prototype = { // Fires when a GraphicFeatureGroupNode is instantiated via $create initialize: function() { ESRI.ADF.Samples.GraphicFeatureGroupNode.callBaseMethod(this, 'initialize'); // Wire updateChildVisibility to the node's checked event this.add_checked(Function.createDelegate(this, this.updateChildVisibility)); // Get the component ID of the graphic feature group associated with the node this._graphicFeatureGroupID = this.get_element().attributes['graphicfeaturegroupid'].value; // Put script to retrieve the component ID of the maptips control associated with the node // in a timeout. We do this because this method may execute before the graphic feature group // and maptips have been instantiated. var mapTipsIDInitScript = "var node = $find('{0}');" + "node._mapTipsID = $find(node._graphicFeatureGroupID).get_mapTips().get_id();"; mapTipsIDInitScript = String.format(mapTipsIDInitScript, this.get_id()); window.setTimeout(mapTipsIDInitScript, 0); }, // Destroys the node instance and those of any child nodes dispose: function() { // Retrieve and destroy the associated MapTips control var mapTips = $find(this._mapTipsID); if (mapTips) mapTips.dispose(); ESRI.ADF.Samples.GraphicFeatureGroupNode.callBaseMethod(this, 'dispose'); }, // Component ID of the graphic feature group associated with the node get_graphicFeatureGroupID: function() { return this._graphicFeatureGroupID; }, // Overrides TreeViewPlusNode's addNode method to ensure the passed-in node is a GraphicFeatureNode addNode: function(node) { if (ESRI.ADF.Samples.GraphicFeatureNode.isInstanceOfType(node)) ESRI.ADF.Samples.GraphicFeatureGroupNode.callBaseMethod(this, 'addNode', [node]); }, // Child nodes that were checked when this node was unchecked and therefore have graphics needing to be // synced with their checkbox's checked states. The synchronization happens when this node is checked. getNodesPendingUpdate: function() { return this._childNodesPendingUpdate; }, // Add a node to the collection of those pending visibility synchronization addNodePendingUpdate: function(nodeID) { this._childNodesPendingUpdate.push(nodeID); }, // Overrides TreeViewPlusNode's set_page method to instantiate any new nodes as GraphicFeatureNodes set_page: function(pageNumber) { // Check whether there are any nodes to initialize if (this._newNodes[pageNumber]) { // Store the new nodes in a local variable before invoking the base class's set_page method. // This is because the base class will clear this array. var newNodes = this._newNodes[pageNumber]; // Invoke the base method to render the requested page ESRI.ADF.Samples.GraphicFeatureGroupNode.callBaseMethod(this, 'set_page', [pageNumber]); // Instantiate GraphicFeatureNodes for any newly displayed child nodes for (var i = 0; i < newNodes.length; i++) { // Create a new GraphicFeatureNode and add it to the child node collection var nodeElement = $get(newNodes[i].nodeID); var node = $create(ESRI.ADF.Samples.GraphicFeatureNode, { 'id': nodeElement.id, 'containingPage': pageNumber }, null, null, nodeElement); this.addNode(node); } } else { ESRI.ADF.Samples.GraphicFeatureGroupNode.callBaseMethod(this, 'set_page', [pageNumber, true]); } }, // Synchronizes the visibility of graphics associated with this node and its child nodes with the checked // states of the nodes' checkboxes updateChildVisibility: function() { // If the parent node is unchecked, the graphics are already not visible if (!this.get_parent().get_isChecked()) return; // Get the node's graphic feature group var graphicFeatureGroup = $find(this._graphicFeatureGroupID); if (this.get_isChecked()) { // Turn on the node's graphic feature group graphicFeatureGroup.set_visible(true); // Synchronize the visibility of child nodes that were checked while this node was unchecked var nodeCount = this._childNodesPendingUpdate.length; for (var i = 0; i < nodeCount; i++) { $find(this._childNodesPendingUpdate[0]).updateVisibility(); this._childNodesPendingUpdate.splice(0, 1); } } else { graphicFeatureGroup.set_visible(false); } } } // Register the GraphicFeatureGroupNode class as an ASP.NET AJAX client control ESRI.ADF.Samples.GraphicFeatureGroupNode.registerClass('ESRI.ADF.Samples.GraphicFeatureGroupNode', ESRI.ADF.Samples.TreeViewPlusNode); // *************************************************************** // GraphicFeatureNode client control implementation // *************************************************************** ESRI.ADF.Samples.GraphicFeatureNode = function(element) { ESRI.ADF.Samples.GraphicFeatureNode.initializeBase(this, [element]); }; ESRI.ADF.Samples.GraphicFeatureNode.prototype = { // Fires when a GraphicFeatureNode is instantiated via $create initialize: function() { ESRI.ADF.Samples.GraphicFeatureNode.callBaseMethod(this, 'initialize'); // Wire the updateVisibility method to the node checkbox's checked event this.add_checked(Function.createDelegate(this, this.updateVisibility)); // Get the component ID of the graphic feature associated with the node this._graphicFeatureID = this.get_element().attributes['graphicid'].value; }, // Component ID of the graphic feature associated with the node get_graphicFeatureID: function() { return this._graphicFeatureID; }, // Synchronizes the associated graphic feature's visibility with the node checkbox's checked state updateVisibility: function() { if (!this.get_parent() || !ESRI.ADF.Samples.GraphicFeatureGroupNode.isInstanceOfType(this.get_parent())) return; // Get the graphic feature group containing the node's graphic feature var graphicFeatureGroup = $find(this.get_parent().get_graphicFeatureGroupID()); // Check whether the graphic feature group is visible if (graphicFeatureGroup && graphicFeatureGroup.get_visible()) { // If the node checkbox is checked, remove the node's graphic feature from the graphic feature // group. Otherwise, add it. var graphicFeature = $find(this._graphicFeatureID); if (this.get_isChecked()) graphicFeatureGroup.add(graphicFeature); else graphicFeatureGroup.remove(graphicFeature); } else { // Since the graphic feature group is not visible, the graphic feature's visibility can't be // updated. So we use the GraphicFeatureGroupNode's array of nodes pending visiblity updates // to manage graphics that need to be updated the next time the GraphicFeatureGroupNode is // checked. var featureNodesPendingUpdate = this.get_parent().getNodesPendingUpdate(); var addNodeToPending = true; for (var i = 0; i < featureNodesPendingUpdate.length; i++) { // If the node is already in the parent node's pending array, then the current node's checked // state matches what it was when the GraphicFeatureGroupNode was unchecked, meaning that the // visibility of the associated graphic feature does not need to be updated. So remove the // node from the pending array. if (featureNodesPendingUpdate[i] == this.get_id()) { featureNodesPendingUpdate.splice(i, 1); addNodeToPending = false; break; } } // Since the node was not already in the pending array, its checked state is the opposite of what // it was when the parent node was unchecked. So add the node to the pending array so the // associated graphic feature is updated the next time the parent node is checked. if (addNodeToPending) this.get_parent().addNodePendingUpdate(this.get_id()); } } } // Register the GraphicFeatureNode class as an ASP.NET AJAX client control ESRI.ADF.Samples.GraphicFeatureNode.registerClass('ESRI.ADF.Samples.GraphicFeatureNode', ESRI.ADF.Samples.TreeViewPlusNode);