@onehat/data 1.8.35 → 1.9.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/cypress/integration/Async.spec.js +1 -1
- package/cypress/integration/Entity.spec.js +3 -3
- package/cypress/integration/OneHatData.spec.js +6 -6
- package/cypress/integration/Property/Base64.spec.js +1 -1
- package/cypress/integration/Property/Boolean.spec.js +1 -1
- package/cypress/integration/Property/Currency.spec.js +1 -1
- package/cypress/integration/Property/Date.spec.js +1 -1
- package/cypress/integration/Property/DateTime.spec.js +1 -1
- package/cypress/integration/Property/Float.spec.js +1 -1
- package/cypress/integration/Property/Integer.spec.js +1 -1
- package/cypress/integration/Property/Json.spec.js +1 -1
- package/cypress/integration/Property/Percent.spec.js +1 -1
- package/cypress/integration/Property/PercentInt.spec.js +1 -1
- package/cypress/integration/Property/Property.spec.js +3 -3
- package/cypress/integration/Property/String.spec.js +1 -1
- package/cypress/integration/Property/Time.spec.js +1 -1
- package/cypress/integration/Property/Uuid.spec.js +1 -1
- package/cypress/integration/Repository/Ajax.spec.js +2 -2
- package/cypress/integration/Repository/LocalFromRemote.spec.js +3 -3
- package/cypress/integration/Repository/Memory.spec.js +29 -3
- package/cypress/integration/Repository/OneBuild.spec.js +4 -4
- package/cypress/integration/Repository/Repository.spec.js +28 -4
- package/cypress/integration/Schema.spec.js +2 -2
- package/cypress/support/index.js +1 -1
- package/package.json +11 -10
- package/src/Repository/Memory.js +33 -2
- package/src/Repository/Repository.js +24 -20
- package/src/Schema/Schema.js +2 -1
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import Entity from '../../src/Entity';
|
|
2
|
-
import Schema from '../../src/Schema';
|
|
3
|
-
import PropertyTypes from '../../src/Property';
|
|
1
|
+
import Entity from '../../src/Entity.js';
|
|
2
|
+
import Schema from '../../src/Schema/index.js';
|
|
3
|
+
import PropertyTypes from '../../src/Property/index.js';
|
|
4
4
|
|
|
5
5
|
|
|
6
6
|
describe('Entity', function() {
|
|
@@ -1,9 +1,9 @@
|
|
|
1
|
-
import { OneHatData } from '../../src/OneHatData';
|
|
2
|
-
import GroupsDefinition from '../fixtures/Definitions/Groups';
|
|
3
|
-
import GroupsUsersDefinition from '../fixtures/Definitions/GroupsUsers';
|
|
4
|
-
import UsersDefinition from '../fixtures/Definitions/Users';
|
|
5
|
-
import groupsUserData from '../fixtures/Data/GroupsUser';
|
|
6
|
-
import KeyValues from '../../src/Schema/KeyValues';
|
|
1
|
+
import { OneHatData } from '../../src/OneHatData.js';
|
|
2
|
+
import GroupsDefinition from '../fixtures/Definitions/Groups.js';
|
|
3
|
+
import GroupsUsersDefinition from '../fixtures/Definitions/GroupsUsers.js';
|
|
4
|
+
import UsersDefinition from '../fixtures/Definitions/Users.js';
|
|
5
|
+
import groupsUserData from '../fixtures/Data/GroupsUser.js';
|
|
6
|
+
import KeyValues from '../../src/Schema/KeyValues.js';
|
|
7
7
|
|
|
8
8
|
|
|
9
9
|
// NOTE: Cypress can't handle async functions for beforeEach,
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import PropertyTypes from '../../../src/Property';
|
|
2
|
-
import Entity from '../../../src/Entity';
|
|
3
|
-
import Schema from '../../../src/Schema';
|
|
1
|
+
import PropertyTypes from '../../../src/Property/index.js';
|
|
2
|
+
import Entity from '../../../src/Entity.js';
|
|
3
|
+
import Schema from '../../../src/Schema/index.js';
|
|
4
4
|
|
|
5
5
|
describe('Property', function() {
|
|
6
6
|
beforeEach(function() {
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import RepositoryTypes from '../../../src/Repository';
|
|
2
|
-
import Schema from '../../../src/Schema';
|
|
1
|
+
import RepositoryTypes from '../../../src/Repository/index.js';
|
|
2
|
+
import Schema from '../../../src/Schema/index.js';
|
|
3
3
|
|
|
4
4
|
describe('OneBuildRepository', function() {
|
|
5
5
|
beforeEach(function() {
|
|
@@ -2,9 +2,9 @@ import {
|
|
|
2
2
|
default as LocalFromRemoteRepository,
|
|
3
3
|
MODE_LOCAL_MIRROR,
|
|
4
4
|
MODE_COMMAND_QUEUE,
|
|
5
|
-
} from '../../../src/Repository/LocalFromRemote';
|
|
6
|
-
import MemoryRepository from '../../../src/Repository/Memory';
|
|
7
|
-
import Schema from '../../../src/Schema';
|
|
5
|
+
} from '../../../src/Repository/LocalFromRemote/index.js';
|
|
6
|
+
import MemoryRepository from '../../../src/Repository/Memory.js';
|
|
7
|
+
import Schema from '../../../src/Schema/index.js';
|
|
8
8
|
import momentAlt from 'relative-time-parser';
|
|
9
9
|
|
|
10
10
|
describe('LocalFromRemote', function() {
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import RepositoryTypes from '../../../src/Repository';
|
|
2
|
-
import Schema from '../../../src/Schema';
|
|
1
|
+
import RepositoryTypes from '../../../src/Repository/index.js';
|
|
2
|
+
import Schema from '../../../src/Schema/index.js';
|
|
3
3
|
|
|
4
4
|
describe('MemoryRepository', function() {
|
|
5
5
|
beforeEach(function() {
|
|
@@ -108,6 +108,32 @@ describe('MemoryRepository', function() {
|
|
|
108
108
|
expect(result3).to.be.eq(1);
|
|
109
109
|
});
|
|
110
110
|
|
|
111
|
+
it('natsort', function() {
|
|
112
|
+
const entities = this.repository.getEntitiesOnPage();
|
|
113
|
+
entities[0].value = 'foo1';
|
|
114
|
+
entities[1].value = 'foo12';
|
|
115
|
+
entities[2].value = 'foo2';
|
|
116
|
+
|
|
117
|
+
const before = this.repository.getRawValues();
|
|
118
|
+
expect(before[0].value).to.be.eq('foo1');
|
|
119
|
+
expect(before[1].value).to.be.eq('foo12');
|
|
120
|
+
expect(before[2].value).to.be.eq('foo2');
|
|
121
|
+
expect(before[3].value).to.be.eq('four');
|
|
122
|
+
expect(before[4].value).to.be.eq('five');
|
|
123
|
+
|
|
124
|
+
this.repository.sort({
|
|
125
|
+
name: 'value',
|
|
126
|
+
direction: 'ASC',
|
|
127
|
+
fn: 'natsort',
|
|
128
|
+
});
|
|
129
|
+
const after = this.repository.getRawValues();
|
|
130
|
+
expect(after[0].value).to.be.eq('five');
|
|
131
|
+
expect(after[1].value).to.be.eq('foo1');
|
|
132
|
+
expect(after[2].value).to.be.eq('foo2');
|
|
133
|
+
expect(after[3].value).to.be.eq('foo12');
|
|
134
|
+
expect(after[4].value).to.be.eq('four');
|
|
135
|
+
});
|
|
136
|
+
|
|
111
137
|
});
|
|
112
138
|
|
|
113
139
|
describe('filtering', function() {
|
|
@@ -210,7 +236,7 @@ describe('MemoryRepository', function() {
|
|
|
210
236
|
expect(result.value).to.be.eq('four');
|
|
211
237
|
});
|
|
212
238
|
|
|
213
|
-
it
|
|
239
|
+
it('getIxById', function() {
|
|
214
240
|
this.repository.setPage(1);
|
|
215
241
|
this.repository.setPageSize(2);
|
|
216
242
|
let ix = this.repository.getIxById(1);
|
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
import { OneHatData } from '../../../src/OneHatData';
|
|
2
|
-
import UsersDefinition from '../../fixtures/Definitions/Users';
|
|
3
|
-
import GroupsDefinition from '../../fixtures/Definitions/Groups';
|
|
4
|
-
import UserData from '../../fixtures/Data/User';
|
|
1
|
+
import { OneHatData } from '../../../src/OneHatData.js';
|
|
2
|
+
import UsersDefinition from '../../fixtures/Definitions/Users.js';
|
|
3
|
+
import GroupsDefinition from '../../fixtures/Definitions/Groups.js';
|
|
4
|
+
import UserData from '../../fixtures/Data/User.js';
|
|
5
5
|
|
|
6
6
|
const baseURL = Cypress.env('baseURL'),
|
|
7
7
|
creds = {
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import RepositoryTypes from '../../../src/Repository';
|
|
2
|
-
import Schema from '../../../src/Schema';
|
|
1
|
+
import RepositoryTypes from '../../../src/Repository/index.js';
|
|
2
|
+
import Schema from '../../../src/Schema/index.js';
|
|
3
3
|
|
|
4
4
|
describe('Repository Base', function() {
|
|
5
5
|
beforeEach(function() {
|
|
@@ -9,8 +9,9 @@ describe('Repository Base', function() {
|
|
|
9
9
|
idProperty: 'key',
|
|
10
10
|
displayProperty: 'value',
|
|
11
11
|
properties: [
|
|
12
|
-
{ name: 'key', type: 'int' },
|
|
13
|
-
{ name: 'value' },
|
|
12
|
+
{ name: 'key', type: 'int', },
|
|
13
|
+
{ name: 'value', },
|
|
14
|
+
{ name: 'json', type: 'json', },
|
|
14
15
|
],
|
|
15
16
|
associations: {
|
|
16
17
|
hasMany: [
|
|
@@ -215,6 +216,7 @@ describe('Repository Base', function() {
|
|
|
215
216
|
expected = {
|
|
216
217
|
direction: 'ASC',
|
|
217
218
|
name: 'value',
|
|
219
|
+
fn: 'default',
|
|
218
220
|
};
|
|
219
221
|
expect(_.isEqual(sorters[0], expected)).to.be.true;
|
|
220
222
|
});
|
|
@@ -229,6 +231,28 @@ describe('Repository Base', function() {
|
|
|
229
231
|
expect(didFireChangeSorters).to.be.true;
|
|
230
232
|
});
|
|
231
233
|
|
|
234
|
+
it('sort by non-existant field', function() {
|
|
235
|
+
let didError = false;
|
|
236
|
+
try {
|
|
237
|
+
this.repository.sort('foo');
|
|
238
|
+
} catch(err) {
|
|
239
|
+
expect(err).to.match(/Sorting property does not exist/);
|
|
240
|
+
didError = true;
|
|
241
|
+
}
|
|
242
|
+
expect(didError).to.be.true;
|
|
243
|
+
});
|
|
244
|
+
|
|
245
|
+
it('sort by non-sortable field', function() {
|
|
246
|
+
let didError = false;
|
|
247
|
+
try {
|
|
248
|
+
this.repository.sort('json');
|
|
249
|
+
} catch(err) {
|
|
250
|
+
expect(err).to.match(/Sorting property type is not sortable/);
|
|
251
|
+
didError = true;
|
|
252
|
+
}
|
|
253
|
+
expect(didError).to.be.true;
|
|
254
|
+
});
|
|
255
|
+
|
|
232
256
|
it('getSortField', function() {
|
|
233
257
|
const sortField = this.repository.getSortField();
|
|
234
258
|
expect(sortField).to.be.eq('value');
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import Schema from '../../src/Schema';
|
|
2
|
-
import GroupsUsersDefinition from '../fixtures/Definitions/GroupsUsers';
|
|
1
|
+
import Schema from '../../src/Schema/index.js';
|
|
2
|
+
import GroupsUsersDefinition from '../fixtures/Definitions/GroupsUsers.js';
|
|
3
3
|
|
|
4
4
|
|
|
5
5
|
describe('Schema', function() {
|
package/cypress/support/index.js
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
1
|
// https://on.cypress.io/configuration
|
|
2
|
-
import './commands';
|
|
2
|
+
import './commands.js';
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@onehat/data",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.9.0",
|
|
4
4
|
"description": "JS data modeling package with adapters for many storage mediums.",
|
|
5
5
|
"main": "src/index.js",
|
|
6
6
|
"type": "module",
|
|
@@ -37,28 +37,29 @@
|
|
|
37
37
|
"dependencies": {
|
|
38
38
|
"@onehat/events": "^1.6.5",
|
|
39
39
|
"accounting-js": "^1.1.1",
|
|
40
|
-
"axios": "^1.2.
|
|
41
|
-
"chrono-node": "^2.4.
|
|
42
|
-
"fast-xml-parser": "^4.0.
|
|
40
|
+
"axios": "^1.2.1",
|
|
41
|
+
"chrono-node": "^2.4.2",
|
|
42
|
+
"fast-xml-parser": "^4.0.12",
|
|
43
43
|
"he": "^1.2.0",
|
|
44
|
-
"js-base64": "^3.7.
|
|
44
|
+
"js-base64": "^3.7.3",
|
|
45
45
|
"lodash": "^4.17.21",
|
|
46
46
|
"moment": "^2.29.4",
|
|
47
|
+
"natsort": "^2.0.3",
|
|
47
48
|
"numeral": "^2.0.6",
|
|
48
49
|
"qs": "^6.11.0",
|
|
49
50
|
"relative-time-parser": "^1.0.15",
|
|
50
51
|
"uuid": "^9.0.0"
|
|
51
52
|
},
|
|
52
53
|
"devDependencies": {
|
|
53
|
-
"@babel/core": "^7.20.
|
|
54
|
-
"@babel/node": "^7.20.
|
|
54
|
+
"@babel/core": "^7.20.5",
|
|
55
|
+
"@babel/node": "^7.20.5",
|
|
55
56
|
"@babel/plugin-proposal-class-properties": "^7.18.6",
|
|
56
57
|
"@babel/plugin-transform-runtime": "^7.19.6",
|
|
57
58
|
"@babel/preset-env": "^7.20.2",
|
|
58
59
|
"@babel/register": "^7.18.9",
|
|
59
|
-
"@babel/runtime": "^7.20.
|
|
60
|
-
"@cypress/webpack-preprocessor": "^5.
|
|
61
|
-
"babel-loader": "^
|
|
60
|
+
"@babel/runtime": "^7.20.6",
|
|
61
|
+
"@cypress/webpack-preprocessor": "^5.16.0",
|
|
62
|
+
"babel-loader": "^8.0.2",
|
|
62
63
|
"cypress": "5.2.0",
|
|
63
64
|
"ink-docstrap": "^1.3.2",
|
|
64
65
|
"jsdoc": "^4.0.0",
|
package/src/Repository/Memory.js
CHANGED
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
import Repository from './Repository.js';
|
|
4
4
|
import Entity from '../Entity.js';
|
|
5
|
+
import natsort from 'natsort';
|
|
5
6
|
import _ from 'lodash';
|
|
6
7
|
|
|
7
8
|
const MEM_PREFIX = 'MEM-';
|
|
@@ -212,6 +213,13 @@ class MemoryRepository extends Repository {
|
|
|
212
213
|
if (_.isFunction(sorter)) {
|
|
213
214
|
return sorter;
|
|
214
215
|
}
|
|
216
|
+
if (sorter.fn && sorter.fn !== 'default') {
|
|
217
|
+
if (sorter.fn === 'natsort') {
|
|
218
|
+
return MemoryRepository._getNatSort(sorter);
|
|
219
|
+
} else if (_.isFunction(sorter.fn)) {
|
|
220
|
+
return sorter.fn;
|
|
221
|
+
}
|
|
222
|
+
}
|
|
215
223
|
return MemoryRepository._getCompareFunction(sorter);
|
|
216
224
|
});
|
|
217
225
|
|
|
@@ -234,6 +242,27 @@ class MemoryRepository extends Repository {
|
|
|
234
242
|
this.isSorted = true;
|
|
235
243
|
}
|
|
236
244
|
|
|
245
|
+
/**
|
|
246
|
+
* Helper for _applySorters().
|
|
247
|
+
* Takes a sorter object and returns a valid compare function for use with Array.prototype.sort
|
|
248
|
+
* @param {object} sorter - Object with two properties: 'name' & 'direction'
|
|
249
|
+
* @return {function} - Compare function
|
|
250
|
+
* @private
|
|
251
|
+
* @static
|
|
252
|
+
*/
|
|
253
|
+
static _getNatSort = (sorter) => {
|
|
254
|
+
const
|
|
255
|
+
name = sorter.name,
|
|
256
|
+
direction = sorter.direction.toUpperCase(),
|
|
257
|
+
sortFn = natsort.default({ desc: direction !== 'ASC', });
|
|
258
|
+
return (entity1, entity2) => {
|
|
259
|
+
const
|
|
260
|
+
a = entity1[name],
|
|
261
|
+
b = entity2[name];
|
|
262
|
+
return sortFn(a, b);
|
|
263
|
+
};
|
|
264
|
+
}
|
|
265
|
+
|
|
237
266
|
/**
|
|
238
267
|
* Helper for _applySorters().
|
|
239
268
|
* Takes a sorter object and returns a valid compare function for use with Array.prototype.sort
|
|
@@ -243,10 +272,12 @@ class MemoryRepository extends Repository {
|
|
|
243
272
|
* @static
|
|
244
273
|
*/
|
|
245
274
|
static _getCompareFunction = (sorter) => {
|
|
246
|
-
const
|
|
275
|
+
const
|
|
276
|
+
name = sorter.name,
|
|
247
277
|
direction = sorter.direction.toUpperCase();
|
|
248
278
|
return (entity1, entity2) => {
|
|
249
|
-
const
|
|
279
|
+
const
|
|
280
|
+
a = entity1[name],
|
|
250
281
|
b = entity2[name];
|
|
251
282
|
if (a === b) {
|
|
252
283
|
return 0;
|
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
/** @module Repository */
|
|
2
2
|
|
|
3
3
|
import EventEmitter from '@onehat/events';
|
|
4
|
-
import Entity from '../Entity.js'
|
|
4
|
+
import Entity from '../Entity.js'
|
|
5
|
+
import PropertyTypes from '../Property/index.js';
|
|
5
6
|
import {
|
|
6
7
|
v4 as uuid,
|
|
7
8
|
} from 'uuid';
|
|
@@ -420,6 +421,9 @@ export default class Repository extends EventEmitter {
|
|
|
420
421
|
* - repository.sort(); // Reverts back to default sort. To actually *clear* all sorters, use this.clearSort()
|
|
421
422
|
* - repository.sort('last_name'); // sort by one property, ASC
|
|
422
423
|
* - repository.sort('last_name', 'ASC'); // sort by one property
|
|
424
|
+
* - repository.sort('last_name', 'ASC', 'natsort'); // sort by one property with specific function
|
|
425
|
+
* - repository.sort('last_name', 'ASC', (a, b) => { ... })); // sort by one property with custom function
|
|
426
|
+
* - repository.sort((a, b) => { ... }); // sort by custom function
|
|
423
427
|
* - repository.sort({ // sort by one property, object notation
|
|
424
428
|
* name: 'last_name',
|
|
425
429
|
* direction: 'ASC',
|
|
@@ -428,6 +432,7 @@ export default class Repository extends EventEmitter {
|
|
|
428
432
|
* {
|
|
429
433
|
* name: 'last_name',
|
|
430
434
|
* direction: 'ASC',
|
|
435
|
+
* fn: 'natsort',
|
|
431
436
|
* },
|
|
432
437
|
* {
|
|
433
438
|
* name: 'first_name',
|
|
@@ -438,7 +443,7 @@ export default class Repository extends EventEmitter {
|
|
|
438
443
|
*
|
|
439
444
|
* @return this
|
|
440
445
|
*/
|
|
441
|
-
sort = (arg1 = null, arg2 = null) => {
|
|
446
|
+
sort = (arg1 = null, arg2 = 'ASC', arg3 = null) => {
|
|
442
447
|
if (this.isDestroyed) {
|
|
443
448
|
throw Error('this.sort is no longer valid. Repository has been destroyed.');
|
|
444
449
|
}
|
|
@@ -446,18 +451,18 @@ export default class Repository extends EventEmitter {
|
|
|
446
451
|
let sorters = [];
|
|
447
452
|
if (_.isNil(arg1)) {
|
|
448
453
|
sorters = this.getDefaultSorters();
|
|
449
|
-
} else if (_.isArray(arg1)) {
|
|
450
|
-
sorters = arg1;
|
|
451
|
-
} else if (_.isObject(arg1)) { // includes functions
|
|
452
|
-
sorters = [arg1];
|
|
453
454
|
} else if (_.isString(arg1)) {
|
|
454
|
-
if (_.isNil(arg2)) {
|
|
455
|
-
arg2 = 'ASC';
|
|
456
|
-
}
|
|
457
455
|
sorters = [{
|
|
458
456
|
name: arg1,
|
|
459
457
|
direction: arg2,
|
|
458
|
+
fn: arg3,
|
|
460
459
|
}];
|
|
460
|
+
} else if (_.isPlainObject(arg1)) {
|
|
461
|
+
sorters = [arg1];
|
|
462
|
+
} else if (_.isArray(arg1)) {
|
|
463
|
+
sorters = arg1;
|
|
464
|
+
} else if (_.isFunction(arg1)) {
|
|
465
|
+
sorters = [arg1];
|
|
461
466
|
}
|
|
462
467
|
|
|
463
468
|
this.setSorters(sorters);
|
|
@@ -480,7 +485,8 @@ export default class Repository extends EventEmitter {
|
|
|
480
485
|
} else if (!_.isNil(this.schema.model.displayProperty)) {
|
|
481
486
|
sorters = [{
|
|
482
487
|
name: this.schema.model.displayProperty,
|
|
483
|
-
direction: 'ASC'
|
|
488
|
+
direction: 'ASC',
|
|
489
|
+
fn: 'default',
|
|
484
490
|
}];
|
|
485
491
|
}
|
|
486
492
|
}
|
|
@@ -508,16 +514,14 @@ export default class Repository extends EventEmitter {
|
|
|
508
514
|
if (_.isFunction(sorter)) {
|
|
509
515
|
return; // skip
|
|
510
516
|
}
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
}
|
|
520
|
-
if (!property.isSortable) {
|
|
517
|
+
const propertyDefinition = _.find(this.schema.model.properties, (property) => property.name === sorter.name);
|
|
518
|
+
if (!propertyDefinition) {
|
|
519
|
+
throw new Error('Sorting property does not exist.');
|
|
520
|
+
}
|
|
521
|
+
const propertyType = propertyDefinition.type;
|
|
522
|
+
if (propertyType && PropertyTypes[propertyType]) {
|
|
523
|
+
const propertyInstance = new PropertyTypes[propertyType]();
|
|
524
|
+
if (!propertyInstance.isSortable) {
|
|
521
525
|
throw new Error('Sorting property type is not sortable.');
|
|
522
526
|
}
|
|
523
527
|
}
|
package/src/Schema/Schema.js
CHANGED
|
@@ -65,9 +65,10 @@ export default class Schema extends EventEmitter {
|
|
|
65
65
|
|
|
66
66
|
/**
|
|
67
67
|
* @member {object[]} sorters - Array of sorter definitions.
|
|
68
|
-
* Each definition is an object with two keys:
|
|
68
|
+
* Each definition is an object with two or three keys:
|
|
69
69
|
* - *name* - Name of Property to sort by.
|
|
70
70
|
* - *direction* - 'ASC'|'DESC'
|
|
71
|
+
* - *fn* (optional) - The sort function to use. Can be either a name like 'nasort', 'default', or a sorting fn
|
|
71
72
|
*/
|
|
72
73
|
sorters: [],
|
|
73
74
|
|