Files
PetitTeton/public/admin/js/LinkedTreeTable.js

149 lines
6.0 KiB
JavaScript

"use strict";
var LinkedTreeTable;
+function($) {
//Pass an element (jquery or html reference), an options object (see DEFAULTS), and an array of node metadata objects (see NODE_DEFAULTS).
LinkedTreeTable = function(element, options, nodeMetadata) {
var _this = this;
this.$element = $(element);
this.options = $.extend({}, LinkedTreeTable.DEFAULTS, options);
this.nodes = [];
this.$selectedRow = null;
this.selectionHandler = function(event) {
$(this).addClass(_this.options.selectionCSS).siblings().removeClass(_this.options.selectionCSS);
_this.$selectedRow = $(this);
};
this.addNodeMetadata = function(nodeMetadata) {
nodeMetadata = $.extend({}, LinkedTreeTable.NODE_DEFAULTS, nodeMetadata);
var nodes = this.nodes[nodeMetadata.type];
nodeMetadata.cls = "NODE";
if(typeof nodes == 'object') {
if(nodes.cls == 'NODE') {
this.nodes[nodeMetadata.type] = [nodes, nodeMetadata];
}
else {
nodes.push(nodeMetadata);
}
}
else {
this.nodes[nodeMetadata.type] = nodeMetadata
}
};
//Add the node metadata to the mapping.
for(var index = 0; index < nodeMetadata.length; index++) {
this.addNodeMetadata(nodeMetadata[index]);
}
};
LinkedTreeTable.DEFAULTS = {
dataAttr: 'data-key-name', //The property name to use to attach the model sent by the server to the table row created for the data.
typeAttr: 'data-type-name', //The attribute name used to attach the type to the table row.
selectionCSS: 'selected',
selection: 'row', //Currently only row is supported.
getIdHandler: null //Optional global function that gets the id for an object. No ID will be used if not provided, in which case the row will not be re-opened or re-selected after refreshing the data from the server.
};
LinkedTreeTable.NODE_DEFAULTS = {
type: '', //The type name this node metadata applies to. Must be provided. The empty type is used to identify the metadata for collecting root nodes.
url: '', //The absolute or relative path to use to query the initial data. Server is expected to respond with a JSON array of objects.
typeHandler: null, //The optional handler called to determine the type for the model sent by the server. Must return a type name that is then matched to the node metadata to get children.
defaultType: null, //The default type to assign to the node if the type handler does not provide one. This normally should be set, particularly if a handler won't always provide a type.
postAddRowHandler: null, //Optional function that is passed the jquery table row and the data object sent by the server for that row. Allows post processing of the row prior to display.
parameters: null, //Optional function that returns an object, or an object whose attributes are passed to the URL as parameters.
getIdHandler: null //Optional function that gets the id for an object. The global version will be used if this one is not provided.
};
LinkedTreeTable.prototype.getSelectedRow = function() {
return this.$selectedRow;
};
//A function that will clean and rebuild the table displaying all the users.
//Note that each row of the table will have a data element attached to the table row. The key will be "model" and the value will be the object sent by the server.
//Pass an optional table row or data object to reference a node in the tree whose children will be refreshed. If not provided then the root nodes will be refreshed.
LinkedTreeTable.prototype.refresh = function(node) {
var _this = this;
var table = this.$element;
var thead = table.find("thead tr");
var tbody = table.find("tbody");
var selection = this.options.selection;
var dataAttr = this.options.dataAttr;
var selectionHandler = this.selectionHandler;
var params;
//TODO: Need to identify the top most visible row?
//TODO: Otherwise identify the scroll position so we can reset it if necessary?
//TODO: Find the ID's of all rows at this tree level or lower that are open so we can re-open them after refreshing?
//TODO: Otherwise map the new data by ID to the rows at this tree level so we can update the rows instead of replacing them?
if(thead.length == 0) {
return;
}
//Empty or Create the table body.
if(tbody.length != 0) {
//Remove the row selection handler.
if(selection == 'row') this.$element.off('click', 'tbody tr', selectionHandler);
//Empty the table of data.
tbody.empty();
}
else {
tbody = $("<tbody></tbody>");
tbody.appendTo(table);
}
if(typeof this.options.parameters == 'function') {
params = this.options.parameters();
//Must be an object.
if(typeof params != 'object') {
params = {};
}
}
else if(typeof this.options.parameters == 'object') {
params = this.options.parameters;
}
else {params = {};}
$.getJSON(this.options.url, params, function(data) {
var headers = thead.children();
var attributes = [];
//Read the table headers to get the data object keys.
for(var headerIndex = 0; headerIndex < headers.length; headerIndex++) {
var nextHeader = headers[headerIndex];
attributes[headerIndex] = $(nextHeader).attr(dataAttr);
}
//Add the table data.
for(var dataIndex = 0; dataIndex < data.length; dataIndex++) {
var rowData = data[dataIndex];
var row = $("<tr></tr>");
row.appendTo(tbody);
//Save the model attached to the table row. Can be retrieved later to get the model sent by the server.
row.data("model", rowData);
for(var attributeIndex = 0; attributeIndex < attributes.length; attributeIndex++) {
var attribute = attributes[attributeIndex];
var cellData = rowData[attribute];
row.append("<td>" + cellData + "</td>");
}
if(_this.options.postAddRowHandler) {
//Call the optional handler after adding the row, passing the jquery row object, and the row data object sent by the server. Allows post processing on the row (adding classes to the row for example).
_this.options.postAddRowHandler(row, rowData);
}
}
//Setup the row selection handler.
if(selection == 'row') table.on('click', 'tbody tr', selectionHandler);
});
}
}(jQuery);