TreeStore.html 24 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711
  1. <!DOCTYPE html>
  2. <html>
  3. <head>
  4. <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
  5. <title>The source code</title>
  6. <link href="../resources/prettify/prettify.css" type="text/css" rel="stylesheet" />
  7. <script type="text/javascript" src="../resources/prettify/prettify.js"></script>
  8. <style type="text/css">
  9. .highlight { display: block; background-color: #ddd; }
  10. </style>
  11. <script type="text/javascript">
  12. function highlight() {
  13. document.getElementById(location.hash.replace(/#/, "")).className = "highlight";
  14. }
  15. </script>
  16. </head>
  17. <body onload="prettyPrint(); highlight();">
  18. <pre class="prettyprint lang-js"><span id='Ext-data-TreeStore'>/**
  19. </span> * The TreeStore is a store implementation that is backed by by an {@link Ext.data.Tree}.
  20. * It provides convenience methods for loading nodes, as well as the ability to use
  21. * the hierarchical tree structure combined with a store. This class is generally used
  22. * in conjunction with {@link Ext.tree.Panel}. This class also relays many events from
  23. * the Tree for convenience.
  24. *
  25. * # Using Models
  26. *
  27. * If no Model is specified, an implicit model will be created that implements {@link Ext.data.NodeInterface}.
  28. * The standard Tree fields will also be copied onto the Model for maintaining their state. These fields are listed
  29. * in the {@link Ext.data.NodeInterface} documentation.
  30. *
  31. * # Reading Nested Data
  32. *
  33. * For the tree to read nested data, the {@link Ext.data.reader.Reader} must be configured with a root property,
  34. * so the reader can find nested data for each node (if a root is not specified, it will default to
  35. * 'children'). This will tell the tree to look for any nested tree nodes by the same keyword, i.e., 'children'.
  36. * If a root is specified in the config make sure that any nested nodes with children have the same name.
  37. * Note that setting {@link #defaultRootProperty} accomplishes the same thing.
  38. */
  39. Ext.define('Ext.data.TreeStore', {
  40. extend: 'Ext.data.AbstractStore',
  41. alias: 'store.tree',
  42. requires: [
  43. 'Ext.util.Sorter',
  44. 'Ext.data.Tree',
  45. 'Ext.data.NodeInterface'
  46. ],
  47. <span id='Ext-data-TreeStore-cfg-root'> /**
  48. </span> * @cfg {Ext.data.Model/Ext.data.NodeInterface/Object} root
  49. * The root node for this store. For example:
  50. *
  51. * root: {
  52. * expanded: true,
  53. * text: &quot;My Root&quot;,
  54. * children: [
  55. * { text: &quot;Child 1&quot;, leaf: true },
  56. * { text: &quot;Child 2&quot;, expanded: true, children: [
  57. * { text: &quot;GrandChild&quot;, leaf: true }
  58. * ] }
  59. * ]
  60. * }
  61. *
  62. * Setting the `root` config option is the same as calling {@link #setRootNode}.
  63. */
  64. <span id='Ext-data-TreeStore-cfg-clearOnLoad'> /**
  65. </span> * @cfg {Boolean} [clearOnLoad=true]
  66. * Remove previously existing child nodes before loading.
  67. */
  68. clearOnLoad : true,
  69. <span id='Ext-data-TreeStore-cfg-clearRemovedOnLoad'> /**
  70. </span> * @cfg {Boolean} [clearRemovedOnLoad=true]
  71. * If `true`, when a node is reloaded, any records in the {@link #removed} record collection that were previously descendants of the node being reloaded will be cleared from the {@link #removed} collection.
  72. * Only applicable if {@link #clearOnLoad} is `true`.
  73. */
  74. clearRemovedOnLoad: true,
  75. <span id='Ext-data-TreeStore-cfg-nodeParam'> /**
  76. </span> * @cfg {String} [nodeParam=&quot;node&quot;]
  77. * The name of the parameter sent to the server which contains the identifier of the node.
  78. */
  79. nodeParam: 'node',
  80. <span id='Ext-data-TreeStore-cfg-defaultRootId'> /**
  81. </span> * @cfg {String} [defaultRootId=&quot;root&quot;]
  82. * The default root id.
  83. */
  84. defaultRootId: 'root',
  85. <span id='Ext-data-TreeStore-cfg-defaultRootProperty'> /**
  86. </span> * @cfg {String} [defaultRootProperty=&quot;children&quot;]
  87. * The root property to specify on the reader if one is not explicitly defined.
  88. */
  89. defaultRootProperty: 'children',
  90. // Keep a copy of the default so we know if it's been changed in a subclass/config
  91. rootProperty: 'children',
  92. <span id='Ext-data-TreeStore-cfg-folderSort'> /**
  93. </span> * @cfg {Boolean} [folderSort=false]
  94. * Set to true to automatically prepend a leaf sorter.
  95. */
  96. folderSort: false,
  97. constructor: function(config) {
  98. var me = this,
  99. root,
  100. fields,
  101. defaultRoot;
  102. config = Ext.apply({}, config);
  103. <span id='Ext-data-TreeStore-property-fields'> /**
  104. </span> * If we have no fields declare for the store, add some defaults.
  105. * These will be ignored if a model is explicitly specified.
  106. */
  107. fields = config.fields || me.fields;
  108. if (!fields) {
  109. config.fields = [
  110. {name: 'text', type: 'string'}
  111. ];
  112. defaultRoot = config.defaultRootProperty || me.defaultRootProperty;
  113. if (defaultRoot !== me.defaultRootProperty) {
  114. config.fields.push({
  115. name: defaultRoot,
  116. type: 'auto',
  117. defaultValue: null,
  118. persist: false
  119. });
  120. }
  121. }
  122. me.callParent([config]);
  123. // We create our data tree.
  124. me.tree = new Ext.data.Tree();
  125. me.relayEvents(me.tree, [
  126. <span id='Ext-data-TreeStore-event-append'> /**
  127. </span> * @event append
  128. * @inheritdoc Ext.data.Tree#append
  129. */
  130. &quot;append&quot;,
  131. <span id='Ext-data-TreeStore-event-remove'> /**
  132. </span> * @event remove
  133. * @inheritdoc Ext.data.Tree#remove
  134. */
  135. &quot;remove&quot;,
  136. <span id='Ext-data-TreeStore-event-move'> /**
  137. </span> * @event move
  138. * @inheritdoc Ext.data.Tree#move
  139. */
  140. &quot;move&quot;,
  141. <span id='Ext-data-TreeStore-event-insert'> /**
  142. </span> * @event insert
  143. * @inheritdoc Ext.data.Tree#insert
  144. */
  145. &quot;insert&quot;,
  146. <span id='Ext-data-TreeStore-event-beforeappend'> /**
  147. </span> * @event beforeappend
  148. * @inheritdoc Ext.data.Tree#beforeappend
  149. */
  150. &quot;beforeappend&quot;,
  151. <span id='Ext-data-TreeStore-event-beforeremove'> /**
  152. </span> * @event beforeremove
  153. * @inheritdoc Ext.data.Tree#beforeremove
  154. */
  155. &quot;beforeremove&quot;,
  156. <span id='Ext-data-TreeStore-event-beforemove'> /**
  157. </span> * @event beforemove
  158. * @inheritdoc Ext.data.Tree#beforemove
  159. */
  160. &quot;beforemove&quot;,
  161. <span id='Ext-data-TreeStore-event-beforeinsert'> /**
  162. </span> * @event beforeinsert
  163. * @inheritdoc Ext.data.Tree#beforeinsert
  164. */
  165. &quot;beforeinsert&quot;,
  166. <span id='Ext-data-TreeStore-event-expand'> /**
  167. </span> * @event expand
  168. * @inheritdoc Ext.data.Tree#expand
  169. */
  170. &quot;expand&quot;,
  171. <span id='Ext-data-TreeStore-event-collapse'> /**
  172. </span> * @event collapse
  173. * @inheritdoc Ext.data.Tree#collapse
  174. */
  175. &quot;collapse&quot;,
  176. <span id='Ext-data-TreeStore-event-beforeexpand'> /**
  177. </span> * @event beforeexpand
  178. * @inheritdoc Ext.data.Tree#beforeexpand
  179. */
  180. &quot;beforeexpand&quot;,
  181. <span id='Ext-data-TreeStore-event-beforecollapse'> /**
  182. </span> * @event beforecollapse
  183. * @inheritdoc Ext.data.Tree#beforecollapse
  184. */
  185. &quot;beforecollapse&quot;,
  186. <span id='Ext-data-TreeStore-event-sort'> /**
  187. </span> * @event sort
  188. * @inheritdoc Ext.data.Tree#sort
  189. */
  190. &quot;sort&quot;,
  191. <span id='Ext-data-TreeStore-event-rootchange'> /**
  192. </span> * @event rootchange
  193. * @inheritdoc Ext.data.Tree#rootchange
  194. */
  195. &quot;rootchange&quot;
  196. ]);
  197. me.tree.on({
  198. scope: me,
  199. remove: me.onNodeRemove,
  200. // this event must follow the relay to beforeitemexpand to allow users to
  201. // cancel the expand:
  202. beforeexpand: me.onBeforeNodeExpand,
  203. beforecollapse: me.onBeforeNodeCollapse,
  204. append: me.onNodeAdded,
  205. insert: me.onNodeAdded,
  206. sort: me.onNodeSort
  207. });
  208. me.onBeforeSort();
  209. root = me.root;
  210. if (root) {
  211. delete me.root;
  212. me.setRootNode(root);
  213. }
  214. //&lt;deprecated since=0.99&gt;
  215. if (Ext.isDefined(me.nodeParameter)) {
  216. if (Ext.isDefined(Ext.global.console)) {
  217. Ext.global.console.warn('Ext.data.TreeStore: nodeParameter has been deprecated. Please use nodeParam instead.');
  218. }
  219. me.nodeParam = me.nodeParameter;
  220. delete me.nodeParameter;
  221. }
  222. //&lt;/deprecated&gt;
  223. },
  224. // inherit docs
  225. setProxy: function(proxy) {
  226. var reader,
  227. needsRoot;
  228. if (proxy instanceof Ext.data.proxy.Proxy) {
  229. // proxy instance, check if a root was set
  230. needsRoot = Ext.isEmpty(proxy.getReader().root);
  231. } else if (Ext.isString(proxy)) {
  232. // string type, means a reader can't be set
  233. needsRoot = true;
  234. } else {
  235. // object, check if a reader and a root were specified.
  236. reader = proxy.reader;
  237. needsRoot = !(reader &amp;&amp; !Ext.isEmpty(reader.root));
  238. }
  239. proxy = this.callParent(arguments);
  240. if (needsRoot) {
  241. reader = proxy.getReader();
  242. reader.root = this.defaultRootProperty;
  243. // force rebuild
  244. reader.buildExtractors(true);
  245. }
  246. },
  247. // inherit docs
  248. onBeforeSort: function() {
  249. if (this.folderSort) {
  250. this.sort({
  251. property: 'leaf',
  252. direction: 'ASC'
  253. }, 'prepend', false);
  254. }
  255. },
  256. <span id='Ext-data-TreeStore-method-onBeforeNodeExpand'> /**
  257. </span> * Called before a node is expanded.
  258. * @private
  259. * @param {Ext.data.NodeInterface} node The node being expanded.
  260. * @param {Function} callback The function to run after the expand finishes
  261. * @param {Object} scope The scope in which to run the callback function
  262. */
  263. onBeforeNodeExpand: function(node, callback, scope) {
  264. if (node.isLoaded()) {
  265. Ext.callback(callback, scope || node, [node.childNodes]);
  266. }
  267. else if (node.isLoading()) {
  268. this.on('load', function() {
  269. Ext.callback(callback, scope || node, [node.childNodes]);
  270. }, this, {single: true});
  271. }
  272. else {
  273. this.read({
  274. node: node,
  275. callback: function() {
  276. Ext.callback(callback, scope || node, [node.childNodes]);
  277. }
  278. });
  279. }
  280. },
  281. //inherit docs
  282. getNewRecords: function() {
  283. return Ext.Array.filter(this.tree.flatten(), this.filterNew);
  284. },
  285. //inherit docs
  286. getUpdatedRecords: function() {
  287. return Ext.Array.filter(this.tree.flatten(), this.filterUpdated);
  288. },
  289. <span id='Ext-data-TreeStore-method-onBeforeNodeCollapse'> /**
  290. </span> * Called before a node is collapsed.
  291. * @private
  292. * @param {Ext.data.NodeInterface} node The node being collapsed.
  293. * @param {Function} callback The function to run after the collapse finishes
  294. * @param {Object} scope The scope in which to run the callback function
  295. */
  296. onBeforeNodeCollapse: function(node, callback, scope) {
  297. callback.call(scope || node, node.childNodes);
  298. },
  299. onNodeRemove: function(parent, node, isMove) {
  300. var me = this,
  301. removed = me.removed;
  302. if (!node.isReplace &amp;&amp; Ext.Array.indexOf(removed, node) == -1) {
  303. removed.push(node);
  304. }
  305. if (me.autoSync &amp;&amp; !me.autoSyncSuspended &amp;&amp; !isMove) {
  306. me.sync();
  307. }
  308. },
  309. onNodeAdded: function(parent, node) {
  310. var me = this,
  311. proxy = me.getProxy(),
  312. reader = proxy.getReader(),
  313. data = node.raw || node[node.persistenceProperty],
  314. dataRoot;
  315. Ext.Array.remove(me.removed, node);
  316. if (!node.isLeaf()) {
  317. dataRoot = reader.getRoot(data);
  318. if (dataRoot) {
  319. me.fillNode(node, reader.extractData(dataRoot));
  320. delete data[reader.root];
  321. }
  322. }
  323. if (me.autoSync &amp;&amp; !me.autoSyncSuspended &amp;&amp; (node.phantom || node.dirty)) {
  324. me.sync();
  325. }
  326. },
  327. onNodeSort: function() {
  328. if(this.autoSync &amp;&amp; !this.autoSyncSuspended) {
  329. this.sync();
  330. }
  331. },
  332. <span id='Ext-data-TreeStore-method-setRootNode'> /**
  333. </span> * Sets the root node for this store. See also the {@link #root} config option.
  334. * @param {Ext.data.Model/Ext.data.NodeInterface/Object} root
  335. * @return {Ext.data.NodeInterface} The new root
  336. */
  337. setRootNode: function(root, /* private */ preventLoad) {
  338. var me = this,
  339. model = me.model,
  340. idProperty = model.prototype.idProperty
  341. root = root || {};
  342. if (!root.isModel) {
  343. // create a default rootNode and create internal data struct.
  344. Ext.applyIf(root, {
  345. id: me.defaultRootId,
  346. text: 'Root',
  347. allowDrag: false
  348. });
  349. if (root[idProperty] === undefined) {
  350. root[idProperty] = me.defaultRootId;
  351. }
  352. Ext.data.NodeInterface.decorate(model);
  353. root = Ext.ModelManager.create(root, model);
  354. } else if (root.isModel &amp;&amp; !root.isNode) {
  355. Ext.data.NodeInterface.decorate(model);
  356. }
  357. // Because we have decorated the model with new fields,
  358. // we need to build new extactor functions on the reader.
  359. me.getProxy().getReader().buildExtractors(true);
  360. // When we add the root to the tree, it will automaticaly get the NodeInterface
  361. me.tree.setRootNode(root);
  362. // If the user has set expanded: true on the root, we want to call the expand function
  363. if (preventLoad !== true &amp;&amp; !root.isLoaded() &amp;&amp; (me.autoLoad === true || root.isExpanded())) {
  364. me.load({
  365. node: root
  366. });
  367. }
  368. return root;
  369. },
  370. <span id='Ext-data-TreeStore-method-getRootNode'> /**
  371. </span> * Returns the root node for this tree.
  372. * @return {Ext.data.NodeInterface}
  373. */
  374. getRootNode: function() {
  375. return this.tree.getRootNode();
  376. },
  377. <span id='Ext-data-TreeStore-method-getNodeById'> /**
  378. </span> * Returns the record node by id
  379. * @return {Ext.data.NodeInterface}
  380. */
  381. getNodeById: function(id) {
  382. return this.tree.getNodeById(id);
  383. },
  384. // inherit docs
  385. getById: function(id) {
  386. return this.getNodeById(id);
  387. },
  388. <span id='Ext-data-TreeStore-method-load'> /**
  389. </span> * Loads the Store using its configured {@link #proxy}.
  390. * @param {Object} options (Optional) config object. This is passed into the {@link Ext.data.Operation Operation}
  391. * object that is created and then sent to the proxy's {@link Ext.data.proxy.Proxy#read} function.
  392. * The options can also contain a node, which indicates which node is to be loaded. If not specified, it will
  393. * default to the root node.
  394. */
  395. load: function(options) {
  396. options = options || {};
  397. options.params = options.params || {};
  398. var me = this,
  399. node = options.node || me.tree.getRootNode();
  400. // If there is not a node it means the user hasnt defined a rootnode yet. In this case lets just
  401. // create one for them.
  402. if (!node) {
  403. node = me.setRootNode({
  404. expanded: true
  405. }, true);
  406. }
  407. // Assign the ID of the Operation so that a REST proxy can create the correct URL
  408. options.id = node.getId();
  409. if (me.clearOnLoad) {
  410. if(me.clearRemovedOnLoad) {
  411. // clear from the removed array any nodes that were descendants of the node being reloaded so that they do not get saved on next sync.
  412. me.clearRemoved(node);
  413. }
  414. // temporarily remove the onNodeRemove event listener so that when removeAll is called, the removed nodes do not get added to the removed array
  415. me.tree.un('remove', me.onNodeRemove, me);
  416. // remove all the nodes
  417. node.removeAll(false);
  418. // reattach the onNodeRemove listener
  419. me.tree.on('remove', me.onNodeRemove, me);
  420. }
  421. Ext.applyIf(options, {
  422. node: node
  423. });
  424. options.params[me.nodeParam] = node ? node.getId() : 'root';
  425. if (node) {
  426. node.set('loading', true);
  427. }
  428. return me.callParent([options]);
  429. },
  430. <span id='Ext-data-TreeStore-method-clearRemoved'> /**
  431. </span> * Removes all records that used to be descendants of the passed node from the removed array
  432. * @private
  433. * @param {Ext.data.NodeInterface} node
  434. */
  435. clearRemoved: function(node) {
  436. var me = this,
  437. removed = me.removed,
  438. id = node.getId(),
  439. removedLength = removed.length,
  440. i = removedLength,
  441. recordsToClear = {},
  442. newRemoved = [],
  443. removedHash = {},
  444. removedNode,
  445. targetNode,
  446. targetId;
  447. if(node === me.getRootNode()) {
  448. // if the passed node is the root node, just reset the removed array
  449. me.removed = [];
  450. return;
  451. }
  452. // add removed records to a hash so they can be easily retrieved by id later
  453. for(; i--;) {
  454. removedNode = removed[i];
  455. removedHash[removedNode.getId()] = removedNode;
  456. }
  457. for(i = removedLength; i--;) {
  458. removedNode = removed[i];
  459. targetNode = removedNode;
  460. while(targetNode &amp;&amp; targetNode.getId() !== id) {
  461. // walk up the parent hierarchy until we find the passed node or until we get to the root node
  462. targetId = targetNode.get('parentId');
  463. targetNode = targetNode.parentNode || me.getNodeById(targetId) || removedHash[targetId];
  464. }
  465. if(targetNode) {
  466. // removed node was previously a descendant of the passed node - add it to the records to clear from &quot;removed&quot; later
  467. recordsToClear[removedNode.getId()] = removedNode;
  468. }
  469. }
  470. // create a new removed array containing only the records that are not in recordsToClear
  471. for(i = 0; i &lt; removedLength; i++) {
  472. removedNode = removed[i];
  473. if(!recordsToClear[removedNode.getId()]) {
  474. newRemoved.push(removedNode);
  475. }
  476. }
  477. me.removed = newRemoved;
  478. },
  479. <span id='Ext-data-TreeStore-method-fillNode'> /**
  480. </span> * Fills a node with a series of child records.
  481. * @private
  482. * @param {Ext.data.NodeInterface} node The node to fill
  483. * @param {Ext.data.Model[]} newNodes The records to add
  484. */
  485. fillNode: function(node, newNodes) {
  486. var me = this,
  487. ln = newNodes ? newNodes.length : 0,
  488. sorters = me.sorters,
  489. i, sortCollection,
  490. needsIndexSort = false,
  491. performLocalSort = ln &amp;&amp; me.sortOnLoad &amp;&amp; !me.remoteSort &amp;&amp; sorters &amp;&amp; sorters.items &amp;&amp; sorters.items.length,
  492. node1, node2;
  493. // See if there are any differing index values in the new nodes. If not, then we do not have to sortByIndex
  494. for (i = 1; i &lt; ln; i++) {
  495. node1 = newNodes[i];
  496. node2 = newNodes[i - 1];
  497. needsIndexSort = node1[node1.persistenceProperty].index != node2[node2.persistenceProperty].index;
  498. if (needsIndexSort) {
  499. break;
  500. }
  501. }
  502. // If there is a set of local sorters defined.
  503. if (performLocalSort) {
  504. // If sorting by index is needed, sort by index first
  505. if (needsIndexSort) {
  506. me.sorters.insert(0, me.indexSorter);
  507. }
  508. sortCollection = new Ext.util.MixedCollection();
  509. sortCollection.addAll(newNodes);
  510. sortCollection.sort(me.sorters.items);
  511. newNodes = sortCollection.items;
  512. // Remove the index sorter
  513. me.sorters.remove(me.indexSorter);
  514. } else if (needsIndexSort) {
  515. Ext.Array.sort(newNodes, me.sortByIndex);
  516. }
  517. node.set('loaded', true);
  518. for (i = 0; i &lt; ln; i++) {
  519. node.appendChild(newNodes[i], undefined, true);
  520. }
  521. return newNodes;
  522. },
  523. <span id='Ext-data-TreeStore-method-sortByIndex'> /**
  524. </span> * Sorter function for sorting records in index order
  525. * @private
  526. * @param {Ext.data.NodeInterface} node1
  527. * @param {Ext.data.NodeInterface} node2
  528. * @return {Number}
  529. */
  530. sortByIndex: function(node1, node2) {
  531. return node1[node1.persistenceProperty].index - node2[node2.persistenceProperty].index;
  532. },
  533. // inherit docs
  534. onProxyLoad: function(operation) {
  535. var me = this,
  536. successful = operation.wasSuccessful(),
  537. records = operation.getRecords(),
  538. node = operation.node;
  539. me.loading = false;
  540. node.set('loading', false);
  541. if (successful) {
  542. if (!me.clearOnLoad) {
  543. records = me.cleanRecords(node, records);
  544. }
  545. records = me.fillNode(node, records);
  546. }
  547. // The load event has an extra node parameter
  548. // (differing from the load event described in AbstractStore)
  549. <span id='Ext-data-TreeStore-event-load'> /**
  550. </span> * @event load
  551. * Fires whenever the store reads data from a remote data source.
  552. * @param {Ext.data.TreeStore} this
  553. * @param {Ext.data.NodeInterface} node The node that was loaded.
  554. * @param {Ext.data.Model[]} records An array of records.
  555. * @param {Boolean} successful True if the operation was successful.
  556. */
  557. // deprecate read?
  558. me.fireEvent('read', me, operation.node, records, successful);
  559. me.fireEvent('load', me, operation.node, records, successful);
  560. //this is a callback that would have been passed to the 'read' function and is optional
  561. Ext.callback(operation.callback, operation.scope || me, [records, operation, successful]);
  562. },
  563. onCreateRecords: function(records) {
  564. this.callParent(arguments);
  565. var i = 0,
  566. len = records.length,
  567. tree = this.tree,
  568. node;
  569. for (; i &lt; len; ++i) {
  570. node = records[i];
  571. tree.onNodeIdChanged(node, null, node.getId());
  572. }
  573. },
  574. cleanRecords: function(node, records){
  575. var nodeHash = {},
  576. childNodes = node.childNodes,
  577. i = 0,
  578. len = childNodes.length,
  579. out = [],
  580. rec;
  581. // build a hash of all the childNodes under the current node for performance
  582. for (; i &lt; len; ++i) {
  583. nodeHash[childNodes[i].getId()] = true;
  584. }
  585. for (i = 0, len = records.length; i &lt; len; ++i) {
  586. rec = records[i];
  587. if (!nodeHash[rec.getId()]) {
  588. out.push(rec);
  589. }
  590. }
  591. return out;
  592. },
  593. // inherit docs
  594. removeAll: function() {
  595. var root = this.getRootNode();
  596. if (root) {
  597. root.destroy(true);
  598. }
  599. this.fireEvent('clear', this);
  600. },
  601. // inherit docs
  602. doSort: function(sorterFn) {
  603. var me = this;
  604. if (me.remoteSort) {
  605. //the load function will pick up the new sorters and request the sorted data from the proxy
  606. me.load();
  607. } else {
  608. me.tree.sort(sorterFn, true);
  609. me.fireEvent('datachanged', me);
  610. me.fireEvent('refresh', me);
  611. }
  612. me.fireEvent('sort', me);
  613. }
  614. }, function() {
  615. var proto = this.prototype;
  616. proto.indexSorter = new Ext.util.Sorter({
  617. sorterFn: proto.sortByIndex
  618. });
  619. });
  620. </pre>
  621. </body>
  622. </html>