| //////////////////////////////////////////////////////////////////////////////// |
| // |
| // Licensed to the Apache Software Foundation (ASF) under one or more |
| // contributor license agreements. See the NOTICE file distributed with |
| // this work for additional information regarding copyright ownership. |
| // The ASF licenses this file to You under the Apache License, Version 2.0 |
| // (the "License"); you may not use this file except in compliance with |
| // the License. You may obtain a copy of the License at |
| // |
| // http://www.apache.org/licenses/LICENSE-2.0 |
| // |
| // Unless required by applicable law or agreed to in writing, software |
| // distributed under the License is distributed on an "AS IS" BASIS, |
| // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| // See the License for the specific language governing permissions and |
| // limitations under the License. |
| // |
| //////////////////////////////////////////////////////////////////////////////// |
| |
| package mx.controls.treeClasses |
| { |
| |
| import flash.events.EventDispatcher; |
| import flash.utils.Dictionary; |
| |
| import mx.collections.ICollectionView; |
| import mx.collections.IViewCursor; |
| import mx.collections.ISort; |
| import mx.collections.XMLListAdapter; |
| import mx.collections.XMLListCollection; |
| import mx.collections.errors.ItemPendingError; |
| import mx.core.EventPriority; |
| import mx.core.mx_internal; |
| import mx.events.CollectionEvent; |
| import mx.events.CollectionEventKind; |
| import mx.events.PropertyChangeEvent; |
| import mx.utils.IXMLNotifiable; |
| import mx.utils.XMLNotifier; |
| |
| use namespace mx_internal; |
| |
| [ExcludeClass] |
| |
| /** |
| * @private |
| * This class provides a hierarchical view of a standard collection. |
| * It is used by Tree to parse user data. |
| */ |
| public class HierarchicalCollectionView extends EventDispatcher |
| implements ICollectionView, IXMLNotifiable |
| { |
| include "../../core/Version.as"; |
| |
| //-------------------------------------------------------------------------- |
| // |
| // Constructor |
| // |
| //-------------------------------------------------------------------------- |
| |
| /** |
| * Constructor. |
| * |
| * @langversion 3.0 |
| * @playerversion Flash 9 |
| * @playerversion AIR 1.1 |
| * @productversion Flex 3 |
| */ |
| public function HierarchicalCollectionView( |
| model:ICollectionView, |
| treeDataDescriptor:ITreeDataDescriptor, |
| itemToUID:Function, |
| argOpenNodes:Object = null) |
| { |
| super(); |
| |
| parentMap = {}; |
| |
| childrenMap = new Dictionary(true); |
| |
| treeData = model; |
| |
| // listen for add/remove events from developer as weak reference |
| treeData.addEventListener(CollectionEvent.COLLECTION_CHANGE, |
| collectionChangeHandler, |
| false, |
| EventPriority.DEFAULT_HANDLER, |
| true); |
| |
| addEventListener(CollectionEvent.COLLECTION_CHANGE, |
| expandEventHandler, |
| false, |
| 0, |
| true); |
| |
| dataDescriptor = treeDataDescriptor; |
| this.itemToUID = itemToUID; |
| openNodes = argOpenNodes; |
| //calc initial length |
| currentLength = calculateLength(); |
| } |
| |
| //-------------------------------------------------------------------------- |
| // |
| // Variables |
| // |
| //-------------------------------------------------------------------------- |
| |
| /** |
| * @private |
| */ |
| private var dataDescriptor:ITreeDataDescriptor; |
| |
| /** |
| * @private |
| */ |
| private var treeData:ICollectionView; |
| |
| /** |
| * @private |
| */ |
| private var cursor:HierarchicalViewCursor; |
| |
| /** |
| * @private |
| * The total number of nodes we know about. |
| */ |
| private var currentLength:int; |
| |
| /** |
| * @private |
| */ |
| public var openNodes:Object; |
| |
| /** |
| * @private |
| * Mapping of UID to parents. Must be maintained as things get removed/added |
| * This map is created as objects are visited |
| */ |
| public var parentMap:Object; |
| |
| /** |
| * @private |
| * Top level XML node if there is one |
| */ |
| private var parentNode:XML; |
| |
| /** |
| * @private |
| * Mapping of nodes to children. Used by getChildren. |
| */ |
| private var childrenMap:Dictionary; |
| |
| /** |
| * @private |
| */ |
| private var itemToUID:Function; |
| |
| //---------------------------------- |
| // filter |
| //---------------------------------- |
| |
| /** |
| * Not Supported in Tree. |
| * |
| * @langversion 3.0 |
| * @playerversion Flash 9 |
| * @playerversion AIR 1.1 |
| * @productversion Flex 3 |
| */ |
| public function get filterFunction():Function |
| { |
| return null; |
| } |
| |
| /** |
| * Not Supported in Tree. |
| * |
| * @langversion 3.0 |
| * @playerversion Flash 9 |
| * @playerversion AIR 1.1 |
| * @productversion Flex 3 |
| */ |
| public function set filterFunction(value:Function):void |
| { |
| //No Impl. |
| } |
| |
| //---------------------------------- |
| // length |
| //---------------------------------- |
| |
| /** |
| * The length of the currently parsed collection. This |
| * length only includes nodes that we know about. |
| * |
| * @langversion 3.0 |
| * @playerversion Flash 9 |
| * @playerversion AIR 1.1 |
| * @productversion Flex 3 |
| */ |
| public function get length():int |
| { |
| return currentLength; |
| } |
| |
| //---------------------------------- |
| // sort |
| //---------------------------------- |
| /** |
| * @private |
| * Not Supported in Tree. |
| */ |
| public function get sort():ISort |
| { |
| return null; |
| } |
| |
| /** |
| * @private |
| * Not Supported in Tree. |
| */ |
| public function set sort(value:ISort):void |
| { |
| //No Impl |
| } |
| |
| //-------------------------------------------------------------------------- |
| // |
| // Methods |
| // |
| //-------------------------------------------------------------------------- |
| |
| /** |
| * Returns the parent of a node. Top level node's parent is null |
| * If we don't know the parent we return undefined. |
| * |
| * @langversion 3.0 |
| * @playerversion Flash 9 |
| * @playerversion AIR 1.1 |
| * @productversion Flex 3 |
| */ |
| public function getParentItem(node:Object):* |
| { |
| var uid:String = itemToUID(node); |
| if (parentMap.hasOwnProperty(uid)) |
| return parentMap[uid]; |
| |
| return undefined; |
| } |
| |
| /** |
| * @private |
| * Calculate the total length of the collection, but only count nodes |
| * that we can reach. |
| */ |
| public function calculateLength(node:Object = null, parent:Object = null):int |
| { |
| var length:int = 0; |
| var childNodes:ICollectionView; |
| var firstNode:Boolean = true; |
| |
| if (node == null) |
| { |
| var modelOffset:int = 0; |
| // special case counting the whole thing |
| // watch for page faults |
| var modelCursor:IViewCursor = treeData.createCursor(); |
| if (modelCursor.beforeFirst) |
| { |
| // indicates that an IPE occured on the first item |
| return treeData.length; |
| } |
| while (!modelCursor.afterLast) |
| { |
| node = modelCursor.current; |
| if (node is XML) |
| { |
| if (firstNode) |
| { |
| firstNode = false; |
| var parNode:* = node.parent(); |
| if (parNode) |
| { |
| startTrackUpdates(parNode); |
| childrenMap[parNode] = treeData; |
| parentNode = parNode; |
| } |
| } |
| startTrackUpdates(node); |
| } |
| |
| if (node === null) |
| length += 1; |
| else |
| length += calculateLength(node, null) + 1; |
| |
| modelOffset++; |
| try |
| { |
| modelCursor.moveNext(); |
| } |
| catch(e:ItemPendingError) |
| { |
| // just stop where we are, no sense paging |
| // the whole thing just to get length. make a rough |
| // guess assuming that all un-paged nodes are closed |
| length += treeData.length - modelOffset; |
| return length; |
| } |
| } |
| } |
| else |
| { |
| var uid:String = itemToUID(node); |
| parentMap[uid] = parent; |
| if (node != null && |
| openNodes[uid] && |
| dataDescriptor.isBranch(node, treeData) && |
| dataDescriptor.hasChildren(node, treeData)) |
| { |
| childNodes = getChildren(node); |
| if (childNodes != null) |
| { |
| var numChildren:int = childNodes.length; |
| for (var i:int = 0; i < numChildren; i++) |
| { |
| if (node is XML) |
| startTrackUpdates(childNodes[i]); |
| length += calculateLength(childNodes[i], node) + 1; |
| } |
| } |
| } |
| } |
| return length; |
| } |
| |
| /** |
| * @private |
| * This method is merely for ICollectionView interface compliance. |
| */ |
| public function describeData():Object |
| { |
| return null; |
| } |
| |
| /** |
| * Returns a new instance of a view iterator over the items in this view |
| * |
| * @see mx.utils.IViewCursor |
| * |
| * @langversion 3.0 |
| * @playerversion Flash 9 |
| * @playerversion AIR 1.1 |
| * @productversion Flex 3 |
| */ |
| public function createCursor():IViewCursor |
| { |
| return new HierarchicalViewCursor( |
| this, treeData, dataDescriptor, itemToUID, openNodes); |
| } |
| |
| /** |
| * Checks the collection for item using standard equality test. |
| * |
| * @langversion 3.0 |
| * @playerversion Flash 9 |
| * @playerversion AIR 1.1 |
| * @productversion Flex 3 |
| */ |
| public function contains(item:Object):Boolean |
| { |
| var cursor:IViewCursor = createCursor(); |
| var done:Boolean = false; |
| while (!done) |
| { |
| if (cursor.current == item) |
| return true; |
| done = cursor.moveNext(); |
| } |
| return false; |
| } |
| |
| /** |
| * @private |
| */ |
| public function disableAutoUpdate():void |
| { |
| //no-op |
| } |
| |
| /** |
| * @private |
| */ |
| public function enableAutoUpdate():void |
| { |
| //no-op |
| } |
| |
| /** |
| * @private |
| */ |
| public function itemUpdated(item:Object, property:Object = null, |
| oldValue:Object = null, |
| newValue:Object = null):void |
| { |
| var event:CollectionEvent = |
| new CollectionEvent(CollectionEvent.COLLECTION_CHANGE); |
| event.kind = CollectionEventKind.UPDATE; |
| |
| var objEvent:PropertyChangeEvent = |
| new PropertyChangeEvent(PropertyChangeEvent.PROPERTY_CHANGE); |
| objEvent.property = property; |
| objEvent.oldValue = oldValue; |
| objEvent.newValue = newValue; |
| event.items.push(objEvent); |
| |
| dispatchEvent(event); |
| } |
| |
| /** |
| * @private |
| */ |
| public function refresh():Boolean |
| { |
| var event:CollectionEvent = |
| new CollectionEvent(CollectionEvent.COLLECTION_CHANGE); |
| event.kind = CollectionEventKind.REFRESH; |
| dispatchEvent(event); |
| |
| return true; |
| } |
| |
| /** |
| * @private |
| * delegate getchildren in order to add event listeners for nested collections |
| */ |
| private function getChildren(node:Object):ICollectionView |
| { |
| var children:ICollectionView = dataDescriptor.getChildren(node, treeData); |
| var oldChildren:ICollectionView = childrenMap[node]; |
| if (oldChildren != children) |
| { |
| if (oldChildren != null) |
| { |
| oldChildren.removeEventListener(CollectionEvent.COLLECTION_CHANGE, |
| nestedCollectionChangeHandler); |
| } |
| if (children) |
| { |
| children.addEventListener(CollectionEvent.COLLECTION_CHANGE, |
| nestedCollectionChangeHandler, false, 0, true); |
| childrenMap[node] = children; |
| } |
| else |
| delete childrenMap[node]; |
| } |
| return children; |
| } |
| |
| /** |
| * @private |
| * Force a recalulation of length |
| */ |
| private function updateLength(node:Object = null, parent:Object = null):void |
| { |
| currentLength = calculateLength(); |
| } |
| |
| /** |
| * @private |
| * Fill the node array with the node and all of its visible children |
| * update the parentMap as you go. |
| */ |
| private function getVisibleNodes(node:Object, parent:Object, nodeArray:Array):void |
| { |
| var childNodes:ICollectionView; |
| nodeArray.push(node); |
| |
| var uid:String = itemToUID(node); |
| parentMap[uid] = parent; |
| if (openNodes[uid] && |
| dataDescriptor.isBranch(node, treeData) && |
| dataDescriptor.hasChildren(node, treeData)) |
| { |
| childNodes = getChildren(node); |
| if (childNodes != null) |
| { |
| var numChildren:int = childNodes.length; |
| for (var i:int = 0; i < numChildren; i++) |
| { |
| getVisibleNodes(childNodes[i], node, nodeArray); |
| } |
| } |
| } |
| } |
| |
| /** |
| * @private |
| * Factor in the open children before this location in the model |
| */ |
| private function getVisibleLocation(oldLocation:int):int |
| { |
| var newLocation:int = 0; |
| var modelCursor:IViewCursor = treeData.createCursor(); |
| for (var i:int = 0; i < oldLocation && !modelCursor.afterLast; i++) |
| { |
| newLocation += calculateLength(modelCursor.current, null) + 1; |
| modelCursor.moveNext(); |
| } |
| return newLocation; |
| } |
| |
| /** |
| * @private |
| * factor in the open children before this location in a sub collection |
| */ |
| private function getVisibleLocationInSubCollection(parent:Object, oldLocation:int):int |
| { |
| var newLocation:int = oldLocation; |
| var target:Object = parent; |
| parent = getParentItem(parent); |
| var children:ICollectionView; |
| var cursor:IViewCursor; |
| while (parent != null) |
| { |
| children = childrenMap[parent]; |
| cursor = children.createCursor(); |
| while (!cursor.afterLast) |
| { |
| if (cursor.current == target) |
| { |
| newLocation++; |
| break; |
| } |
| newLocation += calculateLength(cursor.current, parent) + 1; |
| cursor.moveNext(); |
| } |
| target = parent; |
| parent = getParentItem(parent); |
| } |
| cursor = treeData.createCursor(); |
| while (!cursor.afterLast) |
| { |
| if (cursor.current == target) |
| { |
| newLocation++; |
| break; |
| } |
| newLocation += calculateLength(cursor.current, parent) + 1; |
| cursor.moveNext(); |
| } |
| return newLocation; |
| } |
| |
| //-------------------------------------------------------------------------- |
| // |
| // Event handlers |
| // |
| //-------------------------------------------------------------------------- |
| |
| /** |
| * @private |
| */ |
| public function collectionChangeHandler(event:CollectionEvent):void |
| { |
| var i:int; |
| var n:int; |
| var location:int; |
| var uid:String; |
| var parent:Object; |
| var node:Object; |
| var items:Array; |
| |
| var convertedEvent:CollectionEvent; |
| |
| if (event is CollectionEvent) |
| { |
| var ce:CollectionEvent = CollectionEvent(event); |
| if (ce.kind == CollectionEventKind.RESET) |
| { |
| updateLength(); |
| dispatchEvent(event); |
| } |
| else if (ce.kind == CollectionEventKind.ADD) |
| { |
| n = ce.items.length; |
| convertedEvent = new CollectionEvent( |
| CollectionEvent.COLLECTION_CHANGE, |
| false, |
| true, |
| ce.kind); |
| convertedEvent.location = getVisibleLocation(ce.location); |
| for (i = 0; i < n; i++) |
| { |
| node = ce.items[i]; |
| if (node is XML) |
| startTrackUpdates(node); |
| getVisibleNodes(node, null, convertedEvent.items); |
| } |
| currentLength += convertedEvent.items.length; |
| dispatchEvent(convertedEvent); |
| } |
| else if (ce.kind == CollectionEventKind.REMOVE) |
| { |
| n = ce.items.length; |
| convertedEvent = new CollectionEvent( |
| CollectionEvent.COLLECTION_CHANGE, |
| false, |
| true, |
| ce.kind); |
| convertedEvent.location = getVisibleLocation(ce.location); |
| for (i = 0; i < n; i++) |
| { |
| node = ce.items[i]; |
| if (node is XML) |
| stopTrackUpdates(node); |
| getVisibleNodes(node, null, convertedEvent.items); |
| } |
| currentLength -= convertedEvent.items.length; |
| dispatchEvent(convertedEvent); |
| } |
| else if (ce.kind == CollectionEventKind.UPDATE) |
| { |
| // so far, nobody cares about the details so just |
| // send it |
| //updateLength(); |
| dispatchEvent(event); |
| } |
| else if (ce.kind == CollectionEventKind.REPLACE) |
| { |
| // someday handle case where node is marked as open |
| // before it becomes the replacement. |
| // for now, just pass on the data and remove |
| // old visible rows |
| n = ce.items.length; |
| convertedEvent = new CollectionEvent( |
| CollectionEvent.COLLECTION_CHANGE, |
| false, |
| true, |
| CollectionEventKind.REMOVE); |
| |
| for (i = 0; i < n; i++) |
| { |
| node = ce.items[i].oldValue; |
| if (node is XML) |
| stopTrackUpdates(node); |
| getVisibleNodes(node, null, convertedEvent.items); |
| } |
| |
| // prune the replacements from this list |
| var j:int = 0; |
| for (i = 0; i < n; i++) |
| { |
| node = ce.items[i].oldValue; |
| while (convertedEvent.items[j] != node) |
| j++; |
| convertedEvent.items.splice(j, 1); |
| } |
| if (convertedEvent.items.length) |
| { |
| currentLength -= convertedEvent.items.length; |
| // nobody cares about location yet. |
| dispatchEvent(convertedEvent); |
| } |
| dispatchEvent(event); |
| } |
| } |
| } |
| |
| /** |
| * @private |
| */ |
| public function nestedCollectionChangeHandler(event:CollectionEvent):void |
| { |
| var i:int; |
| var n:int; |
| var location:int; |
| var uid:String; |
| var parent:Object; |
| var node:Object; |
| var items:Array; |
| var convertedEvent:CollectionEvent; |
| |
| if (event is CollectionEvent) |
| { |
| var ce:CollectionEvent = CollectionEvent(event); |
| if (ce.kind == CollectionEventKind.EXPAND) |
| { |
| event.stopImmediatePropagation(); |
| } |
| else if (ce.kind == CollectionEventKind.ADD) |
| { |
| // optimize someday. We do a full tree walk so we can |
| // not only count how many but find the parents of the |
| // new nodes. A better scheme would be to just |
| // increment by the number of visible nodes, but we |
| // don't have a good way to get the parents. |
| updateLength(); |
| n = ce.items.length; |
| convertedEvent = new CollectionEvent( |
| CollectionEvent.COLLECTION_CHANGE, |
| false, |
| true, |
| ce.kind); |
| for (i = 0; i < n; i++) |
| { |
| node = ce.items[i]; |
| if (node is XML) |
| startTrackUpdates(node); |
| parent = getParentItem(node); |
| if (parent != null) |
| getVisibleNodes(node, parent, convertedEvent.items); |
| } |
| convertedEvent.location = getVisibleLocationInSubCollection(parent, ce.location); |
| dispatchEvent(convertedEvent); |
| } |
| else if (ce.kind == CollectionEventKind.REMOVE) |
| { |
| n = ce.items.length; |
| convertedEvent = new CollectionEvent( |
| CollectionEvent.COLLECTION_CHANGE, |
| false, |
| true, |
| ce.kind); |
| for (i = 0; i < n; i++) |
| { |
| node = ce.items[i]; |
| if (node is XML) |
| stopTrackUpdates(node); |
| parent = getParentItem(node); |
| if (parent != null) |
| getVisibleNodes(node, parent, convertedEvent.items); |
| } |
| convertedEvent.location = getVisibleLocationInSubCollection(parent, ce.location); |
| currentLength -= convertedEvent.items.length; |
| dispatchEvent(convertedEvent); |
| } |
| else if (ce.kind == CollectionEventKind.UPDATE) |
| { |
| // so far, nobody cares about the details so just |
| // send it |
| dispatchEvent(event); |
| } |
| else if (ce.kind == CollectionEventKind.REPLACE) |
| { |
| // someday handle case where node is marked as open |
| // before it becomes the replacement. |
| // for now, just pass on the data and remove |
| // old visible rows |
| n = ce.items.length; |
| convertedEvent = new CollectionEvent( |
| CollectionEvent.COLLECTION_CHANGE, |
| false, |
| true, |
| CollectionEventKind.REMOVE); |
| |
| for (i = 0; i < n; i++) |
| { |
| node = ce.items[i].oldValue; |
| parent = getParentItem(node); |
| if (parent != null) |
| getVisibleNodes(node, parent, convertedEvent.items); |
| } |
| |
| // prune the replacements from this list |
| var j:int = 0; |
| for (i = 0; i < n; i++) |
| { |
| node = ce.items[i].oldValue; |
| if (node is XML) |
| stopTrackUpdates(node); |
| while (convertedEvent.items[j] != node) |
| j++; |
| convertedEvent.items.splice(j, 1); |
| } |
| if (convertedEvent.items.length) |
| { |
| currentLength -= convertedEvent.items.length; |
| // nobody cares about location yet. |
| dispatchEvent(convertedEvent); |
| } |
| dispatchEvent(event); |
| } |
| else if (ce.kind == CollectionEventKind.RESET) |
| { |
| // removeAll() sends a RESET. |
| // when we get a reset we don't know what went away |
| // and we don't know how many things went away, so |
| // we just fake a refresh as if there was a filter |
| // applied that filtered out whatever went away |
| updateLength(); |
| convertedEvent = new CollectionEvent( |
| CollectionEvent.COLLECTION_CHANGE, |
| false, |
| true, |
| CollectionEventKind.REFRESH); |
| dispatchEvent(convertedEvent); |
| } |
| } |
| } |
| |
| /** |
| * Called whenever an XML object contained in our list is updated |
| * in some way. The initial implementation stab is very lenient, |
| * any changeType will cause an update no matter how much further down |
| * in a hierarchy. |
| * |
| * @langversion 3.0 |
| * @playerversion Flash 9 |
| * @playerversion AIR 1.1 |
| * @productversion Flex 3 |
| */ |
| public function xmlNotification(currentTarget:Object, |
| type:String, |
| target:Object, |
| value:Object, |
| detail:Object):void |
| { |
| var prop:String; |
| var oldValue:Object; |
| var newValue:Object; |
| var children:XMLListCollection; |
| var location:int; |
| var event:CollectionEvent; |
| var list:XMLListAdapter; |
| |
| // trace("currentTarget", currentTarget.toXMLString()); |
| // trace("target", target.toXMLString()); |
| // trace("value", value.toXMLString()); |
| // trace("type", type); |
| |
| if (currentTarget === target) |
| { |
| |
| switch(type) |
| { |
| case "nodeAdded": |
| { |
| for (var q:* in childrenMap) |
| { |
| if (q === currentTarget) |
| { |
| list = childrenMap[q].list as XMLListAdapter; |
| break; |
| } |
| } |
| |
| if (!list && target is XML && XML(target).children().length() == 1) |
| { |
| // this is a special case (SDK-13807), when you add your first xml node |
| // we need to add the listener, and getChildren() does it for us. |
| list = (getChildren(target) as XMLListCollection).list as XMLListAdapter; |
| } |
| |
| if (list && !list.busy()) |
| { |
| if (childrenMap[q] === treeData) |
| { |
| children = treeData as XMLListCollection; |
| if (parentNode) |
| { |
| children.dispatchResetEvent = false; |
| children.source = parentNode.*; |
| } |
| } |
| else |
| { |
| // this should refresh the collection |
| children = getChildren(q) as XMLListCollection; |
| } |
| if (children) |
| { |
| // now we fake an event on behalf of the |
| // child collection |
| location = value.childIndex(); |
| event = new CollectionEvent(CollectionEvent.COLLECTION_CHANGE); |
| event.kind = CollectionEventKind.ADD; |
| event.location = location; |
| event.items = [ value ]; |
| children.dispatchEvent(event); |
| } |
| } |
| |
| break; |
| } |
| |
| /* needed? |
| case "nodeChanged": |
| { |
| prop = value.localName(); |
| oldValue = detail; |
| newValue = value; |
| break; |
| } |
| * |
| * @langversion 3.0 |
| * @playerversion Flash 9 |
| * @playerversion AIR 1.1 |
| * @productversion Flex 3 |
| */ |
| |
| case "nodeRemoved": |
| { |
| // lookup doesn't work, must scan instead |
| for (var p:* in childrenMap) |
| { |
| if (p === currentTarget) |
| { |
| children = childrenMap[p]; |
| list = children.list as XMLListAdapter; |
| if (list && !list.busy()) |
| { |
| var xmllist:XMLList = children.source as XMLList; |
| |
| if (childrenMap[p] === treeData) |
| { |
| children = treeData as XMLListCollection; |
| if (parentNode) |
| { |
| children.dispatchResetEvent = false; |
| children.source = parentNode.*; |
| } |
| } |
| else |
| { |
| var oldChildren:XMLListCollection = children; |
| // this should refresh the collection |
| children = getChildren(p) as XMLListCollection; |
| if (!children) |
| { |
| // last item got removed so there's no child collection |
| oldChildren.addEventListener(CollectionEvent.COLLECTION_CHANGE, |
| nestedCollectionChangeHandler, false, 0, true); |
| |
| event = new CollectionEvent(CollectionEvent.COLLECTION_CHANGE); |
| event.kind = CollectionEventKind.REMOVE; |
| event.location = 0; |
| event.items = [ value ]; |
| oldChildren.dispatchEvent(event); |
| oldChildren.removeEventListener(CollectionEvent.COLLECTION_CHANGE, |
| nestedCollectionChangeHandler); |
| |
| } |
| } |
| if (children) |
| { |
| var n:int = xmllist.length(); |
| for (var i:int = 0; i < n; i++) |
| { |
| if (xmllist[i] === value) |
| { |
| event = new CollectionEvent(CollectionEvent.COLLECTION_CHANGE); |
| event.kind = CollectionEventKind.REMOVE; |
| event.location = location; |
| event.items = [ value ]; |
| children.dispatchEvent(event); |
| break; |
| } |
| } |
| } |
| } |
| break; |
| } |
| } |
| break; |
| } |
| |
| default: |
| { |
| break; |
| } |
| } |
| } |
| } |
| |
| /** |
| * This is called by addItemAt and when the source is initially |
| * assigned. |
| * |
| * @langversion 3.0 |
| * @playerversion Flash 9 |
| * @playerversion AIR 1.1 |
| * @productversion Flex 3 |
| */ |
| private function startTrackUpdates(item:Object):void |
| { |
| XMLNotifier.getInstance().watchXML(item, this); |
| } |
| |
| /** |
| * This is called by removeItemAt, removeAll, and before a new |
| * source is assigned. |
| * |
| * @langversion 3.0 |
| * @playerversion Flash 9 |
| * @playerversion AIR 1.1 |
| * @productversion Flex 3 |
| */ |
| private function stopTrackUpdates(item:Object):void |
| { |
| XMLNotifier.getInstance().unwatchXML(item, this); |
| } |
| |
| /** |
| * @private |
| */ |
| public function expandEventHandler(event:CollectionEvent):void |
| { |
| if (event is CollectionEvent) |
| { |
| var ce:CollectionEvent = CollectionEvent(event); |
| if (ce.kind == CollectionEventKind.EXPAND) |
| { |
| event.stopImmediatePropagation(); |
| updateLength(); |
| } |
| } |
| } |
| } |
| |
| } |