| //////////////////////////////////////////////////////////////////////////////// |
| // |
| // 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 spark.collections |
| { |
| import flash.events.Event; |
| |
| import mx.collections.IList; |
| import mx.events.CollectionEvent; |
| import mx.events.CollectionEventKind; |
| import mx.resources.IResourceManager; |
| import mx.resources.ResourceManager; |
| import mx.utils.OnDemandEventDispatcher; |
| |
| /** |
| * This IList class generates items that are a sequential series of numbers. |
| * The numbers range between the <code>minimum</code> and <code>maximum</code> |
| * properties. The <code>stepSize</code> property defines the difference between |
| * an item and the next item. |
| * |
| * <p>This class is used primarily as a data provider for the SpinnerList control; for example:</p> |
| * <pre> |
| * <s:SpinnerList> |
| * <s:dataProvider> |
| * <s:NumericDataProvider minimum="0" maximum="23" stepSize="1"/> |
| * </s:dataProvider> |
| * </s:SpinnerList> |
| * </pre> |
| * |
| * <p>The advantage of this class is that the item values are calculated on demand, |
| * instead of stored.</p> |
| * |
| * <p>Because the values are calculated instead of stored, the <code>addItem()</code>, <code>addItemAt()</code>, |
| * <code>removeAll()</code>, <code>removeItemAt()</code>, <code>itemUpdated()</code> and <code>setItemAt()</code> IList methods are not |
| * supported.</p> |
| * |
| * @see spark.components.SpinnerList |
| * @see mx.collections.IList |
| * |
| * @langversion 3.0 |
| * @playerversion Flash 11 |
| * @playerversion AIR 3 |
| * @productversion Flex 4.6 |
| */ |
| public class NumericDataProvider extends OnDemandEventDispatcher implements IList |
| { |
| //---------------------------------------------------------------------------------------------- |
| // |
| // Constructor |
| // |
| //---------------------------------------------------------------------------------------------- |
| |
| /** |
| * Constructor. |
| * |
| * @langversion 3.0 |
| * @playerversion Flash 11 |
| * @playerversion AIR 3 |
| * @productversion Flex 4.6 |
| */ |
| public function NumericDataProvider() |
| { |
| super(); |
| } |
| |
| |
| //---------------------------------------------------------------------------------------------- |
| // |
| // Variables |
| // |
| //---------------------------------------------------------------------------------------------- |
| |
| /** |
| * @private |
| * Used for accessing localized Error messages. |
| */ |
| private var resourceManager:IResourceManager = |
| ResourceManager.getInstance(); |
| |
| //---------------------------------------------------------------------------------------------- |
| // |
| // Properties |
| // |
| //---------------------------------------------------------------------------------------------- |
| |
| //---------------------------------- |
| // length |
| //---------------------------------- |
| /** |
| * @inheritDoc |
| * |
| * @langversion 3.0 |
| * @playerversion Flash 11 |
| * @playerversion AIR 3 |
| * @productversion Flex 4.6 |
| */ |
| public function get length():int |
| { |
| normalizeMinMax(); |
| return (_maximum - _minimum) / Math.abs(stepSize) + 1; |
| } |
| |
| //---------------------------------- |
| // maximum |
| //---------------------------------- |
| |
| private var _maximum:Number = 100; |
| |
| /** |
| * The value of the last item. This value must be |
| * larger than the minimum value. |
| * |
| * @default 100 |
| * |
| * @langversion 3.0 |
| * @playerversion Flash 11 |
| * @playerversion AIR 3 |
| * @productversion Flex 4.6 |
| */ |
| public function get maximum():Number |
| { |
| normalizeMinMax(); |
| return _maximum; |
| } |
| |
| /** |
| * @private |
| */ |
| public function set maximum(value:Number):void |
| { |
| if (_maximum == value) |
| return; |
| |
| _maximum = value; |
| |
| reset(); |
| } |
| |
| //---------------------------------- |
| // minimum |
| //---------------------------------- |
| |
| private var _minimum:Number = 0; |
| |
| /** |
| * The value of the first item. This value must be |
| * smaller than the minimum value. |
| * |
| * @default 0 |
| * |
| * @langversion 3.0 |
| * @playerversion Flash 11 |
| * @playerversion AIR 3 |
| * @productversion Flex 4.6 |
| */ |
| public function get minimum():Number |
| { |
| normalizeMinMax(); |
| return _minimum; |
| } |
| |
| public function set minimum(value:Number):void |
| { |
| if (_minimum == value) |
| return; |
| |
| _minimum = value; |
| |
| reset(); |
| } |
| |
| //---------------------------------- |
| // stepSize |
| //---------------------------------- |
| |
| private var _stepSize:Number = 1; |
| |
| /** |
| * The stepSize property controls the values of items between the first and last items. |
| * |
| * For each item, the value is calculated as the sum of the <code>minimum</code> |
| * and the item's index multiplied by this property. |
| * |
| * <p>For example, if <code>minimum</code> is 10, <code>maximum</code> is 20, and this property is 3, then the |
| * item values of this data provider are 10, 13, 16, 19, and 20.</p> |
| * |
| * @default 1 |
| * |
| * @langversion 3.0 |
| * @playerversion Flash 11 |
| * @playerversion AIR 3 |
| * @productversion Flex 4.6 |
| */ |
| public function get stepSize():Number |
| { |
| return _stepSize; |
| } |
| |
| public function set stepSize(value:Number):void |
| { |
| if (_stepSize == value) |
| return; |
| |
| if (value == 0) |
| { |
| var message:String = resourceManager.getString( |
| "collections", "stepSizeError"); |
| throw new Error(message); |
| } |
| |
| _stepSize = value; |
| |
| reset(); |
| } |
| |
| //---------------------------------------------------------------------------------------------- |
| // |
| // Interface Methods |
| // |
| //---------------------------------------------------------------------------------------------- |
| |
| /** |
| * This function is not supported. |
| * |
| * @langversion 3.0 |
| * @playerversion Flash 11 |
| * @playerversion AIR 3 |
| * @productversion Flex 4.6 |
| */ |
| public function addItem(item:Object):void |
| { |
| var message:String = resourceManager.getString( |
| "collections", "addItemError"); |
| throw new Error(message); |
| } |
| |
| /** |
| * This function is not supported. |
| * |
| * @langversion 3.0 |
| * @playerversion Flash 11 |
| * @playerversion AIR 3 |
| * @productversion Flex 4.6 |
| */ |
| public function addItemAt(item:Object, index:int):void |
| { |
| var message:String = resourceManager.getString( |
| "collections", "addItemAtError"); |
| throw new Error(message); |
| } |
| |
| /** |
| * @inheritDoc |
| * |
| * @langversion 3.0 |
| * @playerversion Flash 11 |
| * @playerversion AIR 3 |
| * @productversion Flex 4.6 |
| */ |
| public function getItemAt(index:int, prefetch:int=0):Object |
| { |
| if (index < 0 || index >= length) |
| { |
| var message:String = resourceManager.getString( |
| "collections", "outOfBounds", [ index ]); |
| throw new RangeError(message); |
| } |
| |
| var scale:Number = 1; |
| var value:Number; |
| var scaledStepSize:Number = stepSize; |
| var baseValue:Number = stepSize > 0 ? minimum : maximum; |
| |
| // If stepSize isn't an integer, there's a possibility that the floating point |
| // approximation of value will be slightly larger or smaller |
| // than the real value. This can lead to errors in calculations like |
| // index * stepSize. To avoid problems, we scale by the implicit precision |
| // of the stepSize and then round. For example if stepSize=0.01, then |
| // we scale by 100. |
| // TODO (jszeto) Refactor into a util class? |
| if (stepSize != Math.round(stepSize)) |
| { |
| const parts:Array = (new String(1 + stepSize)).split("."); |
| scale = Math.pow(10, parts[1].length); |
| scaledStepSize = Math.round(stepSize * scale); |
| |
| value = Math.round(index * scaledStepSize) / scale; |
| } |
| else |
| { |
| value = index * stepSize; |
| } |
| |
| return Math.min(Math.max(value + baseValue, minimum), maximum); |
| } |
| |
| /** |
| * @inheritDoc |
| */ |
| public function getItemIndex(item:Object):int |
| { |
| // Calculate the index by subtracting the item from the minimum and |
| // dividing by the stepSize. Make sure the index is between |
| // 0 and length - 1. |
| var baseValue:Number = stepSize > 0 ? minimum : maximum; |
| |
| return Math.max(Math.min((Number(item) - baseValue) / stepSize, length - 1), 0); |
| } |
| |
| /** |
| * This function is not supported. |
| * |
| * @langversion 3.0 |
| * @playerversion Flash 11 |
| * @playerversion AIR 3 |
| * @productversion Flex 4.6 |
| */ |
| public function itemUpdated(item:Object, property:Object=null, oldValue:Object=null, newValue:Object=null):void |
| { |
| var message:String = resourceManager.getString( |
| "collections", "itemUpdatedError"); |
| throw new Error(message); |
| } |
| |
| /** |
| * This function is not supported. |
| * |
| * @langversion 3.0 |
| * @playerversion Flash 11 |
| * @playerversion AIR 3 |
| * @productversion Flex 4.6 |
| */ |
| public function removeAll():void |
| { |
| var message:String = resourceManager.getString( |
| "collections", "removeAllError"); |
| throw new Error(message); |
| } |
| |
| /** |
| * This function is not supported. |
| * |
| * @langversion 3.0 |
| * @playerversion Flash 11 |
| * @playerversion AIR 3 |
| * @productversion Flex 4.10 |
| */ |
| public function removeItem(item:Object):Boolean |
| { |
| var message:String = resourceManager.getString( |
| "collections", "removeItemError"); |
| throw new Error(message); |
| return null; |
| } |
| |
| /** |
| * This function is not supported. |
| * |
| * @langversion 3.0 |
| * @playerversion Flash 11 |
| * @playerversion AIR 3 |
| * @productversion Flex 4.6 |
| */ |
| public function removeItemAt(index:int):Object |
| { |
| var message:String = resourceManager.getString( |
| "collections", "removeItemAtError"); |
| throw new Error(message); |
| return null; |
| } |
| |
| /** |
| * This function is not supported. |
| * |
| * @langversion 3.0 |
| * @playerversion Flash 11 |
| * @playerversion AIR 3 |
| * @productversion Flex 4.6 |
| */ |
| public function setItemAt(item:Object, index:int):Object |
| { |
| var message:String = resourceManager.getString( |
| "collections", "setItemAtError"); |
| throw new Error(message); |
| return null; |
| } |
| |
| /** |
| * @inheritDoc |
| * |
| * @langversion 3.0 |
| * @playerversion Flash 11 |
| * @playerversion AIR 3 |
| * @productversion Flex 4.6 |
| */ |
| public function toArray():Array |
| { |
| var result:Array = []; |
| var numItems:int = length; |
| var baseValue:Number = stepSize > 0 ? minimum : maximum; |
| |
| // TODO (jszeto) Add in floating point error logic |
| for (var i:int = 0; i < numItems; i++) |
| result.push(Math.min(Math.max(baseValue + (i * stepSize), minimum), maximum)); |
| return result; |
| } |
| |
| //---------------------------------------------------------------------------------------------- |
| // |
| // Functions |
| // |
| //---------------------------------------------------------------------------------------------- |
| |
| private function normalizeMinMax():void |
| { |
| if (_minimum > _maximum) |
| _maximum = _minimum; |
| } |
| |
| private function reset():void |
| { |
| // Easier to just reset the collection instead of |
| // calculating the delta of which items have been |
| // added, removed or updated |
| var event:CollectionEvent = |
| new CollectionEvent(CollectionEvent.COLLECTION_CHANGE); |
| event.kind = CollectionEventKind.RESET; |
| dispatchEvent(event); |
| } |
| } |
| |
| } |