In case Flash no longer exists; a copy of this site is included in the Flashpoint archive's "ultimate" collection.

Dead Code Preservation :: Archived AS3 works from wonderfl.net

Large Table Test

Tab : Right
Shift + Tab : Left
Enter : Down
Shift + Enter : Up
Get Adobe Flash player
by Test_Dept 12 Sep 2011
/**
 * Copyright Test_Dept ( http://wonderfl.net/user/Test_Dept )
 * MIT License ( http://www.opensource.org/licenses/mit-license.php )
 * Downloaded from: http://wonderfl.net/c/nP4A
 */

package {

    import flash.display.Sprite;
    import flash.display.StageAlign;
    import flash.display.StageScaleMode;
    import flash.events.Event;
    
    /**
     * Large Table Test
     * @author Test_Dept
     */
    [SWF(backgroundColor="#ffffff", width="465", height="465")]
    public class LargeTableTest extends Sprite {

        public function LargeTableTest() {
            addEventListener(Event.ADDED_TO_STAGE, addedToStageHandler);
        }

        private function addedToStageHandler(event : Event) : void {
            
            // setup stage
            stage.scaleMode = StageScaleMode.NO_SCALE;
            stage.align = StageAlign.TOP_LEFT;
            stage.stageFocusRect = false;

            var table : Table = new Table(new TestTableModel() );
            table.width = 465;
            table.height = 465;
            table.lockCell(4, 3);
            addChild(table);
        }
    }
}

import flash.display.BitmapData;
import flash.display.Graphics;
import flash.display.Sprite;
import flash.events.Event;
import flash.events.FocusEvent;
import flash.events.KeyboardEvent;
import flash.events.MouseEvent;
import flash.geom.Matrix;
import flash.geom.Rectangle;
import flash.text.TextField;
import flash.text.TextFieldType;
import flash.ui.Keyboard;
import flash.ui.Mouse;

class TestTableModel implements ITableModel {
    private var _map : Object = new Object();
    public function TestTableModel() {
    }
    public function get numRows() : int {
        return 10000;
    }
    public function get numColumns() : int {
        return 10000;
    }
    public function getValueAt(row : int, column : int) : Object {
        var value : Object = _map[getKey(row, column)];
        return (value != null)? value : "(" + row + "," + column + ")";
    }
    public function setValueAt(row : int, column : int, value : Object) : void {
        _map[getKey(row, column)] = value;
    }
    protected function getKey(row : int, column : int) : int {
        return (row - 1) * numColumns + (column - 1);
    }
}

class UIBase extends Sprite {
    
    private var _width : Number = 100;
    private var _height : Number = 100;

    private var _mouseDown : Boolean = false;
    private var _invalidateDisplayFlag : Boolean = false;

    public function UIBase() {
        addEventListener(Event.ENTER_FRAME, enterFrameHandler);
        addEventListener(MouseEvent.MOUSE_DOWN, mouseDownHandler);
    }
    
    override public function get width() : Number {
        return _width;
    }
    override public function set width(value : Number) : void {
        _width = value;
        invalidateDisplay();
    }
    override public function get height() : Number {
        return _height;
    }
    override public function set height(value : Number) : void {
        _height = value;
        invalidateDisplay();
    }
    
    public function get mouseDown() : Boolean {
        return _mouseDown;
    }

    protected function validateDisplay() : void {
        if (_invalidateDisplayFlag) {
            updateDisplay();
            _invalidateDisplayFlag = false;
        }        
    }
    
    public function invalidateDisplay() : void { 
        _invalidateDisplayFlag = true;
    }

    protected function updateDisplay() : void {
    }
        
    protected function enterFrameHandler(event : Event) : void {
        validateDisplay();
    }

    protected function mouseDownHandler(event : MouseEvent) : void {
        if (_mouseDown) return;
        stage.addEventListener(MouseEvent.MOUSE_UP, stage_mouseMouseUpHandler);
        stage.addEventListener(Event.MOUSE_LEAVE, stage_mouseLeaveHandler);
        _mouseDown = true;
    }
    
    protected function mouseReleaseHandler(event : Event) : void     {
        if (!_mouseDown) return;
        stage.removeEventListener(MouseEvent.MOUSE_UP, stage_mouseMouseUpHandler);
        stage.removeEventListener(Event.MOUSE_LEAVE, stage_mouseLeaveHandler);
        _mouseDown = false;
    }
    
    private function stage_mouseMouseUpHandler(event : MouseEvent) : void {
        mouseReleaseHandler(event);
    }

    private function stage_mouseLeaveHandler(event : Event) : void {
        mouseReleaseHandler(event);
    }
}

class Table extends UIBase {

    private var _model : ITableModel = null;
    
    private var _headerWidth : Number = 50;
    private var _headerHeight : Number = 20;

    private var _rowLine : CellLine = null;
    private var _columnLine : CellLine = null;

    private var _selectedCell : Cell = null;
    private var _editingCell : Cell = null;
    private var _editor : * = null;

    private var _maskPane : Sprite = null;

    private var _contentPane : Sprite = null;
    private var _editorPane : Sprite = null;
    private var _cellPane : CachedRendererPane = null;

    private var _frontPane : Sprite = null;
    private var _headerPane : CachedRendererPane = null;
    private var _lockedBorderPane : Sprite = null;
    private var _resizeBarPane : Sprite = null;
    private var _dottedBitmap : BitmapData = null;
    
    private var _resizeTarget : ResizeTarget = null;
    private var _resizeCursor : ResizeCursor = null;
    
    public function Table(model : ITableModel) {

        _model = model;

        _rowLine = new CellLine();
        _rowLine.defaultSize = 20;
        
        _columnLine = new CellLine();
        _columnLine.defaultSize = 50;

        _columnLine.numCells = _model.numColumns;
        _rowLine.numCells = _model.numRows;

        // content pane
        
        _contentPane = new Sprite();
        _contentPane.tabEnabled = true;
        _contentPane.addEventListener(FocusEvent.FOCUS_IN, contentPane_focusInHandler);
        _contentPane.addEventListener(FocusEvent.FOCUS_OUT, contentPane_focusOutHandler);
        _contentPane.addEventListener(FocusEvent.KEY_FOCUS_CHANGE, contentPane_keyFocusChangeHandler);
        _contentPane.addEventListener(FocusEvent.MOUSE_FOCUS_CHANGE, contentPane_mouseFocusChangeHandler);
        _contentPane.addEventListener(MouseEvent.MOUSE_DOWN, contentPane_mouseDownHandler);
        _contentPane.addEventListener(KeyboardEvent.KEY_DOWN, contentPane_keyDownHandler);
        addChild(_contentPane);
        
        _cellPane = new CachedRendererPane();
        _contentPane.addChild(_cellPane);

        _editorPane = new Sprite();
        _contentPane.addChild(_editorPane);

        // front pane
        
        _frontPane = new Sprite();
        addChild(_frontPane);

        _headerPane = new CachedRendererPane();
        _frontPane.addChild(_headerPane);

        _resizeBarPane = new Sprite();
        _frontPane.addChild(_resizeBarPane);

        _lockedBorderPane = new Sprite();
        _frontPane.addChild(_lockedBorderPane);
        
        // resize cursor
        
        _resizeCursor = new ResizeCursor();
        addChild(_resizeCursor);
        
        // mask
        _maskPane = new Sprite();
        addChild(_maskPane);
        mask = _maskPane;
        
        addEventListener(MouseEvent.MOUSE_MOVE, mouseMoveHandler);
    }
    
    override protected function mouseReleaseHandler(event : Event) : void {
        super.mouseReleaseHandler(event);
        if (_resizeTarget) {
            invalidateDisplay();
        }
    }
    
    override protected function enterFrameHandler(event : Event) : void {
        super.enterFrameHandler(event);
        updateResizeBarPane();
        updateHeaderPane();
    }
    
    protected function mouseMoveHandler(event : MouseEvent) : void {
        
        if (mouseDown) {
            
            // drag
            
            if (_resizeTarget) {
                updateResizeTarget();
                if (_resizeTarget.column != -1) {
                    _columnLine.setSizeAt(_resizeTarget.column,
                        _resizeTarget.tempX - _resizeTarget.x);
                } else if (_resizeTarget.row != -1) {
                    _rowLine.setSizeAt(_resizeTarget.row,
                        _resizeTarget.tempY - _resizeTarget.y);
                } else {
                    throw new Error();
                }
            }
            
        } else {
            
            // move
            
            _resizeTarget = getResizeTarget();
            
            if (_resizeTarget != null) {
                _resizeCursor.x = mouseX;
                _resizeCursor.y = mouseY;
                _resizeCursor.horizontal = _resizeTarget.column != -1;
                _resizeCursor.visible = true;
                Mouse.hide();
            } else {
                _resizeCursor.visible = false;
                Mouse.show();
            }
        }
    }

    private function onContentPane(obj : Object) : Boolean {
        if (obj == null) {
            return false;
        } else if (obj == _contentPane) {
            return true;
        } else {
            return onContentPane(obj.parent);
        }
    }
    
    private function isSelfEvent(event : FocusEvent) : Boolean {
        return onContentPane(event.target) && onContentPane(event.relatedObject);        
    }
    
    protected function contentPane_focusInHandler(event : FocusEvent) : void {
        if (isSelfEvent(event) ) return;
        beginEdit(event);
    }

    protected function contentPane_focusOutHandler(event : FocusEvent) : void {
        if (isSelfEvent(event) ) return;
        endEdit(event);
    }    

    protected function contentPane_keyFocusChangeHandler(event : FocusEvent) : void {
        if (_selectedCell == null) {
            return;
        }
        var cell : Cell;
        if ( (cell = moveHorizontal(_selectedCell, event.shiftKey) ) != null) {
            setSelectedCell(cell);
            beginEdit(event);
            event.preventDefault();        
        }
    }

    protected function contentPane_mouseFocusChangeHandler(event : FocusEvent) : void {
    }    

    protected function contentPane_mouseDownHandler(event : MouseEvent) : void {
        setSelectedCell(getCellAt(_contentPane.mouseX, _contentPane.mouseY) );
        beginEdit(event);
        invalidateDisplay();
    }
    
    protected function contentPane_keyDownHandler(event : KeyboardEvent) : void {

        if (_selectedCell == null) {
            return;
        }

        if (event.keyCode == Keyboard.ENTER) {
            var cell : Cell;
            if ( (cell = moveVertical(_selectedCell, event.shiftKey) ) != null) {
                setSelectedCell(cell);
                beginEdit(event);
            }
        }
    }

    public function lockCell(row : int, column : int) : void {

        if (row > 0) {
            // lock
            _rowLine.lockedIndex = row;
            _rowLine.visibleLockedIndex = row - _rowLine.offset;
            _rowLine.offset = Math.max(_rowLine.offset, _rowLine.lockedIndex);
        } else if (_rowLine.lockedIndex > 0) {
            // unlock
            _rowLine.lockedIndex = 0;
            _rowLine.visibleLockedIndex = 0;
            _rowLine.offset -= _rowLine.visibleLockedIndex;
        }

        if (_columnLine.lockedIndex > 0) {
            // lock
            _columnLine.lockedIndex = 0;
            _columnLine.visibleLockedIndex = 0;
            _columnLine.offset -= _columnLine.visibleLockedIndex;
        } else if (column > 0) {
            // unlock
            _columnLine.lockedIndex = column;
            _columnLine.visibleLockedIndex = column - _columnLine.offset;
            _columnLine.offset = Math.max(_columnLine.offset, _columnLine.lockedIndex);
        }
        
        invalidateDisplay();
    }

    private function setSelectedCell(cell : Cell) : void {
        _selectedCell = cell;
        if (_selectedCell != null) {
            makeCellVisible(_selectedCell.row, _selectedCell.column);
        }
    }

    protected function beginEdit(event : Event) : void {

        if (_selectedCell == null) {
            // select default.
            setSelectedCell(new Cell(0, 0) );
        }
        
        if (_editingCell != null) {
            if (_editingCell.row == _selectedCell.row &&
                    _editingCell.column == _selectedCell.column) {
                // already editing same cell.
                return;
            }
            // force end edit.
            endEdit(event);
        }
        
        _editingCell = _selectedCell;

        makeCellVisible(_editingCell.row, _editingCell.column);
        
        var bounds : Rectangle = getCellBoundsAt(
            _editingCell.row, _editingCell.column);
        var tf : TextField = new TextField();
        tf.type = TextFieldType.INPUT;
        tf.x = bounds.x;
        tf.y = bounds.y;
        tf.width = bounds.width;
        tf.height = bounds.height;
        tf.text = String(_model.getValueAt(_editingCell.row, _editingCell.column) );
        tf.setSelection(0, tf.text.length);

        _editor = tf;
        _editorPane.addChild(_editor);
        stage.focus = _editor;
    }

    protected function endEdit(event : Event) : void {

        if (_editingCell == null) {
            return;
        }

        _model.setValueAt(_editingCell.row, _editingCell.column, _editor.text);
        _editorPane.removeChild(_editor);
        
        _editingCell = null;
        
        invalidateDisplay();
    }
    
    override protected function updateDisplay() : void {

        _frontPane.x = _headerWidth;
        _frontPane.y = _headerHeight;
        _contentPane.x = _headerWidth;
        _contentPane.y = _headerHeight;
        
        _columnLine.visibleSize = Math.max(0, width - _headerWidth);
        _rowLine.visibleSize = Math.max(0, height - _headerHeight);

        updateMaskPane();
        updateCellPane();
        updateLockedBorderPane();
    }
    
    private function updateMaskPane() : void {
        var g : Graphics = _maskPane.graphics;
        g.clear();
        g.beginFill(0x000000)
        g.drawRect(0, 0, width, height);
        g.endFill();
    }

    private function updateLockedBorderPane() : void {
        
        var g : Graphics = _lockedBorderPane.graphics;
        g.clear();

        // locked lines
        if (_columnLine.lockedIndex > 0) {
            var lockedX : Number = _columnLine.getVisibleLockedPosition();
            g.lineStyle(1, 0x000000);
            g.moveTo(lockedX, -_headerHeight);
            g.lineTo(lockedX, _rowLine.visibleSize);
        }
        if (_rowLine.lockedIndex > 0) {
            var lockedY : Number = _rowLine.getVisibleLockedPosition();
            g.lineStyle(1, 0x000000);
            g.moveTo(-_headerWidth, lockedY);
            g.lineTo(_columnLine.visibleSize, lockedY);
        }
    }

    private function updateResizeBarPane() : void {

        var g : Graphics = _resizeBarPane.graphics;
        g.clear();

        if (mouseDown && _resizeTarget) {
            
            _resizeCursor.x = mouseX;
            _resizeCursor.y = mouseY;
            
            updateResizeTarget();
            g.lineStyle(1, 0x000000);
            if (_resizeTarget.column != -1) {
                g.lineBitmapStyle(getDottedBitmap(), new Matrix(0, -1, 1, 0) );
                g.moveTo(_resizeTarget.tempX, 0);
                g.lineTo(_resizeTarget.tempX, _rowLine.visibleSize);
            } else if (_resizeTarget.row != -1) {
                g.lineBitmapStyle(getDottedBitmap() );
                g.moveTo(0, _resizeTarget.tempY);
                g.lineTo(_columnLine.visibleSize, _resizeTarget.tempY);
            } else {
                throw new Error();
            }
        }
    }

    private function getDottedBitmap() : BitmapData {
        if (_dottedBitmap == null) {
            _dottedBitmap = new BitmapData(2, 1, false, 0xffffff);
            _dottedBitmap.setPixel(0, 0, 0x999999);
        }
        return _dottedBitmap;
    }
    
    private function updateResizeTarget() : void {
        if (_resizeTarget.column != -1) {
            _resizeTarget.tempX = Math.max(_resizeTarget.x,
                Math.min(_resizeBarPane.mouseX, _columnLine.visibleSize) );
        } else if (_resizeTarget.row != -1) {
            _resizeTarget.tempY = Math.max(_resizeTarget.y,
                Math.min(_resizeBarPane.mouseY, _rowLine.visibleSize) );
        } else {
            throw new Error();
        }
    }

    private function updateHeaderPane() : void {

        var g : Graphics = _headerPane.graphics;
        g.clear();
        
        _headerPane.begin();
        
        // left-top
        drawHeaderCell(g, -1, -1, -_headerWidth, -_headerHeight, _headerWidth, _headerHeight);

        // row header
        _rowLine.step(function(row : int, y : Number, height : Number) : void {
            var x : Number = -_headerWidth;
            var width : Number = _headerWidth;
            drawHeaderCell(g, row, -1, x, y, width, height);
        } );
        
        // column header
        _columnLine.step(function(column : int, x : Number, width : Number) : void {
            var y : Number = -_headerHeight;
            var height : Number = _headerHeight;
            drawHeaderCell(g, -1, column, x, y, width, height);
        } );
        
        _headerPane.end();
    }
    
    protected function drawHeaderCell(
        g : Graphics,
        row : int, column : int, 
        x : Number, y : Number, width : Number, height : Number
    ) : void {

        g.lineStyle(1, 0xcccccc);
        g.beginFill(0xf0f0f0);
        g.drawRect(x, y, width, height);            
        g.endFill();
        
        var tf : TextField = _headerPane.getRenderer() as TextField;
        tf.x = x;
        tf.y = y;
        tf.width = width;
        tf.height = height;

        if (row != -1) {
            tf.text = "R" + row;
        } else if (column != -1) {
            tf.text = "C" + column;
        } else {
            tf.text = "";
        }
    }
    
    private function updateCellPane() : void {
        
        var g : Graphics = _cellPane.graphics;
        g.clear();
        
        // cells
        _cellPane.begin();
        _rowLine.step(function(row : int, y : Number, height : Number) : void {
            _columnLine.step(function(column : int, x : Number, width : Number) : void {
                drawCell(g, row, column, x, y, width, height);
            } );
        } );
        _cellPane.end();
        
        // selected cell
        if (_selectedCell != null) {
            var bounds : Rectangle = getCellBoundsAt(_selectedCell.row, _selectedCell.column);
            g.lineStyle(2, 0x000000);
            g.drawRect(bounds.x, bounds.y, bounds.width, bounds.height);
        }
    }
    
    protected function drawCell(
        g : Graphics,
        row : int, column : int, 
        x : Number, y : Number, width : Number, height : Number
    ) : void {

        var selected : Boolean = (_selectedCell != null &&
            _selectedCell.row == row &&
            _selectedCell.column == column);
        var editing : Boolean = (_editingCell != null &&
            _editingCell.row == row &&
            _editingCell.column == column);
        
        if (selected) {
            g.lineStyle(1, 0xcccccc);
            g.beginFill(0xccccff);
            g.drawRect(x ,y, width, height);
            g.endFill();
        } else {
            g.lineStyle(1, 0xcccccc);
            g.drawRect(x ,y, width, height);
        }

        var tf : TextField = _cellPane.getRenderer() as TextField;
        tf.x = x;
        tf.y = y;
        tf.width = width;
        tf.height = height;
        tf.text = String(_model.getValueAt(row, column) );
        tf.textColor = editing? 0xf0f0f0 : 0x000000;
    }
    
    protected function getResizeTarget() : ResizeTarget {
        
        var gap : Number = 2;
        var target : ResizeTarget = new ResizeTarget();
        var mouseX : Number = _resizeBarPane.mouseX;
        var mouseY : Number = _resizeBarPane.mouseY;
        
        _rowLine.step(function(row : int, y : Number, height : Number) : void {
            if (-_headerWidth <= mouseX && mouseX <= 0 &&
                y + height - gap <= mouseY && mouseY <= y + height + gap) {
                target.y = y;
                target.row = row;
            }
        } );
        
        if (target.row != -1) {
            return target;
        }
        
        _columnLine.step(function(column : int, x : Number, width : Number) : void {
            if (-_headerHeight <= mouseY && mouseY <= 0 &&
                x + width - gap <= mouseX && mouseX <= x + width + gap) {
                target.x = x;
                target.column = column;
            }
        } );
        
        if (target.column != -1) {
            return target;
        }
        
        return null;
    }
    
    public function getCellBoundsAt(row : int, column : int) : Rectangle {
        var x : Number = _columnLine.getPositionAt(column);
        var y : Number = _rowLine.getPositionAt(row);
        var width : Number = _columnLine.getSizeAt(column);
        var height : Number = _rowLine.getSizeAt(row);
        return new Rectangle(x, y, width, height);
    }
    
    public function getCellAt(x : Number, y : Number) : Cell {
        var r : int = _rowLine.getIndexAt(y);
        var c : int = _columnLine.getIndexAt(x);
        return (r != -1 && c != -1)? new Cell(r, c) : null;
    }

    public function makeCellVisible(row : int, column : int) : void {
        _rowLine.offset = _rowLine.getVisibleOffset(row);
        _columnLine.offset = _columnLine.getVisibleOffset(column);
        invalidateDisplay();
    }

    protected function moveHorizontal(cell : Cell, backward : Boolean) :  Cell {
        return moveImpl(cell.row, cell.column, _model.numRows, _model.numColumns, backward);
    }
    
    protected function moveVertical(cell : Cell, backward : Boolean) :  Cell {
        var revCell : Cell = moveImpl(cell.column, cell.row, _model.numColumns, _model.numRows, backward);
        return (revCell != null)? new Cell(revCell.column, revCell.row) : null;
    }
    
    private function moveImpl(
        row : int, column : int, numRows : int, numColumns : int,
        backward : Boolean
    ) :  Cell {
        if (backward) {
            if (column - 1 >= 0) {
                column--;
                return new Cell(row, column);
            } else if (row - 1 >= 0) {
                row--;
                column = numColumns - 1;
                return new Cell(row, column);
            }
        } else {
            if (column + 1 < numColumns) {
                column++;
                return new Cell(row, column);
            } else if (row + 1 < numRows) {
                row++;
                column = 0;
                return new Cell(row, column);
            }
        }
        return null;
    }    
}

interface ITableModel {
    function get numRows() : int;
    function get numColumns() : int;
    function getValueAt(row : int, column : int) : Object;
    function setValueAt(row : int, column : int, value : Object) : void;
}

class Cell {
    private var _row : int;
    private var _column : int;
    public function Cell(row : int = 0, column : int = 0) {
        _row = row;
        _column = column;
    }
    public function get row() : int {
        return _row;
    }
    public function get column() : int {
        return _column;
    }
    public function toString() : String {
        return "Cell(" + _row + "," + _column + ")";
    }
}

class CachedRendererPane extends Sprite {
    
    private var _cache : Array = new Array();
    private var _cacheIndex : int = 0;
    
    public function CachedRendererPane() {
    }
    
    public function begin() : void {
        // reset
        _cacheIndex = 0;
    }
    
    public function end() : void {
        // cleanup rests.
        var renderer : *;
        while (_cacheIndex < _cache.length) {
            renderer = _cache[_cacheIndex++];
            renderer.x = 0;
            renderer.y = 0;
            renderer.width = 0;
            renderer.height = 0;
            renderer.visible = false;
        }        
    }
    
    public function getRenderer() : Object {
        var renderer : *;
        if (_cacheIndex >= _cache.length) {
            renderer = new TextField();
            renderer.type = TextFieldType.DYNAMIC;
            addChild(renderer);
            _cache.push(renderer);
        }
        renderer = _cache[_cacheIndex++];
        renderer.visible = true;
        return renderer;
    }
}

class CellLine {
    
    public var lockedIndex : int = 0;
    public var visibleLockedIndex : int = 0;
    public var offset : int = 0;
    public var numCells : int = 0;
    public var visibleSize : Number = 0;
    public var defaultSize : Number = 80;
    
    private var _size : Object = {};
    
    public function CellLine() {
    }
    
    public function getSizeAt(index : int) : Number {
        var value : Object = _size[index];
        return (value != null)? value as Number : defaultSize;
    }
    
    public function setSizeAt(index : int, size : Number) : void {
        _size[index] = size;
    }
    
    public function getVisibleLockedPosition() : Number {
        var pos : Number = 0;
        for (var v : int = 0; v < visibleLockedIndex; v++) {
            pos += getSizeAt(lockedIndex - visibleLockedIndex + v); 
        }
        return pos;
    }
    
    public function getVisibleOffset(index : int) : int { 
        
        if (index < lockedIndex) {
            // locked
            return offset;
        }

        var minPos: Number = getVisibleLockedPosition();
        var newOffset : int = offset;
        var pos : Number;

        pos = getOffsetPositionAt(index) + getSizeAt(index);
        while (pos > visibleSize && newOffset + 1 < numCells) {
            pos -= getSizeAt(newOffset);
            newOffset++;
        }
        
        pos = getOffsetPositionAt(index);
        while (pos < minPos && newOffset - 1 >= lockedIndex) {
            newOffset--;    
            pos += getSizeAt(newOffset);
        }
        
        return newOffset;
    }
    
    private function getOffsetPositionAt(index : int) : Number {
        var pos : Number = getVisibleLockedPosition();
        while (offset < index) {
            index--;
            pos += getSizeAt(index);
        }
        while (offset > index) {
            pos -= getSizeAt(index);
            index++;                
        }
        return pos;
    }

    public function getPositionAt(index : int) : Number {
        var pos : Number = getVisibleLockedPosition();
        if (index >= lockedIndex) {
            while (offset < index) {
                index--;
                pos += getSizeAt(index);
            }
        } else {
            while (lockedIndex > index) {
                pos -= getSizeAt(index);
                index++;
            }
        }
        return pos;
    }
    
    public function getIndexAt(pos : Number) : int {
        var index : int = -1;
        step(function(index_ : int, pos_ : Number, size_ : Number) : void {
            if (pos_ <= pos && pos < pos_ + size_) {
                index = index_;
            }
        } );
        return index;
    }
    
    public function step(handler : Function) : void {
        
        var index : int;
        var size : Number;
        var pos : Number = 0;
        
        index = lockedIndex - visibleLockedIndex;
        while (pos < visibleSize && index < lockedIndex) {
            size = getSizeAt(index);
            handler(index, pos, size);
            pos += size;
            index++;
        }
        
//        index = offset;
        index = Math.max(offset, lockedIndex);
        while (pos < visibleSize && index < numCells) {
            size = getSizeAt(index);
            handler(index, pos, size);
            pos += size;
            index++;
        }
    }
}

class ResizeTarget {
    public var x : Number = 0;
    public var y : Number = 0;
    public var tempX : Number = 0;
    public var tempY : Number = 0;
    public var row : int = -1;
    public var column : int = -1;
}

class ResizeCursor extends Sprite {
    
    public var horizontal : Boolean = false;
    
    private var _bitmap : BitmapData = null;
    
    public function ResizeCursor() {
        visible = false;
        addEventListener(Event.ENTER_FRAME, enterFrameHandler);
    }
    
    protected function enterFrameHandler(event : Event) : void {
        
        rotation = horizontal? 0 : 90;
        
        if (_bitmap == null) {
            _bitmap = createArrow();
        }
        
        var offX : int = _bitmap.width / 2;
        var offY : int = _bitmap.height / 2;
        
        var g : Graphics = graphics;
        g.clear();
        g.beginBitmapFill(_bitmap, new Matrix(1, 0, 0, 1, -offX, -offY) );
        g.drawRect(-offX, -offY, _bitmap.width, _bitmap.height);
        g.endFill();
    }
    
    private function createArrow() : BitmapData {
        var pattern : Array = [
            "       00 00       ",
            "       0- -0       ",
            "    00 0- -0 00    ",
            "   0-0 0- -0 0-0   ",
            "  0--000- -000--0  ",
            " 0------- -------0 ",
            "0-------- --------0",
            " 0------- -------0 ",
            "  0--000- -000--0  ",
            "   0-0 0- -0 0-0   ",
            "    00 0- -0 00    ",
            "       0- -0       ",
            "       00 00       "
        ];
        var palette : Object = {
            "0" : 0xffffffff,
            "-" : 0xff000000
        };
        return createBitmap(pattern, palette);
    }
}

function createBitmap(pattern : Array, palette : Object) : BitmapData {
    var bitmap : BitmapData = new BitmapData(pattern[0].length, pattern.length, true, 0);
    for (var y : int = 0; y < bitmap.height; y++) {
        for (var x : int = 0; x < bitmap.width; x++) {
            var color : Object = palette[pattern[y].charAt(x)];
            if (color != null) {
                bitmap.setPixel32(x, y, uint(color) );
            }
        }
    }
    return bitmap;
}