| //////////////////////////////////////////////////////////////////////////////// |
| // |
| // 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.collections |
| { |
| |
| import flash.events.EventDispatcher; |
| import flash.utils.Dictionary; |
| import flash.xml.XMLNode; |
| |
| 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.UIDUtil; |
| import mx.utils.XMLNotifier; |
| |
| use namespace mx_internal; |
| |
| /** |
| * The HierarchicalCollectionView class provides a hierarchical view of a standard collection. |
| * |
| * @mxml |
| * |
| * The <code><mx.HierarchicalCollectionView></code> inherits all the tag attributes of its superclass, |
| * and defines the following tag attributes:</p> |
| * |
| * <pre> |
| * <mx:HierarchicalCollectionView |
| * <b>Properties </b> |
| * showRoot="true|false" |
| * source="<i>No default</i>" |
| * /> |
| * </pre> |
| * |
| * @langversion 3.0 |
| * @playerversion Flash 9 |
| * @playerversion AIR 1.1 |
| * @productversion Flex 3 |
| */ |
| public class HierarchicalCollectionView extends EventDispatcher |
| implements IHierarchicalCollectionView, IXMLNotifiable |
| { |
| include "../core/Version.as"; |
| |
| //-------------------------------------------------------------------------- |
| // |
| // Constructor |
| // |
| //-------------------------------------------------------------------------- |
| |
| /** |
| * Constructor. |
| * |
| * @param hierarchicalData The data structure containing the hierarchical data. |
| * |
| * @param argOpenNodes The Object that defines a node to appear as open. |
| * |
| * @langversion 3.0 |
| * @playerversion Flash 9 |
| * @playerversion AIR 1.1 |
| * @productversion Flex 3 |
| */ |
| public function HierarchicalCollectionView( |
| hierarchicalData:IHierarchicalData = null, |
| argOpenNodes:Object = null) |
| { |
| super(); |
| |
| if (hierarchicalData) |
| initializeCollection(hierarchicalData.getRoot(), hierarchicalData, argOpenNodes); |
| } |
| |
| //-------------------------------------------------------------------------- |
| // |
| // Variables |
| // |
| //-------------------------------------------------------------------------- |
| |
| /** |
| * @private |
| */ |
| private var hierarchicalData:IHierarchicalData; |
| |
| /** |
| * @private |
| * The total number of nodes we know about. |
| */ |
| private var currentLength:int; |
| |
| /** |
| * @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 childrenMapCache:Dictionary = new Dictionary(true); |
| |
| /** |
| * @private |
| */ |
| mx_internal var treeData:ICollectionView; |
| |
| /** |
| * @private |
| * Mapping of UID to parents. Must be maintained as things get removed/added |
| * This map is created as objects are visited |
| */ |
| private var parentMap:Object; |
| |
| //-------------------------------------------------------------------------- |
| // |
| // Properties |
| // |
| //-------------------------------------------------------------------------- |
| |
| //---------------------------------- |
| // hasRoot |
| //---------------------------------- |
| |
| private var _hasRoot:Boolean; |
| |
| /** |
| * @inheritDoc |
| * |
| * @langversion 3.0 |
| * @playerversion Flash 9 |
| * @playerversion AIR 1.1 |
| * @productversion Flex 3 |
| */ |
| public function get hasRoot():Boolean |
| { |
| return _hasRoot; |
| } |
| |
| //---------------------------------- |
| // openNodes |
| //---------------------------------- |
| |
| /** |
| * @private |
| * Storage for the openNodes property. |
| */ |
| private var _openNodes:Object; |
| |
| /** |
| * @inheritDoc |
| * |
| * @langversion 3.0 |
| * @playerversion Flash 9 |
| * @playerversion AIR 1.1 |
| * @productversion Flex 3 |
| */ |
| public function get openNodes():Object |
| { |
| return _openNodes; |
| } |
| |
| /** |
| * @private |
| */ |
| public function set openNodes(value:Object):void |
| { |
| // openNodes cant be null |
| if (value) |
| { |
| _openNodes = {}; |
| for each (var item:* in value) |
| { |
| _openNodes[UIDUtil.getUID(item)] = item; |
| } |
| } |
| else |
| _openNodes = {}; |
| |
| if (hierarchicalData) |
| { |
| //calc initial length |
| currentLength = calculateLength(); |
| |
| // need to refresh the collection after setting openNodes |
| var event:CollectionEvent = |
| new CollectionEvent(CollectionEvent.COLLECTION_CHANGE); |
| event.kind = CollectionEventKind.REFRESH; |
| dispatchEvent(event); |
| } |
| } |
| |
| //---------------------------------- |
| // showRoot |
| //---------------------------------- |
| |
| private var _showRoot:Boolean = true; |
| |
| [Bindable] |
| [Inspectable(category="Data", enumeration="true,false", defaultValue="true")] |
| |
| /** |
| * @inheritDoc |
| * |
| * @default true |
| * |
| * @langversion 3.0 |
| * @playerversion Flash 9 |
| * @playerversion AIR 1.1 |
| * @productversion Flex 3 |
| */ |
| public function get showRoot():Boolean |
| { |
| return _showRoot; |
| } |
| |
| /** |
| * @private |
| */ |
| public function set showRoot(value:Boolean):void |
| { |
| if (_showRoot != value) |
| { |
| _showRoot = value; |
| if (hierarchicalData) |
| { |
| initializeCollection(hierarchicalData.getRoot(), hierarchicalData, openNodes); |
| |
| //setting showRoot resets the collection |
| var event:CollectionEvent = |
| new CollectionEvent(CollectionEvent.COLLECTION_CHANGE); |
| event.kind = CollectionEventKind.RESET; |
| dispatchEvent(event); |
| } |
| } |
| } |
| |
| //---------------------------------- |
| // source |
| //---------------------------------- |
| |
| /** |
| * @inheritDoc |
| * |
| * @langversion 3.0 |
| * @playerversion Flash 9 |
| * @playerversion AIR 1.1 |
| * @productversion Flex 3 |
| */ |
| public function get source():IHierarchicalData |
| { |
| return hierarchicalData; |
| } |
| |
| /** |
| * @private |
| */ |
| public function set source(value:IHierarchicalData):void |
| { |
| initializeCollection(value.getRoot(), value, openNodes); |
| } |
| |
| //---------------------------------- |
| // filter |
| //---------------------------------- |
| |
| /** |
| * @private |
| * Storage for the filterFunction property. |
| */ |
| private var _filterFunction:Function; |
| |
| [Bindable("filterFunctionChanged")] |
| [Inspectable(category="General")] |
| |
| /** |
| * @private |
| */ |
| public function get filterFunction():Function |
| { |
| return _filterFunction; |
| } |
| |
| /** |
| * @private |
| */ |
| public function set filterFunction(value:Function):void |
| { |
| _filterFunction = value; |
| } |
| |
| //---------------------------------- |
| // length |
| //---------------------------------- |
| |
| /** |
| * The length of the currently parsed collection (i.e. the number |
| * of nodes that can be accessed by navigating the collection via |
| * HierarchicalCollectionViewCursor) |
| * |
| * @langversion 3.0 |
| * @playerversion Flash 9 |
| * @playerversion AIR 1.1 |
| * @productversion Flex 3 |
| */ |
| public function get length():int |
| { |
| return currentLength; |
| } |
| |
| //---------------------------------- |
| // sort |
| //---------------------------------- |
| |
| /** |
| * @private |
| * Storage for the sort property. |
| */ |
| private var _sort:ISort; |
| |
| [Bindable("sortChanged")] |
| [Inspectable(category="General")] |
| |
| /** |
| * @private |
| */ |
| public function get sort():ISort |
| { |
| return _sort; |
| } |
| |
| /** |
| * @private |
| */ |
| public function set sort(value:ISort):void |
| { |
| _sort = value; |
| } |
| |
| //-------------------------------------------------------------------------- |
| // |
| // ICollectionView Methods |
| // |
| //-------------------------------------------------------------------------- |
| |
| /** |
| * Returns a new instance of a view iterator over the items in this view. |
| * |
| * @return IViewCursor instance. |
| * |
| * @see mx.collections.IViewCursor |
| * |
| * @langversion 3.0 |
| * @playerversion Flash 9 |
| * @playerversion AIR 1.1 |
| * @productversion Flex 3 |
| */ |
| public function createCursor():IViewCursor |
| { |
| return new HierarchicalCollectionViewCursor( |
| this, treeData, hierarchicalData); |
| } |
| |
| /** |
| * Checks the collection for the data item using standard equality test. |
| * |
| * @param item The Object that defines the node to look for. |
| * |
| * @param <code>true</code> if the collection contains the item, |
| * |
| * @return <code>true</code> if the data item is in the collection, |
| * and <code>false</code> if not. |
| * |
| * @langversion 3.0 |
| * @playerversion Flash 9 |
| * @playerversion AIR 1.1 |
| * @productversion Flex 3 |
| */ |
| public function contains(item:Object):Boolean |
| { |
| var cursor:IViewCursor = createCursor(); |
| while (!cursor.afterLast) |
| { |
| if (cursor.current == item) |
| return true; |
| |
| try |
| { |
| cursor.moveNext(); |
| } |
| catch (e:ItemPendingError) |
| { |
| // item is pending. |
| // we are not sure if the item is present or not, |
| // so return false |
| return false; |
| } |
| } |
| return false; |
| } |
| |
| /** |
| * @private |
| */ |
| public function disableAutoUpdate():void |
| { |
| // propogate to all the child collections |
| treeData.disableAutoUpdate(); |
| for (var p:Object in childrenMap) |
| ICollectionView(childrenMap[p]).disableAutoUpdate(); |
| } |
| |
| /** |
| * @private |
| */ |
| public function enableAutoUpdate():void |
| { |
| // propogate to all the child collections |
| treeData.enableAutoUpdate(); |
| for (var p:Object in childrenMap) |
| ICollectionView(childrenMap[p]).enableAutoUpdate(); |
| } |
| |
| /** |
| * @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.source = item; |
| objEvent.property = property; |
| objEvent.oldValue = oldValue; |
| objEvent.newValue = newValue; |
| event.items.push(objEvent); |
| |
| dispatchEvent(event); |
| } |
| |
| /** |
| * @inheritDoc |
| * |
| * @langversion 3.0 |
| * @playerversion Flash 9 |
| * @playerversion AIR 1.1 |
| * @productversion Flex 3 |
| */ |
| public function refresh():Boolean |
| { |
| return internalRefresh(true); |
| } |
| |
| //-------------------------------------------------------------------------- |
| // |
| // IHierarchicalCollectionView Methods |
| // |
| //-------------------------------------------------------------------------- |
| |
| /** |
| * @inheritDoc |
| * |
| * @langversion 3.0 |
| * @playerversion Flash 9 |
| * @playerversion AIR 1.1 |
| * @productversion Flex 3 |
| */ |
| public function getNodeDepth(node:Object):int |
| { |
| var depth:int = 1; |
| var parent:Object = getParentItem(node); |
| |
| while(parent != null) |
| { |
| parent = getParentItem(parent); |
| depth++; |
| } |
| depth = (hasRoot && !showRoot) ? (depth - 1) : depth; |
| |
| // depth cant be less then 1 |
| return (depth < 1) ? 1 : depth; |
| } |
| |
| /** |
| * Returns the parent of a node. |
| * The parent of a top-level node is <code>null</code>. |
| * |
| * @param node The Object that defines the node. |
| * |
| * @return The parent node containing the node, |
| * <code>null</code> for a top-level node, |
| * and <code>undefined</code> if the parent cannot be determined. |
| * |
| * @langversion 3.0 |
| * @playerversion Flash 9 |
| * @playerversion AIR 1.1 |
| * @productversion Flex 3 |
| */ |
| public function getParentItem(node:Object):* |
| { |
| var uid:String = UIDUtil.getUID(node); |
| if (parentMap.hasOwnProperty(uid)) |
| return parentMap[uid]; |
| |
| return undefined; |
| } |
| |
| public function deleteParentMapping(uid:String):void |
| { |
| delete parentMap[uid]; |
| } |
| |
| public function addParentMapping(uid:String, parent:Object, replaceExisting:Boolean = true):void |
| { |
| if(replaceExisting || !parentMap.hasOwnProperty(uid)) |
| parentMap[uid] = parent; |
| } |
| |
| /** |
| * @inheritDoc |
| * |
| * @langversion 3.0 |
| * @playerversion Flash 9 |
| * @playerversion AIR 1.1 |
| * @productversion Flex 3 |
| */ |
| public function getChildren(node:Object):ICollectionView |
| { |
| // Using uid because XML cant be referenced correctly using Dictionary |
| var uid:String = UIDUtil.getUID(node); |
| var children:* = hierarchicalData.getChildren(node); |
| |
| var childrenCollection:ICollectionView = childrenMapCache[uid]; |
| |
| if (children is XMLList && childrenCollection) |
| { |
| //We don't want to send a RESET type of collectionChange event in this case. |
| XMLListCollection(childrenCollection).mx_internal::dispatchResetEvent = false; |
| XMLListCollection(childrenCollection).source = children; |
| |
| // refresh the collection to apply the sort/filter |
| childrenCollection.refresh(); |
| } |
| |
| // check the cache and return from it. |
| // useful in sorting/filtering. |
| if(childrenCollection) |
| { |
| // node might have changed, so update the childrenMap |
| childrenMap[node] = childrenCollection; |
| return childrenCollection; |
| } |
| |
| // if there is no children, return null |
| if (!children) |
| return null; |
| |
| //then wrap children in ICollectionView if necessary |
| if (children is ICollectionView) |
| { |
| childrenCollection = ICollectionView(children); |
| } |
| else if (children is Array) |
| { |
| childrenCollection = new ArrayCollection(children); |
| } |
| else if (children is XMLList) |
| { |
| childrenCollection = new XMLListCollection(children); |
| } |
| else |
| { |
| var childArray:Array = new Array(children); |
| if (childArray != null) |
| { |
| childrenCollection = new ArrayCollection(childArray); |
| } |
| } |
| |
| childrenMapCache[uid] = childrenCollection; |
| |
| |
| var oldChildren:ICollectionView = childrenMap[node]; |
| if (oldChildren != childrenCollection) |
| { |
| if (oldChildren != null) |
| { |
| oldChildren.removeEventListener(CollectionEvent.COLLECTION_CHANGE, |
| nestedCollectionChangeHandler); |
| } |
| childrenCollection.addEventListener(CollectionEvent.COLLECTION_CHANGE, |
| nestedCollectionChangeHandler, false, 0, true); |
| childrenMap[node] = childrenCollection; |
| } |
| |
| return childrenCollection; |
| } |
| |
| /** |
| * @inheritDoc |
| * |
| * @langversion 3.0 |
| * @playerversion Flash 9 |
| * @playerversion AIR 1.1 |
| * @productversion Flex 3 |
| */ |
| public function openNode(node:Object):void |
| { |
| var uid:String = UIDUtil.getUID(node); |
| |
| // check if the node is already opened |
| if (_openNodes[uid] != null) |
| return; |
| |
| // check if the node is accessible, abort if not |
| var parent:* = getParentItem(node); |
| while(parent) |
| { |
| parent = getParentItem(parent); |
| } |
| |
| //undefined means an ancestor is not open, which means the node is inaccessible |
| if(parent === undefined) |
| return; |
| |
| // add the node to the openNodes object and update the length |
| _openNodes[uid] = node; |
| |
| // apply the sort/filter to the child collection of the opened node. |
| var childrenCollection:ICollectionView = getChildren(node); |
| |
| // return if there are no children |
| if (!childrenCollection) |
| return; |
| |
| if (sortCanBeApplied(childrenCollection) && !(childrenCollection.sort == null && sort == null)) |
| { |
| childrenCollection.sort = this.sort; |
| } |
| if (!(childrenCollection.filterFunction == null && filterFunction == null)) |
| { |
| childrenCollection.filterFunction = this.filterFunction; |
| } |
| childrenCollection.refresh(); |
| |
| updateParentMapAndLength(childrenCollection, node); |
| } |
| |
| /** |
| * @inheritDoc |
| * |
| * @langversion 3.0 |
| * @playerversion Flash 9 |
| * @playerversion AIR 1.1 |
| * @productversion Flex 3 |
| */ |
| public function closeNode(node:Object):void |
| { |
| var childrenCollection:ICollectionView = childrenMap[node]; |
| |
| // removes the node from the openNodes object and update the length |
| delete _openNodes[UIDUtil.getUID(node)]; |
| |
| if (childrenCollection) |
| { |
| var cursor:IViewCursor = childrenCollection.createCursor(); |
| while (!cursor.afterLast) |
| { |
| var uid:String = UIDUtil.getUID(cursor.current); |
| deleteParentMapping(uid); |
| |
| try |
| { |
| cursor.moveNext(); |
| } |
| catch (e:ItemPendingError) |
| { |
| break; |
| } |
| |
| } |
| } |
| |
| // update the length |
| updateLength(); |
| } |
| |
| /** |
| * @inheritDoc |
| * |
| * @langversion 3.0 |
| * @playerversion Flash 9 |
| * @playerversion AIR 1.1 |
| * @productversion Flex 3 |
| */ |
| public function addChild(parent:Object, newChild:Object):Boolean |
| { |
| if (parent == null) |
| return addChildAt(parent, newChild, treeData.length); |
| else |
| return addChildAt(parent, newChild, getChildren(parent).length); |
| } |
| |
| /** |
| * @inheritDoc |
| * |
| * @langversion 3.0 |
| * @playerversion Flash 9 |
| * @playerversion AIR 1.1 |
| * @productversion Flex 3 |
| */ |
| public function removeChild(parent:Object, child:Object):Boolean |
| { |
| var cursor:IViewCursor; |
| if (parent == null) |
| { |
| cursor = treeData.createCursor(); |
| } |
| else |
| { |
| var children:ICollectionView = getChildren(parent); |
| cursor = children.createCursor(); |
| } |
| while (!cursor.afterLast) |
| { |
| if (cursor.current == child) |
| { |
| cursor.remove(); |
| return true; |
| } |
| |
| try |
| { |
| cursor.moveNext(); |
| } |
| catch (e:ItemPendingError) |
| { |
| // Items are pending - so return false |
| return false; |
| } |
| } |
| |
| return false; |
| } |
| |
| /** |
| * Add a child node to a node at the specified index. |
| * This implementation does the following: |
| * |
| * <ul> |
| * <li>If the <code>parent</code> is null or undefined, |
| * inserts the <code>child</code> at the |
| * specified <code>index</code> in the collection specified |
| * by <code>source</code>. |
| * </li> |
| * <li>If the <code>parent</code> has a <code>children</code> |
| * field or property, the method adds the <code>child</code> |
| * to it at the <code>index</code> location. |
| * In this case, the <code>source</code> is not required. |
| * </li> |
| * <li>If the <code>parent</code> does not have a <code>children</code> |
| * field or property, the method adds the <code>children</code> |
| * to the <code>parent</code>. The method then adds the |
| * <code>child</code> to the parent at the |
| * <code>index</code> location. |
| * In this case, the <code>source</code> is not required. |
| * </li> |
| * <li>If the <code>index</code> value is greater than the collection |
| * length or number of children in the parent, adds the object as |
| * the last child. |
| * </li> |
| * </ul> |
| * |
| * @param parent The Object that defines the parent node. |
| * |
| * @param newChild The Object that defines the child node. |
| * |
| * @param index The 0-based index of where to insert the child node. |
| * |
| * @return <code>true</code> if the child is added successfully. |
| * |
| * @langversion 3.0 |
| * @playerversion Flash 9 |
| * @playerversion AIR 1.1 |
| * @productversion Flex 3 |
| */ |
| public function addChildAt(parent:Object, newChild:Object, index:int):Boolean |
| { |
| var cursor:IViewCursor; |
| if (!parent) |
| { |
| cursor = treeData.createCursor(); |
| } |
| else |
| { |
| if (!hierarchicalData.canHaveChildren(parent)) |
| return false; |
| |
| var children:ICollectionView = getChildren(parent); |
| |
| cursor = children.createCursor(); |
| } |
| |
| try |
| { |
| cursor.seek(CursorBookmark.FIRST, index); |
| } |
| catch (e:ItemPendingError) |
| { |
| // Item Pending |
| return false; |
| } |
| |
| cursor.insert(newChild); |
| |
| return true; |
| } |
| |
| /** |
| * Removes the child node from a node at the specified index. |
| * |
| * @param parent The Object that defines the parent node. |
| * |
| * @param index The 0-based index of the child node to remove relative to the parent. |
| * |
| * @return <code>true</code> if the child is removed successfully. |
| * |
| * @langversion 3.0 |
| * @playerversion Flash 9 |
| * @playerversion AIR 1.1 |
| * @productversion Flex 3 |
| */ |
| public function removeChildAt(parent:Object, index:int):Boolean |
| { |
| var cursor:IViewCursor; |
| if (!parent) |
| { |
| cursor = treeData.createCursor(); |
| } |
| else |
| { |
| var children:ICollectionView = getChildren(parent); |
| cursor = children.createCursor(); |
| } |
| |
| try |
| { |
| cursor.seek(CursorBookmark.FIRST, index); |
| } |
| catch (e:ItemPendingError) |
| { |
| // Item Pending |
| return false; |
| } |
| |
| if (cursor.beforeFirst || cursor.afterLast) |
| return false; |
| |
| cursor.remove(); |
| return true; |
| } |
| |
| //-------------------------------------------------------------------------- |
| // |
| // Methods |
| // |
| //-------------------------------------------------------------------------- |
| |
| /** |
| * @private |
| * |
| * returns the collection view of the given object |
| */ |
| private function getCollection(value:Object):ICollectionView |
| { |
| // handle strings and xml |
| if (typeof(value)=="string") |
| value = new XML(value); |
| else if (value is XMLNode) |
| value = new XML(XMLNode(value).toString()); |
| else if (value is XMLList) |
| value = new XMLListCollection(value as XMLList); |
| |
| if (value is XML) |
| { |
| var xl:XMLList = new XMLList(); |
| xl += value; |
| return new XMLListCollection(xl); |
| } |
| //if already a collection dont make new one |
| else if (value is ICollectionView) |
| { |
| return ICollectionView(value); |
| } |
| else if (value is Array) |
| { |
| return new ArrayCollection(value as Array); |
| } |
| //all other types get wrapped in an ArrayCollection |
| else if (value is Object) |
| { |
| // convert to an array containing this one item |
| var tmp:Array = []; |
| tmp.push(value); |
| return new ArrayCollection(tmp); |
| } |
| else |
| { |
| return new ArrayCollection(); |
| } |
| } |
| |
| /** |
| * @private |
| * |
| * Initialize the collection. set its various properties and |
| * update its length. |
| */ |
| private function initializeCollection(model:Object, |
| hierarchicalData:IHierarchicalData, |
| argOpenNodes:Object = null):void |
| { |
| parentMap = {}; |
| |
| childrenMap = new Dictionary(true); |
| childrenMapCache = new Dictionary(true); |
| |
| if (treeData) |
| treeData.removeEventListener(CollectionEvent.COLLECTION_CHANGE, |
| collectionChangeHandler, |
| false); |
| |
| if (this.hierarchicalData) |
| this.hierarchicalData.removeEventListener(CollectionEvent.COLLECTION_CHANGE, |
| collectionChangeHandler, |
| false); |
| |
| treeData = getCollection(model); |
| |
| if (treeData) |
| _hasRoot = treeData.length == 1; |
| |
| var tmpCollection:Object = model; |
| // are we swallowing the root? |
| if (hierarchicalData && !showRoot && hasRoot) |
| { |
| var obj:Object = treeData.createCursor().current; |
| if (hierarchicalData.hasChildren(obj)) |
| { |
| // then get rootItem children |
| tmpCollection = hierarchicalData.getChildren(obj); |
| treeData = getCollection(tmpCollection); |
| } |
| } |
| |
| // listen for add/remove events from developer as weak reference |
| treeData.addEventListener(CollectionEvent.COLLECTION_CHANGE, |
| collectionChangeHandler, |
| false, |
| EventPriority.DEFAULT_HANDLER, |
| true); |
| |
| this.hierarchicalData = hierarchicalData; |
| |
| // listen for reset/refresh events |
| this.hierarchicalData.addEventListener( |
| CollectionEvent.COLLECTION_CHANGE, |
| collectionChangeHandler, |
| false, |
| EventPriority.DEFAULT_HANDLER, |
| true); |
| |
| // openNodes cant be null |
| if (argOpenNodes) |
| _openNodes = argOpenNodes; |
| else |
| _openNodes = {}; |
| |
| //calc initial length |
| currentLength = calculateLength(); |
| } |
| |
| /** |
| * @private |
| * |
| * Update the parent map and adjust the length. |
| */ |
| private function updateParentMapAndLength(collection:ICollectionView, node:Object):void |
| { |
| var cursor:IViewCursor = collection.createCursor(); |
| currentLength += collection.length; |
| |
| while (!cursor.afterLast) |
| { |
| var item:Object = cursor.current; |
| var uid:String = UIDUtil.getUID(item); |
| addParentMapping(uid, node); |
| |
| // check that the node is opened or not. |
| // If it is open, then update the length with the node's children. |
| if (_openNodes[uid] != null) |
| { |
| var childrenCollection:ICollectionView = getChildren(item); |
| if (childrenCollection) |
| updateParentMapAndLength(childrenCollection, item); |
| } |
| |
| try |
| { |
| cursor.moveNext(); |
| } |
| catch (e:ItemPendingError) |
| { |
| break; |
| } |
| } |
| } |
| |
| /** |
| * @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 modelOffset:int = 0; |
| var firstNode:Boolean = true; |
| |
| if (node == null) |
| { |
| // 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 != null) |
| { |
| 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 = UIDUtil.getUID(node); |
| addParentMapping(uid, parent); |
| if (node != null && |
| openNodes[uid] && |
| hierarchicalData.canHaveChildren(node) && |
| hierarchicalData.hasChildren(node)) |
| { |
| childNodes = getChildren(node); |
| if (childNodes != null) |
| { |
| var childCursor:IViewCursor = childNodes.createCursor(); |
| try |
| { |
| childCursor.seek(CursorBookmark.FIRST); |
| while (!childCursor.afterLast) |
| { |
| if (node is XML) |
| startTrackUpdates(childCursor.current); |
| length += calculateLength(childCursor.current, node) + 1; |
| modelOffset++; |
| |
| try |
| { |
| childCursor.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 += childNodes.length - modelOffset; |
| return length; |
| } |
| } |
| } |
| catch (e:ItemPendingError) |
| { |
| // assume that the child collection has one item |
| length += 1; |
| } |
| } |
| } |
| } |
| return length; |
| } |
| |
| /** |
| * @private |
| */ |
| private function internalRefresh(dispatch:Boolean):Boolean |
| { |
| var obj:Object; |
| var coll:ICollectionView; |
| var needUpdate:Boolean = false; |
| |
| // apply filter function to all the collections including the child collections |
| if (!(treeData.filterFunction == null && filterFunction == null)) |
| { |
| treeData.filterFunction = filterFunction; |
| treeData.refresh(); |
| needUpdate = true; |
| } |
| |
| for each(obj in openNodes) |
| { |
| coll = getChildren(obj); |
| if (coll && !(coll.filterFunction == null && filterFunction == null)) |
| { |
| coll.filterFunction = filterFunction; |
| coll.refresh(); |
| needUpdate = true |
| } |
| } |
| |
| // if filter is applied to any collection, only then update the length |
| if (needUpdate) |
| updateLength(); // length will change after filtering, so update it. |
| |
| |
| // apply sort to all the collections including the child collections |
| if (sortCanBeApplied(treeData) && !(treeData.sort == null && sort == null)) |
| { |
| treeData.sort = sort; |
| treeData.refresh(); |
| dispatch = true; |
| } |
| |
| // recursive sort for every field |
| for each(obj in openNodes) |
| { |
| |
| coll = getChildren(obj); |
| |
| if (coll && sortCanBeApplied(coll) && !(coll.sort == null && sort == null)) |
| { |
| coll.sort = sort; |
| coll.refresh(); |
| dispatch = true; |
| } |
| } |
| |
| // No concept of a sort level, so commenting the code |
| /* for(var i:int = 0;i<sort.fields.length;i++) |
| { |
| if(sort.fields[i].hasOwnProperty("sortLevel")) |
| sortLevel = sort.fields[i].sortLevel; |
| //else |
| //sortLevel = 1; |
| |
| |
| if(sortLevel == 1) |
| { |
| var srt:ISort = new Sort(); |
| |
| if(sort.usingCustomCompareFunction) |
| { |
| srt.compareFunction = sort.compareFunction; |
| } |
| |
| srt.unique = sort.unique; |
| |
| srt.fields = [sort.fields[i]]; |
| treeData.sort = srt; |
| treeData.refresh(); |
| } |
| else |
| { |
| for each(var obj:Object in openNodes) |
| { |
| |
| var depth:int = 2; |
| var o:Object = obj; |
| while(getParentItem(o) != null) |
| { |
| depth++; |
| o = getParentItem(o); |
| } |
| |
| if(depth == sortLevel) |
| { |
| var coll:ICollectionView = getChildren(obj); |
| |
| var srt:ISort = new Sort(); |
| |
| if(sort.usingCustomCompareFunction) |
| srt.compareFunction = sort.compareFunction; |
| |
| srt.unique = sort.unique; |
| |
| srt.fields = [sort.fields[i]]; |
| |
| coll.sort = srt; |
| coll.refresh(); |
| } |
| } |
| } |
| } */ |
| |
| if (dispatch) |
| { |
| var refreshEvent:CollectionEvent = |
| new CollectionEvent(CollectionEvent.COLLECTION_CHANGE); |
| refreshEvent.kind = CollectionEventKind.REFRESH; |
| dispatchEvent(refreshEvent); |
| } |
| return true; |
| } |
| |
| /** |
| * @private |
| * Check if a collection has the properties on which the sort is applied. |
| */ |
| private function sortCanBeApplied(coll:ICollectionView):Boolean |
| { |
| if (sort == null) |
| return true; |
| |
| // get the current item |
| var obj:Object = coll.createCursor().current; |
| |
| if (!obj || !sort.fields) |
| return false; |
| |
| // check for the properties (sort fields) in the current object |
| for (var i:int = 0; i < sort.fields.length; i++) |
| { |
| var sf:SortField = sort.fields[i]; |
| if (!obj.hasOwnProperty(sf.name)) |
| return false; |
| } |
| return true; |
| } |
| |
| /** |
| * @private |
| * Force a recalculation of length |
| */ |
| private function updateLength():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 = UIDUtil.getUID(node); |
| addParentMapping(uid, parent); |
| if (openNodes[uid] != null && |
| hierarchicalData.canHaveChildren(node) && |
| hierarchicalData.hasChildren(node)) |
| { |
| childNodes = getChildren(node); |
| if (childNodes != null) |
| { |
| var cursor:IViewCursor = childNodes.createCursor(); |
| while (!cursor.afterLast) |
| { |
| getVisibleNodes(cursor.current, node, nodeArray); |
| try |
| { |
| cursor.moveNext(); |
| } |
| catch (e:ItemPendingError) |
| { |
| // Items are pending - so return |
| return; |
| } |
| } |
| } |
| } |
| } |
| |
| /** |
| * @private |
| * Factor in the available 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; |
| try |
| { |
| modelCursor.moveNext(); |
| } |
| catch (e:ItemPendingError) |
| { |
| // just return the location as the items are pending |
| return newLocation; |
| } |
| } |
| 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; |
| try |
| { |
| cursor.moveNext(); |
| } |
| catch (e:ItemPendingError) |
| { |
| break; |
| } |
| } |
| target = parent; |
| parent = getParentItem(parent); |
| } |
| cursor = treeData.createCursor(); |
| while (!cursor.afterLast) |
| { |
| if (cursor.current == target) |
| { |
| newLocation++; |
| break; |
| } |
| newLocation += calculateLength(cursor.current, parent) + 1; |
| try |
| { |
| cursor.moveNext(); |
| } |
| catch (e:ItemPendingError) |
| { |
| break; |
| } |
| } |
| return newLocation; |
| } |
| |
| //-------------------------------------------------------------------------- |
| // |
| // Event handlers |
| // |
| //-------------------------------------------------------------------------- |
| |
| /** |
| * @private |
| */ |
| public function collectionChangeHandler(event:CollectionEvent):void |
| { |
| var i:int; |
| var n:int; |
| var node:Object; |
| |
| var convertedEvent:CollectionEvent; |
| |
| if (event is CollectionEvent) |
| { |
| var ce:CollectionEvent = CollectionEvent(event); |
| if (ce.kind == CollectionEventKind.REFRESH) |
| { |
| // collection refreshed, update length |
| updateLength(); |
| } |
| else if (ce.kind == CollectionEventKind.RESET) |
| { |
| // initialize the collection again - its source is modified |
| if (hierarchicalData) |
| initializeCollection(hierarchicalData.getRoot(), hierarchicalData, openNodes); |
| updateLength(); |
| internalRefresh(false); |
| 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); |
| |
| n = convertedEvent.items.length; |
| for (i = 0; i < n; i++) |
| { |
| deleteParentMapping(UIDUtil.getUID(convertedEvent.items[i])); |
| } |
| } |
| 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; |
| if (node is XML) |
| stopTrackUpdates(node); |
| getVisibleNodes(node, null, convertedEvent.items); |
| } |
| |
| // prune the replacements from this list |
| for (i = 0; i < n; i++) |
| { |
| node = ce.items[i].oldValue; |
| var replacedNodePosition:int = convertedEvent.items.indexOf(node); |
| if(replacedNodePosition != -1) |
| convertedEvent.items.splice(replacedNodePosition, 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 parentOfChangingNode:Object; |
| var changingNode:Object; |
| var convertedEvent:CollectionEvent; |
| |
| if (event is CollectionEvent) |
| { |
| var ce:CollectionEvent = CollectionEvent(event); |
| if (ce.kind == CollectionEventKind.mx_internal::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++) |
| { |
| changingNode = ce.items[i]; |
| if (changingNode is XML) |
| startTrackUpdates(changingNode); |
| parentOfChangingNode = getParentItem(changingNode); |
| if (parentOfChangingNode != null) |
| getVisibleNodes(changingNode, parentOfChangingNode, convertedEvent.items); |
| } |
| convertedEvent.location = getVisibleLocationInSubCollection(parentOfChangingNode, 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++) |
| { |
| changingNode = ce.items[i]; |
| if (changingNode is XML) |
| stopTrackUpdates(changingNode); |
| parentOfChangingNode = getParentItem(changingNode); |
| if (parentOfChangingNode != null) |
| getVisibleNodes(changingNode, parentOfChangingNode, convertedEvent.items); |
| } |
| convertedEvent.location = getVisibleLocationInSubCollection(parentOfChangingNode, 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++) |
| { |
| changingNode = ce.items[i].oldValue; |
| parentOfChangingNode = getParentItem(changingNode); |
| |
| if (parentOfChangingNode != null) |
| { |
| getVisibleNodes(changingNode, parentOfChangingNode, convertedEvent.items); |
| // update the parent of the new item only |
| // if the parent node is opened |
| if (_openNodes[UIDUtil.getUID(parentOfChangingNode)] != null) |
| { |
| addParentMapping(UIDUtil.getUID(ce.items[i].newValue), parentOfChangingNode); |
| } |
| } |
| } |
| |
| // prune the replacements from this list |
| for (i = 0; i < n; i++) |
| { |
| changingNode = ce.items[i].oldValue; |
| if (changingNode is XML) |
| stopTrackUpdates(changingNode); |
| |
| var replacedNodePosition:int = convertedEvent.items.indexOf(changingNode); |
| if(replacedNodePosition != -1) |
| convertedEvent.items.splice(replacedNodePosition, 1); |
| } |
| if (convertedEvent.items.length) |
| { |
| currentLength -= convertedEvent.items.length; |
| // nobody cares about location yet. |
| dispatchEvent(convertedEvent); |
| } |
| dispatchEvent(event); |
| |
| for (i = 0; i < n; i++) |
| { |
| changingNode = ce.items[i].oldValue; |
| parentOfChangingNode = getParentItem(changingNode); |
| |
| if (parentOfChangingNode != null && _openNodes[UIDUtil.getUID(parentOfChangingNode)] != null) |
| deleteParentMapping(UIDUtil.getUID(changingNode)); |
| } |
| } |
| 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); |
| } |
| } |
| } |
| |
| /** |
| * @private |
| * Called whenever an XML object contained in a 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 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; |
| if (list && !list.busy()) |
| { |
| if (childrenMap[q] === treeData) |
| { |
| children = treeData as XMLListCollection; |
| if (parentNode != null) |
| { |
| children.mx_internal::dispatchResetEvent = false; |
| children.source = parentNode.*; |
| children.refresh(); |
| } |
| } |
| 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; |
| } |
| } |
| break; |
| } |
| |
| /* needed? |
| case "nodeChanged": |
| { |
| prop = value.localName(); |
| oldValue = detail; |
| newValue = value; |
| break; |
| } |
| */ |
| |
| 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.mx_internal::dispatchResetEvent = false; |
| children.source = parentNode.*; |
| children.refresh(); |
| } |
| } |
| 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); |
| } |
| } |
| |
| } |