diff --git a/src/com/danielfreeman/madcomponents/UIList.as b/src/com/danielfreeman/madcomponents/UIList.as
index 858d728..f7685d4 100644
--- a/src/com/danielfreeman/madcomponents/UIList.as
+++ b/src/com/danielfreeman/madcomponents/UIList.as
@@ -1 +1,1274 @@
-/**
*
Original Author: Daniel Freeman
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*
* Licensed under The MIT License
* Redistributions of files must retain the above copyright notice.
*/
package com.danielfreeman.madcomponents {
import flash.utils.Timer;
import flash.display.DisplayObject;
import flash.display.InteractiveObject;
import flash.display.Shape;
import flash.display.Sprite;
import flash.events.Event;
import flash.events.MouseEvent;
import flash.events.TextEvent;
import flash.events.TimerEvent;
import flash.geom.Rectangle;
import flash.text.TextFormat;
/**
* A list row was clicked. This is a bubbling event.
*/
[Event( name="clicked", type="flash.events.Event" )]
/**
* A list row was long-clicked.
*/
[Event( name="longClick", type="flash.events.Event" )]
/**
* The Pull-Down-To-Refresh header was activated
*/
[Event( name="pullRefresh", type="flash.events.Event" )]
/**
* MadComponents List
*
* <list
* id = "IDENTIFIER"
* colour = "#rrggbb"
* background = "#rrggbb, #rrggbb, ..."
* visible = "true|false"
* gapV = "NUMBER"
* gapH = "NUMBER"
* border = "true|false"
* autoLayout = "true|false"
* lines = "true|false"
* pullDownRefresh = "true|false"
* pullDownColour = "#rrggbb"
* sortBy = "IDENTIFIER"
* sortMode = "MODE"
* index = "INTEGER"
* highlightPressed = "true|false"
* showPressed = "true|false"
* mask = "true|false"
* alignV = "scroll|no scroll"
* labelField = "IDENTIFIER"
* arrow = "true|false"
* lineGap = "NUMBER"
* />
*
*/
public class UIList extends UIScrollVertical {
public static const CLICK_START:String = "clickStart";
public static const CLICKED:String = "clicked";
public static const CLICKED_END:String = "listClickedEnd";
public static const CLICK_CANCEL:String = "listClickCancel";
public static const LONG_CLICK:String = "longClick";
public static const REFRESH:String = "pullRefresh";
protected static const ARROW:Number = 6;
protected static const TOP:Number = 40;
protected static const PULL_THRESHOLD:Number = 70;
protected const FORMAT:TextFormat = new TextFormat("Tahoma",18);
public static var LONG_CLICK_THRESHOLD:int = 20;
public static var HIGHLIGHT:uint = 0x9999FF;
public static var PULL_DOWN_TEXT:String = "pull down to refresh";
public static var REFRESH_TEXT:String = "refreshing...";
public static var TOUCH_THRESHOLD:int = 1;
protected var _renderer:XML;
protected var _simple:Boolean;
protected var _count:int = 0;
protected var _cellHeight:int = -1;
protected var _pressedCell:int = -1;
protected var _highlight:Shape;
protected var _clickRow:Boolean = true;
protected var _cellTop:int = -99999;
protected var _cellRendererLeft:Number = 0;
protected var _colours:Vector.;
protected var _suffix:String = "";
protected var _font:String = "";
protected var _model:Model = null;
protected var _search:UISearch = null;
protected var _top:int = 0;
protected var _rendererAttributes:Attributes;
protected var _field:String;
protected var _begins:Boolean;
protected var _data:Array;
protected var _filteredData:Array = [];
protected var _sortBy:String = "";
protected var _sortMode:String = "";
protected var _row:DisplayObject;
protected var _lines:Boolean = false;
protected var _cell:*;
protected var _refresh:UIRefresh = null;
protected var _refreshState:Boolean = false;
protected var _showPressed:Boolean = false;
protected var _textAlign:String;
protected var _highlightPressed:Boolean = true;
protected var _rowClick:Boolean = false;
protected var _highlightColour:uint = HIGHLIGHT;
protected var _labelField:String = "label";
protected var _highlightIsOn:Boolean = false;
protected var _saveIndex:int = -1;
protected var _header:int = 0;
protected var _arrows:Boolean;
protected var _lineGap:Number;
protected var _lastPosition:int;
protected var _highlightTimer:Timer = new Timer(CLICK_DURATION, 1);
public function UIList(screen:Sprite, xml:XML, attributes:Attributes) {
_colours = attributes.backgroundColours;
_arrows = xml.@arrows == "true";
_xml = xml;
_lineGap = attributes.paddingH;
if (xml.@header.length() > 0) {
_header = parseInt(xml.@header);
}
_highlightPressed = xml.@highlightPressed != "false";
if (xml.@labelField.length() > 0) {
_labelField = xml.@labelField;
}
if (xml.@highlightColour.length() > 0) {
_highlightColour = UI.toColourValue(xml.@highlightColour);
}
if (xml.@lineGap.length() > 0) {
_lineGap = parseFloat(xml.@lineGap);
}
super(screen, xml, attributes);
_renderer = extractRenderer(xml);
_simple = _renderer.toXMLString() == "";
if (!_simple && !_rendererAttributes) {
initialiseRenderAttributes(xml, attributes);
}
_lines= xml.@lines == "true";
if (xml.font.length()>0) {
_font = xml.font[0].toXMLString();
}
if (xml.search.length()>0) {
addASearch(xml);
}
if (xml.@pullDownRefresh.length()>0 && xml.@pullDownRefresh[0]!="false") {
_refresh = new UIRefresh(_slider, 0, -TOP/2, xml.@pullDownColour.length()>0 ? UI.toColourValue(xml.@pullDownColour[0]) : 0x333333, PULL_DOWN_TEXT);
}
if (xml.@sortBy.length()>0) {
_sortBy = xml.@sortBy[0];
}
if (xml.@sortMode.length()>0) {
_sortMode = xml.@sortMode[0];
}
if (xml.@showPressed.length()>0) {
_showPressed = xml.@showPressed[0]=="true";
}
if (xml.data.length()>0) {
xmlData = xml.data[0];
}
if (xml.model.length()>0) {
_model = new UI.ModelClass(this, xml.model[0]);
}
buttonMode = useHandCursor = true;
if (xml.@index.length()>0) {
index = parseInt(xml.@index);
}
_highlightTimer.addEventListener(TimerEvent.TIMER, clearHighlight);
}
protected function addASearch(xml:XML):void {
var searchAttributes:Attributes = attributes.copy(xml.search[0]);
searchAttributes.x = searchAttributes.y = 0;
_search = new UISearch(_slider,xml.search[0], searchAttributes);
if (xml.search[0].@id.length()>0) {
_search.name = xml.search[0].@id[0];
}
_top = Math.ceil(_search.height);
if (xml.search[0].@field.length()>0) {
_field = xml.search[0].@field;
_begins = xml.search[0].@begins == "true";
_search.addEventListener(TextEvent.TEXT_INPUT, searchHandler);
}
}
/**
* Dynamically set the list renderer
*/
public function set rendererXML(value:XML):void {
_renderer = value;
_simple = value == null;
_autoLayout = !_simple && _xml.@autoLayout == "true";
}
/**
* DEPRECIATED - will be removed soon.
*/
public function set renderer(value:XML):void {
rendererXML = value;
}
public function set arrows(value:Boolean):void {
_arrows = value;
}
public function set header(value:int):void {
_header = value;
}
public function get header():int {
return _header;
}
public function set labelField(value:String):void {
_labelField = value ? value : "label";
}
public function set colours(value:Vector.):void {
_colours = value;
}
protected function initialiseRenderAttributes(xml:XML, attributes:Attributes):void {
_rendererAttributes = attributes.copy();
_rendererAttributes.parse(_renderer);
_rendererAttributes.x = _rendererAttributes.y = 0;
_rendererAttributes.width -= (2*_attributes.paddingH + ((_attributes.style7 && _arrows) ? UI.PADDING : 0));
_rendererAttributes.height -= 2*_attributes.paddingV;
}
protected function indexToScrollPosition(value:int):Number {
return _cellHeight * value - _offset;
}
public function setIndex(value:int, animate:Boolean = false, move:Boolean = false, highlight:Boolean = false, offset:Number = 0):void {
_pressedCell = value;
if (value < 0) {
return;
}
if (move) { // if animate is true too - use an alternative move algorithm.
var position:int = indexToScrollPosition(value) - offset;
// scrollPositionY = (position < 0) ? 0 : position;
scrollPositionY = animate ? ((position < _attributes.height) ? 0 : position - 128) : ((position < 0) ? 0 : position);
}
else if (animate) { // && sliderY + _cellHeight * value < MAXIMUM_DY/2) {
_endSlider = indexToScrollPosition(value);
_moveTimer.start();
}
if (highlight) {
illuminate(value, false);
}
}
/**
* Scroll to index
*/
public function set index(value:int):void {
setIndex(value, false, false, _showPressed);
}
public function get rendererXML():XML {
return _renderer;
}
override protected function doLayoutHandler(event:Event):void {
doLayout();
event.stopPropagation();
}
/**
* Extract renderer XML from XML
*/
protected function extractRenderer(xml:XML):XML {
var children:XMLList = xml.children();
if (children.length()==0)
return XML("");
else if (xml.data.length()==0 && xml.font.length()==0 && xml.model.length()==0 && xml.search.length()==0)
return children[0];
for each (var child:XML in children) {
if (child.localName()!="data" && child.localName()!="font" && child.localName()!="model" && child.localName()!="search" && child.localName()!="detail") {
return child;
}
}
return XML("");
}
/**
* If set to false, the no highlight appears
*/
public function set highlightPressed(value:Boolean):void {
_highlightPressed = value;
}
/**
* If set to true, the click highlight remains
*/
public function set showPressed(value:Boolean):void {
_showPressed = value;
}
public function get showPressed():Boolean {
return _showPressed;
}
/**
* Clears the click highlight.
*/
public function clearPressed():void {
_highlight.graphics.clear();
_highlightIsOn = false;
}
protected function clearHighlight(event:Event):void {
clearPressed();
dispatchEvent(new Event(CLICKED_END, true));
}
protected function dispatchClickedEnd():void {
if (!_classic) {
dispatchEvent(new Event(CLICKED, true));
if (_touchTimer.currentCount < TOUCH_DELAY) {
illuminate(-1, false, true);
if (_showPressed) {
dispatchEvent(new Event(CLICKED_END, true));
}
else {
_highlightTimer.stop();
_highlightTimer.reset();
_highlightTimer.start();
}
}
else {
dispatchEvent(new Event(CLICKED_END, true));
}
}
else {
dispatchEvent(new Event(CLICKED_END, true));
}
}
/**
* Clears the click highlight. - for controlling list externally and firing CLICKED_END event.
* (Most developers will likely never have a need to call this).
*/
public function endPressed(delay:Boolean = false):void {
if (delay) {
_showPressed = false;
_clickTimer.reset();
_clickTimer.start();
}
else {
clearPressed();
dispatchClickedEnd();
}
}
/**
* Set XML data
*/
public function set xmlData(value:XML):void {
var result:Array = [];
var children:XMLList = value.children();
for each (var child:XML in children) {
if (child.nodeKind()!="text") {
result.push(attributesToObject(child));
}
}
data = result;
}
protected function attributesToObject(child:XML):Object {
var attributes:XMLList = child.attributes();
if (attributes.length()==0) {
return {label:child.localName()};
}
else {
var result:Object = new Object();
for (var i:int=0; i 0) {
graphics.beginFill(_colours[0]);
}
else {
graphics.beginFill(0,0);
}
graphics.drawRect(0, 0, _attributes.widthH, _attributes.heightV);
}
/**
* Rearrange the layout to new screen dimensions
*/
override public function layout(attributes:Attributes):void {
var i:int;
var cell:*;
_width = attributes.width;
_height = attributes.height;
if (_search) {
_search.layout(attributes);
}
_attributes = attributes;
drawComponent();
_slider.graphics.clear();
// initDraw();
if (_simple) {
for (i = 0; i<_slider.numChildren; i++) {
cell = _slider.getChildAt(i);
if (cell is UILabel && !(cell is UISearch)) {
UILabel(cell).fixwidth = _attributes.width-2*_attributes.paddingH;
cell.multiline = cell.wordWrap = false;
}
}
}
else {
// initialiseRenderAttributes(xml, attributes);
_rendererAttributes.width=attributes.width - (2*attributes.paddingH + ((_attributes.style7 && _arrows) ? UI.PADDING : 0));
_rendererAttributes.height=attributes.height - 2*attributes.paddingV;
if (_filteredData && _autoLayout) {
autoLayout();
}
else {
for (i = 0; i<_slider.numChildren; i++) {
cell = _slider.getChildAt(i);
if (cell is IComponentUI && !(cell is UISearch)) {
IComponentUI(cell).layout(_rendererAttributes);
}
}
}
}
refreshMasking();
if (!_autoLayout || _simple) {
redrawCells();
}
if (_highlightIsOn && _showPressed) {
setIndex(_pressedCell, false, false, true);
}
calculateMaximumSlide();
}
protected function autoLayout():void {
var last:int = _top + _attributes.paddingV;
var i:int = 0;
initDraw();
for each (var record:Object in _filteredData) {
var cell:* = _slider.getChildByName("label_"+i.toString());
if (cell && cell is IComponentUI) {
IComponentUI(cell).layout(_rendererAttributes);
cell.y = last;
last += Math.ceil(cell.height + _attributes.paddingV);
drawCell(last, i, record);
last += Math.ceil(_attributes.paddingV);
i++;
}
}
}
/**
* Refresh layout
*/
override public function doLayout():void {
layout(_attributes);
adjustMaximumSlide();
}
/**
* Redraw cell chrome
*/
protected function redrawCells():void {
initDraw();
if (!_autoLayout) {
var l:int = 0;
for each (var record:Object in _filteredData) {
drawCell(Math.ceil(_cellHeight*(l+1) + _top), l, record);
l++;
}
}
}
/**
* Set up the scrolling part of the list
*/
override protected function createSlider(xml:XML, attributes:Attributes):void {
addChild(_slider = new Sprite());
_slider.addChild(_highlight=new Shape());
_width = attributes.width;
_height = attributes.height;
}
protected function sortParameter(value:String):* {
if (value.indexOf(",")<0) {
return value;
}
else {
return value.split(",");
}
}
/**
* Assign array of objects data
*/
override public function set data(value:Object):void {
_data = value as Array;
_saveIndex = -1;
if (_sortBy!="" && _data) {
_data.sortOn(sortParameter(_sortBy),sortParameter(_sortMode));
}
clearPressed();
filteredData = value as Array;
}
public function setData(value:Array, index:int = 0):Boolean {
for each(var record:* in value) {
var cell:* = _slider.getChildByName("label_"+index.toString() + _suffix);
if (cell is UILabel) {
UILabel(cell).xmlText = (record is String) ? record : record[_labelField];
}
else if (cell is IContainerUI) {
fillInValues(cell, record);
}
else {
return true;
}
index++;
}
if (_autoLayout) {
doLayout();
calculateMaximumSlide();
}
return false;
}
/**
* Set filtered data ( a sub-set of full data ).
*/
public function set filteredData(value:Array):void {
_filteredData = value;
data0 = value;
}
/**
* Set list data
*/
protected function set data0(value:Array):void {
if (_refresh) {
_refresh.changeState(PULL_DOWN_TEXT, false);
}
clearCells();
initDraw();
if (_simple) {
simpleRenderers(value, _cellTop + 2 * _attributes.paddingV);
}
else {
customRenderers(value, _cellTop + _attributes.paddingV);
}
if (_autoLayout) {
doLayout();
}
calculateMaximumSlide();
}
/**
* Calculate maximum slide
*/
protected function calculateMaximumSlide():void {
_scrollerHeight = _slider.height - (_refresh ? TOP : 0);
_maximumSlide = _scrollerHeight - _height;
if (_maximumSlide < 0) {
_maximumSlide = 0;
}
if (_count>0 && (_cellHeight<0 || _autoLayout)) {
_cellHeight = Math.ceil((_slider.height - _top - (_refresh ? TOP : 0)) / _count);
}
}
override protected function adjustMaximumSlide():void {
}
/**
* Data
*/
public function get data():Object {
return _data;
}
/**
* Filtered data
*/
public function get filteredData():Array {
return _filteredData;
}
protected function filterArray(data:Array, searchFor:String, field:String, caseSensitive:Boolean, begins:Boolean):Array {
var result:Array = [];
var index:int = 0;
for each (var record:Object in data) {
var item:String = record[field];
record.$index = index++;
if (!caseSensitive) {
item = item.toLowerCase();
}
if (begins) {
if (item.substr(0, searchFor.length) == searchFor) {
result.push(record);
}
}
else {
if (item.indexOf(searchFor) >= 0) {
result.push(record);
}
}
}
return result;
}
/**
* Filter the data according to a search string
*/
public function filter(searchFor:String, field:String = "", caseSensitive:Boolean = false, begins:Boolean = false):void {
if (searchFor == "") {
filteredData = _data;
}
else {
if (field == "") {
field = _field;
}
filteredData = filterArray(_data, searchFor, field, caseSensitive, begins);
}
}
/**
* Create list with simple default label rows
*/
protected function simpleRenderers(value:Array, position:Number = -1):void {
if (position < 0) {
position = 2 * _attributes.paddingV;
}
_count = 0;
_textAlign = _attributes.textAlign;
for each (var record:* in value) {
var label:UILabel = labelCell(record, position);
position += Math.ceil(label.height + 2 * _attributes.paddingV);
drawCell(position, _count, record);
position += 2 * _attributes.paddingV;
_cellHeight = Math.ceil(4 * _attributes.paddingV + label.height);
_count++;
}
}
/**
* Create a simple list label row
*/
protected function labelCell(record:*, position:Number):UILabel {
var labelText:String = record is String ? record : record[_labelField];
var label:UILabel = newLabel();//_attributes.x +
label.y = position;
label.name = "label_"+_count.toString()+_suffix;
if (XML(""+labelText+"").hasComplexContent()) {
var xmlString:String = XML(""+labelText+"").toXMLString();
label.htmlText = xmlString;
}
else {
if (_font!="") {
label.htmlText = _font.substr(0,_font.length-2)+ ">" + labelText + "";
}
else {
label.text = labelText;
}
}
if (_textAlign != "") {
var format:TextFormat = new TextFormat();
format.align = _textAlign;
label.setTextFormat(format);
}
label.fixwidth = _attributes.width-2*_attributes.paddingH;
label.multiline = label.wordWrap = false;
return label;
}
/**
* Clear list
*/
protected function initDraw():void {
_slider.graphics.clear();
resizeRefresh();
_slider.graphics.beginFill(_colour);
_slider.graphics.drawRect(0, _top, _width, 1);
_lastPosition = _cellTop = _top;
}
/**
* Resize list row chrome
*/
protected function resizeRefresh():void {
if (_refresh) {
_slider.graphics.beginFill(_colours.length>0 ? _colours[_colours.length-1] : 0xF9F9F9);
_slider.graphics.drawRect(0, -TOP, attributes.width, TOP);
_refresh.x = (attributes.width - _refresh.width) / 2;
}
}
/**
* Clear list
*/
protected function clearCells():void {
var i:int = _slider.numChildren;
while (--i>=(_search ? 2 : 1)+(_refresh ? 1 : 0)) {
var child:DisplayObject = DisplayObject(_slider.getChildAt(i));
if (child.hasOwnProperty("destructor")) {
Object(child).destructor();
}
_slider.removeChildAt(i);
}
}
protected function hasLines(record:Object):Boolean {
return record.hasOwnProperty("lines") ? record.$lines === true || _lines && (record.$lines !== false) : _lines;
}
/**
* Draw row chrome
*/
protected function drawCell(position:Number, count:int, record:*):void {
drawSimpleCell(position, count, record.hasOwnProperty("$colour") ? record.$colour : uint.MAX_VALUE);
if (!(record is String) && hasLines(record)) {
drawLines(position);
}
}
protected function drawArrow(x:Number, y:Number):void {
_slider.graphics.beginFill(_colour);
_slider.graphics.moveTo(x, y);
_slider.graphics.lineTo(x - ARROW, y - ARROW);
if (_attributes.style7) {
_slider.graphics.lineTo(x - ARROW, y - ARROW + 3);
_slider.graphics.lineTo(x - 3, y);
_slider.graphics.lineTo(x - ARROW, y + ARROW - 3);
}
_slider.graphics.lineTo(x - ARROW, y + ARROW);
_slider.graphics.lineTo(x, y);
_slider.graphics.endFill();
}
/**
* Draw row chrome
*/
protected function drawSimpleCell(position:Number, count:int, colour:uint = uint.MAX_VALUE):void {
if (_colours.length > 1 || colour != uint.MAX_VALUE) {
_slider.graphics.beginFill((colour != uint.MAX_VALUE) ? colour : _colours[count % (_colours.length - 1) + 1]);
_slider.graphics.drawRect(0, _lastPosition + (_attributes.style7 ? 1 : 2), _width, position - _lastPosition - (_attributes.style7 ? 0 : 1));
_slider.graphics.endFill();
}
_slider.graphics.beginFill(_colour);
var startLine:Number = _attributes.style7 ? _lineGap : 0;
_slider.graphics.drawRect(startLine, position, _width - startLine, 1);
_slider.graphics.endFill();
if (_arrows && (_header > 0 ? count >= _header : count < _count + _header)) {
drawArrow(_width - _attributes.paddingH, (_lastPosition + position) / 2);
}
_lastPosition = position;
}
/**
* Draw lines within row
*/
protected function drawLines(position:Number):void {
if (_cell is UIForm) {
_slider.graphics.beginFill(_colour);
var positions:Array = UIForm(_cell).positions;
for (var i:int = 1; i < positions.length; i++) {
_slider.graphics.drawRect(_cell.x + positions[i] - _rendererAttributes.paddingH / 2, _cellTop, 1, position - _cellTop);
}
_slider.graphics.endFill();
}
}
/**
* Return DisplayObject of button pressed
*/
override protected function pressButton(show:Boolean = true):DisplayObject {
_scrollBarLayer.graphics.clear();
clearPressed();
if (!show && (!_simple || _slider.mouseY < _top)) {
doSearchHit();
}
illuminate(-1, !show, show);
return _pressButton;
}
public function doClickRow(dispatch:Boolean = true):Boolean {
_highlight.graphics.clear();
_showPressed = true;
illuminate(-1, dispatch);
return dispatch && _pressedCell >= 0 && _pressedCell < _count;
}
protected function pressedCellLimits(groupDetail:Object = null):void {
if (_pressedCell < _header || _pressedCell >= _count) {
_pressedCell = _saveIndex;
}
}
protected function highlightForIndex(rowIndex:int):Boolean {
var rowData:* = _data[rowIndex];
if (rowData.hasOwnProperty("$highlight")) {
return rowData.$highlight;
}
else {
return _highlightPressed;
}
}
protected function illuminate(pressedCell:int = -1, dispatch:Boolean = true, show:Boolean = true):void {
const tweak:Number = 1.0;
var sliderMouseY:Number = _slider.visible ? _slider.mouseY : mouseY - _sliderPosition;
if (!_pressButton && _clickRow) {
if (_autoLayout && !_simple && sliderMouseY > _top) {
_pressedCell = pressedCell>=0 ? pressedCell : autoLayoutPressedCell(sliderMouseY);
pressedCellLimits();
if (_row && _pressedCell >= _header && _pressedCell < _count) {
// if (_highlightPressed) {
if (show && highlightForIndex(_pressedCell)) {
_highlight.graphics.beginFill(_highlightColour);
_highlight.graphics.drawRect(0, _row.y - _attributes.paddingV + tweak, _width, _row.height + 2*_attributes.paddingV - tweak ); //_attributes.x +
_highlight.graphics.endFill();
}
activate(dispatch);
}
}
else {
_pressedCell = pressedCell>=0 ? pressedCell : Math.floor((sliderMouseY - _top)/_cellHeight);
pressedCellLimits();
if (_pressedCell >= _header && _pressedCell < _count) {
// if (_highlightPressed) {
if (show && highlightForIndex(_pressedCell)) {
_highlight.graphics.beginFill(_highlightColour);
_highlight.graphics.drawRect(0, _top + _pressedCell * _cellHeight + tweak, _width, _cellHeight - tweak); //_attributes.x +
_highlight.graphics.endFill();
}
activate(dispatch);
}
}
}
}
/**
* If autoLayout="true", which cell was clicked?
*/
protected function autoLayoutPressedCell(y:Number):int {
var n:int = 0;
for (var l:int=0;l<_slider.numChildren - 1;l++) {
var row:* = _slider.getChildAt(l+1);
if (row) {
_row = DisplayObject(row);
if (_row.y + _row.height + _attributes.paddingV > y)
return n;
n++;
}
}
return -1;
}
/**
* Row has been clicked
*/
protected function activate(dispatch:Boolean = true):void {
if (_classic) {
_touchTimer.stop();
_dragTimer.stop();
stage.removeEventListener(MouseEvent.MOUSE_UP, mouseUp);
addEventListener(MouseEvent.MOUSE_DOWN, mouseDown);
_clickTimer.stop();
_clickTimer.reset();
_clickTimer.start();
}
// stopMovement(); //new - interfered with picker.
_rowClick = true;
if (dispatch) {
// _rowClick = true;
// if (_touchTimer.currentCount >= LONG_CLICK_THRESHOLD) {trace("long click");
// dispatchEvent(new Event(LONG_CLICK));
// }
// else {
dispatchEvent(new Event(_classic ? CLICKED : CLICK_START, true));
// }
}
}
override protected function mouseDown(event:MouseEvent):void {
_saveIndex = _pressedCell;
_rowClick = false;
super.mouseDown(event);
}
override protected function mouseUp(event:MouseEvent):void {
if (!_classic && _rowClick) {
if (!_showPressed) {
clearPressed();
}
if (_touchTimer.currentCount < TOUCH_THRESHOLD || Math.abs(_delta) > DELTA_THRESHOLD) {
doCancel();
}
else {
dispatchClickedEnd();
}
_rowClick = false;
}
super.mouseUp(event);
}
protected function doCancel():void {
_pressedCell = _saveIndex;
dispatchEvent(new Event(CLICK_CANCEL, true));
_highlight.graphics.clear();
if (_showPressed && _pressedCell >= 0) {
illuminate(_pressedCell, false);
}
}
override public function touchCancel():void {
super.touchCancel();
clearPressed();
if (!_classic && _rowClick) {
// _highlight.graphics.clear();
doCancel();
_rowClick = false;
}
}
/**
* Index of last row clicked
*/
public function get index():int {
return _pressedCell;
}
/**
* Data object for last row clicked
*/
public function get row():Object {
return (_pressedCell>=0) ? _filteredData[_pressedCell] : null;
}
/**
* Click up handler
*/
override protected function clickUp(event:TimerEvent):void {
if (!_simple) {
super.clickUp(event);
}
if (_clickRow) {
if (!_showPressed) {
clearPressed();
}
dispatchClickedEnd();
}
_scrollBarLayer.graphics.clear();
}
/**
* Create list with custom renderers
*/
protected function customRenderers(value:Array, position:Number = -1):void {
// var before:Number = _rendererAttributes.paddingH;
if (position < 0)
position = _attributes.paddingV;
_count = 0;
for each (var record:Object in value) {
customCell(record, position);
position += Math.ceil(_cell.height + _attributes.paddingV);
drawCell(position, _count, record);
position += Math.ceil(_attributes.paddingV);
_count++;
}
}
/**
* Instanciate a new list row
*/
protected function newRow(rendererXML:XML):DisplayObject {
var result:DisplayObject;
var before:Number = _rendererAttributes.paddingH;
if (UI.isForm(rendererXML.localName())) {
result = new UI.FormClass(_slider, rendererXML, _rendererAttributes, true);
}
else {
result = UI.containers(_slider, rendererXML, _rendererAttributes.copy());
}
return result;
}
/**
* Instanciate a new list label
*/
protected function newLabel():UILabel {
return new UILabel(_slider, _attributes.paddingH, 0, "", FORMAT);
}
/**
* Create and position a new list row
*/
protected function customCell(record:Object, position:Number):void {
// if (!UI.isForm(_renderer.localName())) {
// var localName:String = _renderer.localName();
// if (!UI.isContainer(localName) && !UI.isForm(localName)) {
// _renderer = XML("" + _renderer.toXMLString() + "");
// }
_cell = newRow(record.hasOwnProperty("$renderer") ? record.$renderer : _renderer);
_cell.x = _cellRendererLeft + _attributes.paddingH;
_cell.y = Math.ceil(position);
if (_cell is Sprite) {
_cell.mouseChildren = false;
}
_cell.name = "label_"+_count.toString()+_suffix;
fillInValues(_cell, record);
}
/**
* Assign data to custom renderer components
*/
protected function fillInValues(cell:DisplayObject, record:Object):void {
var view:DisplayObject;
if (record is String) {
view = (cell is IContainerUI) ? IContainerUI(cell).findViewById(_labelField) : cell;
if (view is UILabel) {
UILabel(view).xmlText = value;
}
else if (view.hasOwnProperty("text")) {
Object(view).text = value;
}
}
else {
for (var id:String in record) {
view = (cell is IContainerUI) ? IContainerUI(cell).findViewById(id) : ((id == _labelField) ? cell : null);
if (view) {
var value:String = record[id];
if (view is UILabel) {
UILabel(view).xmlText = value;
}
else if (view.hasOwnProperty("text")) {
Object(view).text = value;
}
/* try {
if (XML(""+value+"").hasComplexContent() && view is UILabel) {
view.htmlText = value;
}
else {
view.text = value;
}
}
catch (error:Error) {
view.text = (value != null) ? value : "";
}*/
}
}
}
}
/**
* If true, rows are clickable
*/
public function set clickRow(value:Boolean):void {
_clickRow = value;
}
/**
* Model
*/
override public function get model():Model {
return _model;
}
/**
* Handle search field filter
*/
protected function searchHandler(event:Event):void {
filter(_search.text.toLowerCase(), "", false, _begins);
}
/**
* Determine what has been clicked
*/
override protected function doSearchHit():void {
super.doSearchHit();
if (_pressButton is InteractiveObject && !InteractiveObject(_pressButton).mouseEnabled) {
_pressButton = null;
}
}
/**
* Return component matching id within row matching row index
*/
override public function findViewById(id:String, row:int = -1, group:int = -1):DisplayObject {
if (_search && _search.name == id) {
return _search;
}
else if (_simple) {
return _slider.getChildByName(id+"_"+row.toString());
}
else if (row>=0) {
var container:IContainerUI = IContainerUI(_slider.getChildByName("label_"+row.toString()));
return container ? container.findViewById(id, row, group) : null;
}
else {
return null;
}
}
public function get rowContainer():IContainerUI {
return IContainerUI(_slider.getChildAt(_pressedCell+1));
}
/**
* Mouse move handler
*/
override protected function mouseMove(event:TimerEvent):void {
if (_refresh && !_refreshState && sliderY > PULL_THRESHOLD) {
_refresh.changeState(REFRESH_TEXT, true);
if (_model) {
_model.refresh();
}
_refreshState = true;
dispatchEvent(new Event(REFRESH));
}
super.mouseMove(event);
if (_rowClick && _distance < THRESHOLD && _touchTimer.currentCount == LONG_CLICK_THRESHOLD) {
dispatchEvent(new Event(LONG_CLICK));
}
}
/**
* Stop list movement
*/
override protected function stopMovement():void {
super.stopMovement();
_refreshState = false;
}
public function get length():int {
return _count;
}
override public function rowRectangle(y:Number):Rectangle {
for (var l:int=0;l<_slider.numChildren - 1;l++) {
var row:DisplayObject = _slider.getChildAt(l+1);
if (row.y + row.height + _attributes.paddingV > y) {
if (_simple) {
return new Rectangle(0, row.y - 2 * _attributes.paddingV, _attributes.width, row.height + 4 * _attributes.paddingV);
}
else {
return new Rectangle(0, row.y - _attributes.paddingV, _attributes.width, row.height + 2 * _attributes.paddingV);
}
}
}
return null;
}
public function set searchVisible(value:Boolean):void {
if (_search) {
_search.visible = value;
_top = value ? _search.height : 0;
}
}
override public function clear():void {
data = [];
}
override public function destructor():void {
if (_search) {
_search.removeEventListener(TextEvent.TEXT_INPUT, searchHandler);
_search.destructor();
}
_highlightTimer.removeEventListener(TimerEvent.TIMER, clearHighlight);
super.destructor();
}
}
}
\ No newline at end of file
+/**
+ * Original Author: Daniel Freeman
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ *
+ * Licensed under The MIT License
+ * Redistributions of files must retain the above copyright notice.
+ */
+
+package com.danielfreeman.madcomponents {
+
+ import flash.utils.Timer;
+ import flash.display.DisplayObject;
+ import flash.display.InteractiveObject;
+ import flash.display.Shape;
+ import flash.display.Sprite;
+ import flash.events.Event;
+ import flash.events.MouseEvent;
+ import flash.events.TextEvent;
+ import flash.events.TimerEvent;
+ import flash.geom.Rectangle;
+ import flash.text.TextFormat;
+
+/**
+ * A list row was clicked. This is a bubbling event.
+ */
+ [Event( name="clicked", type="flash.events.Event" )]
+
+/**
+ * A list row was long-clicked.
+ */
+ [Event( name="longClick", type="flash.events.Event" )]
+
+/**
+ * The Pull-Down-To-Refresh header was activated
+ */
+ [Event( name="pullRefresh", type="flash.events.Event" )]
+
+
+/**
+ * MadComponents List
+ *
+ * <list
+ * id = "IDENTIFIER"
+ * colour = "#rrggbb"
+ * background = "#rrggbb, #rrggbb, ..."
+ * visible = "true|false"
+ * gapV = "NUMBER"
+ * gapH = "NUMBER"
+ * border = "true|false"
+ * autoLayout = "true|false"
+ * lines = "true|false"
+ * pullDownRefresh = "true|false"
+ * pullDownColour = "#rrggbb"
+ * sortBy = "IDENTIFIER"
+ * sortMode = "MODE"
+ * index = "INTEGER"
+ * highlightPressed = "true|false"
+ * showPressed = "true|false"
+ * mask = "true|false"
+ * alignV = "scroll|no scroll"
+ * labelField = "IDENTIFIER"
+ * arrow = "true|false"
+ * lineGap = "NUMBER"
+ * />
+ *
+ */
+ public class UIList extends UIScrollVertical {
+
+ public static const CLICK_START:String = "clickStart";
+ public static const CLICKED:String = "clicked";
+ public static const CLICKED_END:String = "listClickedEnd";
+ public static const CLICK_CANCEL:String = "listClickCancel";
+ public static const LONG_CLICK:String = "longClick";
+ public static const REFRESH:String = "pullRefresh";
+
+ protected static const ARROW:Number = 6;
+ protected static const TOP:Number = 40;
+ protected static const PULL_THRESHOLD:Number = 70;
+
+ protected const FORMAT:TextFormat = new TextFormat("Tahoma",18);
+
+ public static var LONG_CLICK_THRESHOLD:int = 20;
+ public static var HIGHLIGHT:uint = 0x9999FF;
+ public static var PULL_DOWN_TEXT:String = "pull down to refresh";
+ public static var REFRESH_TEXT:String = "refreshing...";
+ public static var TOUCH_THRESHOLD:int = 1;
+
+ protected var _renderer:XML;
+ protected var _simple:Boolean;
+ protected var _count:int = 0;
+ protected var _cellHeight:int = -1;
+ protected var _pressedCell:int = -1;
+ protected var _highlight:Shape;
+ protected var _clickRow:Boolean = true;
+ protected var _cellTop:int = -99999;
+ protected var _cellRendererLeft:Number = 0;
+ protected var _colours:Vector.;
+ protected var _suffix:String = "";
+ protected var _font:String = "";
+ protected var _model:Model = null;
+ protected var _search:UISearch = null;
+ protected var _top:int = 0;
+ protected var _rendererAttributes:Attributes;
+ protected var _field:String;
+ protected var _begins:Boolean;
+ protected var _data:Array;
+ protected var _filteredData:Array = [];
+ protected var _sortBy:String = "";
+ protected var _sortMode:String = "";
+ protected var _row:DisplayObject;
+ protected var _lines:Boolean = false;
+ protected var _cell:*;
+ protected var _refresh:UIRefresh = null;
+ protected var _refreshState:Boolean = false;
+ protected var _showPressed:Boolean = false;
+ protected var _textAlign:String;
+ protected var _highlightPressed:Boolean = true;
+ protected var _rowClick:Boolean = false;
+ protected var _highlightColour:uint = HIGHLIGHT;
+ protected var _labelField:String = "label";
+ protected var _highlightIsOn:Boolean = false;
+ protected var _saveIndex:int = -1;
+ protected var _header:int = 0;
+ protected var _arrows:Boolean;
+ protected var _lineGap:Number;
+ protected var _lastPosition:int;
+ protected var _highlightTimer:Timer = new Timer(CLICK_DURATION, 1);
+
+
+ public function UIList(screen:Sprite, xml:XML, attributes:Attributes) {
+ _colours = attributes.backgroundColours;
+ _arrows = xml.@arrows == "true";
+ _xml = xml;
+ _lineGap = attributes.paddingH;
+ if (xml.@header.length() > 0) {
+ _header = parseInt(xml.@header);
+ }
+ _highlightPressed = xml.@highlightPressed != "false";
+ if (xml.@labelField.length() > 0) {
+ _labelField = xml.@labelField;
+ }
+ if (xml.@highlightColour.length() > 0) {
+ _highlightColour = UI.toColourValue(xml.@highlightColour);
+ }
+ if (xml.@lineGap.length() > 0) {
+ _lineGap = parseFloat(xml.@lineGap);
+ }
+ super(screen, xml, attributes);
+ _renderer = extractRenderer(xml);
+ _simple = _renderer.toXMLString() == "";
+ if (!_simple && !_rendererAttributes) {
+ initialiseRenderAttributes(xml, attributes);
+ }
+ _lines= xml.@lines == "true";
+ if (xml.font.length()>0) {
+ _font = xml.font[0].toXMLString();
+ }
+ if (xml.search.length()>0) {
+ addASearch(xml);
+ }
+ if (xml.@pullDownRefresh.length()>0 && xml.@pullDownRefresh[0]!="false") {
+ _refresh = new UIRefresh(_slider, 0, -TOP/2, xml.@pullDownColour.length()>0 ? UI.toColourValue(xml.@pullDownColour[0]) : 0x333333, PULL_DOWN_TEXT);
+ _refresh.mouseEnabled = false;
+ }
+ if (xml.@sortBy.length()>0) {
+ _sortBy = xml.@sortBy[0];
+ }
+ if (xml.@sortMode.length()>0) {
+ _sortMode = xml.@sortMode[0];
+ }
+ if (xml.@showPressed.length()>0) {
+ _showPressed = xml.@showPressed[0]=="true";
+ }
+ if (xml.data.length()>0) {
+ xmlData = xml.data[0];
+ }
+ if (xml.model.length()>0) {
+ _model = new UI.ModelClass(this, xml.model[0]);
+ }
+ buttonMode = useHandCursor = true;
+ if (xml.@index.length()>0) {
+ index = parseInt(xml.@index);
+ }
+ _highlightTimer.addEventListener(TimerEvent.TIMER, clearHighlight);
+ }
+
+
+ protected function addASearch(xml:XML):void {
+ var searchAttributes:Attributes = attributes.copy(xml.search[0]);
+ searchAttributes.x = searchAttributes.y = 0;
+ _search = new UISearch(_slider,xml.search[0], searchAttributes);
+ if (xml.search[0].@id.length()>0) {
+ _search.name = xml.search[0].@id[0];
+ }
+ _top = Math.ceil(_search.height);
+ if (xml.search[0].@field.length()>0) {
+ _field = xml.search[0].@field;
+ _begins = xml.search[0].@begins == "true";
+ _search.addEventListener(TextEvent.TEXT_INPUT, searchHandler);
+ }
+ }
+
+/**
+ * Dynamically set the list renderer
+ */
+ public function set rendererXML(value:XML):void {
+ _renderer = value;
+ _simple = value == null;
+ _autoLayout = !_simple && _xml.@autoLayout == "true";
+ }
+
+/**
+ * DEPRECIATED - will be removed soon.
+ */
+ public function set renderer(value:XML):void {
+ rendererXML = value;
+ }
+
+
+ public function set arrows(value:Boolean):void {
+ _arrows = value;
+ }
+
+
+ public function set header(value:int):void {
+ _header = value;
+ }
+
+
+ public function get header():int {
+ return _header;
+ }
+
+
+ public function set labelField(value:String):void {
+ _labelField = value ? value : "label";
+ }
+
+
+ public function set colours(value:Vector.):void {
+ _colours = value;
+ }
+
+
+ protected function initialiseRenderAttributes(xml:XML, attributes:Attributes):void {
+ _rendererAttributes = attributes.copy();
+ _rendererAttributes.parse(_renderer);
+ _rendererAttributes.x = _rendererAttributes.y = 0;
+ _rendererAttributes.width -= (2*_attributes.paddingH + ((_attributes.style7 && _arrows) ? UI.PADDING : 0));
+ _rendererAttributes.height -= 2*_attributes.paddingV;
+ }
+
+
+ protected function indexToScrollPosition(value:int):Number {
+ return _cellHeight * value - _offset;
+ }
+
+
+ public function setIndex(value:int, animate:Boolean = false, move:Boolean = false, highlight:Boolean = false, offset:Number = 0):void {
+ _pressedCell = value;
+ if (value < 0) {
+ return;
+ }
+ if (move) { // if animate is true too - use an alternative move algorithm.
+ var position:int = indexToScrollPosition(value) - offset;
+ // scrollPositionY = (position < 0) ? 0 : position;
+ scrollPositionY = animate ? ((position < _attributes.height) ? 0 : position - 128) : ((position < 0) ? 0 : position);
+ }
+ else if (animate) { // && sliderY + _cellHeight * value < MAXIMUM_DY/2) {
+ _endSlider = indexToScrollPosition(value);
+ _moveTimer.start();
+ }
+
+ if (highlight) {
+ illuminate(value, false);
+ }
+ }
+
+/**
+ * Scroll to index
+ */
+ public function set index(value:int):void {
+ setIndex(value, false, false, _showPressed);
+ }
+
+
+ public function get rendererXML():XML {
+ return _renderer;
+ }
+
+
+ override protected function doLayoutHandler(event:Event):void {
+ doLayout();
+ event.stopPropagation();
+ }
+
+/**
+ * Extract renderer XML from XML
+ */
+ protected function extractRenderer(xml:XML):XML {
+ var children:XMLList = xml.children();
+ if (children.length()==0)
+ return XML("");
+ else if (xml.data.length()==0 && xml.font.length()==0 && xml.model.length()==0 && xml.search.length()==0)
+ return children[0];
+
+ for each (var child:XML in children) {
+ if (child.localName()!="data" && child.localName()!="font" && child.localName()!="model" && child.localName()!="search" && child.localName()!="detail") {
+ return child;
+ }
+ }
+ return XML("");
+ }
+
+
+/**
+ * If set to false, the no highlight appears
+ */
+ public function set highlightPressed(value:Boolean):void {
+ _highlightPressed = value;
+ }
+
+
+/**
+ * If set to true, the click highlight remains
+ */
+ public function set showPressed(value:Boolean):void {
+ _showPressed = value;
+ }
+
+
+ public function get showPressed():Boolean {
+ return _showPressed;
+ }
+
+ /**
+ * Clears the click highlight.
+ */
+ public function clearPressed():void {
+ _highlight.graphics.clear();
+ _highlightIsOn = false;
+ }
+
+
+ protected function clearHighlight(event:Event):void {
+ clearPressed();
+ dispatchEvent(new Event(CLICKED_END, true));
+ }
+
+
+ protected function dispatchClickedEnd():void {
+ if (!_classic) {
+ dispatchEvent(new Event(CLICKED, true));
+ if (_touchTimer.currentCount < TOUCH_DELAY) {
+ illuminate(-1, false, true);
+ if (_showPressed) {
+ dispatchEvent(new Event(CLICKED_END, true));
+ }
+ else {
+ _highlightTimer.stop();
+ _highlightTimer.reset();
+ _highlightTimer.start();
+ }
+ }
+ else {
+ dispatchEvent(new Event(CLICKED_END, true));
+ }
+ }
+ else {
+ dispatchEvent(new Event(CLICKED_END, true));
+ }
+ }
+
+/**
+ * Clears the click highlight. - for controlling list externally and firing CLICKED_END event.
+ * (Most developers will likely never have a need to call this).
+ */
+ public function endPressed(delay:Boolean = false):void {
+ if (delay) {
+ _showPressed = false;
+ _clickTimer.reset();
+ _clickTimer.start();
+ }
+ else {
+ clearPressed();
+ dispatchClickedEnd();
+ }
+ }
+
+/**
+ * Set XML data
+ */
+ public function set xmlData(value:XML):void {
+ var result:Array = [];
+ var children:XMLList = value.children();
+ for each (var child:XML in children) {
+ if (child.nodeKind()!="text") {
+ result.push(attributesToObject(child));
+ }
+ }
+ data = result;
+ }
+
+
+ protected function attributesToObject(child:XML):Object {
+ var attributes:XMLList = child.attributes();
+ if (attributes.length()==0) {
+ return {label:child.localName()};
+ }
+ else {
+ var result:Object = new Object();
+ for (var i:int=0; i 0) {
+ graphics.beginFill(_colours[0]);
+ }
+ else {
+ graphics.beginFill(0,0);
+ }
+ graphics.drawRect(0, 0, _attributes.widthH, _attributes.heightV);
+ }
+
+/**
+ * Rearrange the layout to new screen dimensions
+ */
+ override public function layout(attributes:Attributes):void {
+ var i:int;
+ var cell:*;
+ _width = attributes.width;
+ _height = attributes.height;
+ if (_search) {
+ _search.layout(attributes);
+ }
+ _attributes = attributes;
+ drawComponent();
+ _slider.graphics.clear();
+ // initDraw();
+ if (_simple) {
+ for (i = 0; i<_slider.numChildren; i++) {
+ cell = _slider.getChildAt(i);
+ if (cell is UILabel && !(cell is UISearch)) {
+ UILabel(cell).fixwidth = _attributes.width-2*_attributes.paddingH;
+ cell.multiline = cell.wordWrap = false;
+ }
+ }
+ }
+ else {
+ // initialiseRenderAttributes(xml, attributes);
+ _rendererAttributes.width=attributes.width - (2*attributes.paddingH + ((_attributes.style7 && _arrows) ? UI.PADDING : 0));
+ _rendererAttributes.height=attributes.height - 2*attributes.paddingV;
+ if (_filteredData && _autoLayout) {
+ autoLayout();
+ }
+ else {
+ for (i = 0; i<_slider.numChildren; i++) {
+ cell = _slider.getChildAt(i);
+ if (cell is IComponentUI && !(cell is UISearch)) {
+ IComponentUI(cell).layout(_rendererAttributes);
+ }
+ }
+ }
+ }
+ refreshMasking();
+ if (!_autoLayout || _simple) {
+ redrawCells();
+ }
+ if (_highlightIsOn && _showPressed) {
+ setIndex(_pressedCell, false, false, true);
+ }
+ calculateMaximumSlide();
+ }
+
+
+ protected function autoLayout():void {
+ var last:int = _top + _attributes.paddingV;
+ var i:int = 0;
+ initDraw();
+ for each (var record:Object in _filteredData) {
+ var cell:* = _slider.getChildByName("label_"+i.toString());
+ if (cell && cell is IComponentUI) {
+ IComponentUI(cell).layout(_rendererAttributes);
+ cell.y = last;
+ last += Math.ceil(cell.height + _attributes.paddingV);
+ drawCell(last, i, record);
+ last += Math.ceil(_attributes.paddingV);
+ i++;
+ }
+ }
+ }
+
+/**
+ * Refresh layout
+ */
+ override public function doLayout():void {
+ layout(_attributes);
+ adjustMaximumSlide();
+ }
+
+/**
+ * Redraw cell chrome
+ */
+ protected function redrawCells():void {
+ initDraw();
+ if (!_autoLayout) {
+ var l:int = 0;
+ for each (var record:Object in _filteredData) {
+ drawCell(Math.ceil(_cellHeight*(l+1) + _top), l, record);
+ l++;
+ }
+ }
+ }
+
+/**
+ * Set up the scrolling part of the list
+ */
+ override protected function createSlider(xml:XML, attributes:Attributes):void {
+ addChild(_slider = new Sprite());
+ _slider.addChild(_highlight=new Shape());
+ _width = attributes.width;
+ _height = attributes.height;
+ }
+
+
+ protected function sortParameter(value:String):* {
+ if (value.indexOf(",")<0) {
+ return value;
+ }
+ else {
+ return value.split(",");
+ }
+ }
+
+/**
+ * Assign array of objects data
+ */
+ override public function set data(value:Object):void {
+ _data = value as Array;
+ _saveIndex = -1;
+ if (_sortBy!="" && _data) {
+ _data.sortOn(sortParameter(_sortBy),sortParameter(_sortMode));
+ }
+ clearPressed();
+ filteredData = value as Array;
+ }
+
+
+ public function setData(value:Array, index:int = 0):Boolean {
+ for each(var record:* in value) {
+ var cell:* = _slider.getChildByName("label_"+index.toString() + _suffix);
+ if (cell is UILabel) {
+ UILabel(cell).xmlText = (record is String) ? record : record[_labelField];
+ }
+ else if (cell is IContainerUI) {
+ fillInValues(cell, record);
+ }
+ else {
+ return true;
+ }
+ index++;
+ }
+ if (_autoLayout) {
+ doLayout();
+ calculateMaximumSlide();
+ }
+ return false;
+ }
+
+/**
+ * Set filtered data ( a sub-set of full data ).
+ */
+ public function set filteredData(value:Array):void {
+ _filteredData = value;
+ data0 = value;
+ }
+
+/**
+ * Set list data
+ */
+ protected function set data0(value:Array):void {
+ if (_refresh) {
+ _refresh.changeState(PULL_DOWN_TEXT, false);
+ }
+ clearCells();
+ initDraw();
+ if (_simple) {
+ simpleRenderers(value, _cellTop + 2 * _attributes.paddingV);
+ }
+ else {
+ customRenderers(value, _cellTop + _attributes.paddingV);
+ }
+ if (_autoLayout) {
+ doLayout();
+ }
+ calculateMaximumSlide();
+ }
+
+/**
+ * Calculate maximum slide
+ */
+ protected function calculateMaximumSlide():void {
+ _scrollerHeight = _slider.height - (_refresh ? TOP : 0);
+ _maximumSlide = _scrollerHeight - _height;
+ if (_maximumSlide < 0) {
+ _maximumSlide = 0;
+ }
+ if (_count>0 && (_cellHeight<0 || _autoLayout)) {
+ _cellHeight = Math.ceil((_slider.height - _top - (_refresh ? TOP : 0)) / _count);
+ }
+ }
+
+
+ override protected function adjustMaximumSlide():void {
+ }
+
+/**
+ * Data
+ */
+ public function get data():Object {
+ return _data;
+ }
+
+/**
+ * Filtered data
+ */
+ public function get filteredData():Array {
+ return _filteredData;
+ }
+
+
+ protected function filterArray(data:Array, searchFor:String, field:String, caseSensitive:Boolean, begins:Boolean):Array {
+ var result:Array = [];
+ var index:int = 0;
+ for each (var record:Object in data) {
+ var item:String = record[field];
+ record.$index = index++;
+ if (!caseSensitive) {
+ item = item.toLowerCase();
+ }
+ if (begins) {
+ if (item.substr(0, searchFor.length) == searchFor) {
+ result.push(record);
+ }
+ }
+ else {
+ if (item.indexOf(searchFor) >= 0) {
+ result.push(record);
+ }
+ }
+ }
+ return result;
+ }
+
+/**
+ * Filter the data according to a search string
+ */
+ public function filter(searchFor:String, field:String = "", caseSensitive:Boolean = false, begins:Boolean = false):void {
+ if (searchFor == "") {
+ filteredData = _data;
+ }
+ else {
+ if (field == "") {
+ field = _field;
+ }
+
+ filteredData = filterArray(_data, searchFor, field, caseSensitive, begins);
+ }
+ }
+
+/**
+ * Create list with simple default label rows
+ */
+ protected function simpleRenderers(value:Array, position:Number = -1):void {
+ if (position < 0) {
+ position = 2 * _attributes.paddingV;
+ }
+ _count = 0;
+ _textAlign = _attributes.textAlign;
+ for each (var record:* in value) {
+ var label:UILabel = labelCell(record, position);
+ position += Math.ceil(label.height + 2 * _attributes.paddingV);
+ drawCell(position, _count, record);
+ position += 2 * _attributes.paddingV;
+ _cellHeight = Math.ceil(4 * _attributes.paddingV + label.height);
+ _count++;
+ }
+ }
+
+/**
+ * Create a simple list label row
+ */
+ protected function labelCell(record:*, position:Number):UILabel {
+ var labelText:String = record is String ? record : record[_labelField];
+ var label:UILabel = newLabel();//_attributes.x +
+ label.y = position;
+ label.name = "label_"+_count.toString()+_suffix;
+ if (XML(""+labelText+"").hasComplexContent()) {
+ var xmlString:String = XML(""+labelText+"").toXMLString();
+ label.htmlText = xmlString;
+ }
+ else {
+ if (_font!="") {
+ label.htmlText = _font.substr(0,_font.length-2)+ ">" + labelText + "";
+ }
+ else {
+ label.text = labelText;
+ }
+ }
+
+ if (_textAlign != "") {
+ var format:TextFormat = new TextFormat();
+ format.align = _textAlign;
+ label.setTextFormat(format);
+ }
+
+ label.fixwidth = _attributes.width-2*_attributes.paddingH;
+ label.multiline = label.wordWrap = false;
+
+ return label;
+ }
+
+/**
+ * Clear list
+ */
+ protected function initDraw():void {
+ _slider.graphics.clear();
+ resizeRefresh();
+ _slider.graphics.beginFill(_colour);
+ _slider.graphics.drawRect(0, _top, _width, 1);
+ _lastPosition = _cellTop = _top;
+ }
+
+/**
+ * Resize list row chrome
+ */
+ protected function resizeRefresh():void {
+ if (_refresh) {
+ _slider.graphics.beginFill(_colours.length>0 ? _colours[_colours.length-1] : 0xF9F9F9);
+ _slider.graphics.drawRect(0, -TOP, attributes.width, TOP);
+ _refresh.x = (attributes.width - _refresh.width) / 2;
+ }
+ }
+
+/**
+ * Clear list
+ */
+ protected function clearCells():void {
+ var i:int = _slider.numChildren;
+ while (--i>=(_search ? 2 : 1)+(_refresh ? 1 : 0)) {
+ var child:DisplayObject = DisplayObject(_slider.getChildAt(i));
+ if (child.hasOwnProperty("destructor")) {
+ Object(child).destructor();
+ }
+ _slider.removeChildAt(i);
+ }
+ }
+
+
+ protected function hasLines(record:Object):Boolean {
+ return record.hasOwnProperty("lines") ? record.$lines === true || _lines && (record.$lines !== false) : _lines;
+ }
+
+/**
+ * Draw row chrome
+ */
+ protected function drawCell(position:Number, count:int, record:*):void {
+ drawSimpleCell(position, count, record.hasOwnProperty("$colour") ? record.$colour : uint.MAX_VALUE);
+ if (!(record is String) && hasLines(record)) {
+ drawLines(position);
+ }
+ }
+
+
+ protected function drawArrow(x:Number, y:Number):void {
+ _slider.graphics.beginFill(_colour);
+ _slider.graphics.moveTo(x, y);
+ _slider.graphics.lineTo(x - ARROW, y - ARROW);
+ if (_attributes.style7) {
+ _slider.graphics.lineTo(x - ARROW, y - ARROW + 3);
+ _slider.graphics.lineTo(x - 3, y);
+ _slider.graphics.lineTo(x - ARROW, y + ARROW - 3);
+ }
+ _slider.graphics.lineTo(x - ARROW, y + ARROW);
+ _slider.graphics.lineTo(x, y);
+ _slider.graphics.endFill();
+ }
+
+/**
+ * Draw row chrome
+ */
+ protected function drawSimpleCell(position:Number, count:int, colour:uint = uint.MAX_VALUE):void {
+ if (_colours.length > 1 || colour != uint.MAX_VALUE) {
+ _slider.graphics.beginFill((colour != uint.MAX_VALUE) ? colour : _colours[count % (_colours.length - 1) + 1]);
+ _slider.graphics.drawRect(0, _lastPosition + (_attributes.style7 ? 1 : 2), _width, position - _lastPosition - (_attributes.style7 ? 0 : 1));
+ _slider.graphics.endFill();
+ }
+ _slider.graphics.beginFill(_colour);
+ var startLine:Number = _attributes.style7 ? _lineGap : 0;
+ _slider.graphics.drawRect(startLine, position, _width - startLine, 1);
+ _slider.graphics.endFill();
+ if (_arrows && (_header > 0 ? count >= _header : count < _count + _header)) {
+ drawArrow(_width - _attributes.paddingH, (_lastPosition + position) / 2);
+ }
+ _lastPosition = position;
+ }
+
+/**
+ * Draw lines within row
+ */
+ protected function drawLines(position:Number):void {
+ if (_cell is UIForm) {
+ _slider.graphics.beginFill(_colour);
+ var positions:Array = UIForm(_cell).positions;
+ for (var i:int = 1; i < positions.length; i++) {
+ _slider.graphics.drawRect(_cell.x + positions[i] - _rendererAttributes.paddingH / 2, _cellTop, 1, position - _cellTop);
+ }
+ _slider.graphics.endFill();
+ }
+ }
+
+/**
+ * Return DisplayObject of button pressed
+ */
+ override protected function pressButton(show:Boolean = true):DisplayObject {
+ _scrollBarLayer.graphics.clear();
+ clearPressed();
+ if (!show && (!_simple || _slider.mouseY < _top)) {
+ doSearchHit();
+ }
+ illuminate(-1, !show, show);
+ return _pressButton;
+ }
+
+
+ public function doClickRow(dispatch:Boolean = true):Boolean {
+ _highlight.graphics.clear();
+ _showPressed = true;
+ illuminate(-1, dispatch);
+ return dispatch && _pressedCell >= 0 && _pressedCell < _count;
+ }
+
+
+ protected function pressedCellLimits(groupDetail:Object = null):void {
+ if (_pressedCell < _header || _pressedCell >= _count) {
+ _pressedCell = _saveIndex;
+ }
+ }
+
+
+ protected function highlightForIndex(rowIndex:int):Boolean {
+ var rowData:* = _data[rowIndex];
+ if (rowData.hasOwnProperty("$highlight")) {
+ return rowData.$highlight;
+ }
+ else {
+ return _highlightPressed;
+ }
+ }
+
+
+ protected function illuminate(pressedCell:int = -1, dispatch:Boolean = true, show:Boolean = true):void {
+ const tweak:Number = 1.0;
+ var sliderMouseY:Number = _slider.visible ? _slider.mouseY : mouseY - _sliderPosition;
+ if (!_pressButton && _clickRow) {
+ if (_autoLayout && !_simple && sliderMouseY > _top) {
+ _pressedCell = pressedCell>=0 ? pressedCell : autoLayoutPressedCell(sliderMouseY);
+ pressedCellLimits();
+ if (_row && _pressedCell >= _header && _pressedCell < _count) {
+ // if (_highlightPressed) {
+ if (show && highlightForIndex(_pressedCell)) {
+ _highlight.graphics.beginFill(_highlightColour);
+ _highlight.graphics.drawRect(0, _row.y - _attributes.paddingV + tweak, _width, _row.height + 2*_attributes.paddingV - tweak ); //_attributes.x +
+ _highlight.graphics.endFill();
+ }
+ activate(dispatch);
+ }
+ }
+ else {
+ _pressedCell = pressedCell>=0 ? pressedCell : Math.floor((sliderMouseY - _top)/_cellHeight);
+ pressedCellLimits();
+ if (_pressedCell >= _header && _pressedCell < _count) {
+ // if (_highlightPressed) {
+ if (show && highlightForIndex(_pressedCell)) {
+ _highlight.graphics.beginFill(_highlightColour);
+ _highlight.graphics.drawRect(0, _top + _pressedCell * _cellHeight + tweak, _width, _cellHeight - tweak); //_attributes.x +
+ _highlight.graphics.endFill();
+ }
+ activate(dispatch);
+ }
+ }
+ }
+ }
+
+/**
+ * If autoLayout="true", which cell was clicked?
+ */
+ protected function autoLayoutPressedCell(y:Number):int {
+ var n:int = 0;
+ for (var l:int=0;l<_slider.numChildren - 1;l++) {
+ var row:*;
+ if (!_refresh)
+ {
+ row = _slider.getChildAt(l + 1);
+ }
+ else
+ {
+ row = _slider.getChildAt(l + 2);
+ }
+
+ if (row) {
+ _row = DisplayObject(row);
+ if (_row.y + _row.height + _attributes.paddingV > y)
+ return n;
+ n++;
+ }
+ }
+ return -1;
+ }
+
+
+/**
+ * Row has been clicked
+ */
+ protected function activate(dispatch:Boolean = true):void {
+ if (_classic) {
+ _touchTimer.stop();
+ _dragTimer.stop();
+ stage.removeEventListener(MouseEvent.MOUSE_UP, mouseUp);
+ addEventListener(MouseEvent.MOUSE_DOWN, mouseDown);
+ _clickTimer.stop();
+ _clickTimer.reset();
+ _clickTimer.start();
+ }
+ // stopMovement(); //new - interfered with picker.
+ _rowClick = true;
+ if (dispatch) {
+ // _rowClick = true;
+ // if (_touchTimer.currentCount >= LONG_CLICK_THRESHOLD) {trace("long click");
+ // dispatchEvent(new Event(LONG_CLICK));
+ // }
+ // else {
+ dispatchEvent(new Event(_classic ? CLICKED : CLICK_START, true));
+ // }
+ }
+ }
+
+
+ override protected function mouseDown(event:MouseEvent):void {
+ _saveIndex = _pressedCell;
+ _rowClick = false;
+ super.mouseDown(event);
+ }
+
+
+ override protected function mouseUp(event:MouseEvent):void {
+ if (!_classic && _rowClick) {
+ if (!_showPressed) {
+ clearPressed();
+ }
+ if (_touchTimer.currentCount < TOUCH_THRESHOLD || Math.abs(_delta) > DELTA_THRESHOLD) {
+ doCancel();
+ }
+ else {
+ dispatchClickedEnd();
+ }
+ _rowClick = false;
+ }
+ super.mouseUp(event);
+ }
+
+
+ protected function doCancel():void {
+ _pressedCell = _saveIndex;
+ dispatchEvent(new Event(CLICK_CANCEL, true));
+ _highlight.graphics.clear();
+ if (_showPressed && _pressedCell >= 0) {
+ illuminate(_pressedCell, false);
+ }
+ }
+
+
+ override public function touchCancel():void {
+ super.touchCancel();
+ clearPressed();
+ if (!_classic && _rowClick) {
+ // _highlight.graphics.clear();
+ doCancel();
+ _rowClick = false;
+ }
+ }
+
+/**
+ * Index of last row clicked
+ */
+ public function get index():int {
+ return _pressedCell;
+ }
+
+/**
+ * Data object for last row clicked
+ */
+ public function get row():Object {
+ return (_pressedCell>=0) ? _filteredData[_pressedCell] : null;
+ }
+
+/**
+ * Click up handler
+ */
+ override protected function clickUp(event:TimerEvent):void {
+ if (!_simple) {
+ super.clickUp(event);
+ }
+ if (_clickRow) {
+ if (!_showPressed) {
+ clearPressed();
+ }
+ dispatchClickedEnd();
+ }
+ _scrollBarLayer.graphics.clear();
+ }
+
+/**
+ * Create list with custom renderers
+ */
+ protected function customRenderers(value:Array, position:Number = -1):void {
+ // var before:Number = _rendererAttributes.paddingH;
+ if (position < 0)
+ position = _attributes.paddingV;
+ _count = 0;
+ for each (var record:Object in value) {
+ customCell(record, position);
+ position += Math.ceil(_cell.height + _attributes.paddingV);
+ drawCell(position, _count, record);
+ position += Math.ceil(_attributes.paddingV);
+ _count++;
+ }
+ }
+
+/**
+ * Instanciate a new list row
+ */
+ protected function newRow(rendererXML:XML):DisplayObject {
+ var result:DisplayObject;
+ var before:Number = _rendererAttributes.paddingH;
+ if (UI.isForm(rendererXML.localName())) {
+ result = new UI.FormClass(_slider, rendererXML, _rendererAttributes, true);
+ }
+ else {
+ result = UI.containers(_slider, rendererXML, _rendererAttributes.copy());
+ }
+ return result;
+ }
+
+
+/**
+ * Instanciate a new list label
+ */
+ protected function newLabel():UILabel {
+ return new UILabel(_slider, _attributes.paddingH, 0, "", FORMAT);
+ }
+
+/**
+ * Create and position a new list row
+ */
+ protected function customCell(record:Object, position:Number):void {
+ // if (!UI.isForm(_renderer.localName())) {
+ // var localName:String = _renderer.localName();
+ // if (!UI.isContainer(localName) && !UI.isForm(localName)) {
+ // _renderer = XML("" + _renderer.toXMLString() + "");
+ // }
+ _cell = newRow(record.hasOwnProperty("$renderer") ? record.$renderer : _renderer);
+ _cell.x = _cellRendererLeft + _attributes.paddingH;
+ _cell.y = Math.ceil(position);
+ if (_cell is Sprite) {
+ _cell.mouseChildren = false;
+ }
+ _cell.name = "label_"+_count.toString()+_suffix;
+ fillInValues(_cell, record);
+ }
+
+/**
+ * Assign data to custom renderer components
+ */
+ protected function fillInValues(cell:DisplayObject, record:Object):void {
+ var view:DisplayObject;
+ if (record is String) {
+ view = (cell is IContainerUI) ? IContainerUI(cell).findViewById(_labelField) : cell;
+ if (view is UILabel) {
+ UILabel(view).xmlText = value;
+ }
+ else if (view.hasOwnProperty("text")) {
+ Object(view).text = value;
+ }
+ }
+ else {
+ for (var id:String in record) {
+ view = (cell is IContainerUI) ? IContainerUI(cell).findViewById(id) : ((id == _labelField) ? cell : null);
+ if (view) {
+ var value:String = record[id];
+ if (view is UILabel) {
+ UILabel(view).xmlText = value;
+ }
+ else if (view.hasOwnProperty("text")) {
+ Object(view).text = value;
+ }
+ /* try {
+ if (XML(""+value+"").hasComplexContent() && view is UILabel) {
+ view.htmlText = value;
+ }
+ else {
+ view.text = value;
+ }
+ }
+ catch (error:Error) {
+ view.text = (value != null) ? value : "";
+ }*/
+ }
+ }
+ }
+ }
+
+/**
+ * If true, rows are clickable
+ */
+ public function set clickRow(value:Boolean):void {
+ _clickRow = value;
+ }
+
+/**
+ * Model
+ */
+ override public function get model():Model {
+ return _model;
+ }
+
+/**
+ * Handle search field filter
+ */
+ protected function searchHandler(event:Event):void {
+ filter(_search.text.toLowerCase(), "", false, _begins);
+ }
+
+/**
+ * Determine what has been clicked
+ */
+ override protected function doSearchHit():void {
+ super.doSearchHit();
+ if (_pressButton is InteractiveObject && !InteractiveObject(_pressButton).mouseEnabled) {
+ _pressButton = null;
+ }
+ }
+
+/**
+ * Return component matching id within row matching row index
+ */
+ override public function findViewById(id:String, row:int = -1, group:int = -1):DisplayObject {
+ if (_search && _search.name == id) {
+ return _search;
+ }
+ else if (_simple) {
+ return _slider.getChildByName(id+"_"+row.toString());
+ }
+ else if (row>=0) {
+ var container:IContainerUI = IContainerUI(_slider.getChildByName("label_"+row.toString()));
+ return container ? container.findViewById(id, row, group) : null;
+ }
+ else {
+ return null;
+ }
+ }
+
+
+ public function get rowContainer():IContainerUI {
+ return IContainerUI(_slider.getChildAt(_pressedCell+1));
+ }
+
+/**
+ * Mouse move handler
+ */
+ override protected function mouseMove(event:TimerEvent):void {
+ if (_refresh && !_refreshState && sliderY > PULL_THRESHOLD) {
+ _refresh.changeState(REFRESH_TEXT, true);
+ if (_model) {
+ _model.refresh();
+ }
+ _refreshState = true;
+ dispatchEvent(new Event(REFRESH));
+ }
+ super.mouseMove(event);
+ if (_rowClick && _distance < THRESHOLD && _touchTimer.currentCount == LONG_CLICK_THRESHOLD) {
+ dispatchEvent(new Event(LONG_CLICK));
+ }
+ }
+
+/**
+ * Stop list movement
+ */
+ override protected function stopMovement():void {
+ super.stopMovement();
+ _refreshState = false;
+ }
+
+
+ public function get length():int {
+ return _count;
+ }
+
+
+ override public function rowRectangle(y:Number):Rectangle {
+ for (var l:int=0;l<_slider.numChildren - 1;l++) {
+ var row:DisplayObject = _slider.getChildAt(l+1);
+ if (row.y + row.height + _attributes.paddingV > y) {
+ if (_simple) {
+ return new Rectangle(0, row.y - 2 * _attributes.paddingV, _attributes.width, row.height + 4 * _attributes.paddingV);
+ }
+ else {
+ return new Rectangle(0, row.y - _attributes.paddingV, _attributes.width, row.height + 2 * _attributes.paddingV);
+ }
+
+ }
+ }
+ return null;
+ }
+
+
+ public function set searchVisible(value:Boolean):void {
+ if (_search) {
+ _search.visible = value;
+ _top = value ? _search.height : 0;
+ }
+ }
+
+
+ override public function clear():void {
+ data = [];
+ }
+
+
+ override public function destructor():void {
+ if (_search) {
+ _search.removeEventListener(TextEvent.TEXT_INPUT, searchHandler);
+ _search.destructor();
+ }
+ _highlightTimer.removeEventListener(TimerEvent.TIMER, clearHighlight);
+ super.destructor();
+ }
+ }
+}