| //////////////////////////////////////////////////////////////////////////////// |
| // |
| // 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.Event; |
| import flash.utils.getQualifiedClassName; |
| |
| import mx.collections.errors.ItemPendingError; |
| import mx.core.mx_internal; |
| import mx.events.CollectionEvent; |
| import mx.events.CollectionEventKind; |
| import mx.events.PropertyChangeEvent; |
| import mx.events.PropertyChangeEventKind; |
| import mx.utils.OnDemandEventDispatcher; |
| |
| use namespace mx_internal; // for mx_internal functions pendingItemSucceeded,Failed() |
| |
| /** |
| * Dispatched when the list's length has changed or when a list |
| * element is replaced. |
| * |
| * @eventType mx.events.CollectionEvent.COLLECTION_CHANGE |
| * |
| * @langversion 3.0 |
| * @playerversion Flash 9 |
| * @playerversion AIR 1.1 |
| * @productversion Flex 3 |
| */ |
| [Event(name="collectionChange", type="mx.events.CollectionEvent")] |
| |
| /** |
| * The AsyncListView class is an implementation of the IList interface |
| * that handles ItemPendingErrors errors |
| * thrown by the <code>getItemAt()</code>, <code>removeItemAt()</code>, |
| * and <code>toArray()</code> methods. |
| * |
| * <p>The <code>getItemAt()</code> method handles ItemPendingErrors by returning a provisional |
| * "pending" item until the underlying request succeeds or fails. The provisional |
| * item is produced by calling the function specified by the <code>createPendingItemFunction</code> |
| * property. . If the request |
| * succeeds, the actual item replaces the provisional one. |
| * If it fails, the provisional item is replaced with the item returned by calling |
| * the function specified by the <code>createFailedItemFunction</code> property.</p> |
| * |
| * <p>This class delegates the IList methods and properties to its <code>list</code>. |
| * If a list isn't specified, methods that mutate the collection are no-ops, |
| * and methods that query the collection return an empty value, such as null or zero |
| * as appropriate.</p> |
| * |
| * <p>This class is intended to be used with Spark components based on DataGroup, |
| * such as List and ComboBox. The Spark classes do not provide intrinsic support for |
| * ItemPendingError handling.</p> |
| * |
| * <p>AsyncListView does not support re-insertion of pending or failed items. Once |
| * a failed or pending item is removed, its connection to a pending request for data |
| * is lost. Using drag and drop to move a pending item in an ASyncListView, or sorting |
| * an ASyncListView that contains pending or failed items, is not supported because |
| * these operations remove and then re-insert list items.</p> |
| * |
| * @mxml |
| * |
| * <p>The <code><mx:AsyncListView></code> tag inherits all the attributes of its |
| * superclass, and adds the following attributes:</p> |
| * |
| * <pre> |
| * <mx:AsyncListView |
| * <b>Properties</b> |
| * createFailedItemFunction="null" |
| * createPendingItemFunction="null" |
| * list="null" |
| * /> |
| * </pre> |
| * |
| * @langversion 3.0 |
| * @playerversion Flash 10 |
| * @playerversion AIR 1.5 |
| * @productversion Flex 4 |
| */ |
| public class AsyncListView extends OnDemandEventDispatcher implements IList |
| { |
| /** |
| * Constructor. |
| * |
| * @param list Initial value of the list property, the IList we're delegating to. |
| * |
| * @langversion 3.0 |
| * @playerversion Flash 10 |
| * @playerversion AIR 1.5 |
| * @productversion Flex 4 |
| */ |
| public function AsyncListView(list:IList = null) |
| { |
| super(); |
| this.list = list; |
| } |
| |
| //-------------------------------------------------------------------------- |
| // |
| // Properties |
| // |
| //-------------------------------------------------------------------------- |
| |
| //---------------------------------- |
| // list |
| //---------------------------------- |
| |
| private var _list:IList; |
| |
| [Inspectable(category="General")] |
| [Bindable("listChanged")] |
| |
| /** |
| * The IList object that this collection wraps. That means the object to which all of |
| * the IList methods are delegated. |
| * |
| * <p>If this property is null, the IList mutation methods, such as <code>setItemAt()</code>, |
| * are no-ops. The IList query methods, such <code>getItemAt()</code>, return null |
| * or zero (-1 for <code>getItemIndex()</code>), as appropriate.</p> |
| * |
| * @default null |
| * |
| * @langversion 3.0 |
| * @playerversion Flash 10 |
| * @playerversion AIR 1.5 |
| * @productversion Flex 4 |
| */ |
| public function get list():IList |
| { |
| return _list; |
| } |
| |
| /** |
| * @private |
| */ |
| public function set list(value:IList):void |
| { |
| if (_list == value) |
| return; |
| |
| deleteAllPendingResponders(); |
| oldLength = -1; |
| if (_list) |
| _list.removeEventListener(CollectionEvent.COLLECTION_CHANGE, handleCollectionChangeEvent); |
| _list = value; |
| if (_list) |
| { |
| _list.addEventListener(CollectionEvent.COLLECTION_CHANGE, handleCollectionChangeEvent, false, 0, true); |
| oldLength = _list.length; |
| } |
| |
| dispatchEvent(new Event("listChanged")); |
| dispatchEvent(new CollectionEvent(CollectionEvent.COLLECTION_CHANGE, false, false, CollectionEventKind.RESET)); |
| } |
| |
| /** |
| * @private |
| */ |
| private function deleteAllPendingResponders():void |
| { |
| for each (var responder:ListItemResponder in pendingResponders) |
| { |
| if (responder) |
| responder.index = -1; |
| } |
| pendingResponders.length = 0; |
| failedItems.length = 0; |
| } |
| |
| /** |
| * The previous known length of the list before handling a CollectionEvent. |
| * oldLength is updated by the list setter and isValidCollectionEvent(). |
| */ |
| private var oldLength:int = -1; |
| |
| /** |
| * This method checks the validity of incoming CollectionEvents. |
| * In some cases, a CollectionEvent from the underlying list may have already |
| * been received once, or have been erroneously dispatched (See SDK-30594). |
| * Thus, we check the incoming event's location against the last known length' |
| * of the list (oldLength). |
| * |
| * <p>Returns false if the index is less than 0 or greater than the previous |
| * length of the list. |
| * This only applies to ADD, REMOVE, REPLACE, and MOVE CollectionEvents. |
| * It also updates oldLength to be the current length of the list, so it |
| * should not be called twice.</p> |
| */ |
| private function isValidCollectionEvent(ce:CollectionEvent):Boolean |
| { |
| if (oldLength < 0) |
| return true; |
| |
| const location:int = ce.location; |
| |
| switch (ce.kind) |
| { |
| case CollectionEventKind.ADD: |
| { |
| if (location < 0 || location > oldLength) |
| return false; |
| break; |
| } |
| |
| case CollectionEventKind.REMOVE: |
| case CollectionEventKind.REPLACE: |
| case CollectionEventKind.MOVE: |
| { |
| if (location < 0 || location >= oldLength) |
| return false; |
| break; |
| } |
| } |
| |
| oldLength = length; |
| return true; |
| } |
| |
| /** |
| * @private |
| * Fixup the pendingResponders and failedItems arrays after a change to the list. |
| * Generally speaking, if a list[index] item changes, the pending responder for |
| * that index is no longer needed. |
| * |
| * All "collectionChange" events are redispatched to the AsyncListView listeners. |
| */ |
| private function handleCollectionChangeEvent(ce:CollectionEvent):void |
| { |
| if (!isValidCollectionEvent(ce)) |
| return; |
| |
| switch (ce.kind) |
| { |
| case CollectionEventKind.REPLACE: |
| case CollectionEventKind.UPDATE: |
| deletePendingResponders(ce); |
| break; |
| |
| case CollectionEventKind.MOVE: |
| movePendingResponders(ce); |
| break; |
| |
| case CollectionEventKind.ADD: |
| shiftPendingRespondersRight(ce); |
| break; |
| |
| case CollectionEventKind.REMOVE: |
| shiftPendingRespondersLeft(ce); |
| break; |
| |
| case CollectionEventKind.RESET: |
| case CollectionEventKind.REFRESH: |
| deleteAllPendingResponders(); |
| break; |
| } |
| |
| dispatchEvent(ce); // redispatch to CollectionEvent listeners on this |
| } |
| |
| /** |
| * @private |
| * Delete the ListItemResponder at the specified index, if any. |
| * If a pending responder exists, return its item. |
| * |
| * This method assumes that the responder hasn't run yet, it sets |
| * the ListItemResponder index to -1 to prevent it from updating |
| * this AsyncListView later. |
| */ |
| private function deletePendingResponder(index:int):Object |
| { |
| if ((index < 0) || (index >= pendingResponders.length)) |
| return null; |
| |
| const pendingResponder:ListItemResponder = pendingResponders[index]; |
| if (pendingResponder) |
| { |
| delete pendingResponders[index]; |
| ListItemResponder(pendingResponder).index = -1; |
| return pendingResponder.item; |
| } |
| |
| return null; |
| } |
| |
| /** |
| * @private |
| * Handler for a CollectionEventKind.UPDATE or REPLACE event. In either |
| * case a contiguous block of items (ce.items) beginning with index=ce.location |
| * has been changed. If there are any pending requests for these indices, we |
| * assume they're no longer valid, i.e. we assume that getItemAt() should no longer |
| * return the pending item. Likewise for failed items. |
| */ |
| private function deletePendingResponders(ce:CollectionEvent):void |
| { |
| var index:int = ce.location; |
| for each (var item:Object in ce.items) |
| { |
| deletePendingResponder(index); |
| delete failedItems[index]; |
| index += 1; |
| } |
| } |
| |
| /** |
| * @private |
| * Handler for a CollectionEventKind.MOVE event. The event indicates that a |
| * contiguous block of items (ce.items), beginning with index=ce.oldLocation, |
| * has been moved to ce.location. If pendingRequests already exist at ce.location, |
| * they're deleted first. |
| */ |
| private function movePendingResponders(ce:CollectionEvent):void |
| { |
| var fromIndex:int = ce.oldLocation; |
| var toIndex:int = ce.location; |
| for each (var item:Object in ce.items) |
| { |
| var pendingResponder:ListItemResponder = pendingResponders[fromIndex]; |
| if (pendingResponder) |
| { |
| delete pendingResponders[fromIndex]; |
| ListItemResponder(pendingResponder).index = toIndex; |
| deletePendingResponder(toIndex); // in case we're copying over a pending request |
| pendingResponders[toIndex] = pendingResponder; |
| } |
| |
| var failedItem:* = failedItems[fromIndex]; |
| if (failedItem !== undefined) |
| { |
| delete failedItems[fromIndex]; |
| failedItems[toIndex] = failedItem; |
| } |
| |
| fromIndex += 1; |
| toIndex += 1; |
| } |
| } |
| |
| /** |
| * @private |
| * Handler for a CollectionEventKind.ADD. The event indicates |
| * that a block of ce.items.length items starting at ce.location was inserted, |
| * which implies that all of the pendingResponders whose index is greater than or |
| * equal to ce.location, must be shifted right by ce.items.length. The failedItems |
| * array is handled similarly. |
| */ |
| private function shiftPendingRespondersRight(ce:CollectionEvent):void |
| { |
| const delta:int = ce.items.length; |
| const startIndex:int = ce.location; |
| |
| const pendingRespondersCopy:Array = sparseCopy(pendingResponders); |
| pendingResponders.length = 0; |
| for each (var responder:ListItemResponder in pendingRespondersCopy) |
| { |
| if (responder.index >= startIndex) |
| responder.index += delta; |
| pendingResponders[responder.index] = responder; |
| } |
| |
| for (var index:int = failedItems.length - 1; index >= startIndex; index--) |
| { |
| var failedItem:* = failedItems[index]; |
| if (failedItem !== undefined) |
| { |
| delete failedItems[index]; |
| failedItems[index + delta] = failedItem; |
| } |
| } |
| } |
| |
| /** |
| * @private |
| * Handler for a CollectionEventKind.REMOVE. The event indicates |
| * that a block of ce.items.length items starting at ce.location was removed, |
| * which implies that all of the pendingResponders whose index is greater than or |
| * equal to ce.location, must be shifted left by ce.items.length. The failedItems |
| * array is handled similarly. |
| */ |
| private function shiftPendingRespondersLeft(ce:CollectionEvent):void |
| { |
| const delta:int = ce.items.length; |
| const startIndex:int = ce.location + delta; |
| |
| const pendingRespondersCopy:Array = sparseCopy(pendingResponders); |
| pendingResponders.length = 0; |
| for each (var responder:ListItemResponder in pendingRespondersCopy) |
| { |
| if (responder.index >= startIndex) |
| responder.index -= delta; |
| pendingResponders[responder.index] = responder; |
| } |
| |
| const failedItemsLength:int = failedItems.length; |
| for (var index:int = startIndex; index < failedItemsLength; index++) |
| { |
| var failedItem:* = failedItems[index]; |
| if (failedItem !== undefined) |
| { |
| delete failedItems[index]; |
| failedItems[index - delta] = failedItem; |
| } |
| } |
| } |
| |
| /** |
| * Applying concat() to a sparse array produces a new array that's |
| * not sparse, nulls replace items that were undefined. Although the |
| * result of this method is not sparse, it only includes items that |
| * were in the original array. |
| */ |
| private function sparseCopy(a:Array):Array |
| { |
| const r:Array = []; |
| var index:int = 0; |
| for each (var item:* in a) |
| { |
| if (item !== undefined) |
| r[index++] = item; |
| } |
| return r; |
| } |
| |
| //---------------------------------- |
| // createPendingItemFunction |
| //---------------------------------- |
| |
| private var _createPendingItemFunction:Function = defaultCreatePendingItemFunction; |
| |
| /** |
| * @private |
| */ |
| private function defaultCreatePendingItemFunction(index:int, ipe:ItemPendingError):Object |
| { |
| return null; |
| } |
| |
| /** |
| * A callback function used to create a provisional item when |
| * the initial request causes an <code>ItemPendingError</code> to be thrown. |
| * If the request eventually succeeds, the provisional item is automatically |
| * replaced by the actual item. If the request fails, then the item is replaced |
| * with one created with the callback function specified by the |
| * <code>createFailedItemFunction</code> property. |
| * |
| * <p>The value of this property must be a function with two parameters: the index |
| * of the requested data provider item, and the ItemPendingError itself. In most |
| * cases, the second parameter can be ignored. |
| * The following example shows an implementation of the callback function: |
| * |
| * <pre> |
| * function createPendingItem(index:int, ipe:ItemPendingError):Object |
| * { |
| * return "[" + index + "request is pending...]"; |
| * } |
| * </pre> |
| * </p> |
| * |
| * <p>Setting this property does not affect provisional pending items that were already |
| * created. Setting this property to null prevents provisional pending items |
| * from being created.</p> |
| * |
| * @default A function that unconditionally returns null. |
| * @see #getItemAt() |
| * @see #createFailedItemFunction |
| * @see mx.collections.errors.ItemPendingError |
| * |
| * @langversion 3.0 |
| * @playerversion Flash 10 |
| * @playerversion AIR 1.5 |
| * @productversion Flex 4 |
| */ |
| public function get createPendingItemFunction():Function |
| { |
| return _createPendingItemFunction; |
| } |
| |
| /** |
| * @private |
| */ |
| public function set createPendingItemFunction(value:Function):void |
| { |
| _createPendingItemFunction = value; |
| } |
| |
| |
| //---------------------------------- |
| // createFailedItemFunction |
| //---------------------------------- |
| |
| private var _createFailedItemFunction:Function = defaultCreateFailedItemFunction; |
| |
| /** |
| * @private |
| */ |
| private function defaultCreateFailedItemFunction(index:int, info:Object):Object |
| { |
| return null; |
| } |
| |
| /** |
| * A callback function used to create a substitute item when |
| * a request which had caused an <code>ItemPendingError</code> to be thrown, |
| * subsequently fails. The existing item, typically a pending item created |
| * by the callback function specified by the <code>createPendingItemFunction()</code> property, |
| * is replaced with the failed item. |
| * |
| * <p>The value of this property must be a function with two parameters: the index |
| * of the requested item, and the failure "info" object, which is |
| * passed along from the IResponder <code>fault()</code> method. |
| * In most cases you can ignore the second parameter. |
| * Shown below is an example implementation of the callback function:</p> |
| * |
| * <pre> |
| * function createFailedItem(index:int, info:Object):Object |
| * { |
| * return "[" + index + "request failed]"; |
| * } |
| * </pre> |
| * |
| * |
| * <p>Setting this property does not affect failed items that were already |
| * created. Setting this property to null prevents failed items from being created. |
| * </p> |
| * |
| * @default A function that unconditionally returns null. |
| * @see #getItemAt() |
| * @see #createPendingItemFunction |
| * @see mx.rpc.IResponder#fault |
| * |
| * @langversion 3.0 |
| * @playerversion Flash 10 |
| * @playerversion AIR 1.5 |
| * @productversion Flex 4 |
| */ |
| public function get createFailedItemFunction():Function |
| { |
| return _createFailedItemFunction; |
| } |
| |
| /** |
| * @private |
| */ |
| public function set createFailedItemFunction(value:Function):void |
| { |
| _createFailedItemFunction = value; |
| } |
| |
| //-------------------------------------------------------------------------- |
| // |
| // Methods |
| // |
| //-------------------------------------------------------------------------- |
| |
| private const pendingResponders:Array = []; |
| private const failedItems:Array = []; |
| |
| /** |
| * @private |
| * Called by the ListItemProvider/result() method when a pending request |
| * completes successfully. |
| * |
| * @param index The item's index. |
| * @param info The informational object passed to IResponder/result(). |
| * @see mx.rpc.IResponder#result |
| */ |
| mx_internal function pendingRequestSucceeded(index:int, info:Object):void |
| { |
| delete pendingResponders[index]; |
| } |
| |
| /** |
| * @private |
| * Called by the ListItemProvider/fault() method when a pending request |
| * fails. |
| * |
| * @param index The item's index. |
| * @param info The informational object passed to IResponder/fault(). |
| * @see mx.rpc.IResponder#fault |
| */ |
| mx_internal function pendingRequestFailed(index:int, info:Object):void |
| { |
| delete pendingResponders[index]; |
| |
| if (createFailedItemFunction === null) |
| return; |
| |
| const item:Object = createFailedItemFunction(index, info); |
| failedItems[index] = item; |
| |
| // dispatch collection and property change events |
| |
| const hasCollectionListener:Boolean = hasEventListener(CollectionEvent.COLLECTION_CHANGE); |
| const hasPropertyListener:Boolean = hasEventListener(PropertyChangeEvent.PROPERTY_CHANGE); |
| var pce:PropertyChangeEvent; |
| |
| if (hasCollectionListener || hasPropertyListener) |
| { |
| pce = new PropertyChangeEvent(PropertyChangeEvent.PROPERTY_CHANGE); |
| pce.kind = PropertyChangeEventKind.UPDATE; |
| pce.oldValue = null; |
| pce.newValue = item; |
| pce.property = index; |
| } |
| |
| if (hasCollectionListener) |
| { |
| var ce:CollectionEvent = new CollectionEvent(CollectionEvent.COLLECTION_CHANGE); |
| ce.kind = CollectionEventKind.REPLACE; |
| ce.location = index; |
| ce.items.push(pce); |
| dispatchEvent(ce); |
| } |
| |
| if (hasPropertyListener) |
| dispatchEvent(pce); |
| } |
| |
| |
| //-------------------------------------------------------------------------- |
| // |
| // IList Implementation |
| // |
| //-------------------------------------------------------------------------- |
| |
| [Bindable("collectionChange")] |
| |
| /** |
| * @inheritDoc |
| * |
| * @langversion 3.0 |
| * @playerversion Flash 10 |
| * @playerversion AIR 1.5 |
| * @productversion Flex 4 |
| */ |
| public function get length():int |
| { |
| try |
| { |
| return (list) ? list.length : 0; |
| } |
| catch (ignore:ItemPendingError) |
| { |
| // The mx.data DataList class can throw an IPE here. We ignore it because |
| // when the length is determined, a CollectionChanged event will be |
| // be dispatched. See handleCollectionChangeEvent(). |
| } |
| |
| return 0; |
| } |
| |
| /** |
| * @inheritDoc |
| * |
| * @langversion 3.0 |
| * @playerversion Flash 10 |
| * @playerversion AIR 1.5 |
| * @productversion Flex 4 |
| */ |
| public function addItem(item:Object):void |
| { |
| if (list) |
| { |
| try |
| { |
| list.addItem(item); |
| } |
| catch (ignore:ItemPendingError) |
| { |
| // The mx.data DataList class can throw an IPE here. We ignore it because |
| // when the item is actually added, a CollectionChanged event will be |
| // be dispatched. See handleCollectionChangeEvent(). |
| } |
| } |
| } |
| |
| /** |
| * @inheritDoc |
| * |
| * @langversion 3.0 |
| * @playerversion Flash 10 |
| * @playerversion AIR 1.5 |
| * @productversion Flex 4 |
| */ |
| public function addItemAt(item:Object, index:int):void |
| { |
| if (list) |
| { |
| try |
| { |
| list.addItemAt(item, index); |
| } |
| catch (ignore:ItemPendingError) |
| { |
| // The mx.data DataList class can throw an IPE here. We ignore it because |
| // when the item is actually added, a CollectionChanged event will be |
| // be dispatched. See handleCollectionChangeEvent(). |
| } |
| } |
| } |
| |
| /** |
| * Returns the value of <code>list.getItemAt(index)</code>. |
| * |
| * <p>This method catches ItemPendingErrors (IPEs) generated as a consequence of |
| * calling <code>getItemAt()</code>. If an IPE is thrown, an <code>IResponder</code> is added to |
| * the IPE and a provisional "pending" item, created with the |
| * <code>createPendingItemFunction</code> is returned. If the underlying request |
| * eventually succeeds, the pending item is replaced with the real item. If it fails, |
| * the pending item is replaced with a value produced by calling |
| * <code>createFailedItemFunction</code>.</p> |
| * |
| * @param index The list index from which to retrieve the item. |
| * |
| * @param prefetch An <code>int</code> indicating both the direction |
| * and number of items to fetch during the request if the item is not local. |
| * |
| * @throws RangeError if <code>index < 0</code> or <code>index >= length</code>. |
| * |
| * @return The list item at the specified index. |
| * |
| * @see #createPendingItemFunction |
| * @see #createFailedItemFunction |
| * @see mx.collections.errors.ItemPendingError |
| * @see mx.rpc.IResponder |
| * |
| * @langversion 3.0 |
| * @playerversion Flash 10 |
| * @playerversion AIR 1.5 |
| * @productversion Flex 4 |
| */ |
| public function getItemAt(index:int, prefetch:int=0):Object |
| { |
| if (!list) |
| return null; |
| |
| const failedItem:* = failedItems[index]; |
| if (failedItem !== undefined) |
| return failedItem; |
| |
| const pendingResponder:ListItemResponder = pendingResponders[index]; |
| if (pendingResponder) |
| return pendingResponder.item; |
| |
| var item:Object = null; |
| try |
| { |
| return list.getItemAt(index, prefetch); |
| } |
| catch (ipe:ItemPendingError) |
| { |
| const createPendingItem:Function = createPendingItemFunction; |
| if (createPendingItem !== null) |
| item = createPendingItem(index, ipe); |
| var responder:ListItemResponder = new ListItemResponder(this, index, item); |
| pendingResponders[index] = responder; |
| ipe.addResponder(responder); |
| } |
| return item; |
| } |
| |
| /** |
| * @inheritDoc |
| * |
| * @langversion 3.0 |
| * @playerversion Flash 10 |
| * @playerversion AIR 1.5 |
| * @productversion Flex 4 |
| */ |
| public function getItemIndex(item:Object):int |
| { |
| const failedItemIndex:int = failedItems.indexOf(item); |
| if (failedItemIndex != -1) |
| return failedItemIndex; |
| |
| for each (var responder:ListItemResponder in pendingResponders) |
| if (responder && responder.item === item) |
| return responder.index; |
| return (list) ? list.getItemIndex(item) : -1; |
| } |
| |
| /** |
| * @inheritDoc |
| * |
| * @langversion 3.0 |
| * @playerversion Flash 10 |
| * @playerversion AIR 1.5 |
| * @productversion Flex 4 |
| */ |
| public function itemUpdated(item:Object, property:Object=null, oldValue:Object=null, newValue:Object=null):void |
| { |
| if (list) |
| list.itemUpdated(item, property, oldValue, newValue); |
| } |
| |
| /** |
| * @inheritDoc |
| * |
| * @langversion 3.0 |
| * @playerversion Flash 10 |
| * @playerversion AIR 1.5 |
| * @productversion Flex 4 |
| */ |
| public function removeAll():void |
| { |
| if (list) |
| list.removeAll(); |
| } |
| |
| /** |
| * Removes the specified item from this list, should it exist. |
| * Relies on ArrayList implementation |
| * |
| * @param item Object reference to the item that should be removed. |
| * @return Boolean indicating if the item was removed. |
| * |
| * @langversion 3.0 |
| * @playerversion Flash 9 |
| * @playerversion AIR 1.1 |
| * @productversion Apache Flex 4.10 |
| */ |
| public function removeItem(item:Object):Boolean |
| { |
| var _item:Object = removeItemAt(getItemIndex(item)); |
| return _item != null; |
| } |
| |
| /** |
| * Removes the actual or pending item at the specified index and returns it. |
| * All items whose index is greater than the specified index |
| * have their index reduced by 1. |
| * |
| * <p>If there is no actual or pending item at the specified index, for |
| * example because a call to <code>getItemAt(index)</code> hasn't caused the data to be |
| * paged in, then the underlying <code>list</code> may throw an ItemPendingError. |
| * The implementation ignores the ItemPendingError and returns null.</p> |
| * |
| * @param index The list index from which to retrieve the item. |
| * |
| * @throws RangeError if <code>index < 0</code> or <code>index >= length</code>. |
| * |
| * @return The item that was removed or null. |
| * |
| * @langversion 3.0 |
| * @playerversion Flash 10 |
| * @playerversion AIR 1.5 |
| * @productversion Flex 4 |
| */ |
| public function removeItemAt(index:int):Object |
| { |
| if (!list) |
| return null; |
| |
| const failedItem:* = failedItems[index]; |
| delete failedItems[index]; |
| const pendingItem:Object = deletePendingResponder(index); |
| try |
| { |
| const actualItem:Object = list.removeItemAt(index); |
| if (failedItem !== undefined) |
| return failedItem; |
| return (pendingItem) ? pendingItem : actualItem; |
| |
| } |
| catch (ipe:ItemPendingError) |
| { |
| // If list[index] doesn't exist yet, an IPE will be thrown. There's nothing |
| // we can do about that, so ignore it. |
| } |
| return (failedItem !== undefined) ? failedItem : pendingItem; |
| } |
| |
| /** |
| * @inheritDoc |
| * |
| * @langversion 3.0 |
| * @playerversion Flash 10 |
| * @playerversion AIR 1.5 |
| * @productversion Flex 4 |
| */ |
| public function setItemAt(item:Object, index:int):Object |
| { |
| if (!list) |
| return null; |
| |
| const failedItem:* = failedItems[index]; |
| const pendingResponder:ListItemResponder = pendingResponders[index]; |
| |
| var setItemValue:Object = null; // return null if IPE |
| try |
| { |
| setItemValue = list.setItemAt(item, index); |
| } |
| catch (ignore:ItemPendingError) |
| { |
| // The mx.data DataList class can throw an IPE here. We ignore it because |
| // when the item is actually changed, a CollectionChanged event will be |
| // be dispatched. See handleCollectionChangeEvent(). |
| } |
| |
| if (failedItem !== undefined) |
| return failedItem; |
| else |
| return (pendingResponder) ? pendingResponder.item : setItemValue; |
| } |
| |
| /** |
| * Returns an array with the same elements as this AsyncListView. The array is initialized |
| * by retrieving each item with <code>getItemAt()</code>, so pending items are substituted where actual |
| * values aren't available yet. The array will not be updated when the AsyncListView replaces |
| * the pending items with actual (or failed) values. |
| * |
| * @return an array with the same elements as this AsyncListView. |
| * |
| * @langversion 3.0 |
| * @playerversion Flash 10 |
| * @playerversion AIR 1.5 |
| * @productversion Flex 4 |
| */ |
| public function toArray():Array |
| { |
| if (!list) |
| return []; |
| |
| const a:Array = new Array(list.length); |
| for(var i:int = 0; i < a.length; i++) |
| a[i] = getItemAt(i); |
| return a; |
| } |
| |
| |
| /** |
| * Returns a string that contains the list's length and the number of pending item requests. |
| * It does not trigger pending requests. |
| * |
| * @return A brief description of the list. |
| * |
| * @langversion 3.0 |
| * @playerversion Flash 10 |
| * @playerversion AIR 1.5 |
| * @productversion Flex 4 |
| */ |
| public function toString():String |
| { |
| var s:String = getQualifiedClassName(this); |
| |
| if (list) |
| { |
| var nRequests:int = 0; |
| for each (var responder:ListItemResponder in pendingResponders) |
| { |
| if (responder) |
| nRequests += 1; |
| } |
| s += " length=" + length + ", " + nRequests + " pending requests"; |
| } |
| else |
| s += " no list"; |
| |
| return s; |
| } |
| |
| } |
| } |
| |
| import mx.rpc.IResponder; |
| import mx.collections.AsyncListView; |
| import mx.core.mx_internal; |
| |
| use namespace mx_internal; // for mx_internal functions pendingItemSucceeded,Failed() |
| |
| class ListItemResponder implements IResponder |
| { |
| private var asyncListView:AsyncListView; |
| public var index:int = -1; |
| public var item:Object = null; |
| |
| public function ListItemResponder(asyncListView:AsyncListView, index:int, item:Object) |
| { |
| super(); |
| this.asyncListView = asyncListView; |
| this.index = index; |
| this.item = item; |
| } |
| |
| public function result(info:Object):void |
| { |
| if (index != -1) |
| asyncListView.pendingRequestSucceeded(index, info); |
| } |
| |
| public function fault(info:Object):void |
| { |
| if (index != -1) |
| asyncListView.pendingRequestFailed(index, info); |
| } |
| } |