define([
|
'dojo/_base/declare',
|
'dojo/_base/lang',
|
'dojo/dom-construct',
|
'dojo/dom-class',
|
'dojo/on',
|
'dojo/has',
|
'./util/misc',
|
'dojo/_base/sniff'
|
], function (declare, lang, domConstruct, domClass, listen, has, miscUtil) {
|
// Add user agent/feature CSS classes needed for structural CSS
|
var featureClasses = [];
|
if (has('mozilla')) {
|
featureClasses.push('has-mozilla');
|
}
|
if (has('touch')) {
|
featureClasses.push('has-touch');
|
}
|
domClass.add(document.documentElement, featureClasses);
|
|
// Add a feature test for pointer (only Dojo 1.10 has pointer-events and MSPointer tests)
|
has.add('pointer', function (global) {
|
return 'PointerEvent' in global ? 'pointer' :
|
'MSPointerEvent' in global ? 'MSPointer' : false;
|
});
|
|
var oddClass = 'dgrid-row-odd',
|
evenClass = 'dgrid-row-even',
|
scrollbarWidth, scrollbarHeight;
|
|
function byId(id) {
|
return document.getElementById(id);
|
}
|
|
function cleanupTestElement(element) {
|
element.className = '';
|
if (element.parentNode) {
|
document.body.removeChild(element);
|
}
|
}
|
|
function getScrollbarSize(element, dimension) {
|
// Used by has tests for scrollbar width/height
|
element.className = 'dgrid-scrollbar-measure';
|
document.body.appendChild(element);
|
var size = element['offset' + dimension] - element['client' + dimension];
|
cleanupTestElement(element);
|
return size;
|
}
|
has.add('dom-scrollbar-width', function (global, doc, element) {
|
return getScrollbarSize(element, 'Width');
|
});
|
has.add('dom-scrollbar-height', function (global, doc, element) {
|
return getScrollbarSize(element, 'Height');
|
});
|
|
has.add('dom-rtl-scrollbar-left', function (global, doc, element) {
|
var div = document.createElement('div'),
|
isLeft;
|
|
element.className = 'dgrid-scrollbar-measure';
|
element.setAttribute('dir', 'rtl');
|
element.appendChild(div);
|
document.body.appendChild(element);
|
|
// position: absolute makes modern IE and Edge always report child's offsetLeft as 0,
|
// but other browsers factor in the position of the scrollbar if it is to the left.
|
// All versions of IE and Edge are known to move the scrollbar to the left side for rtl.
|
isLeft = !!has('ie') || !!has('trident') || /\bEdge\//.test(navigator.userAgent) ||
|
div.offsetLeft >= has('dom-scrollbar-width');
|
cleanupTestElement(element);
|
domConstruct.destroy(div);
|
element.removeAttribute('dir');
|
return isLeft;
|
});
|
|
// var and function for autogenerating ID when one isn't provided
|
var autoId = 0;
|
function generateId() {
|
return List.autoIdPrefix + autoId++;
|
}
|
|
// common functions for class and className setters/getters
|
// (these are run in instance context)
|
function setClass(cls) {
|
domClass.replace(this.domNode, cls, this._class || '');
|
|
// Store for later retrieval/removal.
|
this._class = cls;
|
}
|
function getClass() {
|
return this._class;
|
}
|
|
// window resize event handler, run in context of List instance
|
var winResizeHandler = function () {
|
if (this._started) {
|
this.resize();
|
}
|
};
|
|
var List = declare(null, {
|
tabableHeader: false,
|
|
// showHeader: Boolean
|
// Whether to render header (sub)rows.
|
showHeader: false,
|
|
// showFooter: Boolean
|
// Whether to render footer area. Extensions which display content
|
// in the footer area should set this to true.
|
showFooter: false,
|
|
// maintainOddEven: Boolean
|
// Whether to maintain the odd/even classes when new rows are inserted.
|
// This can be disabled to improve insertion performance if odd/even styling is not employed.
|
maintainOddEven: true,
|
|
// cleanAddedRules: Boolean
|
// Whether to track rules added via the addCssRule method to be removed
|
// when the list is destroyed. Note this is effective at the time of
|
// the call to addCssRule, not at the time of destruction.
|
cleanAddedRules: true,
|
|
// addUiClasses: Boolean
|
// Whether to add jQuery UI classes to various elements in dgrid's DOM.
|
addUiClasses: true,
|
|
// highlightDuration: Integer
|
// The amount of time (in milliseconds) that a row should remain
|
// highlighted after it has been updated.
|
highlightDuration: 250,
|
|
// resizeThrottleDelay: Integer
|
// The delay (in milliseconds) passed to the resizeThrottleMethod.
|
// A lower value will provide more responsive grid resizing. If there are a large number of grids on
|
// the page, a higher value can improve performance (or specify 'debounce' for 'resizeThrottleMethod').
|
resizeThrottleDelay: miscUtil.defaultDelay,
|
|
// resizeThrottleMethod: String or Function
|
// String: the name of a method from dgrid/util/misc ('debounce', 'throttle', 'throttleDelayed') to throttle or debounce the window resize handler.
|
// Function: a function to throttle or debounce the window resize handler. The function will receive
|
// two parameters:
|
// callback (Function): the function to be throttled
|
// delay (Integer): the value of the resizeThrottleDelay property
|
// The function must return a function that executes the callback function.
|
resizeThrottleMethod: 'throttleDelayed',
|
|
postscript: function (params, srcNodeRef) {
|
// perform setup and invoke create in postScript to allow descendants to
|
// perform logic before create/postCreate happen (a la dijit/_WidgetBase)
|
var grid = this;
|
|
(this._Row = function (id, object, element) {
|
this.id = id;
|
this.data = object;
|
this.element = element;
|
}).prototype.remove = function () {
|
grid.removeRow(this.element);
|
};
|
|
if (srcNodeRef) {
|
// normalize srcNodeRef and store on instance during create process.
|
// Doing this in postscript is a bit earlier than dijit would do it,
|
// but allows subclasses to access it pre-normalized during create.
|
this.srcNodeRef = srcNodeRef =
|
srcNodeRef.nodeType ? srcNodeRef : byId(srcNodeRef);
|
}
|
this.create(params, srcNodeRef);
|
},
|
listType: 'list',
|
|
create: function (params, srcNodeRef) {
|
var domNode = this.domNode = srcNodeRef || document.createElement('div'),
|
cls;
|
|
if (params) {
|
this.params = params;
|
declare.safeMixin(this, params);
|
|
// Check for initial class or className in params or on domNode
|
cls = params['class'] || params.className || domNode.className;
|
}
|
|
// ensure arrays and hashes are initialized
|
this.sort = this.sort || [];
|
this._listeners = [];
|
this._rowIdToObject = {};
|
|
this.postMixInProperties && this.postMixInProperties();
|
|
// Apply id to widget and domNode,
|
// from incoming node, widget params, or autogenerated.
|
this.id = domNode.id = domNode.id || this.id || generateId();
|
|
// Perform initial rendering, and apply classes if any were specified.
|
this.buildRendering();
|
if (cls) {
|
setClass.call(this, cls);
|
}
|
|
this.postCreate();
|
|
// remove srcNodeRef instance property post-create
|
delete this.srcNodeRef;
|
// to preserve "it just works" behavior, call startup if we're visible
|
if (this.domNode.offsetHeight) {
|
this.startup();
|
}
|
},
|
buildRendering: function () {
|
var domNode = this.domNode,
|
addUiClasses = this.addUiClasses,
|
self = this,
|
headerNode,
|
bodyNode,
|
footerNode,
|
isRTL,
|
throttledResizeHandler;
|
|
// Detect RTL on html/body nodes; taken from dojo/dom-geometry
|
isRTL = this.isRTL = (document.body.dir || document.documentElement.dir ||
|
document.body.style.direction).toLowerCase() === 'rtl';
|
|
// Clear out className (any pre-applied classes will be re-applied via the
|
// class / className setter), then apply standard classes/attributes
|
domNode.className = '';
|
|
domNode.setAttribute('role', 'grid');
|
domClass.add(domNode, 'dgrid dgrid-' + this.listType +
|
(addUiClasses ? ' ui-widget' : ''));
|
|
// Place header node (initially hidden if showHeader is false).
|
headerNode = this.headerNode = domConstruct.create('div', {
|
className: 'dgrid-header dgrid-header-row' + (addUiClasses ? ' ui-widget-header' : '') +
|
(this.showHeader ? '' : ' dgrid-header-hidden')
|
}, domNode);
|
|
bodyNode = this.bodyNode = domConstruct.create('div', {
|
className: 'dgrid-scroller'
|
}, domNode);
|
|
// Firefox 4+ adds overflow: auto elements to the tab index by default;
|
// force them to not be tabbable, but restrict this to Firefox,
|
// since it breaks accessibility support in other browsers
|
if (has('ff')) {
|
bodyNode.tabIndex = -1;
|
}
|
|
this.headerScrollNode = domConstruct.create('div', {
|
className: 'dgrid-header dgrid-header-scroll dgrid-scrollbar-width' +
|
(addUiClasses ? ' ui-widget-header' : '')
|
}, domNode);
|
|
// Place footer node (initially hidden if showFooter is false).
|
footerNode = this.footerNode = domConstruct.create('div', {
|
className: 'dgrid-footer' + (this.showFooter ? '' : ' dgrid-footer-hidden')
|
}, domNode);
|
|
if (isRTL) {
|
domNode.className += ' dgrid-rtl' +
|
(has('dom-rtl-scrollbar-left') ? ' dgrid-rtl-swap' : '');
|
}
|
|
listen(bodyNode, 'scroll', function (event) {
|
if (self.showHeader) {
|
// keep the header aligned with the body
|
headerNode.scrollLeft = event.scrollLeft || bodyNode.scrollLeft;
|
}
|
// re-fire, since browsers are not consistent about propagation here
|
event.stopPropagation();
|
listen.emit(domNode, 'scroll', {scrollTarget: bodyNode});
|
});
|
this.configStructure();
|
this.renderHeader();
|
|
this.contentNode = this.touchNode = domConstruct.create('div', {
|
className: 'dgrid-content' + (addUiClasses ? ' ui-widget-content' : '')
|
}, this.bodyNode);
|
|
if (typeof this.resizeThrottleMethod === 'string' && miscUtil[this.resizeThrottleMethod]) {
|
throttledResizeHandler = miscUtil[this.resizeThrottleMethod](winResizeHandler, this, this.resizeThrottleDelay);
|
} else if (typeof this.resizeThrottleMethod === 'function') {
|
throttledResizeHandler = this.resizeThrottleMethod(lang.hitch(this, winResizeHandler), this.resizeThrottleDelay);
|
} else {
|
console.warn('Invalid value specified for resizeThrottleMethod: ' + this.resizeThrottleMethod);
|
throttledResizeHandler = miscUtil.throttleDelayed(winResizeHandler, this, this.resizeThrottleDelay);
|
}
|
|
// add window resize handler, with reference for later removal if needed
|
this._resizeHandle = listen(window, 'resize', throttledResizeHandler);
|
this._listeners.push(this._resizeHandle);
|
},
|
|
postCreate: function () {
|
},
|
|
startup: function () {
|
// summary:
|
// Called automatically after postCreate if the component is already
|
// visible; otherwise, should be called manually once placed.
|
|
if (this._started) {
|
return;
|
}
|
this.inherited(arguments);
|
this._started = true;
|
this.resize();
|
// apply sort (and refresh) now that we're ready to render
|
this.set('sort', this.sort);
|
},
|
|
configStructure: function () {
|
// does nothing in List, this is more of a hook for the Grid
|
},
|
resize: function () {
|
var bodyNode = this.bodyNode,
|
headerNode = this.headerNode,
|
footerNode = this.footerNode,
|
headerHeight = headerNode.offsetHeight,
|
footerHeight = this.showFooter ? footerNode.offsetHeight : 0;
|
|
this.headerScrollNode.style.height = bodyNode.style.marginTop = headerHeight + 'px';
|
bodyNode.style.marginBottom = footerHeight + 'px';
|
|
if (!scrollbarWidth) {
|
// Measure the browser's scrollbar width using a DIV we'll delete right away
|
scrollbarWidth = has('dom-scrollbar-width');
|
scrollbarHeight = has('dom-scrollbar-height');
|
|
// Avoid issues with certain widgets inside in IE7, and
|
// ColumnSet scroll issues with all supported IE versions
|
if (has('ie')) {
|
scrollbarWidth++;
|
scrollbarHeight++;
|
}
|
|
// add rules that can be used where scrollbar width/height is needed
|
miscUtil.addCssRule('.dgrid-scrollbar-width', 'width: ' + scrollbarWidth + 'px');
|
miscUtil.addCssRule('.dgrid-scrollbar-height', 'height: ' + scrollbarHeight + 'px');
|
|
if (scrollbarWidth !== 17) {
|
// for modern browsers, we can perform a one-time operation which adds
|
// a rule to account for scrollbar width in all grid headers.
|
miscUtil.addCssRule('.dgrid-header-row', 'right: ' + scrollbarWidth + 'px');
|
// add another for RTL grids
|
miscUtil.addCssRule('.dgrid-rtl-swap .dgrid-header-row', 'left: ' + scrollbarWidth + 'px');
|
}
|
}
|
},
|
|
addCssRule: function (selector, css) {
|
// summary:
|
// Version of util/misc.addCssRule which tracks added rules and removes
|
// them when the List is destroyed.
|
|
var rule = miscUtil.addCssRule(selector, css);
|
if (this.cleanAddedRules) {
|
// Although this isn't a listener, it shares the same remove contract
|
this._listeners.push(rule);
|
}
|
return rule;
|
},
|
|
on: function (eventType, listener) {
|
// delegate events to the domNode
|
var signal = listen(this.domNode, eventType, listener);
|
if (!has('dom-addeventlistener')) {
|
this._listeners.push(signal);
|
}
|
return signal;
|
},
|
|
cleanup: function () {
|
// summary:
|
// Clears out all rows currently in the list.
|
|
var i;
|
for (i in this._rowIdToObject) {
|
if (this._rowIdToObject[i] !== this.columns) {
|
var rowElement = byId(i);
|
if (rowElement) {
|
this.removeRow(rowElement, true);
|
}
|
}
|
}
|
},
|
destroy: function () {
|
// summary:
|
// Destroys this grid
|
|
// Remove any event listeners and other such removables
|
if (this._listeners) { // Guard against accidental subsequent calls to destroy
|
for (var i = this._listeners.length; i--;) {
|
this._listeners[i].remove();
|
}
|
this._listeners = null;
|
}
|
|
this._started = false;
|
this.cleanup();
|
// destroy DOM
|
domConstruct.destroy(this.domNode);
|
},
|
refresh: function () {
|
// summary:
|
// refreshes the contents of the grid
|
this.cleanup();
|
this._rowIdToObject = {};
|
this._autoRowId = 0;
|
|
// make sure all the content has been removed so it can be recreated
|
this.contentNode.innerHTML = '';
|
// Ensure scroll position always resets
|
this.scrollTo({ x: 0, y: 0 });
|
},
|
|
highlightRow: function (rowElement, delay) {
|
// summary:
|
// Highlights a row. Used when updating rows due to store
|
// notifications, but potentially also useful in other cases.
|
// rowElement: Object
|
// Row element (or object returned from the row method) to
|
// highlight.
|
// delay: Number
|
// Number of milliseconds between adding and removing the
|
// ui-state-highlight class.
|
|
var classes = 'dgrid-highlight' + (this.addUiClasses ? ' ui-state-highlight' : '');
|
|
rowElement = rowElement.element || rowElement;
|
domClass.add(rowElement, classes);
|
setTimeout(function () {
|
domClass.remove(rowElement, classes);
|
}, delay || this.highlightDuration);
|
},
|
|
adjustRowIndices: function (firstRow) {
|
// this traverses through rows to maintain odd/even classes on the rows when indexes shift;
|
var next = firstRow;
|
var rowIndex = next.rowIndex;
|
if (rowIndex > -1) { // make sure we have a real number in case this is called on a non-row
|
do {
|
// Skip non-numeric, non-rows
|
if (next.rowIndex > -1) {
|
if (this.maintainOddEven) {
|
if (domClass.contains(next, 'dgrid-row')) {
|
domClass.replace(next, (rowIndex % 2 === 1 ? oddClass : evenClass),
|
(rowIndex % 2 === 0 ? oddClass : evenClass));
|
}
|
}
|
next.rowIndex = rowIndex++;
|
}
|
} while ((next = next.nextSibling) && next.rowIndex !== rowIndex);
|
}
|
},
|
renderArray: function (results, beforeNode, options) {
|
// summary:
|
// Renders an array of objects as rows, before the given node.
|
|
options = options || {};
|
var self = this,
|
start = options.start || 0,
|
rowsFragment = document.createDocumentFragment(),
|
rows = [],
|
container,
|
i = 0,
|
len = results.length;
|
|
if (!beforeNode) {
|
this._lastCollection = results;
|
}
|
|
// Insert a row for each item into the document fragment
|
while (i < len) {
|
rows[i] = this.insertRow(results[i], rowsFragment, null, start++, options);
|
i++;
|
}
|
|
// Insert the document fragment into the appropriate position
|
container = beforeNode ? beforeNode.parentNode : self.contentNode;
|
if (container && container.parentNode &&
|
(container !== self.contentNode || len)) {
|
container.insertBefore(rowsFragment, beforeNode || null);
|
if (len) {
|
self.adjustRowIndices(rows[len - 1]);
|
}
|
}
|
|
return rows;
|
},
|
|
renderHeader: function () {
|
// no-op in a plain list
|
},
|
|
_autoRowId: 0,
|
insertRow: function (object, parent, beforeNode, i, options) {
|
// summary:
|
// Creates a single row in the grid.
|
|
// Include parentId within row identifier if one was specified in options.
|
// (This is used by tree to allow the same object to appear under
|
// multiple parents.)
|
var id = this.id + '-row-' + ((this.collection && this.collection.getIdentity) ?
|
this.collection.getIdentity(object) : this._autoRowId++),
|
row = byId(id),
|
previousRow = row && row.previousSibling;
|
|
if (row) {
|
// If it existed elsewhere in the DOM, we will remove it, so we can recreate it
|
if (row === beforeNode) {
|
beforeNode = (beforeNode.connected || beforeNode).nextSibling;
|
}
|
this.removeRow(row, false, options);
|
}
|
row = this.renderRow(object, options);
|
row.className = (row.className || '') + ' dgrid-row ' +
|
(i % 2 === 1 ? oddClass : evenClass) +
|
(this.addUiClasses ? ' ui-state-default' : '');
|
// Get the row id for easy retrieval
|
this._rowIdToObject[row.id = id] = object;
|
parent.insertBefore(row, beforeNode || null);
|
|
row.rowIndex = i;
|
if (previousRow && previousRow.rowIndex !== (row.rowIndex - 1)) {
|
// In this case, we are pulling the row from another location in the grid,
|
// and we need to readjust the rowIndices from the point it was removed
|
this.adjustRowIndices(previousRow);
|
}
|
return row;
|
},
|
renderRow: function (value) {
|
// summary:
|
// Responsible for returning the DOM for a single row in the grid.
|
// value: Mixed
|
// Value to render
|
// options: Object?
|
// Optional object with additional options
|
|
var div = document.createElement('div');
|
div.appendChild(document.createTextNode(value));
|
return div;
|
},
|
removeRow: function (rowElement, preserveDom) {
|
// summary:
|
// Simply deletes the node in a plain List.
|
// Column plugins may aspect this to implement their own cleanup routines.
|
// rowElement: Object|DOMNode
|
// Object or element representing the row to be removed.
|
// preserveDom: Boolean?
|
// If true, the row element will not be removed from the DOM; this can
|
// be used by extensions/plugins in cases where the DOM will be
|
// massively cleaned up at a later point in time.
|
// options: Object?
|
// May be specified with a `rows` property for the purpose of
|
// cleaning up collection tracking (used by `_StoreMixin`).
|
|
rowElement = rowElement.element || rowElement;
|
delete this._rowIdToObject[rowElement.id];
|
if (!preserveDom) {
|
domConstruct.destroy(rowElement);
|
}
|
},
|
|
row: function (target) {
|
// summary:
|
// Get the row object by id, object, node, or event
|
var id;
|
|
if (target instanceof this._Row) {
|
return target; // No-op; already a row
|
}
|
|
if (target.target && target.target.nodeType) {
|
// Event
|
target = target.target;
|
}
|
if (target.nodeType) {
|
// Row element, or child of a row element
|
var object;
|
do {
|
var rowId = target.id;
|
if ((object = this._rowIdToObject[rowId])) {
|
return new this._Row(rowId.substring(this.id.length + 5), object, target);
|
}
|
target = target.parentNode;
|
}while (target && target !== this.domNode);
|
return;
|
}
|
|
if (typeof target === 'object') {
|
// Assume target represents a collection item
|
id = this.collection.getIdentity(target);
|
}
|
else {
|
// Assume target is a row ID
|
id = target;
|
target = this._rowIdToObject[this.id + '-row-' + id];
|
}
|
return new this._Row(id, target, byId(this.id + '-row-' + id));
|
},
|
cell: function (target) {
|
// this doesn't do much in a plain list
|
return {
|
row: this.row(target)
|
};
|
},
|
|
_move: function (item, steps, targetClass, visible) {
|
var nextSibling, current, element;
|
// Start at the element indicated by the provided row or cell object.
|
element = current = item.element;
|
steps = steps || 1;
|
|
do {
|
// Outer loop: move in the appropriate direction.
|
if ((nextSibling = current[steps < 0 ? 'previousSibling' : 'nextSibling'])) {
|
do {
|
// Inner loop: advance, and dig into children if applicable.
|
current = nextSibling;
|
if (current && (current.className + ' ').indexOf(targetClass + ' ') > -1) {
|
// Element with the appropriate class name; count step, stop digging.
|
element = current;
|
steps += steps < 0 ? 1 : -1;
|
break;
|
}
|
// If the next sibling isn't a match, drill down to search, unless
|
// visible is true and children are hidden.
|
} while ((nextSibling = (!visible || !current.hidden) &&
|
current[steps < 0 ? 'lastChild' : 'firstChild']));
|
}
|
else {
|
current = current.parentNode;
|
if (!current || current === this.bodyNode || current === this.headerNode) {
|
// Break out if we step out of the navigation area entirely.
|
break;
|
}
|
}
|
}while (steps);
|
// Return the final element we arrived at, which might still be the
|
// starting element if we couldn't navigate further in that direction.
|
return element;
|
},
|
|
up: function (row, steps, visible) {
|
// summary:
|
// Returns the row that is the given number of steps (1 by default)
|
// above the row represented by the given object.
|
// row:
|
// The row to navigate upward from.
|
// steps:
|
// Number of steps to navigate up from the given row; default is 1.
|
// visible:
|
// If true, rows that are currently hidden (i.e. children of
|
// collapsed tree rows) will not be counted in the traversal.
|
// returns:
|
// A row object representing the appropriate row. If the top of the
|
// list is reached before the given number of steps, the first row will
|
// be returned.
|
if (!row.element) {
|
row = this.row(row);
|
}
|
return this.row(this._move(row, -(steps || 1), 'dgrid-row', visible));
|
},
|
down: function (row, steps, visible) {
|
// summary:
|
// Returns the row that is the given number of steps (1 by default)
|
// below the row represented by the given object.
|
// row:
|
// The row to navigate downward from.
|
// steps:
|
// Number of steps to navigate down from the given row; default is 1.
|
// visible:
|
// If true, rows that are currently hidden (i.e. children of
|
// collapsed tree rows) will not be counted in the traversal.
|
// returns:
|
// A row object representing the appropriate row. If the bottom of the
|
// list is reached before the given number of steps, the last row will
|
// be returned.
|
if (!row.element) {
|
row = this.row(row);
|
}
|
return this.row(this._move(row, steps || 1, 'dgrid-row', visible));
|
},
|
|
scrollTo: function (options) {
|
if (typeof options.x !== 'undefined') {
|
this.bodyNode.scrollLeft = options.x;
|
}
|
if (typeof options.y !== 'undefined') {
|
this.bodyNode.scrollTop = options.y;
|
}
|
},
|
|
getScrollPosition: function () {
|
return {
|
x: this.bodyNode.scrollLeft,
|
y: this.bodyNode.scrollTop
|
};
|
},
|
|
get: function (/*String*/ name /*, ... */) {
|
// summary:
|
// Get a property on a List instance.
|
// name:
|
// The property to get.
|
// returns:
|
// The property value on this List instance.
|
// description:
|
// Get a named property on a List object. The property may
|
// potentially be retrieved via a getter method in subclasses. In the base class
|
// this just retrieves the object's property.
|
|
var fn = '_get' + name.charAt(0).toUpperCase() + name.slice(1);
|
|
if (typeof this[fn] === 'function') {
|
return this[fn].apply(this, [].slice.call(arguments, 1));
|
}
|
|
// Alert users that try to use Dijit-style getter/setters so they don’t get confused
|
// if they try to use them and it does not work
|
if (!has('dojo-built') && typeof this[fn + 'Attr'] === 'function') {
|
console.warn('dgrid: Use ' + fn + ' instead of ' + fn + 'Attr for getting ' + name);
|
}
|
|
return this[name];
|
},
|
|
set: function (/*String*/ name, /*Object*/ value /*, ... */) {
|
// summary:
|
// Set a property on a List instance
|
// name:
|
// The property to set.
|
// value:
|
// The value to set in the property.
|
// returns:
|
// The function returns this List instance.
|
// description:
|
// Sets named properties on a List object.
|
// A programmatic setter may be defined in subclasses.
|
//
|
// set() may also be called with a hash of name/value pairs, ex:
|
// | myObj.set({
|
// | foo: "Howdy",
|
// | bar: 3
|
// | })
|
// This is equivalent to calling set(foo, "Howdy") and set(bar, 3)
|
|
if (typeof name === 'object') {
|
for (var k in name) {
|
this.set(k, name[k]);
|
}
|
}
|
else {
|
var fn = '_set' + name.charAt(0).toUpperCase() + name.slice(1);
|
|
if (typeof this[fn] === 'function') {
|
this[fn].apply(this, [].slice.call(arguments, 1));
|
}
|
else {
|
// Alert users that try to use Dijit-style getter/setters so they don’t get confused
|
// if they try to use them and it does not work
|
if (!has('dojo-built') && typeof this[fn + 'Attr'] === 'function') {
|
console.warn('dgrid: Use ' + fn + ' instead of ' + fn + 'Attr for setting ' + name);
|
}
|
|
this[name] = value;
|
}
|
}
|
|
return this;
|
},
|
|
// Accept both class and className programmatically to set domNode class.
|
_getClass: getClass,
|
_setClass: setClass,
|
_getClassName: getClass,
|
_setClassName: setClass,
|
|
_setSort: function (property, descending) {
|
// summary:
|
// Sort the content
|
// property: String|Array
|
// String specifying field to sort by, or actual array of objects
|
// with property and descending properties
|
// descending: boolean
|
// In the case where property is a string, this argument
|
// specifies whether to sort ascending (false) or descending (true)
|
|
this.sort = typeof property !== 'string' ? property :
|
[{property: property, descending: descending}];
|
|
this._applySort();
|
},
|
|
_applySort: function () {
|
// summary:
|
// Applies the current sort
|
// description:
|
// This is an extension point to allow specializations to apply the sort differently
|
|
this.refresh();
|
|
if (this._lastCollection) {
|
var sort = this.sort;
|
if (sort && sort.length > 0) {
|
var property = sort[0].property,
|
descending = !!sort[0].descending;
|
this._lastCollection.sort(function (a, b) {
|
var aVal = a[property], bVal = b[property];
|
// fall back undefined values to "" for more consistent behavior
|
if (aVal === undefined) {
|
aVal = '';
|
}
|
if (bVal === undefined) {
|
bVal = '';
|
}
|
return aVal === bVal ? 0 : (aVal > bVal !== descending ? 1 : -1);
|
});
|
}
|
this.renderArray(this._lastCollection);
|
}
|
},
|
|
_setShowHeader: function (show) {
|
// this is in List rather than just in Grid, primarily for two reasons:
|
// (1) just in case someone *does* want to show a header in a List
|
// (2) helps address IE < 8 header display issue in List
|
|
var headerNode = this.headerNode;
|
|
this.showHeader = show;
|
|
// add/remove class which has styles for "hiding" header
|
domClass.toggle(headerNode, 'dgrid-header-hidden', !show);
|
|
this.renderHeader();
|
this.resize(); // resize to account for (dis)appearance of header
|
|
if (show) {
|
// Update scroll position of header to make sure it's in sync.
|
headerNode.scrollLeft = this.getScrollPosition().x;
|
}
|
},
|
|
_setShowFooter: function (show) {
|
this.showFooter = show;
|
|
// add/remove class which has styles for hiding footer
|
domClass.toggle(this.footerNode, 'dgrid-footer-hidden', !show);
|
|
this.resize(); // to account for (dis)appearance of footer
|
}
|
});
|
|
List.autoIdPrefix = 'dgrid_';
|
|
return List;
|
});
|