Extending Nevron JavaScript Library
The Nevron JavaScript Library (or Library for short) is designed to allow third party developers to easily add new client side and AJAX functionality to the Nevron web controls. There are two ways to extend the Library, recommended by Nevron: creating custom AJAX commands and creating custom tools.
On a lower level the Library communicates with the server by sending AJAX commands through asynchronous requests and reading the response to get the command results. An AJAX command is a string, accompanied by an associative array of arguments. Any text can be used as an AJAX command and any set of arguments can be sent to the server.
There are several AJAX commands with predefined arguments that are used intrinsically: “mouseClick“,“mouseDoubleClick“,“mouseDown“,“mouseUp“,“mouseMove“,“mouseOver“,“mouseOut“,“autoRefresh“,“refresh“, and “getImageMapXml“. All mouse AJAX commands are sent to the server on user mouse input and cause the mouse-related server side events of the corresponding Nevron web control to be fired. The “autoRefresh“AJAX command is sent to the server periodically when auto refresh is enabled. The “refresh“ and “getImageMapXml“AJAX commands can be invoked from user JavaScript in order to refresh the chart forcibly or acquire the current image map data.
Additionally, custom AJAX commands can be sent to the server side control from within user JavaScript, like this:
JavaScript |
Copy Code
|
//Make sure the NChartCallbackServicetype is loaded
if(typeof(NChartCallbackService) !="undefined")
{
//Acquire the instance of the client side JavaScript object
// that is associated with the web control
varcs = NChartCallbackService.GetCallbackService('MyChartControl');
//Build an associative array to store the arguments
varattributes = new Array();
attributes["arg1"] = "value 1";
attributes["arg2"] = "value 2";
//Invoke the custom AJAX command
cs.InvokeCustomCommand("customCommand",attributes);
}
|
At the server, when the command is received, the AsyncCustomCommand event of MyChartControl is fired:
C# |
Copy Code
|
protected void MyChartControl_AsyncCustomCommand(object sender, EventArgs e)
{
NCallbackCustomCommandArgs args = e as NCallbackCustomCommandArgs;
NCallbackCommand command = args.Command;
switch (command.Name)
{
case "customCommand":
string arg1Text = command.Arguments["arg1"] as string;
stringarg2Text = command.Arguments["arg2"] as string;
...
//Perform some action
...
break;
}
}
|
Visual Basic |
Copy Code
|
Protected Sub MyChartControl_AsyncCustomCommand(ByVal Sender As Object, ByVal E As EventArgs)
Dim Args As ArgsNCallbackCustomCommandArgs = TryCast(E, NCallbackCustomCommandArgs)
Dim Command As NCallbackCommand = Args.Command
Select Case Command.Name
Case "customCommand"
Dim Arg1Text AS String = CString(Command.Arguments("arg1"))
Dim Arg2Text As String = CString(Command.Arguments("arg2"))
...
'Perform some action
...
End Select
End Sub
|
If the command is supposed to return result to the client, the AsyncQueryCommandResult event of the web control must also be handled:
C# |
Copy Code
|
protected void MyChartControl_AsyncQueryCommandResult(object sender, EventArgs e)
{
NCallbackQueryCommandResultArgs args = e as NCallbackQueryCommandResultArgs;
NCallbackCommand command = args.Command;
NAjaxXmlTransportBuilder resultBuilder = args.ResultBuilder;
switch (command.Name)
{
case "customCommand":
// add a built-in data section that will enforce full
// image refresh at the client
if (!resultBuilder.ContainsRedrawDataSection())
resultBuilder.AddRedrawDataSection(MyChartControl);
NAjaxXmlDataSection dataSection = new NAjaxXmlDataSection("someSection");
dataSection.Data ="Some data";
resultBuilder.AddDataSection(dataSection);
break;
}
}
|
Visual Basic |
Copy Code
|
Protected Sub MyChartControl_AsyncQueryCommandResult(ByVal Sender As object, ByVal E As EventArgs)
Dim Args NCallbackQueryCommandResultArgs = TryCast(E, NCallbackQueryCommandResultArgs)
Dim Command As NCallbackCommand = Args.Command
Dim ResultBuilder As NAjaxXmlTransportBuilder = Args.ResultBuilder
Select Case command.Name
Case "customCommand"
' add a built-in data section that will enforce full
' image refresh at the client
If Not ResultBuilder.ContainsRedrawDataSection() Then _
ResultBuilder.AddRedrawDataSection(MyChartControl)
Dim DataSection As NAjaxXmlDataSection = New NAjaxXmlDataSection("someSection")
DataSection.Data ="Some data"
ResultBuilder.AddDataSection(DataSection)
End Select
End Sub
|
The result can be read at the client in the following way:
JavaScript |
Copy Code
|
// subscribe for the callback success event
NEventSinkService.AsyncCallbackSucceeded.Subscribe(CallbackSucceeded);
// event handler that is invoked when an async command is completed successfully
functionCallbackSucceeded(self, result, context)
{
switch(context.command)
{
case 'customCommand':
var transport = new NAjaxXmlTransport();
transport.Deserialize(result);
if(!NReflection.IsInstance(transport.DataSections["someSection"]))
break;
var someSectionText = transport.DataSections["someSection"].Data;
...
//Peroform some action
...
break;
}
}
|
Custom AJAX commands can be used for both extending the interactivity of the web control and performing any general client-server tasks that must be synchronized with the web control event handling.
Both the Nevron Chart and Diagram web examples that are installed by the Nevron .NET Vision installation contain an example on custom commands, which is located at AJAX\Engine\NCustomCommandsUC.ascx.
A higher level abstraction of the event processing is provided by the controller-tools architecture that is part of the Nevron JavaScript Library. All client-side interactivity is streamlined through a controller object, which maintains a collection of tools. All tools that are able to process a specific event generate commands (please note the difference between the controller commands and the AJAX commands) until all tools have been queried or a tool has marked the event as handled. Next, the controller commands are executed in a row. Some of the controller commands perform pure client side actions, and others send AJAX commands to the server.
All AJAX commands described in the previous section are generated by corresponding tools. Additional tools are implemented by Nevron to provide client side tooltips, dynamic cursors, browser redirect and other basic and advanced features.
Custom tools can be implemented in order to extend the interactivity of a web control, while remaining compatible with the existing interactive tools.
To implement a custom tool, new classes must be implemented at both server and client side. At server side, a tool definition class must be created that derives from NAjaxToolDefinition:
C# |
Copy Code
|
[Serializable]
public class NAjaxCustomTool : NAjaxToolDefinition
{
public NAjaxCustomTool(bool defaultEnabled, string customConfField)
: base("NCustomTool", defaultEnabled)
{
this.customConfField = customConfField;
}
protected override string GetConfigurationObjectJavaScript()
{
return string.Format("new NCustomToolConfig('{0}')", this.customConfField);
}
private string customConfField;
}
|
Visual Basic |
Copy Code
|
<Serializable>
Public Class NAjaxCustomTool
Inherits NAjaxToolDefinition
Public Sub New(ByVal DefaultEnabled As Boolean , ByVal CustomConfField As String)
MyBase("NCustomTool", DefaultEnabled)
Me.CustomConfField = CustomConfField
End Sub
Protected Overrides Function GetConfigurationObjectJavaScript() As String
Return String.Format("New NCustomToolConfig('{0}')", Me.CustomConfField)
End Function
Private CustomConfField As String
End Class
|
The "NCustomTool"parameter in the base class constructor invocation specifies the name of the JavaScript class that will represent the tool at the client. The return value of the GetConfigurationObjectJavaScript() method is the actual JavaScript code that will be used to create the tools configuration JavaScript, when the web control is being rendered to HTML. Additionally, the above sample code demonstrates how tool configuration fields defined at the server can be communicated to the client tools. When writing your own tool definitions, please make sure to mark them as serializable.
The complete Nevron JavaScript Library is implemented with JavaScript classes. A JavaScript class is a Nevron extension to the JavaScript object and provides common OOP features like inheritance, virtual methods, events, static methods and runtime type information. There are certain coding patterns that must be followed in order to implement a JavaScript class, rather than a simple JavaScript object. These patterns are illustrated in the code below:
JavaScript |
Copy Code
|
functionNCustomToolConfig(customConfField)
{
this.InitializeRuntimeTypeInfo(NCustomToolConfig.prototype);
this.NAjaxToolConfig(false);
this.customConfField = customConfField;
};
NCustomToolConfig.DeriveFrom(NAjaxToolConfig);
|
This JavaScript code defines the new JavaScript class NcustomToolConfig.The InitializeRuntimeTypeInfo call is required to read and cache all runtime type information like the name of the class and the name of the base class. The line this.NAjaxToolConfig(false);invokes the parent constructor and is an analogue to the :base(false)C# syntax. The DeriveFromcall copies all methods and fields from the base object’s prototype to the NCustomToolConfig prototype, making them available in the derived class.
The NCustomToolConfig JavaScript class implements the configuration class of a new custom tool. An instance of this class will be passed to the tool, making the server side configuration available in the client side JavaScript code.
To implement a complete custom tool, more new JavaScript classes must be defined. The following code demonstrates how to implement the class of the tool itself:
JavaScript |
Copy Code
|
functionNCustomTool(callbackService, toolConfig)
{
this.InitializeRuntimeTypeInfo(NCustomTool.prototype);
this.NAjaxTool(callbackService, toolConfig);
// initialize any internal tool fields
this.internalToolField=null;
...
if (NReflection.IsInstance(toolConfig.configurationObject))
this.config = toolConfig.configurationObject;
else
this.config = new NCustomToolConfig();
};
NCustomTool.DeriveFrom(NAjaxTool);
|
The above class specifies its base class, a constructor and possible data members, but is missing any methods. The class definition continues with a method that overrides a virtual method from the NajaxTool base class:
JavaScript |
Copy Code
|
NCustomTool.prototype.RequiresMouseMoveEvent = function(self)
{
return true;
};
|
Please note the self argument. It is an alternative for this,explicitly passed as a parameter. self must be used instead of this in all virtual functions, overrides, and event handlers that are implemented within JavaScript classes. The specific method in the above code is implemented in order to notify the controller that the tool will process mouse events.
JavaScript |
Copy Code
|
NCustomTool.prototype.ProcessMouseMove = function(self, commandQueue, eventData)
{
// Implement some logic here, prior to enqueuing new commands
commandQueue.Enqueue(new NCustomControllerCommand(self.callbackService, self, commandQueue, eventData));
};
|
This is another method that overrides a base virtual method. It generates new controller commands and appends them to the command queue.
Next, the NcustomControllerCommand class must be defined:
JavaScript |
Copy Code
|
functionNDisplayDetailedChartCommand(callbackService, tool, commandQueue, eventData)
{
this.InitializeRuntimeTypeInfo(NDisplayDetailedChartCommand.prototype);
this.NCallbackCommand(callbackService, tool, commandQueue);
this.eventData = null;
if (NReflection.IsInstance(eventData))
this.eventData = eventData;
};
NDisplayDetailedChartCommand.DeriveFrom(NCallbackCommand);
NDisplayDetailedChartCommand.prototype.Execute = function(self)
{
// Here comes the actual code that is performed by the command
...
NDisplayDetailedChartCommand.GetBase().Execute(self);
};
|
Please note the syntax of the base method invocation. It also requires the explicit selfparameter.
There is one more class to implement in order to allow the creation of the custom tool object. A custom tool factory must be defined that knows of the NCustomToolConfig class:
JavaScript |
Copy Code
|
functionNCustomToolFactory()
{
this.InitializeRuntimeTypeInfo(NCustomToolFactory.prototype);
this.NToolFactory(arguments[0], arguments[1], arguments[2], arguments[3], arguments[4], arguments[5], arguments[6], arguments[7], arguments[8], arguments[9]);
};
NCustomToolFactory.DeriveFrom(NToolFactory);
NCustomToolFactory.prototype.InitializeTools = function(self, callbackService)
{
// initialize the built-in tools
NCustomToolFactory.GetBase().InitializeTools(self, callbackService);
// initialize the custom tools
for (var i = 0; i < self.configurationItems.dictionaryEntries.length; i++)
{
var de = self.configurationItems.dictionaryEntries[i];
var toolConfig = de.val;
var tool = null;
if (toolConfig.toolId == NCustomTool.GetClassName())
{
tool = new NCustomTool(callbackService, toolConfig);
}
if (tool != null)
{
callbackService.controller.tools[callbackService.controller.tools.length] = tool;
tool.controller = callbackService.controller;
}
}
};
|
And finally, the server side web control must be configured to use the new custom tool factory in the generated JavaScript code:
C# |
Copy Code
|
protected void Page_Load(object sender, System.EventArgs e)
{
MyChartControl.AjaxToolsFactoryType = "NCustomToolFactory";
...
}
|
Visual Basic |
Copy Code
|
Protected Sub Page_Load(ByVal Sender As Object ,ByVal E As System.EventArgs)
MyChartControl.AjaxToolsFactoryType = "NCustomToolFactory"
...
End Sub
|
The Nevron documentation contains a complete code reference for the Nevron JavaScript Library and can be of assistance to developers who want to implement custom AJAX tools.
Both the Nevron Chart and Diagram web examples that are installed by the Nevron .NET Vision installation contain an example on custom tools, which is located at AJAX\Engine\NCustomToolsUC.ascx.