/**
* Datagrid submodule.
*
* @module RAMP
* @submodule Datagrid
* @main Datagrid
*/
/**
* The Datagrid class represents the side bar table shown next to the map. The data grid displays all map objects in a text format and allows the user to see more
* details (same as clicking the map object) and navigate to the object. This class create the UI panel, events, and event-handles for the data grid conatiner.
*
* @class Datagrid
* @static
* @uses dojo/_base/declare
* @uses dojo/_base/lang
* @uses dojo/query
* @uses dojo/_base/array
* @uses dojo/dom-class
* @uses dojo/dom-attr
* @uses dojo/dom-construct
* @uses dojo/topic
* @uses dojo/on
* @uses esri/layers/FeatureLayer
* @uses esri/tasks/query
* @uses Ramp
* @uses GraphicExtension
* @uses GlobalStorage
* @uses Gui
* @uses DatagridClickHandler
* @uses Util
* @uses Array
* @uses Dictionary
* @uses PopupManager
* @uses TmplHelper
*/
/*global define, window, tmpl */
/*jslint white: true */
define([
/* Dojo */
"dojo/_base/declare", "dojo/_base/lang", "dojo/query", "dojo/_base/array", "dojo/dom-class",
"dojo/dom-attr", "dojo/dom-construct", "dojo/topic", "dojo/on",
/* Text */
"dojo/text!./templates/datagrid_template.json",
"dojo/text!./templates/extended_datagrid_template.json",
// Esri
"esri/layers/FeatureLayer", "esri/tasks/query",
// Ramp
"ramp/ramp", "ramp/graphicExtension", "ramp/globalStorage", "ramp/datagridClickHandler", "ramp/map",
"ramp/eventManager",
// Util
"utils/util", "utils/array", "utils/dictionary", "utils/popupManager", "utils/tmplHelper"],
function (
// Dojo
declare, lang, dojoQuery, dojoArray, domClass, domAttr,
domConstruct, topic, dojoOn,
//Text
data_grid_template_json,
extended_datagrid_template_json,
// Esri
FeatureLayer, EsriQuery,
// Ramp
Ramp, GraphicExtension, GlobalStorage, DatagridClickHandler, RampMap, EventManager,
// Util
utilMisc, utilArray, utilDict, popupManager, tmplHelper) {
"use strict";
var config,
layerConfig,
gridConfig,
// The jquery table
oTable,
// The JQuery Object representing the grid
jqgrid,
// A Boolean used to keep track of whether or not the grid should apply an
// extent filter when the datagrid gets selected
extentFilterExpired = true,
zoomToGraphic,
lastExtent,
// Stores functions used to sort the datagrid
sortFcns = {},
// The name of the current sort function
currentSortFcn = "ascending",
// Name of the attribute used to store the oid
// in the details and zoomTo buttons
featureOidField = "feature-oid",
// Name of the attribute used to store the feature url
// in the details and zoomTo buttons
featureUrlField = "feature-url",
ui = (function () {
/**
* Creates a data grid row that has the following features:
* highlight for a give graphic
* un-highlight
* scroll to for a give graphic
*
* @method createRowPrototype
* @private
* @param cssClass {String} the style that highlights the row.
* @return {Object} an object containing features of a data grid row
*/
function createRowPrototype(cssClass) {
var index = -1,
node = null;
/**
* Returns the index of the given graphic object in the data grid
*
* @method getGraphicIndex
* @param {Object} graphic
* @private
*/
function getGraphicIndex(graphic) {
var targetData = getDataObject(graphic),
data = jqgrid.dataTable().fnGetData();
return utilArray.binaryIndexOf(data, function (entry) {
return sortFcns[currentSortFcn](entry, targetData);
});
}
return {
graphic: null,
focusedButton: null,
/**
* Navigate to the page the row is in and scroll to it. Returns true
* if the row exists in the data grid, false otherwise.
*
* @method navigateToRow
* @return {Boolean} A value indicating is the navigation is sucessful
* @private
*/
navigateToRow: function () {
if (index !== -1) {
// Figure out which page the entry is in and navigate to that page
var page = Math.floor(index / gridConfig.rowsPerPage);
jqgrid.dataTable().fnPageChange(page);
jqgridTableWrapper.scrollTo(node, 300, {
axis: "y",
offset: {
left: 0,
top: -node.height() * 1.5
}
});
return true;
}
return false;
},
/**
* Finds a row node corresponding to the given graphic object.
*
* @method update
* @private
* @return {{node: jObject, page: number}} A row node that displays graphic information. If none found, returns an object with empty jNode.
*/
update: function () {
if (this.graphic) {
index = getGraphicIndex(this.graphic);
if (index !== -1) {
node = $(jqgrid.dataTable().fnGetNodes(index));
}
} else {
index = -1;
}
},
/**
* Finds a row node corresponding to the given graphic object.
*
* @method getNode
* @private
* @return {{node: jObject, page: number}} A row node that displays graphic information. If none found, returns an object with empty jNode.
*/
getNode: function () {
this.update();
return node;
},
/**
* Highlights the the given graphic object using the specified cssClass.
*
* @method activate
* @private
*/
activate: function () {
if (this.graphic) {
this.getNode().addClass(cssClass);
if (this.focusedButton) {
node.find(this.focusedButton).focus();
this.focusedButton = null;
}
}
},
/**
* Removes a specified cssClass from a given graphic object in the data grid
*
* @method deactivate
* @private
*/
deactivate: function () {
if (this.graphic) {
this.getNode().removeClass(cssClass);
this.graphic = null;
}
}
};
}
var highlightRow = createRowPrototype("selected-row"),
zoomlightRow = createRowPrototype("highlighted-row"),
sectionNode,
datagridStatusLine,
datagridGlobalToggles,
jqgridTableWrapper,
_isReady = false; // indicates if the UI has fully rendered
/**
* Generates a data grid row data with a checkbox to be used in template
*
* @method generateToggleButtonDataForTemplate
* @private
* @return {String} the generated row data object.
*/
function generateToggleButtonDataForTemplate() {
// TODO: if no more modification is needed, this can be omitted and merged in the higher level.
// create object for template
// TODO: JKW
// Note, the following object is for passing to the template, properties
// were guessed. Need to add more detail later on. Empty values were provided
// eg. there were value of "" assigned to the toggle button template, in the code
// provided. A temporary property name "attribute" is used. Not sure if this will be
// used by ECDMP, therefore, leave the empty value in the template
var toggleButtonData = { "buttonLabel": "Sort", "classAddition": "font-medium global-button", "someAttribute": "" };
return toggleButtonData;
}
/**
* Creates a Data table based on the grid configuration specified in the application config object. See http://www.datatables.net/usage/columns for addition information on config parameters.
*
* @method createDatatable
* @private
*/
function createDatatable() {
var jqg_layout = dojoArray.map(gridConfig.gridColumns, function (column, i) {
return {
"sTitle": column.title,
"sWidth": column.width,
"sType": column.sortType,
"sClass": column.alignment ? "" : "center",
"mRender": undefined,
"aTargets": [i]
};
});
oTable = jqgrid.dataTable({
"bSort": false, // Disable sorting since we're sorting the array ourselves
"sDom": '<"jqgrid_table_wrapper"t><"status-line "p>',
"aoColumns": jqg_layout,
"bFilter": false,
"bInfo": false,
"bLengthChange": false,
"bPaginate": true,
"iDisplayLength": gridConfig.rowsPerPage,
"sPaginationType": "ramp",
"bDestroy": true,
"oLanguage": config.gridstrings,
"bDeferRender": true,
"fnDrawCallback": function () {
topic.publish(EventManager.Datagrid.DRAW_COMPLETE);
}
}).on("page", function () {
topic.publish(EventManager.GUI.SUBPANEL_DOCK, { origin: "datagrid" });
console.log("subPanleDock");
});
jqgridTableWrapper = sectionNode.find(".jqgrid_table_wrapper");
}
/**
* Initialize tooltips for the data grid
*
* @method initTooltips
* @private
*/
function initTooltips() {
popupManager.registerPopup(sectionNode, "hoverIntent",
function () {
if (this.target.attr("title")) {
if (this.target.isOverflowed()) {
this.target.tooltipster({ theme: '.tooltipster-dark' }).tooltipster("show");
} else {
this.target.removeAttr("title");
}
}
},
{
handleSelector: ".point-name, .category-name",
useAria: false,
timeout: 500
}
);
}
/**
* Adds event-handling for buttons inside the data grid's row elements (i.e 'Details', 'Zoom To' buttons)
*
* @method setButtonEvents
* @private
*/
function setButtonEvents() {
sectionNode.on("click", "button.details", function () {
var buttonNode = $(this),
node = buttonNode.parents(".record-row").parent().parent(); //TODO: replace with better selector
highlightRow.focusedButton = "button.details";
if (highlightRow.graphic && highlightRow.getNode()[0] === node[0]) {
DatagridClickHandler.onDetailDeselect();
} else {
var graphic = getGraphicFromButton(buttonNode);
DatagridClickHandler.onDetailSelect(buttonNode, graphic);
}
});
// Event handling for "Zoom To" button
sectionNode.on("click", "button.zoomto", function (evt) {
var zoomNode = $(this);
zoomlightRow.focusedButton = "button.zoomto";
// Zoom To
if (zoomNode.text() === config.stringResources.txtGrid_zoomTo) {
handleGridEvent(evt, function () {
zoomToGraphic = getGraphicFromButton(zoomNode);
//store the current extent, then zoom to point.
lastExtent = RampMap.getMap().extent.clone();
DatagridClickHandler.onZoomTo(RampMap.getMap().extent.clone(), zoomToGraphic);
// Update "zoom back" text after the extent change, if we update it
// before the extent change, it won't work since the datagrid gets
// repopulated after an extent change
utilMisc.subscribeOnce(EventManager.Datagrid.EXTENT_FILTER_END, function () {
// Find the first node with the same oid, featureUrl
var newNode = $(String.format("button.zoomto[data-{0}='{1}'][data-{2}='{3}']:eq(0)",
featureOidField, GraphicExtension.getOid(zoomToGraphic),
featureUrlField, zoomToGraphic.getLayer().url));
newNode.text(config.stringResources.txtGrid_zoomBack);
});
});
} else { // Zoom back
DatagridClickHandler.onZoomBack(zoomToGraphic);
zoomNode.text(config.stringResources.txtGrid_zoomTo);
}
});
sectionNode.on("click", "button.global-button", function () {
var buttonNode = $(this);
if (currentSortFcn === "ascending") {
buttonNode.addClass("state-expanded");
currentSortFcn = "descending";
} else {
buttonNode.removeClass("state-expanded");
currentSortFcn = "ascending";
}
applyExtentFilter();
});
//Adds an event trigger for the expansion of the data grid control
sectionNode.on("click", "button.expand", function () {
console.log("grid expanded!");
topic.publish("gui/grid/expand");
});
popupManager.registerPopup(sectionNode, "hover, focus",
function (d) {
this.target.removeClass("wb-invisible");
d.resolve();
},
{
handleSelector: "tr",
targetSelector: ".record-controls",
closeHandler: function (d) {
this.target.addClass("wb-invisible");
d.resolve();
},
activeClass: "background-light",
useAria: false
}
);
}
/**
* Apply's or removes the scrollbar from the data grid based on the height of its container.
*
* @method initScrollListeners
* @private
*/
function initScrollListeners() {
jqgridTableWrapper.scroll(function () {
var currentScroll = jqgridTableWrapper.scrollTop();
if (currentScroll === 0) {
datagridGlobalToggles.removeClass("scroll");
} else {
datagridGlobalToggles.addClass("scroll");
}
});
jqgridTableWrapper.scroll(function () {
var currentScroll = jqgridTableWrapper.scrollTop() + jqgridTableWrapper.outerHeight() - datagridStatusLine.outerHeight();
if (currentScroll - datagridGlobalToggles.outerHeight() === jqgrid.height()) {
datagridStatusLine.removeClass("scroll");
} else {
datagridStatusLine.addClass("scroll");
}
});
}
/**
* Highlights the row according to the graphic stored in the event. Sets the hightlightRow variable to the graphic object inside the sent event
*
* @method highlightrowShow
* @private
* @param {Object} event A thrown event that contains a graphic object inside the grid
*/
function highlightrowShow(event) {
highlightrowHide();
highlightRow.graphic = event.graphic;
if (event.scroll) {
ui.activateRows();
//applyExtentFilter();
}
}
/**
* Un-highlights the row that is currently highlighted
*
* @method highlightrowHide
* @private
*/
function highlightrowHide() {
highlightRow.deactivate();
DatagridClickHandler.onZoomCancel();
}
/**
* Stores the graphic in the given event in the variable zoomlightRow
*
* @method zoomlightrowShow
* @private
* @param {Object} event A thrown event that contains a graphic object inside the grid
*/
function zoomlightrowShow(event) {
zoomlightRow.graphic = event.graphic;
}
/**
* De-activiates the row stored in zoomlightRow
*
* @method zoomlightrowHide
* @private
*/
function zoomlightrowHide() {
zoomlightRow.deactivate();
}
/**
* Registers event handlers for following events:
* datagrid/highlightrow-show (!!2 handlers for this event!!)
* datagrid/zoomlightrow-show
* datagrid/zoomlightrow-hide
* @method initUIListeners
* @private
*/
function initUIListeners() {
topic.subscribe(EventManager.Datagrid.HIGHLIGHTROW_SHOW, highlightrowShow);
topic.subscribe(EventManager.Datagrid.HIGHLIGHTROW_HIDE, highlightrowHide);
topic.subscribe(EventManager.Datagrid.ZOOMLIGHTROW_SHOW, zoomlightrowShow);
topic.subscribe(EventManager.Datagrid.ZOOMLIGHTROW_HIDE, zoomlightrowHide);
}
return {
/**
* The constructor method for the data grid. Adds the grid's panel to the UI, adds the data rows, and creates all event triggers
*
* @method init
* @constructor
*
*/
init: utilMisc.once(
function () {
// using template to generate global checkboxes
var globalCheckBoxesData = generateToggleButtonDataForTemplate();
var templateData = { "buttons": globalCheckBoxesData, "tableId": "jqgrid", "tableCss": "display table-condensed table-simplify" };
var section;
var templateKey = "";
tmpl.cache = {};
templateKey = "datagrid_manager_Template"; // Ramp.getLayerConfig(layerUrl).mapTipSettings.anchorTemplate;
tmpl.templates = JSON.parse(tmplHelper.stringifyTemplate(data_grid_template_json));
// generate the content using rowData and given template
section = tmpl(templateKey, templateData);
sectionNode = $("#gridpane");
// fade out the loading animation
sectionNode.addClass('animated fadeOut');
window.setTimeout(
function () {
jqgrid = sectionNode
.empty().append(section)
.find("table");
createDatatable();
sectionNode
.removeClass("fadeOut")
.addClass('animated fadeIn');
datagridGlobalToggles = sectionNode.find('#datagridGlobalToggles');
datagridStatusLine = sectionNode.find('.status-line');
initTooltips();
setButtonEvents();
initScrollListeners();
initUIListeners();
//add button role to sortable cols
//dojoQuery(".sorting").attr("role", "button");
//remove alert role from table body
dojoQuery("tbody").removeAttr("role");
_isReady = true;
applyExtentFilter();
window.setTimeout(function () { sectionNode.removeClass('animated fadeIn'); }, 400);
},
300
);
}
),
/**
* Indicates that the Data grid is fully rendered
* @method isReady
* @returns {Boolean} A flag indicating the render status of the data grid
*/
isReady: function () {
return _isReady;
},
/**
* Removes the animating CSS class to prevent flickering
*
* @method onHide
* @private
*/
onHide: utilMisc.once(
function () {
sectionNode.removeClass('animated fadeIn');
}
),
/**
* Adjusts the width of the data grid panel to accommodate the scrollbar.
*
* @method adjustPanelWidth
*/
adjustPanelWidth: function () {
utilMisc.adjustWidthForSrollbar(jqgridTableWrapper, [datagridGlobalToggles, datagridStatusLine]);
},
/**
* Navigates the grid to a row. Based on the following precedent: 1. User selected point, 2. Highlighted row, 3. zoomed row, 4. first row in the grid.
*
* @method activateRows
*/
activateRows: function () {
// If there was previously a selected point,
// navigate to the correct page and highlight it in the data grid
highlightRow.update();
zoomlightRow.update();
// Scroll to the highlighted row first, if it fails,
// scroll to the zoomed row
if (!highlightRow.navigateToRow()) {
zoomlightRow.navigateToRow();
}
// set the focus on the first button in the data grid
// is not a really good idea to just move focus around
//jqgrid.dataTable().find("button:first").focus();
zoomlightRow.activate();
highlightRow.activate();
this.capturePanel();
},
/**
* publishes the subPanel_Capture event to the GUI class
* @method capturePanel
*/
capturePanel: function () {
if (highlightRow.graphic) {
topic.publish(EventManager.GUI.SUBPANEL_CAPTURE, {
target: highlightRow.getNode().find(".record-controls"),
consumeOrigin: "datagrid",
origin: "datagrid"
});
}
}
};
} ());
/**
* Returns the config Object for the given featureLayerUrl
*
* @method getGridConfig
* @param {String} url
* @return {Object} grid config
*/
function getGridConfig(url) {
return Ramp.getLayerConfig(url).datagrid;
}
/**
* A handler that handlers the Enter key press and Click mouse event of the data grid.
* It is actually a binder that binds the key / mouse event to a handler specified.
* This is wired up to grid cells in the bootstrapper to achieve click/keypress functions
*
* @method handleGridEvent
* @private
* @param {Event} e the event object
* @param {Function} fcn the callback function
*/
function handleGridEvent(e, callback) {
if ((e.type === "keypress" && e.keyCode === 13) || e.type === "click") {
callback();
//Ramp.setHTML(oid); // just update info hit
}
}
/**
* Gets all layer data in the current map extent that are visible, and put the data into the data grid.
* @method applyExtentFilter
*/
function applyExtentFilter() {
var visibleFeatures = {},
visibleGridLayers = RampMap.getVisibleFeatureLayers(),
q = new EsriQuery();
q.geometry = RampMap.getMap().extent;
q.outFields = ["*"];
var deferredList = dojoArray.map(visibleGridLayers, function (gridLayer) {
return gridLayer.queryFeatures(q).then(function (features) {
if (features.features.length > 0) {
var layer = features.features[0].getLayer(),
layerUrl = layer.url;
if (!layer.visible) {
visibleFeatures[layerUrl] = [];
} else {
visibleFeatures[layerUrl] = features.features;
}
}
});
});
// Execute this only after all the deferred objects has resolved
utilMisc.afterAll(deferredList, function () {
fetchRecords(visibleFeatures);
});
}
/**
* Given a map feature, return a data object used to represent the feature in the data grid.
*
* @method getDataObject
* @private
* @param {Object} feature the feature needs to be represented in the data grid
* return {Array} an array representing the data the given feature contains.
*/
function getDataObject(feature) {
//TODO call the template here???
var url = feature.getLayer().url;
//attribute = feature.attributes;
//Remember, case sensitivity MATTERS in the attribute name.
var innerArray,
tmplData;
//TODO change to have a new parameter that indicates what kind of data object we want:
// a summary object or full grid object. For now, hardcode to true to always pick summary
if (true) {
var sumTemplate = getGridConfig(url).summaryRowTemplate;
tmpl.cache = {};
//TODO can we cache this parsed value? we will use this template possibly twice for every feature.
// will save some processing if we dont stringify and parse it every darn time.
tmpl.templates = JSON.parse(tmplHelper.stringifyTemplate(data_grid_template_json));
//bundle feature into the template data object
tmplData = tmplHelper.dataBuilder(feature, url);
//result of template placed in an array
innerArray = [tmpl(sumTemplate, tmplData)];
} else {
//make array containing values for each column in the full grid
var row = [];
tmpl.cache = {};
tmpl.templates = JSON.parse(tmplHelper.stringifyTemplate(extended_datagrid_template_json));
//bundle feature into the template data object
tmplData = tmplHelper.dataBuilder(feature, url);
// retrieve extendedGrid config object
var extendedGrid = getGridConfig(url).extendedGridColumns;
// add columnIdx property, and set initial value
tmplData.columnIdx = 0;
// process each column and add to row
for (var i = 0; i < extendedGrid.length; i++) {
// set columnIdx for template
tmplData.columnIdx = i;
row.push(tmpl(extendedGrid[i].columnTemplate, tmplData));
}
innerArray = row;
}
//TODO may want to move the generation of this custom object to a separate area, as this data will be useful in
// both the summary and full grid state. Will need some thinking
// Includes fields that are useful which are not derived from the config.featureSources
// this should not draw, as there will be no column defined for it
innerArray.push({
"featureUrl": url,
"layerName": Ramp.getLayerConfig(url).displayName,
"feature": feature
});
return innerArray;
}
/**
* Populate the data grid with data in visibleFeatures
*
* @method fetchRecords
* @param {Array} visibleFeatures a dictionary mapping
* service url to an array of feature objects
* @private
*/
function fetchRecords(visibleFeatures) {
jqgrid.dataTable().fnClearTable();
jqgrid.dataTable().fnPageChange(0);
if (Object.keys(visibleFeatures).isEmpty()) {
return;
}
var data = [];
//for each feature layer
utilDict.forEachEntry(visibleFeatures, function (key, features) {
//for each feature in a specific layer
data = data.concat(dojoArray.map(features, function (feature) {
//return the appropriate data object for the feature (.map puts them in array form)
// "cache" the data object so we don't have to generate it again
if (!feature.rampDataObj) {
//TODO need an extended and summary data object to handle both grid states
feature.rampDataObj = getDataObject(feature);
}
return feature.rampDataObj;
}));
});
//TODO keep sort for now just for sanity. will overhaul later
data.sort(sortFcns[currentSortFcn]);
utilMisc.subscribeOnce(EventManager.Datagrid.DRAW_COMPLETE, function () {
// Draw complete fires after fnAddData is complete and the datagrid UI
// finishes updating
ui.activateRows();
ui.adjustPanelWidth();
topic.publish(EventManager.Datagrid.EXTENT_FILTER_END);
});
//add the data to the grid
try {
jqgrid.dataTable().fnAddData(data);
} catch (e) {
console.error(e);
}
// NOTE: fnAddData should be the last thing that happens in this function
// if you want to add something after this point, use the fnDrawCallback
// function in the jqgrid initialization
}
/**
* Returns the graphic object of a feature layer which is contained in the given buttonNode.
*
* @method getGraphicFromButton
* @private
* @param {JObject} buttonNode the node containing the feature layer
* @return {Object} the graphic object of the feature layer.
*/
function getGraphicFromButton(buttonNode) {
var featureUrl = buttonNode.data(featureUrlField),
// Need to parse the index into an integer since it
// comes as a String
oid = parseInt(buttonNode.data(featureOidField)),
featureLayer = RampMap.getFeatureLayer(featureUrl),
graphic = utilArray.binaryFind(featureLayer.graphics,
function (a_graphic) {
return GraphicExtension.getOid(a_graphic) - oid;
});
return graphic;
}
/**
* Binding event handling for events:
* filterManager/layer-visibility-toggled
* filterManager/global-layer-visibility-toggled
* datagrid/applyExtentFilter
*
* @method initListeners
* @private
*/
function initListeners() {
topic.subscribe(EventManager.FilterManager.LAYER_VISIBILITY_TOGGLED, function () {
extentFilterExpired = true;
});
topic.subscribe(EventManager.FilterManager.GLOBAL_LAYER_VISIBILITY_TOGGLED, function () {
extentFilterExpired = true;
});
/* UI EVENTS */
topic.subscribe(EventManager.GUI.TAB_SELECTED, function (arg) {
if ((arg.tabName === "datagrid") && (extentFilterExpired)) {
extentFilterExpired = false;
applyExtentFilter();
} else if ((arg.tabName === "datagrid") && (!extentFilterExpired)) {
ui.capturePanel();
} else {
ui.adjustPanelWidth();
}
});
topic.subscribe(EventManager.GUI.TAB_DESELECTED, function (arg) {
if (arg.tabName === "datagrid") {
ui.onHide();
topic.publish(EventManager.GUI.SUBPANEL_DOCK, {
origin: "datagrid"
});
console.log("subPanleDock");
}
});
topic.subscribe(EventManager.Datagrid.APPLY_EXTENT_FILTER, function () {
if (!ui.isReady()) {
ui.init();
}
applyExtentFilter();
});
}
return {
init: function () {
/// <summary>
/// initialize the datagrid. must be called before any properties can be accessed.
/// </summary>
config = GlobalStorage.config;
layerConfig = config.featureLayers;
gridConfig = layerConfig[0].datagrid; //this is just to configure the structure of the grid. since all layers have same structure, just pick first one
initListeners();
/**
* Sort by feature name, then by oid, then by feature url (alphabetically ascending)
*
* @method featureSorter
* @private
* @param {Object} e1 custom data object
* @param {Object} e2
* @return {Integer} a positive integer if e1 is greater (in sort order) than e2, a negative integer
* if e2 is greater than e1, 0 if the two are considered equal by sort order
*/
function featureSorter(e1, e2) {
var result = e1[0].localeCompare(e2[0]);
if (result === 0) {
result = e1[1] - e2[1]; // oids are integers
if (result === 0) {
result = e1.last().featureUrl.localeCompare(e2.last().featureUrl);
}
}
return result;
}
/**
* Sorts by layer name then by feature (alphabetically ascending)
*
* @method sortFcns
* @param {Object} e1 custom data object
* @param {Object} e2
* @return {Array} A array of sorted objects
*/
sortFcns["default"] = function (e1, e2) {
var layerName1 = e1.last().layerName,
layerName2 = e2.last().layerName,
result = layerName1.localeCompare(layerName2);
if (result === 0) {
result = featureSorter(e1, e2);
}
return result;
};
sortFcns.ascending = featureSorter;
sortFcns.descending = function (e1, e2) {
// Swap the order the parameters are passed in to make it
// descending
return featureSorter(e2, e1);
};
} //InitDataGrid
};
});