X-Git-Url: http://git.ithinksw.org/extjs.git/blobdiff_plain/0494b8d9b9bb03ab6c22b34dae81261e3cd7e3e6..7a654f8d43fdb43d78b63d90528bed6e86b608cc:/src/data/NodeStore.js diff --git a/src/data/NodeStore.js b/src/data/NodeStore.js new file mode 100644 index 00000000..e5da19c6 --- /dev/null +++ b/src/data/NodeStore.js @@ -0,0 +1,247 @@ +/** + * @class Ext.data.NodeStore + * @extends Ext.data.AbstractStore + * Node Store + * @ignore + */ +Ext.define('Ext.data.NodeStore', { + extend: 'Ext.data.Store', + alias: 'store.node', + requires: ['Ext.data.NodeInterface'], + + /** + * @cfg {Ext.data.Record} node The Record you want to bind this Store to. Note that + * this record will be decorated with the Ext.data.NodeInterface if this is not the + * case yet. + */ + node: null, + + /** + * @cfg {Boolean} recursive Set this to true if you want this NodeStore to represent + * all the descendents of the node in its flat data collection. This is useful for + * rendering a tree structure to a DataView and is being used internally by + * the TreeView. Any records that are moved, removed, inserted or appended to the + * node at any depth below the node this store is bound to will be automatically + * updated in this Store's internal flat data structure. + */ + recursive: false, + + /** + * @cfg {Boolean} rootVisible false to not include the root node in this Stores collection (defaults to true) + */ + rootVisible: false, + + constructor: function(config) { + var me = this, + node; + + config = config || {}; + Ext.apply(me, config); + + // + if (Ext.isDefined(me.proxy)) { + Ext.Error.raise("A NodeStore cannot be bound to a proxy. Instead bind it to a record " + + "decorated with the NodeInterface by setting the node config."); + } + // + + config.proxy = {type: 'proxy'}; + me.callParent([config]); + + me.addEvents('expand', 'collapse', 'beforeexpand', 'beforecollapse'); + + node = me.node; + if (node) { + me.node = null; + me.setNode(node); + } + }, + + setNode: function(node) { + var me = this; + + if (me.node && me.node != node) { + // We want to unbind our listeners on the old node + me.mun(me.node, { + expand: me.onNodeExpand, + collapse: me.onNodeCollapse, + append: me.onNodeAppend, + insert: me.onNodeInsert, + remove: me.onNodeRemove, + sort: me.onNodeSort, + scope: me + }); + me.node = null; + } + + if (node) { + Ext.data.NodeInterface.decorate(node); + me.removeAll(); + if (me.rootVisible) { + me.add(node); + } + me.mon(node, { + expand: me.onNodeExpand, + collapse: me.onNodeCollapse, + append: me.onNodeAppend, + insert: me.onNodeInsert, + remove: me.onNodeRemove, + sort: me.onNodeSort, + scope: me + }); + me.node = node; + if (node.isExpanded() && node.isLoaded()) { + me.onNodeExpand(node, node.childNodes, true); + } + } + }, + + onNodeSort: function(node, childNodes) { + var me = this; + + if ((me.indexOf(node) !== -1 || (node === me.node && !me.rootVisible) && node.isExpanded())) { + me.onNodeCollapse(node, childNodes, true); + me.onNodeExpand(node, childNodes, true); + } + }, + + onNodeExpand: function(parent, records, suppressEvent) { + var me = this, + insertIndex = me.indexOf(parent) + 1, + ln = records ? records.length : 0, + i, record; + + if (!me.recursive && parent !== me.node) { + return; + } + + if (!me.isVisible(parent)) { + return; + } + + if (!suppressEvent && me.fireEvent('beforeexpand', parent, records, insertIndex) === false) { + return; + } + + if (ln) { + me.insert(insertIndex, records); + for (i = 0; i < ln; i++) { + record = records[i]; + if (record.isExpanded()) { + if (record.isLoaded()) { + // Take a shortcut + me.onNodeExpand(record, record.childNodes, true); + } + else { + record.set('expanded', false); + record.expand(); + } + } + } + } + + if (!suppressEvent) { + me.fireEvent('expand', parent, records); + } + }, + + onNodeCollapse: function(parent, records, suppressEvent) { + var me = this, + ln = records.length, + collapseIndex = me.indexOf(parent) + 1, + i, record; + + if (!me.recursive && parent !== me.node) { + return; + } + + if (!suppressEvent && me.fireEvent('beforecollapse', parent, records, collapseIndex) === false) { + return; + } + + for (i = 0; i < ln; i++) { + record = records[i]; + me.remove(record); + if (record.isExpanded()) { + me.onNodeCollapse(record, record.childNodes, true); + } + } + + if (!suppressEvent) { + me.fireEvent('collapse', parent, records, collapseIndex); + } + }, + + onNodeAppend: function(parent, node, index) { + var me = this, + refNode, sibling; + + if (me.isVisible(node)) { + if (index === 0) { + refNode = parent; + } else { + sibling = node.previousSibling; + while (sibling.isExpanded() && sibling.lastChild) { + sibling = sibling.lastChild; + } + refNode = sibling; + } + me.insert(me.indexOf(refNode) + 1, node); + if (!node.isLeaf() && node.isExpanded()) { + if (node.isLoaded()) { + // Take a shortcut + me.onNodeExpand(node, node.childNodes, true); + } + else { + node.set('expanded', false); + node.expand(); + } + } + } + }, + + onNodeInsert: function(parent, node, refNode) { + var me = this, + index = this.indexOf(refNode); + + if (index != -1 && me.isVisible(node)) { + me.insert(index, node); + if (!node.isLeaf() && node.isExpanded()) { + if (node.isLoaded()) { + // Take a shortcut + me.onNodeExpand(node, node.childNodes, true); + } + else { + node.set('expanded', false); + node.expand(); + } + } + } + }, + + onNodeRemove: function(parent, node, index) { + var me = this; + if (me.indexOf(node) != -1) { + if (!node.isLeaf() && node.isExpanded()) { + me.onNodeCollapse(node, node.childNodes, true); + } + me.remove(node); + } + }, + + isVisible: function(node) { + var parent = node.parentNode; + while (parent) { + if (parent === this.node && !this.rootVisible && parent.isExpanded()) { + return true; + } + + if (this.indexOf(parent) === -1 || !parent.isExpanded()) { + return false; + } + + parent = parent.parentNode; + } + return true; + } +}); \ No newline at end of file