//////////////////////////////////////////////////////////////////////////////// // // 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 { import flash.events.Event; import flash.events.EventDispatcher; import mx.collections.IList; import mx.collections.errors.ItemPendingError; import mx.events.CollectionEvent; import mx.events.CollectionEventKind; import mx.events.PropertyChangeEvent; import mx.events.PropertyChangeEventKind; import mx.resources.IResourceManager; import mx.resources.ResourceManager; import mx.rpc.IResponder; [Event(name="collectionChange", type="mx.events.CollectionEvent")] /** * An IList whose items are fetched asynchronously by a user provided function. The * loadItemsFunction initiates an asynchronous request for a pageSize block items, typically * from a web service. When the request sucessfully completes, the storeItemsAt() method * must be called. If the request fails, then failItemsAt(). * *
PagedList divides its length
items into pageSize
blocks or
* "pages". It tracks which items exist locally, typically because they've been stored with
* storeItemsAt(). When an item that does not exist locally is requested with getItemAt(),
* the loadItemsFunction is called and then an IPE is thrown. When the loadItemsFunction
* either completes or fails, it must call storeItemsAt() or failItemsAt() which causes
* the IPE's responders to run and a "replace" CollectionEvent to be dispatched for the
* updated page. The failItemsAt() method resets the corresponding items to undefined,
* which means that subsequent calls to getItemAt() will cause an IPE to be thrown.
Unlike some other IList implementations, the only method here that can thrown an * IPE is getItemAt(). Methods like getItemIndex() and toArray() just report items * that aren't local as null.
* *This class is intended to be used as the "list" source for an ASyncListView.
*/ public class PagedList extends EventDispatcher implements IList { /** * @private */ private static function get resourceManager():IResourceManager { return ResourceManager.getInstance(); } /** * @private */ private static function checkItemIndex(index:int, listLength:int):void { if (index < 0 || (index >= listLength)) { const message:String = resourceManager.getString("collections", "outOfBounds", [ index ]); throw new RangeError(message); } } /** * @private * The IList's items. */ private const data:Vector.<*> = new Vector.<*>(); /** * Construct a PagedList with the specified length and pageSize. */ public function PagedList(length:int=1000, pageSize:int=10) { this.data.length = length; this.pageSize = pageSize; for (var i:int = 0; i < data.length; i++) data[i] = undefined; } //---------------------------------- // loadItemsFunction //---------------------------------- private var _loadItemsFunction:Function = null; /** * The value of this property must be a function that loads a contiguous * block of items and then callsstoreItemsAt()
or
* failItemsAt()
. A loadItemsFunction must be defined as follows:
* * myLoadItems(list:PagedList, index:int, count:int):void ** *
Typically the loadItemsFunction will make one or more network requests * to retrieve the items. It must do all of its work asynchronously to avoid * blocking the application's GUI. * */ public function get loadItemsFunction():Function { return _loadItemsFunction; } /** * @private */ public function set loadItemsFunction(value:Function):void { _loadItemsFunction = value; } //---------------------------------- // length //---------------------------------- [Bindable("collectionChange")] /** * The number of items in the list. * *
The length of the list can be changed directly however the "-1" indeterminate * length value is not supported.
*/ public function get length():int { return data.length; } /** * @private */ public function set length(value:int):void { const oldLength:int = data.length; const newLength:int = value; if (oldLength == newLength) return; var ce:CollectionEvent = null; if (hasEventListener(CollectionEvent.COLLECTION_CHANGE)) ce = new CollectionEvent(CollectionEvent.COLLECTION_CHANGE); if (oldLength < newLength) { if (ce) { ce.location = Math.max(oldLength - 1, 0); ce.kind = CollectionEventKind.ADD; const itemsLength:int = newLength - oldLength; for (var i:int = 0; i < itemsLength; i++) ce.items.push(undefined); } data.length = newLength; for (var newIndex:int = Math.max(oldLength - 1, 0); newIndex < newLength; newIndex++) data[newIndex] = undefined; } else // oldLength > newLength { if (ce) { ce.location = Math.max(newLength - 1, 0); ce.kind = CollectionEventKind.REMOVE; for (var oldIndex:int = Math.max(newLength - 1, 0); oldIndex < oldLength; oldIndex++) ce.items.push(data[oldIndex]); } data.length = newLength; } if (ce) dispatchEvent(ce); } //---------------------------------- // pageSize //---------------------------------- private var _pageSize:int = 10; /** * Items are loaded in contiguous pageSize blocks. The value of this property should be greater than * zero, smaller than the PageList's length, and a reasonable working size for the loadItemsFunction. */ public function get pageSize():int { return _pageSize; } /** * @private */ public function set pageSize(value:int):void { _pageSize = value; } /** * Resets the entire list to its initial state. All local and pending items are * cleared. */ public function clearItems():void { var index:int = 0; for each (var item:Object in data) data[index++] = undefined; if (hasEventListener(CollectionEvent.COLLECTION_CHANGE)) { var ce:CollectionEvent = new CollectionEvent(CollectionEvent.COLLECTION_CHANGE); ce.kind = CollectionEventKind.RESET; dispatchEvent(ce); } } /** * @private */ private static function createUpdatePCE(itemIndex:Object, oldValue:Object, newValue:Object):PropertyChangeEvent { const pce:PropertyChangeEvent = new PropertyChangeEvent(PropertyChangeEvent.PROPERTY_CHANGE); pce.kind = PropertyChangeEventKind.UPDATE; pce.property = itemIndex; pce.oldValue = oldValue; pce.newValue = newValue; return pce; } /** * @private */ private static function createCE(kind:String, location:int, item:Object):CollectionEvent { const ce:CollectionEvent = new CollectionEvent(CollectionEvent.COLLECTION_CHANGE); ce.kind = kind; ce.location = location; if (item is Array) ce.items = item as Array; else ce.items.push(item); return ce; } /** * This method must be called by the loadItemsFunction after a block of requested * items have been successfully retrieved. It stores the specified items in the * internal data vector and clears the "pending" state associated with the original * request. */ public function storeItemsAt(items:Vector.