Persisting json data in jstree through postback via asp:hiddenfield - asp.net

I've been pouring over this for hours and I've yet to make much headway so I was hoping one of the wonderful denizens of SO could help me out. Here's the problem...
I'm implementing a tree via the jstree plugin for jQuery. I'm pulling the data with which I populate the tree programatically from our webapp via json dumped into an asp:HiddenField, basically like this:
JavaScriptSerializer serializer = new JavaScriptSerializer();
string json = serializer.Serialize(Items);
json = json.ToLower();
data.Value = json;
Then, the tree pulls the json from the hidden field to build itself. This works perfectly fine up until I try to persist data for which nodes are selected/opened. To simplify my problem I've hardcoded some json data into the tree and attempted to use the cookie plugin to persist the tree state data. This does not work for whatever reason. I've seen other issues where people need to load the plugins in a specific order, etc, this did not solve my issue. I tried the same setup with html_data and it works perfectly. With this working persistence I converted the cookie plugin to persist the data in a different asp:hiddenfield (we can't use cookies for this type of thing in our application.)
essentially the cookie operations are identical, it just saves the array of nodes as the value of a hidden field. This works with the html_data, still not with the json and I have yet to be able to put my finger on where it's failing.
This is the jQuery.cookie.js replacement:
jQuery.persist = function(name, value) {
if (typeof value != 'undefined') { // name and value given, set persist
if (value === null) {
value = '';
}
jQuery('#' + name).attr('value', value);
} else { // only name given, get value
var persistValue = null;
persistValue = jQuery('#' + name).attr('value');
return persistValue;
}
};
The jstree.cookie.js code is identical save for a few variable name changes.
And this is my tree:
$(function() {
$("#demo1").jstree({
"json_data": {
"data" : [
{
"data" : "A node",
"children" : [ "Child 1", "Child 2" ]
},
{
"attr": { "id": "li.node.id" },
"data" : {
"title": "li.node.id",
"attr": { "href": "#" }
},
"children": ["Child 1", "Child 2"]
}
]
},
"persistence": {
"save_opened": "<%= open.ClientID %>",
"save_selected": "<%= select.ClientID %>",
"auto_save": true
},
"plugins": ["themes", "ui", "persistence", "json_data"]
});
});
The data -is- being stored appropriately in the hiddenfields, the problem occurs on a postback, it does not reopen the nodes. Any help would be greatly appreciated.
After looking through this some more, I just wanted to explain that it appears to me that the issue is that the tree has not yet been built from the JSON_data when the persistence operations are being attempted. Is there any way to postpone these actions until after the tree is fully loaded?

If anyone is still attempting to perform the same type of operation on a jsTree version 3.0+ there is an easier way to accomplish the same type of functionality, without editing any of the jsTree's core JavaScript, and without relying on the "state" plugin (Version 1.0 - "Persistence"):
var jsTreeControl = $("#jsTreeControl");
//Can be a "asp:HiddenField"
var stateJSONControl = $("#stateJSONControl");
var url = "exampleURL";
jsTreeControl.jstree({
'core': {
"data": function (node, cb) {
var thisVar = this;
//On the initial load, if the "state" already exists in the hidden value
//then simply use that rather than make a AJAX call
if (stateJSONControl.val() !== "" && node.id === "#") {
cb.call(thisVar, { d: JSON.parse(stateJSONControl.val()) });
}
else {
$.ajax({
type: "POST",
url: url,
async: true,
success: function (json) {
cb.call(thisVar, json);
},
contentType: "application/json; charset=utf-8",
dataType: "json"
}).responseText;
}
}
}
});
//If the user changes the jsTree, save the full JSON of the jsTree into the hidden value,
//this will then be restored on postback by the "data" function in the jsTree decleration
jsTreeControl.on("changed.jstree", function (e, data) {
if (typeof (data.node) != 'undefined') {
stateJSONControl.val(JSON.stringify(jsTreeControl.jstree(true).get_json()));
}
});
This code will create a jsTree and save it's "state" into a hidden value, then upon postback when the jsTree is recreated, it will use its old "state" restored from the "HiddenField" rather than make a new AJAX call and lose the expansions/selections that the user has made.

Got it working properly with JSON data. I had to edit the "reopen" and "reselect" functions inside jstree itself.
Here's the new functioning reopen function for anyone who needs it.
reopen: function(is_callback) {
var _this = this,
done = true,
current = [],
remaining = [];
if (!is_callback) { this.data.core.reopen = false; this.data.core.refreshing = true; }
if (this.data.core.to_open.length) {
$.each(this.data.core.to_open, function(i, val) {
val = val.replace(/^#/, "")
if (val == "#") { return true; }
if ($(("li[id=" + val + "]")).length && $(("li[id=" + val + "]")).is(".jstree-closed")) { current.push($(("li[id=" + val + "]"))); }
else { remaining.push(val); }
});
if (current.length) {
this.data.core.to_open = remaining;
$.each(current, function(i, val) {
_this.open_node(val, function() { _this.reopen(true); }, true);
});
done = false;
}
}
if (done) {
// TODO: find a more elegant approach to syncronizing returning requests
if (this.data.core.reopen) { clearTimeout(this.data.core.reopen); }
this.data.core.reopen = setTimeout(function() { _this.__callback({}, _this); }, 50);
this.data.core.refreshing = false;
}
},
The problem was that it was trying to find the element by a custom attribute. It was just pushing these strings into the array to search when it was expecting node objects. Using this line
if ($(("li[id=" + val + "]")).length && $(("li[id=" + val + "]")).is(".jstree-closed")) { current.push($(("li[id=" + val + "]"))); }
instead of
if ($(val).length && $(val).is(".jstree-closed")) { current.push(val); }
was all it took. Using a similar process I was able to persist the selected nodes this way as well.
Hope this is of help to someone.

Related

Mapbox GL and .net core webApi

I am moving from Leaflet to Mapbox GL and have some data issues. My webApi is proven but I cannot smoothly integrate them.
The approach I gave up on, based upon their examples and my own research, looks like:
map = new mapboxgl.Map({
container: 'mapdiv',
style: 'mapbox://styles/mapbox/streets-v10'
, center: start
, zoom: $scope.zoom
, transformRequest: (url, resourceType) => {
if (resourceType === 'Source' && url.startsWith(CONFIG.API_URL)) {
return {
headers: {
'Authorization': 'Bearer ' + localStorageService.get("authorizationData")
, 'Access-Control-Allow-Origin': CONFIG.APP_URL
, 'Access-Control-Allow-Credentials': 'true'
}
}
}
}
});
This is passing my OAuth2 token (or at least I think it should be) and the Cross site scripting part CORS.
Accompanying the above with:
map.addSource(layerName, { type: 'geojson', url: getLayerURL($scope.remLayers[i]) });
map.getSource(layerName).setData(getLayerURL($scope.remLayers[i]));
Having also tried to no avail:
map.addSource(layerName, { "type": 'geojson', "data": { "type": "FeatureCollection", "features": [] }});
map.getSource(layerName).setData(getLayerURL($scope.remLayers[i]));
Although there are no errors Fiddler does not show any requests being made to my layer webApi. All the others show but Mapbox does not appear to raising them.
The Url looks like:
http://localhost:49198/api/layer/?bbox=36.686654090881355,34.72821077223763,36.74072742462159,34.73664000652042&dtype=l&id=cf0e1df7-9510-4d03-9319-d4a1a7d6646d&sessionId=9a7d7daf-76fc-4dd8-af4f-b55d341e60e4
Because this was not working I attempted to make it more manual using my existing $http calls which partially works.
map = new mapboxgl.Map({
container: 'mapdiv',
style: 'mapbox://styles/mapbox/streets-v10'
, center: start
, zoom: $scope.zoom
, transformRequest: (url, resourceType) => {
if (resourceType === 'Source' && url.startsWith(CONFIG.API_URL)) {
return {
headers: {
'Authorization': 'Bearer ' + localStorageService.get("authorizationData")
}
}
}
}
});
map.addSource(layerName,
{
"type": 'geojson',
"data": { "type": "FeatureCollection", "features": [] }
});
The tricky part is to know when to run the data retrieval call. The only place I could find was on the maps data event which now looks like:
map.on('data', function (e) {
if (e.dataType === 'source' && e.isSourceLoaded === false && e.tile === undefined) {
// See if the datasource is known
for (var i = 0; i < $scope.remLayers.length; i++) {
if (e.sourceId === $scope.remLayers[i].name) {
askForData(i)
}
}
}
});
function askForData(i) {
var data = getBBoxString(map);
var mapZoomLevel = map.getZoom();
if (checkZoom(mapZoomLevel, $scope.remLayers[i].minZoom, $scope.remLayers[i].maxZoom)) {
mapWebSvr.getData({
bbox: data, dtype: 0, id: $scope.remLayers[i].id, buffer: $scope.remLayers[i].isBuffer, sessionId
},
function (data, indexValue, indexType) {
showNewData(data, indexValue, indexType);
},
function () {
// Not done yet.
},
i,
0
);
}
}
function showNewData(ajxresponse, index, indexType) {
map.getSource($scope.remLayers[index].name).setData(ajxresponse);
map.getSource($scope.remLayers[index].name).isSourceLoaded = true;
}
This is all working with one exception. It keeps firing time and time again. Some of these calls return a lot of data for a web call so its not a solution at the moment.
Its like its never satisfied with the data even though its showing it on the map!
There is a parameter on the data event, isSourceLoaded but it does not get set to true.
I have searched for an example, have tried setting isSourceLoaded in a number of places (as with the code above) but to no avail.
Does anyone have a method accomplishing this basic data retrieval function successfully or can point out the error(s) in my code? Or even point me to a working example...
I have spent too long on this now and could do with some help.
After a bit of a run around I have a solution.
A Mapbox email pointed to populating the data in the load event - which I am now doing.
This was not however the solution I was looking for as the data needs refreshing when the map moves, zooms etc - further look ups are required.
Following a bit more a examination a solution was found.
Using the code blow on the render event will request the information when the bounding box is changed.
var renderStaticBounds = getBoundsString(map.getBounds());
map.on('render', function (e) {
if (renderStaticBounds != getBoundsString(map.getBounds())) {
renderStaticBounds = getBoundsString(map.getBounds());
for (var i = 0; i < $scope.remLayers.length; i++) {
askForData(i);
}
}
});
function getBoundsString(mapBounds) {
var left = mapBounds._sw.lng;
var bottom = mapBounds._sw.lat;
var right = mapBounds._ne.lng;
var top = mapBounds._ne.lat;
return left + ',' + bottom + ',' + right + ',' + top;
}
This hopefully will save someone some development time.

Invoking repo actions from multi-select

I've created an UI action using various guides (include Jeff Potts really great ones) successfully and it function exactly as expected - but I want to add that action to the multi-select tool as well. It has been really difficult finding much documentation.
Some things I've tried:
Tried to find out if there was an applicable actionGroup - which there doesn't seem to be.
Tried adding the multi-select tags to my share-config-custom.xml to define the item - it shows up, but I obviously can't seem to use the action ID to reference that action.
My next step was to try and create a js file with a registerAction function in it, which I am able to do and have it run (I can see the console.log dump) but I don't really have any idea how I would go about invoking my repo action from there).
How can I complete this task?
There is already function exist for invoking the repository custom action.This function is defined inside below file.
share-war\components\documentlibrary\actions.js
You can take reference of below code for invoking the repository action.
onActionSimpleRepoAction: function dlA_onActionSimpleRepoAction(record, owner)
{
//ACE-2470 : Clone: Clicking multiple times the simple Workflow approval menu item gives unexpected results.
if (owner.title.indexOf("_deactivated") == -1)
{
// Get action params
var params = this.getAction(record, owner).params,
displayName = record.displayName,
namedParams = ["function", "action", "success", "successMessage", "failure", "failureMessage", "async"],
repoActionParams = {};
for (var name in params)
{
if (params.hasOwnProperty(name) && !Alfresco.util.arrayContains(namedParams, name))
{
repoActionParams[name] = params[name];
}
}
//Deactivate action
var ownerTitle = owner.title;
owner.title = owner.title + "_deactivated";
var async = params.async ? "async=" + params.async : null;
// Prepare genericAction config
var config =
{
success:
{
event:
{
name: "metadataRefresh",
obj: record
}
},
failure:
{
message: this.msg(params.failureMessage, displayName),
fn: function showAction()
{
owner.title = ownerTitle;
},
scope: this
},
webscript:
{
method: Alfresco.util.Ajax.POST,
stem: Alfresco.constants.PROXY_URI + "api/",
name: "actionQueue",
queryString: async
},
config:
{
requestContentType: Alfresco.util.Ajax.JSON,
dataObj:
{
actionedUponNode: record.nodeRef,
actionDefinitionName: params.action,
parameterValues: repoActionParams
}
}
};
// Add configured success callbacks and messages if provided
if (YAHOO.lang.isFunction(this[params.success]))
{
config.success.callback =
{
fn: this[params.success],
obj: record,
scope: this
};
}
if (params.successMessage)
{
config.success.message = this.msg(params.successMessage, displayName);
}
// Acd configured failure callback and message if provided
if (YAHOO.lang.isFunction(this[params.failure]))
{
config.failure.callback =
{
fn: this[params.failure],
obj: record,
scope: this
};
}
if (params.failureMessage)
{
config.failure.message = this.msg(params.failureMessage, displayName);
}
// Execute the repo action
this.modules.actions.genericAction(config);
}
},

Dynamically toggle resource column visibility

I have a FullCalendar scheduler on a webapp which has 2 way databinding for resources and events, all working great. I want to be able to present the user with a dropdown that enables them to toggle the visibility of a column, ideally completely client side.
I have tried a combination of addResource / removeResource however my issue here is that a rerender of the calendar (e.g. when a new event is added) then displays the previously removed resource. I can work around this however would prefer a really simple approach using JS / CSS. I currently cannot find a way to set a resource to not be visible, or to have zero width - is this possible?
There is an easy way to do this:
Store resources in an array variable resourceData.
Create another array called visibleResourceIds to store the ids of any resources you want to show.
In the resources callback function, filter resourceData to only contain the resources where the resource id exists in visibleResourceIds. Return the filtered array and fullcalendar will only add the desired resources for you.
To remove a resource from view, simply remove the resource id from visibleResourceIds and refetchResources. To add the resource back in, add the id to visibleResourceIds and refetchResources. DONE.
JSFiddle
var resourceData = [
{id: "1", title: "R1"},
{id: "2", title: "R2"},
{id: "3", title: "R3"}
];
var visibleResourceIds = ["1", "2", "3"];
// Your button/dropdown will trigger this function. Feed it resourceId.
function toggleResource(resourceId) {
var index = visibleResourceIds.indexOf(resourceId);
if (index !== -1) {
visibleResourceIds.splice(index, 1);
} else {
visibleResourceIds.push(resourceId);
}
$('#calendar').fullCalendar('refetchResources');
}
$('#calendar').fullCalendar({
defaultView: 'agendaDay',
resources: function(callback) {
// Filter resources by whether their id is in visibleResourceIds.
var filteredResources = [];
filteredResources = resourceData.filter(function(x) {
return visibleResourceIds.indexOf(x.id) !== -1;
});
callback(filteredResources);
}
});
I had the same challenge. Instead of a dropdown, I use checkboxes, but the workings will be the same.
My resources are stored in a variable, when I uncheck a box, the resource is removed and the resource's object is added to another array with the resourceId as key, and the index added to the object to restore the object in the same column as it originally was. When re-checking the box, the object is added to the resources array and the resources refetched.
/* retrieve the resources from the server */
var planningResources;
var removedResource = [];
$.ajax({
url: '/planning/resources/',
method: 'get',
success: function (response) {
planningResources = response;
showCalendar();
}
, error: function () {
if (typeof console == "object") {
console.log(xhr.status + "," + xhr.responseText + "," + textStatus + "," + error);
}
}
});
/* create the calendar */
showCalendar = function () {
$('#calendar').fullCalendar({
...
});
}
/* checkbox on click */
$('.resource').click(function() {
var resourceId = $(this).val();
var hideResource = !$(this)[0].checked;
$('.status:checkbox:checked').each(function () {
});
if(hideResource) {
$.each(planningResources, function(index, value){
if( value && value.id == resourceId ) {
value.ndx = index;
removedResource[resourceId] = value;
planningResources.splice(index,1);
return false;
}
});
$('#planningoverview').fullCalendar(
'removeResource',
resourceId
);
}
else {
planningResources.splice(removedResource[resourceId].ndx, 0, removedResource[resourceId]);
$('#planningoverview').fullCalendar('refetchResources');
}
});
showCalendar();
It probably doesn't get first price in a beauty contest, but it works for me ...
Cheers
You can use the resourceColumns option for this. In the column objects you can set the width property to a number of pixels or a percentage. If you pass a function here you can easily handle the width property someplace else. Your hide/show function can then set the width to 0 to hide the column. After that you can trigger reinitView to update the view: $('#calendar').fullCalendar("reinitView");

How to make Chrome Extension run for each new Iframe added?

I created a Chrome Extension as a solution to override the helpText bubbles in SalesForce Console pages. The helpText bubbles show up the text without the ability to link URLs. It looks like this:
The extension is taking the helpText bubble (which in the SalesForce console window, is inside an iFrame) and makes the URL click-able. It also adds word wrap and marks the links in blue.
The solution works fine when the page loads with the initial iFrame (or iFrames) on it, meaning when you open the SalesForce console the first time (https://eu3.salesforce.com/console).
When a new tab is created at the SalesForce console, my inject script doesn't run.
Can you please assist in understanding how to inject the script on each and every new Tab SalesForce Console is creating?
The Extension as follows:
manifest.js:
{
"browser_action": {
"default_icon": "icons/icon16.png"
},
"content_scripts": [ {
"all_frames": true,
"js": [ "js/jquery/jquery.js", "src/inject/inject.js" ],
"matches": [ "https://*.salesforce.com/*", "http://*.salesforce.com/*" ]
} ],
"default_locale": "en",
"description": "This extension Fix SalesForce help bubbles",
"icons": {
"128": "icons/icon128.png",
"16": "icons/icon16.png",
"48": "icons/icon48.png"
},
"manifest_version": 2,
"name": "--Fix SalesForce bubble text--",
"permissions": [ "https://*.salesforce.com/*", "http://*.salesforce.com/*" ],
"update_url": "https://clients2.google.com/service/update2/crx",
"version": "5"
}
And this is the inject.js:
chrome.extension.sendMessage({}, function(response) {
var readyStateCheckInterval = setInterval(function() {
if (document.readyState === "complete") {
clearInterval(readyStateCheckInterval);
var frame = jQuery('#servicedesk iframe.x-border-panel');
frame = frame.contents();
function linkify(inputText) {
var replacedText, replacePattern1, replacePattern2, replacePattern3;
var originalText = inputText;
//URLs starting with http://, https://, file:// or ftp://
replacePattern1 = /(\b(https?|ftp|file):\/\/[-A-Z0-9+&##\/%?=~_|!:,.;]*[-A-Z0-9+&##\/%=~_|])/gim;
replacedText = inputText.replace(replacePattern1, '$1');
//URLs starting with "www." (without // before it, or it'd re-link the ones done above).
replacePattern2 = /(^|[^\/f])(www\.[\S]+(\b|$))/gim;
replacedText = replacedText.replace(replacePattern2, '$1$2');
//Change email addresses to mailto:: links.
replacePattern3 = /(([a-zA-Z0-9\-\_\.])+#[a-zA-Z\_]+?(\.[a-zA-Z]{2,6})+)/gim;
replacedText = replacedText.replace(replacePattern3, '$1');
//If there are hrefs in the original text, let's split
// the text up and only work on the parts that don't have urls yet.
var count = originalText.match(/<a href/g) || [];
if(count.length > 0){
var combinedReplacedText;
//Keep delimiter when splitting
var splitInput = originalText.split(/(<\/a>)/g);
for (i = 0 ; i < splitInput.length ; i++){
if(splitInput[i].match(/<a href/g) == null){
splitInput[i] = splitInput[i].replace(replacePattern1, '$1').replace(replacePattern2, '$1$2').replace(replacePattern3, '$1');
}
}
combinedReplacedText = splitInput.join('');
return combinedReplacedText;
} else {
return replacedText;
}
}
var helpOrbReady = setInterval(function() {
var helpOrb = frame.find('.helpOrb');
if (helpOrb) {
clearInterval(helpOrbReady)
} else {
return;
}
helpOrb.on('mouseout', function(event) {
event.stopPropagation();
event.preventDefault();
setTimeout(function() {
var helpText = frame.find('.helpText')
helpText.css('display', 'block');
helpText.css('opacity', '1');
helpText.css('word-wrap', 'break-word');
var text = helpText.html()
text = text.substr(text.indexOf('http'))
text = text.substr(0, text.indexOf(' '))
var newHtml = helpText.html()
helpText.html(linkify(newHtml))
}, 500); });
}, 1000);
}
}, 1000);
});
It is possible (I have not tested it, but it sounds plausible from a few questions I've seen here) that Chrome does not automatically inject manifest-specified code into newly-created <iframe> elements.
In that case, you will have to use a background script to re-inject your script:
chrome.runtime.onMessage.addListener( function(request, sender, sendResponse) {
if(request.reinject) {
chrome.tabs.executeScript(
sender.tab.id,
{ file: "js/jquery/jquery.js", "all_frames": true },
function(){
chrome.tabs.executeScript(
sender.tab.id,
{ file: "js/inject/inject.js", "all_frames": true }
);
}
);
});
Content script:
// Before everything: include guard, ensure injected only once
if(injected) return;
var injected = true;
function onNewIframe(){
chrome.runtime.sendMessage({reinject: true});
}
Now, I have many questions about your code, which are not directly related to your question.
Why the pointless sendMessage wrapper? No-one is even listening, so your code basically returns with an error set.
Why all the intervals? Use events instead of polling.
If you are waiting on document to become ready, jQuery offers $(document).ready(...)
If you're waiting on DOM modifications, learn to use DOM Mutation Observers, as documented and as outlined here or here. This would be, by the way, the preferred way to call onNewIframe().

How to attach element to another Ajax component

All, Forgive me I am not familiar with the ASP.NET Ajax. I knew the method Create is attaching an html element to ajax component. But I don't know how to detach it from the current component . and attach another one.
Let's say there is a element ctl00_PlaceHolderMain_UserRegistration_txbPassword1 has been attached to a component type AccelaWebControlExtender.HelperBehavior, and the created component id is ctl00_PlaceHolderMain_UserRegistration_txbPassword1_helper_bhv. The code looks like below. please review it .
Sys.Application.add_init(function() {
$create(AccelaWebControlExtender.HelperBehavior, {"closeTitle":"Close","id":"ctl00_PlaceHolderMain_UserRegistration_txbPassword1_helper_bhv","isRTL":false,"title":"Help"}, null, null, $get("ctl00_PlaceHolderMain_UserRegistration_txbPassword1"));
});
I think firstly I should retrieve the component by id, then do the detach and attach work. Hope someone can give me some help.Thanks.
After doing some research, I found It is called Extend Web server control that encapsulates a client behavior in Asp.net Ajax, And I found the attachment of component is done by Asp.net automatically . We can see the Sys.Application.add_init(function() code is generated in the aspx page by Asp.net automatically. So if we want to customize the original behavior of Web Server Control, I believe it can be made in the Javascript OOP way(old and same).
For example :
If the original behavior code is blow.
// Register the namespace for the control.
Type.registerNamespace('Samples');
//
// Define the behavior properties.
//
Samples.FocusBehavior = function(element) {
Samples.FocusBehavior.initializeBase(this, [element]);
this._highlightCssClass = null;
this._nohighlightCssClass = null;
}
//
// Create the prototype for the behavior.
//
Samples.FocusBehavior.prototype = {
initialize : function() {
Samples.FocusBehavior.callBaseMethod(this, 'initialize');
$addHandlers(this.get_element(),
{ 'focus' : this._onFocus,
'blur' : this._onBlur },
this);
this.get_element().className = this._nohighlightCssClass;
},
dispose : function() {
$clearHandlers(this.get_element());
Samples.FocusBehavior.callBaseMethod(this, 'dispose');
},
//
// Event delegates
//
_onFocus : function(e) {
if (this.get_element() && !this.get_element().disabled) {
this.get_element().className = this._highlightCssClass;
}
},
_onBlur : function(e) {
if (this.get_element() && !this.get_element().disabled) {
this.get_element().className = this._nohighlightCssClass;
}
},
//
// Behavior properties
//
get_highlightCssClass : function() {
return this._highlightCssClass;
},
set_highlightCssClass : function(value) {
if (this._highlightCssClass !== value) {
this._highlightCssClass = value;
this.raisePropertyChanged('highlightCssClass');
}
},
get_nohighlightCssClass : function() {
return this._nohighlightCssClass;
},
set_nohighlightCssClass : function(value) {
if (this._nohighlightCssClass !== value) {
this._nohighlightCssClass = value;
this.raisePropertyChanged('nohighlightCssClass');
}
}
}
// Optional descriptor for JSON serialization.
Samples.FocusBehavior.descriptor = {
properties: [ {name: 'highlightCssClass', type: String},
{name: 'nohighlightCssClass', type: String} ]
}
// Register the class as a type that inherits from Sys.UI.Control.
Samples.FocusBehavior.registerClass('Samples.FocusBehavior', Sys.UI.Behavior);
if (typeof(Sys) !== 'undefined') Sys.Application.notifyScriptLoaded();
I think we can override some of the methods of the Javascript Object Samples.FocusBehavior and it's prototype object to achieve customization.
For example .
I can override Samples.FocusBehavior.prototype._onFocus in the script like this.
Samples.FocusBehavior.prototype._onFocus = function (e) {
alert('test');
if (this.get_element() && !this.get_element().disabled) {
this.get_element().className = this._highlightCssClass;
}
};
Just make sure this code is parsed after original one by Browser.
I am not sure if this is the right way to make it . I hope someone can help to verify it .Thank you very much.
Here is a tutorial of it. please review it .
Cheers.

Resources