data_source.js 20 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636
  1. /**
  2. * DevExtreme (data/data_source/data_source.js)
  3. * Version: 19.1.16
  4. * Build date: Tue Oct 18 2022
  5. *
  6. * Copyright (c) 2012 - 2022 Developer Express Inc. ALL RIGHTS RESERVED
  7. * Read about DevExtreme licensing here: https://js.devexpress.com/Licensing/
  8. */
  9. "use strict";
  10. var Class = require("../../core/class");
  11. var extend = require("../../core/utils/extend").extend;
  12. var commonUtils = require("../../core/utils/common");
  13. var iteratorUtils = require("../../core/utils/iterator");
  14. var ajax = require("../../core/utils/ajax");
  15. var typeUtils = require("../../core/utils/type");
  16. var dataUtils = require("../utils");
  17. var arrayUtils = require("../array_utils");
  18. var Store = require("../abstract_store");
  19. var ArrayStore = require("../array_store");
  20. var CustomStore = require("../custom_store");
  21. var EventsMixin = require("../../core/events_mixin");
  22. var errors = require("../errors").errors;
  23. var array = require("../../core/utils/array");
  24. var queue = require("../../core/utils/queue");
  25. var deferredUtils = require("../../core/utils/deferred");
  26. var when = deferredUtils.when;
  27. var Deferred = deferredUtils.Deferred;
  28. var __isString = typeUtils.isString;
  29. var __isNumber = typeUtils.isNumeric;
  30. var __isBoolean = typeUtils.isBoolean;
  31. var __isDefined = typeUtils.isDefined;
  32. var CANCELED_TOKEN = "canceled";
  33. function OperationManager() {
  34. this._counter = -1;
  35. this._deferreds = {}
  36. }
  37. OperationManager.prototype.constructor = OperationManager;
  38. OperationManager.prototype.add = function(deferred) {
  39. this._counter += 1;
  40. this._deferreds[this._counter] = deferred;
  41. return this._counter
  42. };
  43. OperationManager.prototype.remove = function(operationId) {
  44. return delete this._deferreds[operationId]
  45. };
  46. OperationManager.prototype.cancel = function(operationId) {
  47. if (operationId in this._deferreds) {
  48. this._deferreds[operationId].reject(CANCELED_TOKEN);
  49. return true
  50. }
  51. return false
  52. };
  53. OperationManager.prototype.cancelAll = function() {
  54. while (this._counter > -1) {
  55. this.cancel(this._counter);
  56. this._counter--
  57. }
  58. };
  59. function isPending(deferred) {
  60. return "pending" === deferred.state()
  61. }
  62. function normalizeDataSourceOptions(options, normalizationOptions) {
  63. var store;
  64. function createCustomStoreFromLoadFunc() {
  65. var storeConfig = {};
  66. iteratorUtils.each(["useDefaultSearch", "key", "load", "loadMode", "cacheRawData", "byKey", "lookup", "totalCount", "insert", "update", "remove"], function() {
  67. storeConfig[this] = options[this];
  68. delete options[this]
  69. });
  70. return new CustomStore(storeConfig)
  71. }
  72. function createStoreFromConfig(storeConfig) {
  73. var alias = storeConfig.type;
  74. delete storeConfig.type;
  75. return Store.create(alias, storeConfig)
  76. }
  77. function createCustomStoreFromUrl(url) {
  78. return new CustomStore({
  79. load: function() {
  80. return ajax.sendRequest({
  81. url: url,
  82. dataType: "json"
  83. })
  84. },
  85. loadMode: normalizationOptions && normalizationOptions.fromUrlLoadMode
  86. })
  87. }
  88. if ("string" === typeof options) {
  89. options = {
  90. paginate: false,
  91. store: createCustomStoreFromUrl(options)
  92. }
  93. }
  94. if (void 0 === options) {
  95. options = []
  96. }
  97. if (Array.isArray(options) || options instanceof Store) {
  98. options = {
  99. store: options
  100. }
  101. } else {
  102. options = extend({}, options)
  103. }
  104. if (void 0 === options.store) {
  105. options.store = []
  106. }
  107. store = options.store;
  108. if ("load" in options) {
  109. store = createCustomStoreFromLoadFunc()
  110. } else {
  111. if (Array.isArray(store)) {
  112. store = new ArrayStore(store)
  113. } else {
  114. if (typeUtils.isPlainObject(store)) {
  115. store = createStoreFromConfig(extend({}, store))
  116. }
  117. }
  118. }
  119. options.store = store;
  120. return options
  121. }
  122. function normalizeStoreLoadOptionAccessorArguments(originalArguments) {
  123. switch (originalArguments.length) {
  124. case 0:
  125. return;
  126. case 1:
  127. return originalArguments[0]
  128. }
  129. return [].slice.call(originalArguments)
  130. }
  131. function generateStoreLoadOptionAccessor(optionName) {
  132. return function() {
  133. var args = normalizeStoreLoadOptionAccessorArguments(arguments);
  134. if (void 0 === args) {
  135. return this._storeLoadOptions[optionName]
  136. }
  137. this._storeLoadOptions[optionName] = args
  138. }
  139. }
  140. function mapDataRespectingGrouping(items, mapper, groupInfo) {
  141. function mapRecursive(items, level) {
  142. if (!Array.isArray(items)) {
  143. return items
  144. }
  145. return level ? mapGroup(items, level) : iteratorUtils.map(items, mapper)
  146. }
  147. function mapGroup(group, level) {
  148. return iteratorUtils.map(group, function(item) {
  149. var result = {
  150. key: item.key,
  151. items: mapRecursive(item.items, level - 1)
  152. };
  153. if ("aggregates" in item) {
  154. result.aggregates = item.aggregates
  155. }
  156. return result
  157. })
  158. }
  159. return mapRecursive(items, groupInfo ? dataUtils.normalizeSortingInfo(groupInfo).length : 0)
  160. }
  161. function normalizeLoadResult(data, extra) {
  162. if (data && !Array.isArray(data) && data.data) {
  163. extra = data;
  164. data = data.data
  165. }
  166. if (!Array.isArray(data)) {
  167. data = [data]
  168. }
  169. return {
  170. data: data,
  171. extra: extra
  172. }
  173. }
  174. var DataSource = Class.inherit({
  175. ctor: function(options) {
  176. var _this = this;
  177. var that = this;
  178. options = normalizeDataSourceOptions(options);
  179. var onPushHandler = 0 !== options.pushAggregationTimeout ? dataUtils.throttleChanges(this._onPush, function() {
  180. if (void 0 === options.pushAggregationTimeout) {
  181. return 5 * that._changedTime
  182. }
  183. return options.pushAggregationTimeout
  184. }) : this._onPush;
  185. this._changedTime = 0;
  186. this._onPushHandler = function(changes) {
  187. _this._aggregationTimeoutId = onPushHandler.call(_this, changes)
  188. };
  189. this._store = options.store;
  190. this._store.on("push", this._onPushHandler);
  191. this._storeLoadOptions = this._extractLoadOptions(options);
  192. this._mapFunc = options.map;
  193. this._postProcessFunc = options.postProcess;
  194. this._pageIndex = void 0 !== options.pageIndex ? options.pageIndex : 0;
  195. this._pageSize = void 0 !== options.pageSize ? options.pageSize : 20;
  196. this._loadingCount = 0;
  197. this._loadQueue = this._createLoadQueue();
  198. this._searchValue = "searchValue" in options ? options.searchValue : null;
  199. this._searchOperation = options.searchOperation || "contains";
  200. this._searchExpr = options.searchExpr;
  201. this._paginate = options.paginate;
  202. this._reshapeOnPush = __isDefined(options.reshapeOnPush) ? options.reshapeOnPush : false;
  203. iteratorUtils.each(["onChanged", "onLoadError", "onLoadingChanged", "onCustomizeLoadResult", "onCustomizeStoreLoadOptions"], function(_, optionName) {
  204. if (optionName in options) {
  205. that.on(optionName.substr(2, 1).toLowerCase() + optionName.substr(3), options[optionName])
  206. }
  207. });
  208. this._operationManager = new OperationManager;
  209. this._init()
  210. },
  211. _init: function() {
  212. this._items = [];
  213. this._userData = {};
  214. this._totalCount = -1;
  215. this._isLoaded = false;
  216. if (!__isDefined(this._paginate)) {
  217. this._paginate = !this.group()
  218. }
  219. this._isLastPage = !this._paginate
  220. },
  221. dispose: function() {
  222. this._store.off("push", this._onPushHandler);
  223. this._disposeEvents();
  224. clearTimeout(this._aggregationTimeoutId);
  225. delete this._store;
  226. if (this._delayedLoadTask) {
  227. this._delayedLoadTask.abort()
  228. }
  229. this._operationManager.cancelAll();
  230. this._disposed = true
  231. },
  232. _extractLoadOptions: function(options) {
  233. var result = {};
  234. var names = ["sort", "filter", "select", "group", "requireTotalCount"];
  235. var customNames = this._store._customLoadOptions();
  236. if (customNames) {
  237. names = names.concat(customNames)
  238. }
  239. iteratorUtils.each(names, function() {
  240. result[this] = options[this]
  241. });
  242. return result
  243. },
  244. loadOptions: function() {
  245. return this._storeLoadOptions
  246. },
  247. items: function() {
  248. return this._items
  249. },
  250. pageIndex: function(newIndex) {
  251. if (!__isNumber(newIndex)) {
  252. return this._pageIndex
  253. }
  254. this._pageIndex = newIndex;
  255. this._isLastPage = !this._paginate
  256. },
  257. paginate: function(value) {
  258. if (!__isBoolean(value)) {
  259. return this._paginate
  260. }
  261. if (this._paginate !== value) {
  262. this._paginate = value;
  263. this.pageIndex(0)
  264. }
  265. },
  266. pageSize: function(value) {
  267. if (!__isNumber(value)) {
  268. return this._pageSize
  269. }
  270. this._pageSize = value
  271. },
  272. isLastPage: function() {
  273. return this._isLastPage
  274. },
  275. sort: generateStoreLoadOptionAccessor("sort"),
  276. filter: function() {
  277. var newFilter = normalizeStoreLoadOptionAccessorArguments(arguments);
  278. if (void 0 === newFilter) {
  279. return this._storeLoadOptions.filter
  280. }
  281. this._storeLoadOptions.filter = newFilter;
  282. this.pageIndex(0)
  283. },
  284. group: generateStoreLoadOptionAccessor("group"),
  285. select: generateStoreLoadOptionAccessor("select"),
  286. requireTotalCount: function(value) {
  287. if (!__isBoolean(value)) {
  288. return this._storeLoadOptions.requireTotalCount
  289. }
  290. this._storeLoadOptions.requireTotalCount = value
  291. },
  292. searchValue: function(value) {
  293. if (arguments.length < 1) {
  294. return this._searchValue
  295. }
  296. this._searchValue = value;
  297. this.pageIndex(0)
  298. },
  299. searchOperation: function(op) {
  300. if (!__isString(op)) {
  301. return this._searchOperation
  302. }
  303. this._searchOperation = op;
  304. this.pageIndex(0)
  305. },
  306. searchExpr: function(expr) {
  307. var argc = arguments.length;
  308. if (0 === argc) {
  309. return this._searchExpr
  310. }
  311. if (argc > 1) {
  312. expr = [].slice.call(arguments)
  313. }
  314. this._searchExpr = expr;
  315. this.pageIndex(0)
  316. },
  317. store: function() {
  318. return this._store
  319. },
  320. key: function() {
  321. return this._store && this._store.key()
  322. },
  323. totalCount: function() {
  324. return this._totalCount
  325. },
  326. isLoaded: function() {
  327. return this._isLoaded
  328. },
  329. isLoading: function() {
  330. return this._loadingCount > 0
  331. },
  332. beginLoading: function() {
  333. this._changeLoadingCount(1)
  334. },
  335. endLoading: function() {
  336. this._changeLoadingCount(-1)
  337. },
  338. _createLoadQueue: function() {
  339. return queue.create()
  340. },
  341. _changeLoadingCount: function(increment) {
  342. var oldLoading = this.isLoading();
  343. this._loadingCount += increment;
  344. var newLoading = this.isLoading();
  345. if (oldLoading ^ newLoading) {
  346. this.fireEvent("loadingChanged", [newLoading])
  347. }
  348. },
  349. _scheduleLoadCallbacks: function(deferred) {
  350. var that = this;
  351. that.beginLoading();
  352. deferred.always(function() {
  353. that.endLoading()
  354. })
  355. },
  356. _scheduleFailCallbacks: function(deferred) {
  357. var that = this;
  358. deferred.fail(function() {
  359. if (arguments[0] === CANCELED_TOKEN) {
  360. return
  361. }
  362. that.fireEvent("loadError", arguments)
  363. })
  364. },
  365. _fireChanged: function(args) {
  366. var date = new Date;
  367. this.fireEvent("changed", args);
  368. this._changedTime = new Date - date
  369. },
  370. _scheduleChangedCallbacks: function(deferred) {
  371. var _this2 = this;
  372. deferred.done(function() {
  373. _this2._fireChanged()
  374. })
  375. },
  376. loadSingle: function(propName, propValue) {
  377. var that = this;
  378. var d = new Deferred;
  379. var key = this.key();
  380. var store = this._store;
  381. var options = this._createStoreLoadOptions();
  382. var handleDone = function(data) {
  383. if (!__isDefined(data) || array.isEmpty(data)) {
  384. d.reject(new errors.Error("E4009"))
  385. } else {
  386. if (!Array.isArray(data)) {
  387. data = [data]
  388. }
  389. d.resolve(that._applyMapFunction(data)[0])
  390. }
  391. };
  392. this._scheduleFailCallbacks(d);
  393. if (arguments.length < 2) {
  394. propValue = propName;
  395. propName = key
  396. }
  397. delete options.skip;
  398. delete options.group;
  399. delete options.refresh;
  400. delete options.pageIndex;
  401. delete options.searchString;
  402. function shouldForceByKey() {
  403. return store instanceof CustomStore && !store._byKeyViaLoad()
  404. }(function() {
  405. if (propName === key || shouldForceByKey()) {
  406. return store.byKey(propValue, options)
  407. }
  408. options.take = 1;
  409. options.filter = options.filter ? [options.filter, [propName, propValue]] : [propName, propValue];
  410. return store.load(options)
  411. })().fail(d.reject).done(handleDone);
  412. return d.promise()
  413. },
  414. load: function() {
  415. var that = this;
  416. var d = new Deferred;
  417. function loadTask() {
  418. if (that._disposed) {
  419. return
  420. }
  421. if (!isPending(d)) {
  422. return
  423. }
  424. return that._loadFromStore(loadOperation, d)
  425. }
  426. this._scheduleLoadCallbacks(d);
  427. this._scheduleFailCallbacks(d);
  428. this._scheduleChangedCallbacks(d);
  429. var loadOperation = this._createLoadOperation(d);
  430. this.fireEvent("customizeStoreLoadOptions", [loadOperation]);
  431. this._loadQueue.add(function() {
  432. if ("number" === typeof loadOperation.delay) {
  433. that._delayedLoadTask = commonUtils.executeAsync(loadTask, loadOperation.delay)
  434. } else {
  435. loadTask()
  436. }
  437. return d.promise()
  438. });
  439. return d.promise({
  440. operationId: loadOperation.operationId
  441. })
  442. },
  443. _onPush: function(changes) {
  444. var _this3 = this;
  445. if (this._reshapeOnPush) {
  446. this.load()
  447. } else {
  448. this.fireEvent("changing", [{
  449. changes: changes
  450. }]);
  451. var group = this.group();
  452. var items = this.items();
  453. var groupLevel = 0;
  454. var dataSourceChanges = this.paginate() || group ? changes.filter(function(item) {
  455. return "update" === item.type
  456. }) : changes;
  457. if (group) {
  458. groupLevel = Array.isArray(group) ? group.length : 1
  459. }
  460. if (this._mapFunc) {
  461. dataSourceChanges.forEach(function(item) {
  462. if ("insert" === item.type) {
  463. item.data = _this3._mapFunc(item.data)
  464. }
  465. })
  466. }
  467. arrayUtils.applyBatch(this.store(), items, dataSourceChanges, groupLevel, true);
  468. this._fireChanged([{
  469. changes: changes
  470. }])
  471. }
  472. },
  473. _createLoadOperation: function(deferred) {
  474. var id = this._operationManager.add(deferred);
  475. var options = this._createStoreLoadOptions();
  476. deferred.always(function() {
  477. this._operationManager.remove(id)
  478. }.bind(this));
  479. return {
  480. operationId: id,
  481. storeLoadOptions: options
  482. }
  483. },
  484. reload: function() {
  485. var store = this.store();
  486. if (store instanceof CustomStore) {
  487. store.clearRawDataCache()
  488. }
  489. this._init();
  490. return this.load()
  491. },
  492. cancel: function(operationId) {
  493. return this._operationManager.cancel(operationId)
  494. },
  495. cancelAll: function() {
  496. return this._operationManager.cancelAll()
  497. },
  498. _addSearchOptions: function(storeLoadOptions) {
  499. if (this._disposed) {
  500. return
  501. }
  502. if (this.store()._useDefaultSearch) {
  503. this._addSearchFilter(storeLoadOptions)
  504. } else {
  505. storeLoadOptions.searchOperation = this._searchOperation;
  506. storeLoadOptions.searchValue = this._searchValue;
  507. storeLoadOptions.searchExpr = this._searchExpr
  508. }
  509. },
  510. _createStoreLoadOptions: function() {
  511. var result = extend({}, this._storeLoadOptions);
  512. this._addSearchOptions(result);
  513. if (this._paginate) {
  514. if (this._pageSize) {
  515. result.skip = this._pageIndex * this._pageSize;
  516. result.take = this._pageSize
  517. }
  518. }
  519. result.userData = this._userData;
  520. return result
  521. },
  522. _addSearchFilter: function(storeLoadOptions) {
  523. var value = this._searchValue;
  524. var op = this._searchOperation;
  525. var selector = this._searchExpr;
  526. var searchFilter = [];
  527. if (!value) {
  528. return
  529. }
  530. if (!selector) {
  531. selector = "this"
  532. }
  533. if (!Array.isArray(selector)) {
  534. selector = [selector]
  535. }
  536. iteratorUtils.each(selector, function(i, item) {
  537. if (searchFilter.length) {
  538. searchFilter.push("or")
  539. }
  540. searchFilter.push([item, op, value])
  541. });
  542. if (storeLoadOptions.filter) {
  543. storeLoadOptions.filter = [searchFilter, storeLoadOptions.filter]
  544. } else {
  545. storeLoadOptions.filter = searchFilter
  546. }
  547. },
  548. _loadFromStore: function(loadOptions, pendingDeferred) {
  549. var that = this;
  550. function handleSuccess(data, extra) {
  551. function processResult() {
  552. var loadResult = extend(normalizeLoadResult(data, extra), loadOptions);
  553. that.fireEvent("customizeLoadResult", [loadResult]);
  554. when(loadResult.data).done(function(data) {
  555. loadResult.data = data;
  556. that._processStoreLoadResult(loadResult, pendingDeferred)
  557. }).fail(pendingDeferred.reject)
  558. }
  559. if (that._disposed) {
  560. return
  561. }
  562. if (!isPending(pendingDeferred)) {
  563. return
  564. }
  565. processResult()
  566. }
  567. if (loadOptions.data) {
  568. return (new Deferred).resolve(loadOptions.data).done(handleSuccess)
  569. }
  570. return this.store().load(loadOptions.storeLoadOptions).done(handleSuccess).fail(pendingDeferred.reject)
  571. },
  572. _processStoreLoadResult: function(loadResult, pendingDeferred) {
  573. var that = this;
  574. var data = loadResult.data;
  575. var extra = loadResult.extra;
  576. var storeLoadOptions = loadResult.storeLoadOptions;
  577. function resolvePendingDeferred() {
  578. that._isLoaded = true;
  579. that._totalCount = isFinite(extra.totalCount) ? extra.totalCount : -1;
  580. return pendingDeferred.resolve(data, extra)
  581. }
  582. function proceedLoadingTotalCount() {
  583. that.store().totalCount(storeLoadOptions).done(function(count) {
  584. extra.totalCount = count;
  585. resolvePendingDeferred()
  586. }).fail(pendingDeferred.reject)
  587. }
  588. if (that._disposed) {
  589. return
  590. }
  591. data = that._applyPostProcessFunction(that._applyMapFunction(data));
  592. if (!typeUtils.isPlainObject(extra)) {
  593. extra = {}
  594. }
  595. that._items = data;
  596. if (!data.length || !that._paginate || that._pageSize && data.length < that._pageSize) {
  597. that._isLastPage = true
  598. }
  599. if (storeLoadOptions.requireTotalCount && !isFinite(extra.totalCount)) {
  600. proceedLoadingTotalCount()
  601. } else {
  602. resolvePendingDeferred()
  603. }
  604. },
  605. _applyMapFunction: function(data) {
  606. if (this._mapFunc) {
  607. return mapDataRespectingGrouping(data, this._mapFunc, this.group())
  608. }
  609. return data
  610. },
  611. _applyPostProcessFunction: function(data) {
  612. if (this._postProcessFunc) {
  613. return this._postProcessFunc(data)
  614. }
  615. return data
  616. }
  617. }).include(EventsMixin);
  618. exports.DataSource = DataSource;
  619. exports.normalizeDataSourceOptions = normalizeDataSourceOptions;
  620. exports.normalizeLoadResult = normalizeLoadResult;