blob: 535d2b99b177fdd20c86bec86aa7490e9a8e62ce [file] [log] [blame]
////////////////////////////////////////////////////////////////////////////////
//
// 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.core
{
import flash.display.DisplayObject;
import flash.events.Event;
import mx.collections.ArrayCollection;
import mx.collections.CursorBookmark;
import mx.collections.ICollectionView;
import mx.collections.IList;
import mx.collections.IViewCursor;
import mx.collections.ItemResponder;
import mx.collections.ListCollectionView;
import mx.collections.XMLListCollection;
import mx.collections.errors.ItemPendingError;
import mx.events.CollectionEvent;
import mx.events.CollectionEventKind;
import mx.events.FlexEvent;
import mx.managers.ISystemManager;
import mx.managers.SystemManager;
import mx.automation.IAutomationObject;
use namespace mx_internal;
//--------------------------------------
// Events
//--------------------------------------
/**
* Dispatched each time an item is processed and the
* <code>currentIndex</code> and <code>currentItem</code>
* properties are updated.
*
* @eventType mx.events.FlexEvent.REPEAT
*
* @langversion 3.0
* @playerversion Flash 9
* @playerversion AIR 1.1
* @productversion Flex 3
*/
[Event(name="repeat", type="mx.events.FlexEvent")]
/**
* Dispatched after all the subcomponents of a repeater are created.
* This event is triggered even if the <code>dataProvider</code>
* property is empty or <code>null</code>.
*
* @eventType mx.events.FlexEvent.REPEAT_END
*
* @langversion 3.0
* @playerversion Flash 9
* @playerversion AIR 1.1
* @productversion Flex 3
*/
[Event(name="repeatEnd", type="mx.events.FlexEvent")]
/**
* Dispatched when Flex begins processing the <code>dataProvider</code>
* property and begins creating the specified subcomponents.
* This event is triggered even if the <code>dataProvider</code>
* property is empty or <code>null</code>.
*
* @eventType mx.events.FlexEvent.REPEAT_START
*
* @langversion 3.0
* @playerversion Flash 9
* @playerversion AIR 1.1
* @productversion Flex 3
*/
[Event(name="repeatStart", type="mx.events.FlexEvent")]
//--------------------------------------
// Other metadata
//--------------------------------------
[IconFile("Repeater.png")]
[ResourceBundle("core")]
/**
* The Repeater class is the runtime object that corresponds
* to the <code>&lt;mx:Repeater&gt;</code> tag.
* It creates multiple instances of its subcomponents
* based on its dataProvider.
* The repeated components can be any standard or custom
* controls or containers.
*
* <p>You can use the <code>&lt;mx:Repeater&gt;</code> tag
* anywhere a control or container tag is allowed, with the exception
* of the <code>&lt;mx:Application&gt;</code> container tag.
* To repeat a user interface component, you place its tag
* in the <code>&lt;mx:Repeater&gt;</code> tag.
* You can use more than one <code>&lt;mx:Repeater&gt;</code> tag
* in an MXML document.
* You can also nest <code>&lt;mx:Repeater&gt;</code> tags.</p>
*
* <p>You cannot use the <code>&lt;mx:Repeater&gt;</code> tag
* for objects that do not extend the UIComponent class.</p>
*
* @mxml
*
* <p>The &lt;Repeater&gt; class has the following properties:</p>
*
* <pre>
* &lt;mx:Repeater
* <strong>Properties</strong>
* id="<i>No default</i>"
* childDescriptors="<i>No default</i>"
* count="<i>No default</i>"
* dataProvider="<i>No default</i>"
* recycleChildren="false|true"
* startingIndex="0"
*
* <strong>Events</strong>
* repeat="<i>No default</i>"
* repeatEnd="<i>No default</i>"
* repeatStart="<i>No default</i>"
* &gt;
* </pre>
*
* @includeExample examples/RepeaterExample.mxml
*
* @see mx.core.Container
* @see mx.core.UIComponent
* @see mx.core.UIComponentDescriptor
* @see flash.events.EventDispatcher
*
* @langversion 3.0
* @playerversion Flash 9
* @playerversion AIR 1.1
* @productversion Flex 3
*/
public class Repeater extends UIComponent implements IRepeater
{
include "../core/Version.as";
//--------------------------------------------------------------------------
//
// Constructor
//
//--------------------------------------------------------------------------
/**
* Constructor.
*
* @langversion 3.0
* @playerversion Flash 9
* @playerversion AIR 1.1
* @productversion Flex 3
*/
public function Repeater()
{
super();
}
//--------------------------------------------------------------------------
//
// Variables
//
//--------------------------------------------------------------------------
/**
* @private
*/
private var iterator:IViewCursor;
/**
* @private
* Flag indicating whether this Repeater has been fully created.
* Used to avoid createComponentFromDescriptor() calling execute().
*/
private var created:Boolean = false;
/**
* @private
* The index of this Repeater's UIComponentDescriptor in the
* container's child descriptors.
*/
private var descriptorIndex:int;
/**
* @private
* The Array of components created during the previous execution.
* This array is used during recycle().
*
* mx_internal for automation delegate access
*/
mx_internal var createdComponents:Array;
//--------------------------------------------------------------------------
//
// Overridden properties
//
//--------------------------------------------------------------------------
//----------------------------------
// showInAutomationHierarchy
//----------------------------------
/**
* @private
*/
override public function set showInAutomationHierarchy(value:Boolean):void
{
//do not allow value changes
}
//--------------------------------------------------------------------------
//
// Properties
//
//--------------------------------------------------------------------------
//----------------------------------
// childDescriptors
//----------------------------------
/**
* An Array of UIComponentDescriptor objects for this Repeater's children.
*
* @langversion 3.0
* @playerversion Flash 9
* @playerversion AIR 1.1
* @productversion Flex 3
*/
public var childDescriptors:Array /* of UIComponentDescriptor */;
//----------------------------------
// container
//----------------------------------
/**
* @private
* Storage for the 'container' read-only property.
* Initialized by the constructor.
*/
private var _container:Container;
/**
* The container that contains this Repeater.
*
* @langversion 3.0
* @playerversion Flash 9
* @playerversion AIR 1.1
* @productversion Flex 3
*/
public function get container():IContainer
{
return _container as IContainer;
}
//----------------------------------
// count
//----------------------------------
/**
* @private
* Storage for the 'count' property.
*/
private var _count:int = -1;
[Bindable("countChanged")]
[Inspectable(category="General", defaultValue="-1")]
/**
* @inheritDoc
*
* @langversion 3.0
* @playerversion Flash 9
* @playerversion AIR 1.1
* @productversion Flex 3
*/
public function get count():int
{
return _count;
}
/**
* @private
*/
public function set count(value:int):void
{
_count = value;
execute();
dispatchEvent(new Event("countChanged"));
}
//----------------------------------
// currentIndex
//----------------------------------
/**
* @private
* Storage for the 'currentIndex' property.
*/
private var _currentIndex:int;
[Bindable("nextRepeaterItem")]
[Inspectable(category="General", defaultValue="-1")]
/**
* @inheritDoc
*
* @langversion 3.0
* @playerversion Flash 9
* @playerversion AIR 1.1
* @productversion Flex 3
*/
public function get currentIndex():int
{
if (_currentIndex == -1)
{
var message:String = resourceManager.getString(
"core", "notExecuting");
throw new Error(message);
}
return _currentIndex;
}
//----------------------------------
// currentItem
//----------------------------------
[Bindable("nextRepeaterItem")]
[Inspectable(category="General", defaultValue="null")]
/**
* @inheritDoc
*
* @langversion 3.0
* @playerversion Flash 9
* @playerversion AIR 1.1
* @productversion Flex 3
*/
public function get currentItem():Object
{
if (_currentIndex == -1)
{
var message:String = resourceManager.getString(
"core", "notExecuting");
throw new Error(message);
}
var result:Object;
if (iterator)
{
try
{
iterator.seek(CursorBookmark.FIRST, _currentIndex);
result = iterator.current;
}
catch(itemPendingError:ItemPendingError)
{
itemPendingError.addResponder(new ItemResponder(responderResultHandler, responderFaultHandler));
}
}
return result;
}
//----------------------------------
// dataProvider
//----------------------------------
/**
* @private
* Storage for the 'dataProvider' property.
*/
private var collection:ICollectionView;
[Bindable("collectionChange")]
[Inspectable(category="General", defaultValue="null")]
/**
* @inheritDoc
*
* @langversion 3.0
* @playerversion Flash 9
* @playerversion AIR 1.1
* @productversion Flex 3
*/
public function get dataProvider():Object
{
return collection;
}
/**
* @private
*/
public function set dataProvider(value:Object):void
{
var hadValue:Boolean = false;
if (collection)
{
hadValue = true;
collection.removeEventListener(CollectionEvent.COLLECTION_CHANGE, collectionChangedHandler);
collection = null;
iterator = null;
}
if (value is Array)
{
collection = new ArrayCollection(value as Array);
}
else if (value is ICollectionView)
{
collection = ICollectionView(value);
}
else if (value is IList)
{
collection = new ListCollectionView(IList(value));
}
else if (value is XMLList)
{
collection = new XMLListCollection(value as XMLList);
}
else if (value is XML)
{
var xl:XMLList = new XMLList();
xl += value;
collection = new XMLListCollection(xl);
}
else if (value != null)
{
// convert it to an array containing this one item
var tmp:Array = [value];
collection = new ArrayCollection(tmp);
}
if (collection)
{
// weak reference
collection.addEventListener(CollectionEvent.COLLECTION_CHANGE, collectionChangedHandler, false, 0, true);
iterator = collection.createCursor();
}
dispatchEvent(new Event("collectionChange"));
if (collection || hadValue)
{
execute();
}
}
//----------------------------------
// numCreatedChildren
//----------------------------------
/**
* @private
*/
private function get numCreatedChildren():int
{
var result:int = 0;
for (var i:int = 0; i < createdComponents.length; i++)
{
var component:IFlexDisplayObject = createdComponents[i];
if (component is Repeater)
{
var repeater:Repeater = Repeater(component);
result += repeater.numCreatedChildren;
}
else
{
result += 1;
}
}
return result;
}
//----------------------------------
// recycleChildren
//----------------------------------
/**
* @private
* Storage for the recycleChildren property.
*/
private var _recycleChildren:Boolean = false;
[Inspectable(category="Other", defaultValue="false")]
/**
* @inheritDoc
*
* @langversion 3.0
* @playerversion Flash 9
* @playerversion AIR 1.1
* @productversion Flex 3
*/
public function get recycleChildren():Boolean
{
return _recycleChildren;
}
/**
* @private
*/
public function set recycleChildren(value:Boolean):void
{
_recycleChildren = value;
}
//----------------------------------
// startingIndex
//----------------------------------
/**
* @private
* Storage for the 'startingIndex' property.
*/
private var _startingIndex:int = 0;
[Bindable("startingIndexChanged")]
[Inspectable(category="General", defaultValue="0")]
/**
* @inheritDoc
*
* @langversion 3.0
* @playerversion Flash 9
* @playerversion AIR 1.1
* @productversion Flex 3
*/
public function get startingIndex():int
{
return _startingIndex;
}
/**
* @private
*/
public function set startingIndex(value:int):void
{
_startingIndex = value;
execute();
dispatchEvent(new Event("startingIndexChanged"));
}
//--------------------------------------------------------------------------
//
// Overridden methods
//
//--------------------------------------------------------------------------
/**
* @private
*/
override public function toString():String
{
return Object(container).toString() + "." + super.toString();
}
//--------------------------------------------------------------------------
//
// Methods
//
//--------------------------------------------------------------------------
/**
* @inheritDoc
*
* @langversion 3.0
* @playerversion Flash 9
* @playerversion AIR 1.1
* @productversion Flex 3
*/
public function initializeRepeater(container:IContainer,
recurse:Boolean):void
{
_container = Container(container);
descriptorIndex = container.numChildren;
created = true;
// Create instances of the components contained within
// this Repeater
if (collection)
{
createComponentsFromDescriptors(recurse);
}
if (owner == null)
owner = Container(container);
}
/**
* @inheritDoc
*
* @langversion 3.0
* @playerversion Flash 9
* @playerversion AIR 1.1
* @productversion Flex 3
*/
public function executeChildBindings():void
{
var n:int = container.numChildren;
for (var i:int = 0; i < n; i++)
{
var child:IRepeaterClient = IRepeaterClient(container.getChildAt(i));
if (hasDescendant(child) && child is IDeferredInstantiationUIComponent)
IDeferredInstantiationUIComponent(child).executeBindings();
}
}
/**
* @private
* Used by data binding code generation.
*/
mx_internal function getItemAt(index:int):Object
{
var result:Object;
if (iterator)
{
try
{
iterator.seek(CursorBookmark.FIRST, index);
result = iterator.current;
}
catch(itemPendingError:ItemPendingError)
{
itemPendingError.addResponder(
new ItemResponder(responderResultHandler, responderFaultHandler));
}
}
return result;
}
/**
* @private
*/
private function responderResultHandler(data:Object, info:Object):void
{
execute();
}
/**
* @private
*/
private function responderFaultHandler(data:Object, info:Object):void
{
}
/**
* @private
* Determines whether the specified UIComponent or Repeater
* is inside this Repeater, by searching its 'repeaters' array.
*
* The methods removeAllChildren() and removeAllChildRepeaters()
* use this to determine which child UIComponents and child Repeaters
* of this Repeater's container should be removed, since some of
* the container's children may be outside this Repeater.
*/
private function hasDescendant(o:Object):Boolean
{
var repeaters:Array = o.repeaters;
if (repeaters == null)
return false;
var n:int = repeaters.length;
for (var i:int = 0; i < n; i++)
{
if (repeaters[i] == this)
return true;
}
return false;
}
/**
* @private
* Removes all the child UIComponents in this Repeater's container
* which belong to this Repeater. Returns the lowest index of the
* removed children, or null if none were removed.
*
* The method execute() uses this to clean up previously created
* child UIComponents when this Repeater's dataProvider, startingIndex,
* or count changes.
*/
private function removeAllChildren(container:IContainer):void
{
// It's simpler and more efficient
// to loop backwards when removing children.
var n:int = container.numChildren;
for (var i:int = n - 1; i >= 0; i--)
{
var child:IRepeaterClient = IRepeaterClient(container.getChildAt(i));
if (hasDescendant(child))
{
if (child is Container)
{
removeAllChildren(Container(child));
removeAllChildRepeaters(Container(child));
}
container.removeChildAt(i);
if (child is IDeferredInstantiationUIComponent)
{
IDeferredInstantiationUIComponent(child).
deleteReferenceOnParentDocument(
IFlexDisplayObject(parentDocument));
}
}
}
}
/**
* @private
* Removes all the child Repeaters in this Repeater's container
* which belong to this Repeater.
*
* The method execute() uses this to clean up previously created
* child Repeaters when this Repeater's dataProvider, startingIndex,
* or count changes.
*/
private function removeAllChildRepeaters(container:Container):void
{
// It's simpler and more efficient
// to loop backwards when removing repeaters.
if (container.childRepeaters)
{
var n:int = container.childRepeaters.length;
for (var i:int = n - 1; i >= 0; i--)
{
var repeater:Repeater = container.childRepeaters[i];
if (hasDescendant(repeater))
{
removeRepeater(repeater);
}
}
}
}
/**
* @private
*/
private function removeChildRepeater(container:Container, repeater:Repeater):void
{
var i:int = 0;
var n:int = container.childRepeaters.length;
while (i < n)
{
if (container.repeaters[i] == repeater)
{
container.repeaters.splice(i, 1);
break;
}
i++;
}
}
/**
* @private
*/
private function createComponentFromDescriptor(
instanceIndex:int,
descriptorIndex:int,
recurse:Boolean):IFlexDisplayObject
{
var descriptor:UIComponentDescriptor = childDescriptors[descriptorIndex];
if (!descriptor.document)
{
descriptor.document = document;
}
// Ensure that the newly created component gets the proper
// 'instanceIndices', 'repeaters' and 'repeaterIndices'
// properties.
descriptor.instanceIndices = instanceIndices ? instanceIndices : [];
descriptor.instanceIndices.push(instanceIndex)
descriptor.repeaters = repeaters;
descriptor.repeaters.push(this);
descriptor.repeaterIndices = repeaterIndices;
descriptor.repeaterIndices.push(startingIndex + instanceIndex);
// Do not reuse the descriptor's properties, because we don't want repeated items
// to share non scalar properties. Otherwise they will have references to the
// same objects as opposed to having their own instances.
descriptor.invalidateProperties();
// Create object described by the descriptor (which might be
// either a UIComponent or a nested Repeater).
var child:IFlexDisplayObject = Container(container).createComponentFromDescriptor(
descriptor, recurse);
// get rid of the repeater-specific properties on the
// descriptor now that createComponent is done
descriptor.instanceIndices = null;
descriptor.repeaters = null;
descriptor.repeaterIndices = null;
// Execute any binding that is watching the current item.
dispatchEvent(new Event("nextRepeaterItem"));
return child;
}
/**
* @private
* Create repeated instances of the UIComponents and Repeaters
* that are owned by this Repeater, based on its dataProvider.
*
* This method is the heart of the Repeater class. It is called
* when this repeater is created by createComponentFromDescriptor(),
* and when this Repeater gets re-executed when its dataProvider,
* startingIndex, or count changes.
*/
private function createComponentsFromDescriptors(recurse:Boolean):void
{
dispatchEvent(new FlexEvent(FlexEvent.REPEAT_START));
createdComponents = [];
if (collection && (collection.length > 0) && (collection.length - startingIndex > 0))
{
var n:int = positiveMin(collection.length - startingIndex, count);
// Loop over the items of the dataProvider.
for (var i:int = 0; i < n; i++)
{
// When a repeater is repeating, 'currentIndex' and 'currentItem'
// can be read.
_currentIndex = startingIndex + i;
// Dispatch a "repeat" event.
dispatchEvent(new FlexEvent(FlexEvent.REPEAT));
if (childDescriptors && (childDescriptors.length > 0))
{
var m:int = childDescriptors.length;
// Loop over the child descriptors of the repeater.
for (var j:int = 0; j < m; j++)
{
var component:IFlexDisplayObject =
createComponentFromDescriptor(i, j, recurse);
createdComponents.push(component);
if (component is IUIComponent)
IUIComponent(component).owner = this;
if (component is IAutomationObject)
IAutomationObject(component).showInAutomationHierarchy = true;
}
}
}
// When a repeater isn't repeating, 'currentIndex' is -1
// and 'currentItem' is null.
_currentIndex = -1;
}
dispatchEvent(new FlexEvent(FlexEvent.REPEAT_END));
}
/**
* @private
* This method is used by execute() to determine where the newly-created
* UIComponents should be inserted in the container.
*/
private function getIndexForFirstChild():int
{
var locationInfo:LocationInfo = new LocationInfo();
var i:int = 0;
var cc:Array = Container(container).createdComponents;
var length:int = cc ? cc.length : 0;
while (i < length)
{
var component:IFlexDisplayObject = Container(container).createdComponents[i];
if (component == this)
{
locationInfo.found = true;
break;
}
else if (component is Repeater)
{
var repeater:Repeater = Repeater(component);
repeater.getIndexForRepeater(this, locationInfo);
if (locationInfo.found)
{
break;
}
}
else
{
locationInfo.index += 1;
}
i++;
}
return locationInfo.found ? locationInfo.index : container.numChildren;
}
/**
* @private
*/
private function getIndexForRepeater(target:Repeater,
locationInfo:LocationInfo):void
{
var i:int = 0;
var length:int = createdComponents.length;
while (i < length)
{
var component:IFlexDisplayObject = createdComponents[i];
if (component == target)
{
locationInfo.found = true;
break;
}
else if (component is Repeater)
{
var repeater:Repeater = Repeater(component);
repeater.getIndexForRepeater(target, locationInfo);
if (locationInfo.found)
break;
}
else
{
locationInfo.index += 1;
}
i++;
}
}
/**
* @private
* This method is used by execute() to move the newly-created UIComponents,
* which get created at the end of the container, to their proper indexes
* in the container.
*/
private function reindexDescendants(from:int, to:int):void
{
// Determine how many children were created by
// createComponentsFromDescriptors(), which includes the
// children created by nested Repeaters in this container.
var n:int = container.numChildren - from;
// Set the child index of each one.
for (var i:int = 0; i < n; i++)
{
var child:IRepeaterClient =
IRepeaterClient(container.getChildAt(from + i));
container.setChildIndex(DisplayObject(child), to + i);
}
}
/**
* @private
*/
private function resetRepeaterIndices(o:IRepeaterClient, index:int):void
{
var repeaterIndices:Array = o.repeaterIndices;
repeaterIndices[repeaterIndices.length - 1] = index;
o.repeaterIndices = repeaterIndices;
if (o is Container)
{
var fc:Container = Container(o);
var n:int = fc.numChildren;
for (var i:int = 0; i < n; i++)
{
var child:IRepeaterClient = IRepeaterClient(fc.getChildAt(i));
resetRepeaterIndices(child, index);
}
}
}
/**
* @private
*/
private function recycle():void
{
dispatchEvent(new FlexEvent(FlexEvent.REPEAT_START));
var n:int = 0;
var i:int;
var m:int;
var j:int;
var numComponents:int = 0;
if (collection && (collection.length > 0) && (collection.length - startingIndex > 0))
{
n = positiveMin(collection.length - startingIndex, count);
var previousChildIndex:int = 0;
// Loop over the items of the dataProvider.
for (i = 0; i < n; i++)
{
// When a repeater is repeating, 'currentIndex' and 'currentItem'
// can be read.
_currentIndex = startingIndex + i;
// Dispatch a "repeat" event.
dispatchEvent(new FlexEvent(FlexEvent.REPEAT));
if (childDescriptors)
{
m = childDescriptors.length;
var repeater:Repeater;
if (createdComponents.length >= ((i + 1) * m))
{
// Loop over the components previously created by this Repeater
for (j = 0; j < m; j++)
{
var createdComponent:IRepeaterClient = createdComponents[(i * m) + j];
if (createdComponent is Repeater)
{
repeater = Repeater(createdComponent);
resetRepeaterIndices(repeater, _currentIndex);
repeater.owner = this;
repeater.execute();
}
else
{
resetRepeaterIndices(createdComponent, _currentIndex);
if (createdComponent is IDeferredInstantiationUIComponent)
IDeferredInstantiationUIComponent(createdComponent).executeBindings(true);
}
numComponents++;
}
}
else
{
for (j = 0; j < m; j++)
{
var from:int = container.numChildren;
var to:int = getIndexForFirstChild() + numCreatedChildren;
var component:IRepeaterClient =
IRepeaterClient(createComponentFromDescriptor(i, j, true));
createdComponents.push(component);
if (component is IUIComponent)
IUIComponent(component).owner = this;
if (component is IAutomationObject)
IAutomationObject(component).showInAutomationHierarchy = true;
if (component is Repeater)
{
repeater = Repeater(component);
repeater.reindexDescendants(from, to);
}
else
{
container.setChildIndex(DisplayObject(component), to);
}
numComponents++;
}
}
}
}
}
// When a repeater isn't repeating, 'currentIndex' is -1 and
// 'currentItem' is null.
_currentIndex = -1;
// Remove any extra children created the last time that we no
// longer need.
for (i = createdComponents.length - 1; i >= numComponents; i--)
{
var extra:IRepeaterClient = createdComponents.pop();
if (extra is Repeater)
{
removeRepeater(Repeater(extra));
}
else if (extra)
{
if (extra is Container)
{
removeAllChildren(Container(extra));
removeAllChildRepeaters(Container(extra));
}
if (container.contains(DisplayObject(extra)))
container.removeChild(DisplayObject(extra));
if (extra is IDeferredInstantiationUIComponent)
{
IDeferredInstantiationUIComponent(extra).
deleteReferenceOnParentDocument(
IFlexDisplayObject(parentDocument));
}
}
}
dispatchEvent(new FlexEvent(FlexEvent.REPEAT_END));
}
/**
* @private
*/
private function recreate():void
{
// Clean out what this Repeater created last time.
removeAllChildren(container);
removeAllChildRepeaters(Container(container));
// Determine how to re-index the new children.
var from:int = container.numChildren;
var to:int = getIndexForFirstChild();
// Create new child UIComponents and child Repeaters.
// The child UIComponents get added at the "end"
// of the container by addChild().
createComponentsFromDescriptors(true);
// Re-index them to where they belong.
if (from != to)
reindexDescendants(from, to);
}
/**
* @private
* Execute this Repeater a second time, after its dataProvider,
* startingIndex, or count changes.
*/
private function execute():void
{
if (!created)
return;
if (recycleChildren && createdComponents && (createdComponents.length > 0))
recycle();
else
recreate();
}
/**
* @private
* Handles "Change" event sent by calls to Collection APIs
* on this Repeater's dataProvider.
*/
private function collectionChangedHandler(collectionEvent:CollectionEvent):void
{
switch (collectionEvent.kind)
{
case CollectionEventKind.UPDATE:
{
break;
}
default:
{
execute();
}
}
}
/**
* @private
*/
private function addItems(firstIndex:int, lastIndex:int):void
{
if (startingIndex > lastIndex)
return;
var i:int;
// Determine the child index at which the newly created
// components will be inserted.
var index:int = -1;
var n:int = container.numChildren;
var child:IRepeaterClient;
var repeaterIndex:int;
// If the data items have just been appended to the dataProvider,
// loop over the children of the container in reverse order,
// looking for the first child that was associated with this
// repeater. We'll insert the newly created components after
// that child.
if (lastIndex == (collection.length - 1))
{
for (i = n - 1; i >= 0; i--)
{
child = IRepeaterClient(container.getChildAt(i));
repeaterIndex = getRepeaterIndex(child);
if (repeaterIndex != -1)
{
index = i + 1;
break;
}
}
}
// If the data items have just been inserted before the end
// of the dataProvider, loop over the the children of the container,
// looking for the first one in the specified range.
// For example, if items 3 through 5 were just inserted,
// look for the first child that previously was
// associated with item 3, 4, or 5.
else
{
var numItemsAdded:int = (lastIndex - firstIndex) + 1;
for (i = 0; i < n; i++)
{
child = IRepeaterClient(container.getChildAt(i));
repeaterIndex = getRepeaterIndex(child);
if (repeaterIndex != -1)
{
if (firstIndex <= repeaterIndex &&
repeaterIndex <= lastIndex &&
index == -1)
{
index = i;
}
if (repeaterIndex >= firstIndex)
adjustIndices(child, numItemsAdded);
}
}
}
// Create components for the new dataProvider items
if (count == -1)
{
n = lastIndex;
}
else
{
n = positiveMin(startingIndex + count - 1, lastIndex);
}
for (i = Math.max(startingIndex, firstIndex); i <= n; i++)
{
var m:int = childDescriptors.length;
_currentIndex = i;
dispatchEvent(new FlexEvent(FlexEvent.REPEAT));
for (var j:int = 0; j < m; j++)
{
var from:int = container.numChildren;
var to:int = getIndexForFirstChild() + numCreatedChildren;
var component:IFlexDisplayObject =
createComponentFromDescriptor(i - startingIndex, j, true);
createdComponents.push(component);
if (component is IUIComponent)
IUIComponent(component).owner = this;
if (component is IAutomationObject)
IAutomationObject(component).showInAutomationHierarchy = true;
// The newly created components are at the end of the container.
// Move them to the indexes where they belong.
if (component is Repeater)
{
var r:Repeater = Repeater(component);
r.owner = this;
r.reindexDescendants(from, to);
}
else
{
container.setChildIndex(DisplayObject(component), to);
}
}
}
_currentIndex = -1;
}
/**
* @private
*/
private function removeItems(firstIndex:int, lastIndex:int):void
{
if (createdComponents && (createdComponents.length > 0))
{
for (var i:int = createdComponents.length - 1; i >= 0; i--)
{
var component:IRepeaterClient = createdComponents[i];
var repeaterIndex:int = getRepeaterIndex(component);
if (((firstIndex <= repeaterIndex) &&
((repeaterIndex < lastIndex) || (lastIndex == -1))) ||
(repeaterIndex >= dataProvider.length))
{
if (component is Repeater)
{
var repeater:Repeater = Repeater(component);
removeRepeater(repeater);
}
else if (container.contains(DisplayObject(component)))
{
container.removeChild(DisplayObject(component));
}
if (component is IDeferredInstantiationUIComponent)
{
IDeferredInstantiationUIComponent(component).
deleteReferenceOnParentDocument(
IFlexDisplayObject(parentDocument));
}
createdComponents.splice(i, 1);
}
else if ((firstIndex <= repeaterIndex) && (lastIndex != -1) &&
(repeaterIndex >= lastIndex))
{
adjustIndices(component, (lastIndex - firstIndex) + 1);
if (component is IDeferredInstantiationUIComponent)
{
IDeferredInstantiationUIComponent(component).
executeBindings(true);
}
}
}
}
}
/**
* @private
*/
private function removeRepeater(repeater:Repeater):void
{
repeater.removeAllChildren(repeater.container);
repeater.removeAllChildRepeaters(Container(repeater.container));
removeChildRepeater(Container(container), repeater);
repeater.deleteReferenceOnParentDocument(
IFlexDisplayObject(parentDocument));
// Null out the dataProvider, so that listeners will be removed.
repeater.dataProvider = null;
}
/**
* @private
*/
private function updateItems(firstIndex:int, lastIndex:int):void
{
if (recycleChildren)
{
var n:int = container.numChildren;
for (var i:int = 0; i < n; i++)
{
var child:IRepeaterClient = IRepeaterClient(container.getChildAt(i));
var repeaterIndex:int = getRepeaterIndex(child);
if (repeaterIndex != -1 &&
firstIndex <= repeaterIndex &&
repeaterIndex <= lastIndex)
{
if (child is IDeferredInstantiationUIComponent)
IDeferredInstantiationUIComponent(child).executeBindings(true);
}
}
}
else
{
removeItems(firstIndex, lastIndex);
addItems(firstIndex, lastIndex);
}
}
/**
* @private
*/
private function sort():void
{
execute();
}
/**
* @private
*/
private function getRepeaterIndex(o:IRepeaterClient):int
{
var repeaters:Array = o.repeaters;
if (repeaters == null)
return -1;
var n:int = repeaters.length;
for (var i:int = 0; i < n; i++)
{
if (repeaters[i] == this)
return o.repeaterIndices[i];
}
return -1;
}
/**
* @private
*/
private function adjustIndices(o:IRepeaterClient, adjustment:int):void
{
var repeaters:Array = o.repeaters;
if (repeaters == null)
return;
var n:int = repeaters.length;
for (var i:int = 0; i < n; i++)
{
if (repeaters[i] == this)
{
o.repeaterIndices[i] += adjustment;
o.instanceIndices[i] += adjustment;
break;
}
}
}
/**
* @private
* This function is like Math.min(),
* but if x is negative, it is ignored.
* If y is negative, it is ignored.
* If both are negative, zero is returned.
*/
private function positiveMin(x:int, y:int):int
{
var result:int = 0;
if (x >= 0)
{
if (y >= 0)
{
if (x < y)
result = x;
else
result = y;
}
else
{
result = x;
}
}
else
{
result = y;
}
return result;
}
}
}
////////////////////////////////////////////////////////////////////////////////
/**
* @private
*/
class LocationInfo
{
//--------------------------------------------------------------------------
//
// Constructor
//
//--------------------------------------------------------------------------
/**
* @private
*/
public function LocationInfo()
{
super();
}
//--------------------------------------------------------------------------
//
// Variables
//
//--------------------------------------------------------------------------
/**
* @private
*/
public var found:Boolean = false;
/**
* @private
*/
public var index:int = 0;
}