OK, here's a sneak peak at the 3.0 compatible jQuery widget I made. It's essentially a wrapper around the jsTree widget. You invoke it like this in JavaScript:
var json = getTreeJson(); // Get the JSON from the server-side TreeView.ToJson() method
var treeOptions = {}; // Specify tree options. See $.fn.gspTreeView.defaults
If you pass null for the json variable, the widget will get the JSON by invoking a GET request against the URL specified in the option treeDataUrl.
Below is the gspTreeView widget. Note that it may have external references you need to fix. Two I can think of:
//#region gspTreeView plug-in
$.fn.gspTreeView = function (data, options) {
var self = this;
var settings = $.extend({}, $.fn.gspTreeView.defaults, options);
var getTreeDataAndRender = function () {
$.ajax({
type: "GET",
url: options.treeDataUrl,
contentType: "application/json; charset=utf-8",
complete: function () {
self.removeClass('gsp_wait');
},
success: function (tagTreeJson) {
var tv = new GspTreeView(self, $.parseJSON(tagTreeJson), settings);
tv.render();
},
error: function (response) {
$.gspShowMsg("Action Aborted", response.responseText, { msgType: 'error', autoCloseDelay: 0 });
}
});
};
if (data == null) {
getTreeDataAndRender();
} else {
var gspTv = new GspTreeView(this, data, settings);
gspTv.render();
}
return this;
};
$.fn.gspTreeView.defaults = {
clientId: '', // The ID of the HTML element containing the entire gallery. Used to scroll node into view in left pane. Omit if left pane scrolling not needed
allowMultiSelect: false, // Indicates whether more than one node can be selected at a time
albumIdsToSelect: null, // An array of the album IDs of any nodes to be selected during rendering
checkedAlbumIdsHiddenFieldClientId: '', // The client ID of the hidden input field that stores a comma-separated list of the album IDs of currently checked nodes
theme: 'gsp', // Used to generate the CSS class name that is applied to the HTML DOM element that contains the treeview. Ex: "gsp" is rendered as CSS class "jstree-gsp"
requiredSecurityPermissions: 1, //ViewAlbumOrMediaObject
navigateUrl: '', // The URL to the current page without query string parms. Used during lazy load ajax call. Example: "/dev/gs/gallery.aspx"
enableCheckboxPlugin: false, // Indicates whether a checkbox is to be rendered for each node
treeDataUrl: '' // The URL for retrieving tree data. Ignored when tree data is passed via data parameter
};
window.GspTreeView = function (target, data, options) {
this.$target = target; // A jQuery object to receive the rendered treeview.
this.TreeViewOptions = options;
this.Data = data;
};
GspTreeView.prototype.render = function () {
var self = this;
this._updateNodeDataWithAlbumIdsToSelect();
var jstreeOptions = {
core: {
data: function (node, cb) {
return cb(self.Data);
}
$.ajax({
url: window.Gsp.GalleryResourcesRoot + '/handler/gettreeview.ashx',
data: {
// Query string parms to be added to the AJAX request
id: node.li_attr['data-id'],
secaction: self.TreeViewOptions.requiredSecurityPermissions,
sc: $.inArray('checkbox', this.settings.plugins) >= 0, // Whether checkboxes are being used
navurl: self.TreeViewOptions.navigateUrl
},
dataType: "json",
error: function (response, textStatus, errorThrown) {
if (textStatus == "error") {
alert("Oops! An error occurred while retrieving the treeview data. It has been logged in the gallery's event log.");
}
},
success: function (data) {
return cb(data);
}
});
return null;
},
multiple: this.TreeViewOptions.allowMultiSelect,
themes: {
name: this.TreeViewOptions.theme,
dots: false,
icons: false,
responsive: false
}
},
};
if (this.TreeViewOptions.enableCheckboxPlugin) {
jstreeOptions.plugins = ['checkbox'];
jstreeOptions.checkbox = {
keep_selected_style: false,
three_state: this.TreeViewOptions.allowMultiSelect
};
}
this.$target.jstree(jstreeOptions)
.on("ready.jstree", function (e, data) {
self.onLoaded(e, data);
})
.on("changed.jstree", function (e, data) {
self.onChangeState(e, data);
})
.on("deselect_node.jstree", function (e, data) {
self.onDeselectNode(e, data);
});
};
GspTreeView.prototype._storeSelectedNodesInHiddenFormField = function (data) {
// Grab the data-id values from the top selected nodes, concatenate them and store them in a hidden
// form field. This can later be retrieved by server side code to determine what was selected.
if (this.TreeViewOptions.checkedAlbumIdsHiddenFieldClientId == null || this.TreeViewOptions.checkedAlbumIdsHiddenFieldClientId.length == 0)
return;
var topSelectedNodes = data.instance.get_top_selected(true);
var albumIds = $.map(topSelectedNodes, function (val, i) {
return val.li_attr['data-id'];
}).join();
$('#' + this.TreeViewOptions.checkedAlbumIdsHiddenFieldClientId).val(albumIds);
};
GspTreeView.prototype._updateNodeDataWithAlbumIdsToSelect = function () {
// Process the albumIdsToSelect array - find the matching node in the data and change state.selected to true
// Note that in many cases the nodes are pre-selected in server side code. This function isn't needed in those cases.
if (Gsp.isNullOrEmpty(this.TreeViewOptions.albumIdsToSelect))
return;
var findMatch = function (nodeArray, dataId) {
// Search nodeArray for a node having data-id=dataId, acting recursively
if (Gsp.isNullOrEmpty(nodeArray))
return null;
var matchingNode = $.grep(nodeArray, function (n) { return n.li_attr['data-id'] === dataId; })[0] || null;
if (matchingNode != null)
return matchingNode;
// Didn't find it, so recursively search node data
$.each(nodeArray, function (idx, n) {
matchingNode = findMatch(n.children, dataId);
if (matchingNode != null) {
return false; // Break out of $.each
}
});
return matchingNode;
};
var self = this;
$.each(this.TreeViewOptions.albumIdsToSelect, function (idx, id) {
var node = findMatch(self.Data, id);
if (node != null) {
node.state.selected = true;
}
});
};
GspTreeView.prototype.onChangeState = function (e, data) {
if (data.action == 'select_node') {
var url = data.instance.get_node(data.node, true).children('a').attr('href');
if (url != null && url.length > 1) {
// Selected node is a hyperlink with an URL, so navigate to it.
document.location = url;
return;
}
}
if (data.action == 'deselect_node' || data.action == 'select_node') {
this._storeSelectedNodesInHiddenFormField(data);
}
};
GspTreeView.prototype.onDeselectNode = function (e, data) {
// Don't let user deselect the only selected node when allowMultiSelect=false
if (!this.TreeViewOptions.allowMultiSelect && data.instance.get_selected().length == 0) {
data.instance.select_node(data.node);
}
};
GspTreeView.prototype.onLoaded = function (e, data) {
this._storeSelectedNodesInHiddenFormField(data);
// Scroll the left pane if necessary so that the selected node is visible
if (this.TreeViewOptions.clientId.length < 1)
return;
var selectedIds = data.instance.get_selected();
if (selectedIds != null && selectedIds.length == 1) {
var nodeOffsetTop = $('#' + selectedIds[0]).position().top;
var leftPaneHeight = $('#' + this.TreeViewOptions.clientId + '_lpHtml').height();
if (nodeOffsetTop > leftPaneHeight) {
$('#' + this.TreeViewOptions.clientId + '_lpHtml').animate({ scrollTop: nodeOffsetTop }, 200, "linear");
}
}
};
//#endregion gspTreeView plug-in