/*
# Copyright (c) 2008-2010 rPath, Inc.
#
# This program is distributed under the terms of the MIT License as found
# in a file called LICENSE. If it is not present, the license
# is always available at http://www.opensource.org/licenses/mit-license.php.
#
# This program is distributed in the hope that it will be useful, but
# without any warranty; without even the implied warranty of merchantability
# or fitness for a particular purpose. See the MIT License for full details.
#
*/

package com.rpath.xobj
{
import mx.collections.Sort;
import flash.utils.Dictionary;
import mx.collections.SortField;

[Bindable]
public class XSmartURL extends URL
{
    public function XSmartURL(s:*=null)
    {
        // unwrap the URL passed in
        if (s is XSmartURL)
        {
            s = (s as XSmartURL).baseURL;
        }

        super(s);

        // keep track of the base URL distinct from
        // our current value
        baseURL = new URL(this.value);
    }

    public function get baseURL():URL
    {
        return _baseURL;
    }

    private var _baseURL:URL;

    public function set baseURL(value:URL):void
    {
        _baseURL = value;
        recomputeQualifiedURL();
    }

    /** descriptor provides the encoding chars to use
     * which is a poor man's metadata right now
     */
    [xobjTransient]
    private var _descriptor:XSmartURLDescriptor;

    public function get descriptor():XSmartURLDescriptor
    {
        return _descriptor;
    }

    public function set descriptor(value:XSmartURLDescriptor):void
    {
        _descriptor = value;
        recomputeQualifiedURL();
    }

    /** paging support
     */

    [xobjTransient]
    public var rangeRequest:Boolean;

    [xobjTransient]
    private var _startIndex:Number;

    public function get startIndex():Number
    {
        return _startIndex;
    }

    public function set startIndex(value:Number):void
    {
        _startIndex = value;
        rangeRequest = true;
        recomputeQualifiedURL();
    }

    [xobjTransient]
    private var _limit:Number;

    public function get limit():Number
    {
        return _limit;
    }

    public function set limit(value:Number):void
    {
        _limit = value;
        rangeRequest = true;
        recomputeQualifiedURL();
    }

    /** filterTerms is an array of (field, operator, value) clauses
     * that are assumed to be AND clauses
     */
    private var _filterTerms:Array;

    [xobjTransient]
    public function get filterTerms():Array
    {
        return _filterTerms;
    }

    [xobjTransient]
    public function set filterTerms(value:Array):void
    {
        _filterTerms = value;
        recomputeQualifiedURL();
    }

    /** sortTerms are an ordered set of (asc, desc) fields to order by
     */
    private var _sortTerms:Sort;

    [xobjTransient]
    public function get sortTerms():Sort
    {
        return _sortTerms;
    }

    [xobjTransient]
    public function set sortTerms(value:Sort):void
    {
        _sortTerms = value;
        recomputeQualifiedURL();
    }

    /** freeSearch is for google-like full text searching
     */
    private var _freeSearch:String;

    [xobjTransient]
    public function get freeSearch():String
    {
        return _freeSearch;
    }

    [xobjTransient]
    public function set freeSearch(value:String):void
    {
        _freeSearch = value;
        recomputeQualifiedURL();
    }

    /** url takes the baseURL and modifies it to encode the query,
     * page, etc. params requested
     */

    // TODO: optimize this via setter/getter pairs on query terms
    [xobjTransient]
    public function get qualifiedURL():String
    {
        return _qualifiedURL;
    }

    private var _qualifiedURL:String;

    [xobjTransient]
    public function set qualifiedURL(s:String):void
    {
        _qualifiedURL = s;
    }

    [xobjTransient]
    public function recomputeQualifiedURL():void
    {
        if (!baseURL)
            return;

        var fullURL:String = baseURL.value;

        var queryFrag:String = queryFragment();

        if (queryFrag)
        {
            if (fullURL.indexOf(descriptor.paramDelimiter) != -1)
            {
                fullURL = fullURL + descriptor.paramDelimiter + queryFrag;
            }
            else
            {
                fullURL = fullURL + descriptor.paramMarker + queryFrag;
            }
        }

        qualifiedURL = fullURL;
    }

    [xobjTransient]
    public function queryFragment():String
    {
        var params:Dictionary = new Dictionary();
        var query:String = "";

        if (!descriptor)
            return query;

        if (rangeRequest)
        {
            if (descriptor.startKey)
                params[descriptor.startKey] = startIndex;
            if (descriptor.limitKey)
                params[descriptor.limitKey] = limit;
        }

        // TODO: use descriptor to tell us how to express sort
        if (sortTerms)
        {
            var terms:String = "";

            if (sortTerms.fields && sortTerms.fields.length > 0)
            {
                var first:Boolean = true;

                for each (var term:SortField in sortTerms.fields)
                {
                    if (!term || !term.name)
                        continue;

                    terms = terms
                        + (first ? "": descriptor.sortConjunction)
                        + (term.descending ? descriptor.sortDescMarker: descriptor.sortAscMarker)
                        + term.name;
                    first = false;
                }

                if (terms)
                {
                    params[descriptor.sortKey] = terms;
                }
            }
        }

        if (filterTerms)
        {
            var search:String = "";
            first = true;
            for each (var searchTerm:FilterTerm in filterTerms)
            {
                search = search
                    + (first ? "": descriptor.filterTermConjunction)
                    + descriptor.filterTermStart
                    + searchTerm.name + descriptor.filterTermSeperator+ searchTerm.operator +descriptor.filterTermSeperator + searchTerm.value
                    + descriptor.filterTermEnd;
                first = false;
            }

            if (search)
            {
                params[descriptor.filterKey] = search;
            }
        }

        // Google like full text searching
        if (freeSearch)
        {
            params[descriptor.searchKey] = freeSearch;
        }

        first = true;
        for (var param:String in params)
        {
            query = query + (first ? "" : descriptor.paramDelimiter) + param + "=" + params[param];
            first = false;
        }

        return query;
    }

    [xobjTransient]
    public function addSearchTerm(term:FilterTerm):void
    {
        if (!filterTerms)
            filterTerms = [];

        filterTerms.push(term);

        recomputeQualifiedURL();
    }

    /* TODO: decide if headers really belong on resource spec */
    [xobjTransient]
    public var headers:Dictionary;
    [xobjTransient]
    public var hasHeaders:Boolean;

    [xobjTransient]
    public function setHeader(name:String, value:String):void
    {
        headers[name] = value;
        hasHeaders = true;
    }

}
}

/*
# Copyright (c) 2008-2010 rPath, Inc.
#
# This program is distributed under the terms of the MIT License as found
# in a file called LICENSE. If it is not present, the license
# is always available at http://www.opensource.org/licenses/mit-license.php.
#
# This program is distributed in the hope that it will be useful, but
# without any warranty; without even the implied warranty of merchantability
# or fitness for a particular purpose. See the MIT License for full details.
#
*/

package com.rpath.xobj
{
import flash.utils.Dictionary;

import mx.collections.Sort;
import mx.collections.SortField;

[Bindable]
public class XSmartURL extends URL
{
    public function XSmartURL(s:*=null)
    {
        // unwrap the URL passed in
        if (s is XSmartURL)
        {
            s = (s as XSmartURL).baseURL;
        }

        super(s);

        // keep track of the base URL distinct from
        // our current value
        baseURL = new URL(this.value);
    }

    public function get baseURL():URL
    {
        return _baseURL;
    }

    private var _baseURL:URL;

    public function set baseURL(value:URL):void
    {
        _baseURL = value;
        recomputeQualifiedURL();
    }

    /** descriptor provides the encoding chars to use
     * which is a poor man's metadata right now
     */
    [xobjTransient]
    private var _descriptor:XSmartURLDescriptor;

    public function get descriptor():XSmartURLDescriptor
    {
        return _descriptor;
    }

    public function set descriptor(value:XSmartURLDescriptor):void
    {
        _descriptor = value;
        recomputeQualifiedURL();
    }

    /** paging support
     */

    [xobjTransient]
    public var rangeRequest:Boolean;

    [xobjTransient]
    private var _startIndex:Number;

    public function get startIndex():Number
    {
        return _startIndex;
    }

    public function set startIndex(value:Number):void
    {
        _startIndex = value;
        rangeRequest = true;
        recomputeQualifiedURL();
    }

    [xobjTransient]
    private var _limit:Number;

    public function get limit():Number
    {
        return _limit;
    }

    public function set limit(value:Number):void
    {
        _limit = value;
        rangeRequest = true;
        recomputeQualifiedURL();
    }

    /** filterTerms is an array of (field, operator, value) clauses
     * that are assumed to be AND clauses
     */
    private var _filterTerms:Array;

    [xobjTransient]
    public function get filterTerms():Array
    {
        return _filterTerms;
    }

    [xobjTransient]
    public function set filterTerms(value:Array):void
    {
        _filterTerms = value;
        recomputeQualifiedURL();
    }

    /** sortTerms are an ordered set of (asc, desc) fields to order by
     */
    private var _sortTerms:Sort;

    [xobjTransient]
    public function get sortTerms():Sort
    {
        return _sortTerms;
    }

    [xobjTransient]
    public function set sortTerms(value:Sort):void
    {
        _sortTerms = value;
        recomputeQualifiedURL();
    }

    /** freeSearch is for google-like full text searching
     */
    private var _freeSearch:String;

    [xobjTransient]
    public function get freeSearch():String
    {
        return _freeSearch;
    }

    [xobjTransient]
    public function set freeSearch(value:String):void
    {
        _freeSearch = value;
        recomputeQualifiedURL();
    }

    /** url takes the baseURL and modifies it to encode the query,
     * page, etc. params requested
     */

    // TODO: optimize this via setter/getter pairs on query terms
    [xobjTransient]
    public function get qualifiedURL():String
    {
        return _qualifiedURL;
    }

    private var _qualifiedURL:String;

    [xobjTransient]
    public function set qualifiedURL(s:String):void
    {
        _qualifiedURL = s;
    }

    [xobjTransient]
    public function recomputeQualifiedURL():void
    {
        if (!baseURL)
            return;

        var fullURL:String = baseURL.value;

        if (!fullURL)
            return;

        var queryFrag:String = queryFragment();

        if (queryFrag)
        {
            if (fullURL.indexOf(descriptor.paramDelimiter) != -1)
            {
                fullURL = fullURL + descriptor.paramDelimiter + queryFrag;
            }
            else
            {
                fullURL = fullURL + descriptor.paramMarker + queryFrag;
            }
        }

        qualifiedURL = fullURL;
    }

    [xobjTransient]
    public function queryFragment():String
    {
        var params:Dictionary = new Dictionary();
        var query:String = "";

        if (!descriptor)
            return query;

        if (rangeRequest)
        {
            if (descriptor.startKey)
                params[descriptor.startKey] = startIndex;
            if (descriptor.limitKey)
                params[descriptor.limitKey] = limit;
        }

        // TODO: use descriptor to tell us how to express sort
        if (sortTerms)
        {
            var terms:String = "";

            if (sortTerms.fields && sortTerms.fields.length > 0)
            {
                var first:Boolean = true;

                for each (var term:SortField in sortTerms.fields)
                {
                    if (!term || !term.name)
                        continue;

                    terms = terms
                        + (first ? "": descriptor.sortConjunction)
                        + (term.descending ? descriptor.sortDescMarker: descriptor.sortAscMarker)
                        + term.name;
                    first = false;
                }

                if (terms)
                {
                    params[descriptor.sortKey] = terms;
                }
            }
        }

        if (filterTerms)
        {
            var search:String = "";
            first = true;
            for each (var searchTerm:FilterTerm in filterTerms)
            {
                search = search
                    + (first ? "": descriptor.filterTermConjunction)
                    + descriptor.filterTermStart
                    + searchTerm.name + descriptor.filterTermSeperator+ searchTerm.operator +descriptor.filterTermSeperator + searchTerm.value
                    + descriptor.filterTermEnd;
                first = false;
            }

            if (search)
            {
                params[descriptor.filterKey] = search;
            }
        }

        // Google like full text searching
        if (freeSearch)
        {
            params[descriptor.searchKey] = freeSearch;
        }

        first = true;
        for (var param:String in params)
        {
            query = query + (first ? "" : descriptor.paramDelimiter) + param + "=" + params[param];
            first = false;
        }

        return query;
    }

    public function addSearchTerm(term:FilterTerm):void
    {
        if (!filterTerms)
            filterTerms = [];

        filterTerms.push(term);

        recomputeQualifiedURL();
    }

    public function removeSearchTerm(term:FilterTerm):void
    {
        var index:int = filterTerms.indexOf(term);
        if (index != -1)
        {
            filterTerms.splice(index,1);
        }

        recomputeQualifiedURL();
    }

    /* TODO: decide if headers really belong on resource spec */
    [xobjTransient]
    public var headers:Dictionary;
    [xobjTransient]
    public var hasHeaders:Boolean;

    [xobjTransient]
    public function setHeader(name:String, value:String):void
    {
        headers[name] = value;
        hasHeaders = true;
    }

}
}

Remove searchTerms as well as add...

Remove searchTerms as well as add...


//
// $Id$
//
// Nenya library - tools for developing networked games
// Copyright (C) 2002-2007 Three Rings Design, Inc., All Rights Reserved
// http://www.threerings.net/code/nenya/
//
// This library is free software; you can redistribute it and/or modify it
// under the terms of the GNU Lesser General Public License as published
// by the Free Software Foundation; either version 2.1 of the License, or
// (at your option) any later version.
//
// This library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
// Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public
// License along with this library; if not, write to the Free Software
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA

package com.threerings.flash {

import flash.events.Event;
import flash.events.MouseEvent;

import flash.filters.GlowFilter;

import flash.system.Capabilities;
import flash.system.System;

import flash.text.TextField;
import flash.text.TextFieldAutoSize;
import flash.text.TextFieldType;
import flash.text.TextFormat;
import flash.text.TextFormatAlign;

public class TextFieldUtil
{
    /** A fudge factor that must be added to a TextField's textWidth when setting the width. */
    public static const WIDTH_PAD :int = 5;

    /** A fudge factor that must be added to a TextField's textHeight when setting the height. */
    public static const HEIGHT_PAD :int = 4;

    /**
     * Create a TextField.
     *
     * args: contains properties with which to initialize the TextField
     */
    public static function createField (
        text :String, args :Object = null, clazz :Class = null) :TextField
    {
        var tf :TextField = (clazz == null) ? new TextField() : TextField(new clazz());
        tf.background = getArg(args, "background", false);
        tf.border = getArg(args, "border", false);
        tf.multiline = getArg(args, "multiline", false);
        tf.type = getArg(args, "type", TextFieldType.DYNAMIC);

        var format :* = getArg(args, "format");
        if (format !== undefined) {
            tf.defaultTextFormat = format;
        }

        var outColor :* = getArg(args, "outlineColor");
        if (outColor !== undefined) {
            tf.filters = [ new GlowFilter(uint(outColor), 1, 2, 2, 255) ];
        }

        tf.autoSize = getArg(args, "autoSize", TextFieldAutoSize.LEFT);
        tf.text = text;
        if (tf.autoSize != null) {
            tf.width = tf.textWidth + 5;
            tf.height = tf.textHeight + 4;
        }

        return tf;
    }

    /**
     * Create a TextFormat.
     */
    public static function createFormat (args :Object) :TextFormat
    {
        var f :TextFormat = new TextFormat();
        f.align = getArg(args, "align", TextFormatAlign.LEFT);
        f.blockIndent = getArg(args, "blockIndent", null);
        f.bold = getArg(args, "bold", false);
        f.bullet = getArg(args, "bullet", null);

        f.size = getArg(args, "size", 18);
        f.font = getArg(args, "font", "Arial");
        f.color = getArg(args, "color", 0x000000);

        return f;
    }

    /**
     * Include the specified TextField in a set of TextFields in which only
     * one may have a selection at a time.
     */
    public static function trackSingleSelectable (textField :TextField) :void
    {
        textField.addEventListener(MouseEvent.MOUSE_MOVE, handleTrackedSelection);

        // immediately put the kibosh on any selection
        textField.setSelection(0, 0);
    }

    /**
     * Internal method related to tracking a single selectable TextField.
     */
    protected static function handleTrackedSelection (event :MouseEvent) :void
    {
        if (event.buttonDown) {
            var field :TextField = event.target as TextField;
            if (field == _lastSelected) {
                updateSelection(field);

            } else if (field.selectionBeginIndex != field.selectionEndIndex) {
                // clear the last one..
                if (_lastSelected != null) {
                    handleLastSelectedRemoved();
                }
                _lastSelected = field;
                _lastSelected.addEventListener(Event.REMOVED_FROM_STAGE, handleLastSelectedRemoved);
                updateSelection(field);
            }
        }
    }

    /**
     * Process the selection.
     */
    protected static function updateSelection (field :TextField) :void
    {
        if (-1 != Capabilities.os.indexOf("Linux")) {
            var str :String = field.text.substring(
                field.selectionBeginIndex, field.selectionEndIndex);
            System.setClipboard(str);
        }
    }

    /**
     * Internal method related to tracking a single selectable TextField.
     */
    protected static function handleLastSelectedRemoved (... ignored) :void
    {
        _lastSelected.setSelection(0, 0);
        _lastSelected.removeEventListener(Event.REMOVED_FROM_STAGE, handleLastSelectedRemoved);
        _lastSelected = null;
    }

    /**
     * Utility function for createTextField() and createFormat().
     */
    protected static function getArg (args :Object, name :String, defVal :* = undefined) :*
    {
        if (args != null && (name in args)) {
            return args[name];
        }
        return defVal;
    }

    /** The last tracked TextField to be selected. */
    protected static var _lastSelected :TextField;
}
}

//
// $Id$
//
// Nenya library - tools for developing networked games
// Copyright (C) 2002-2007 Three Rings Design, Inc., All Rights Reserved
// http://www.threerings.net/code/nenya/
//
// This library is free software; you can redistribute it and/or modify it
// under the terms of the GNU Lesser General Public License as published
// by the Free Software Foundation; either version 2.1 of the License, or
// (at your option) any later version.
//
// This library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
// Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public
// License along with this library; if not, write to the Free Software
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA

package com.threerings.flash {

import flash.events.Event;
import flash.events.MouseEvent;

import flash.filters.GlowFilter;

import flash.system.Capabilities;
import flash.system.System;

import flash.text.TextField;
import flash.text.TextFieldAutoSize;
import flash.text.TextFieldType;
import flash.text.TextFormat;
import flash.text.TextFormatAlign;

import com.threerings.util.Util;

public class TextFieldUtil
{
    /** A fudge factor that must be added to a TextField's textWidth when setting the width. */
    public static const WIDTH_PAD :int = 5;

    /** A fudge factor that must be added to a TextField's textHeight when setting the height. */
    public static const HEIGHT_PAD :int = 4;

    /**
     * Create a TextField.
     *
     * @param initProps contains properties with which to initialize the TextField.
     * Additionally it may contain the following properties:
     *    outlineColor: uint
     *
     * initProps may be destructively modified.
     */
    public static function createField (
        text :String, initProps :Object = null, clazz :Class = null) :TextField
    {
        var tf :TextField = (clazz == null) ? new TextField() : TextField(new clazz());

        if ("outlineColor" in initProps) {
            tf.filters = [ new GlowFilter(uint(initProps["outlineColor"]), 1, 2, 2, 255) ];
        }

        Util.init(tf, initProps, null, MASK_FIELD_PROPS);
        tf.text = text;
        if (tf.autoSize != null) {
            tf.width = tf.textWidth + WIDTH_PAD;
            tf.height = tf.textHeight + HEIGHT_PAD;
        }

        return tf;
    }

    /**
     * Create a TextFormat using initProps.
     * If unspecified, the following properties have default values:
     *  size: 18
     *  font: _sans
     */
    public static function createFormat (initProps :Object) :TextFormat
    {
        var f :TextFormat = new TextFormat();
        Util.init(f, initProps, DEFAULT_FORMAT_PROPS);
        return f;
    }

    /**
     * Include the specified TextField in a set of TextFields in which only
     * one may have a selection at a time.
     */
    public static function trackSingleSelectable (textField :TextField) :void
    {
        textField.addEventListener(MouseEvent.MOUSE_MOVE, handleTrackedSelection);

        // immediately put the kibosh on any selection
        textField.setSelection(0, 0);
    }

    /**
     * Internal method related to tracking a single selectable TextField.
     */
    protected static function handleTrackedSelection (event :MouseEvent) :void
    {
        if (event.buttonDown) {
            var field :TextField = event.target as TextField;
            if (field == _lastSelected) {
                updateSelection(field);

            } else if (field.selectionBeginIndex != field.selectionEndIndex) {
                // clear the last one..
                if (_lastSelected != null) {
                    handleLastSelectedRemoved();
                }
                _lastSelected = field;
                _lastSelected.addEventListener(Event.REMOVED_FROM_STAGE, handleLastSelectedRemoved);
                updateSelection(field);
            }
        }
    }

    /**
     * Process the selection.
     */
    protected static function updateSelection (field :TextField) :void
    {
        if (-1 != Capabilities.os.indexOf("Linux")) {
            var str :String = field.text.substring(
                field.selectionBeginIndex, field.selectionEndIndex);
            System.setClipboard(str);
        }
    }

    /**
     * Internal method related to tracking a single selectable TextField.
     */
    protected static function handleLastSelectedRemoved (... ignored) :void
    {
        _lastSelected.setSelection(0, 0);
        _lastSelected.removeEventListener(Event.REMOVED_FROM_STAGE, handleLastSelectedRemoved);
        _lastSelected = null;
    }

    /** The last tracked TextField to be selected. */
    protected static var _lastSelected :TextField;

    protected static const MASK_FIELD_PROPS :Object = { outlineColor: true };

    protected static const DEFAULT_FORMAT_PROPS :Object = { size: 18, font: "_sans" };
}
}

Use Util.init() to initialize TextFields and TextFormats.

git-svn-id: b675b909355d5cf946977f44a8ec5a6ceb3782e4@381 ed5b42cb-e716-0410-a449-f6a68f950b19


//
// $Id$
//
// Narya library - tools for developing networked games
// Copyright (C) 2002-2007 Three Rings Design, Inc., All Rights Reserved
// http://www.threerings.net/code/narya/
//
// This library is free software; you can redistribute it and/or modify it
// under the terms of the GNU Lesser General Public License as published
// by the Free Software Foundation; either version 2.1 of the License, or
// (at your option) any later version.
//
// This library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
// Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public
// License along with this library; if not, write to the Free Software
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA

package com.threerings.util {

import flash.utils.getQualifiedClassName;

/**
 * A simple logging mechanism.
 *
 * Log instances are created for modules, and the logging level can be configured per
 * module in a hierarchical fashion.
 *
 * Typically, you should create a module name based on the full path to a class:
 * calling getLog() and passing an object or Class will do this. Alternattely, you
 * may create a Log to share in several classes in a package, in which case the
 * module name can be like "com.foocorp.games.bunnywar". Finally, you can just
 * create made-up module names like "mygame" or "util", but this is not recommended.
 * You really should name things based on your packages, and your packages should be
 * named according to Sun's recommendations for Java packages.
 *
 * Typical usage for creating a Log to be used by the entire class would be:
 * public class MyClass
 * {
 *     private static const log :Log = Log.getLog(MyClass);
 *     ...
 *
 * OR, if you just need a one-off Log:
 *     protected function doStuff (thingy :Thingy) :void
 *     {
 *          if (!isValid(thingy)) {
 *              Log.getLog(this).warn("Invalid thingy specified", "thingy", thingy);
 *              ....
 */
public class Log
{
    /** Log level constants. */
    public static const DEBUG :int = 0;
    public static const INFO :int = 1;
    public static const WARNING :int = 2;
    public static const ERROR :int = 3;
    public static const OFF :int = 4;
    // if you add to this, update LEVEL_NAMES and stringToLevel() at the bottom...

    /**
     * Retrieve a Log for the specified module.
     *
     * @param moduleSpec can be a String of the module name, or any Object or Class to
     * have the module name be the full package and name of the class (recommended).
     */
    public static function getLog (moduleSpec :*) :Log
    {
        const module :String = (moduleSpec is String) ? String(moduleSpec)
            : getQualifiedClassName(moduleSpec).replace("::", ".");
        return new Log(module);
    }

    /**
     * A convenience function for quickly and easily inserting printy
     * statements during application development.
     */
    public static function testing (... params) :void
    {
        var log :Log = new Log("testing");
        log.debug.apply(log, params);
    }

    /**
     * A convenience function for quickly printing a stack trace
     * to the log, useful for debugging.
     */
    public static function dumpStack () :void
    {
        testing(new Error("dumpStack").getStackTrace());
    }

    /**
     * Add a logging target.
     */
    public static function addTarget (target :LogTarget) :void
    {
        _targets.push(target);
    }

    /**
     * Remove a logging target.
     */
    public static function removeTarget (target :LogTarget) :void
    {
        var dex :int = _targets.indexOf(target);
        if (dex != -1) {
            _targets.splice(dex, 1);
        }
    }

    /**
     * Set the log level for the specified module.
     *
     * @param module The smallest prefix desired to configure a log level.
     * For example, you can set the global level with Log.setLevel("", Log.INFO);
     * Then you can Log.setLevel("com.foo.game", Log.DEBUG). Now, everything
     * logs at INFO level except for modules within com.foo.game, which is at DEBUG.
     */
    public static function setLevel (module :String, level :int) :void
    {
        _setLevels[module] = level;
        _levels = {}; // reset cached levels
    }

    /**
     * Parses a String in the form of ":info;com.foo.game:debug;com.bar.util:warning"
     *
     * Semicolons separate modules, colons separate a module name from the log level.
     * An empty string specifies the top-level (global) module.
     */
    public static function setLevels (settingString :String) :void
    {
        for each (var module :String in settingString.split(";")) {
            var setting :Array = module.split(":");
            _setLevels[setting[0]] = stringToLevel(String(setting[1]));
        }
        _levels = {}; // reset cached levels
    }

    /**
     * Use Log.getLog();
     *
     * @private
     */
    public function Log (module :String)
    {
        if (module == null) module = "";
        _module = module;
    }

    /**
     * Log a message with 'debug' priority.
     *
     * @param args The first argument is the actual message to log. After that, each pair
     * of parameters is printed in key/value form, the benefit being that if no log
     * message is generated then toString() will not be called on the values.
     * A final parameter may be an Error, in which case the stack trace is printed.
     *
     * @example
     * <listing version="3.0">
     *    log.debug("Message", "key1", value1, "key2", value2, optionalError);
     * </listing>
     */
    public function debug (... args) :void
    {
        doLog(DEBUG, args);
    }

    /**
     * Log a message with 'info' priority.
     *
     * @param args The first argument is the actual message to log. After that, each pair
     * of parameters is printed in key/value form, the benefit being that if no log
     * message is generated then toString() will not be called on the values.
     * A final parameter may be an Error, in which case the stack trace is printed.
     *
     * @example
     * <listing version="3.0">
     *    log.info("Message", "key1", value1, "key2", value2, optionalError);
     * </listing>
     */
    public function info (... args) :void
    {
        doLog(INFO, args);
    }

    /**
     * Log a message with 'warning' priority.
     *
     * @param args The first argument is the actual message to log. After that, each pair
     * of parameters is printed in key/value form, the benefit being that if no log
     * message is generated then toString() will not be called on the values.
     * A final parameter may be an Error, in which case the stack trace is printed.
     *
     * @example
     * <listing version="3.0">
     *    log.warning("Message", "key1", value1, "key2", value2, optionalError);
     * </listing>
     */
    public function warning (... args) :void
    {
        doLog(WARNING, args);
    }

    /**
     * Log a message with 'error' priority.
     *
     * @param args The first argument is the actual message to log. After that, each pair
     * of parameters is printed in key/value form, the benefit being that if no log
     * message is generated then toString() will not be called on the values.
     * A final parameter may be an Error, in which case the stack trace is printed.
     *
     * @example
     * <listing version="3.0">
     *    log.error("Message", "key1", value1, "key2", value2, optionalError);
     * </listing>
     */
    public function error (... args) :void
    {
        doLog(ERROR, args);
    }

    /**
     * Log just a stack trace with 'warning' priority.
     * Deprecated, sorta. Just use warning("Message", error);
     */
    public function logStackTrace (error :Error) :void
    {
        warning(error.getStackTrace());
    }

    protected function doLog (level :int, args :Array) :void
    {
        if (level < getLevel(_module)) {
            return; // we don't want to log it!
        }
        var logMessage :String = formatMessage(level, args);
        trace(logMessage);
        // possibly also dispatch to any other log targets.
        for each (var target :LogTarget in _targets) {
            target.log(logMessage);
        }
    }

    protected function formatMessage (level :int, args :Array) :String
    {
        var msg :String = getTimeStamp() + " " + LEVEL_NAMES[level] + ": " + _module;
        if (args.length > 0) {
            msg += " " + String(args[0]); // the primary log message
            var err :Error = null;
            if (args.length % 2 == 0) { // there's one extra arg
                var lastArg :Object = args.pop();
                if (lastArg is Error) {
                    err = lastArg as Error; // ok, it's an error, we like those
                } else {
                    args.push(lastArg, ""); // what? Well, cope by pushing it back with a ""
                }
            }
            if (args.length > 1) {
                for (var ii :int = 1; ii < args.length; ii += 2) {
                    msg += (ii == 1) ? " [" : ", ";
                    msg += String(args[ii]) + "=" + String(args[ii + 1]);
                }
                msg += "]";
            }
            if (err != null) {
                msg += "\n" + err.getStackTrace();
            }
        }
        return msg;
    }

    protected function getTimeStamp () :String
    {
        var d :Date = new Date();
        // return d.toLocaleTimeString();

        // format it like the date format in our java logs
        return d.fullYear + "-" +
            StringUtil.prepad(String(d.month + 1), 2, "0") + "-" +
            StringUtil.prepad(String(d.date), 2, "0") + " " +
            StringUtil.prepad(String(d.hours), 2, "0") + ":" +
            StringUtil.prepad(String(d.minutes), 2, "0") + ":" +
            StringUtil.prepad(String(d.seconds), 2, "0") + "," +
            StringUtil.prepad(String(d.milliseconds), 3, "0");
    }

    /**
     * Get the logging level for the specified module.
     */
    protected static function getLevel (module :String) :int
    {
        // we probably already have the level cached for this module
        var lev :Object = _levels[module];
        if (lev == null) {
            // cache miss- copy some parent module's level...
            var ancestor :String = module;
            while (true) {
                lev = _setLevels[ancestor];
                if (lev != null || ancestor == "") {
                    // bail if we found a setting or get to the top level,
                    // but always save the level from _setLevels into _levels
                    _levels[module] = int(lev); // if lev was null, this will become 0 (DEBUG)
                    break;
                }
                var dex :int = ancestor.lastIndexOf(".");
                ancestor = (dex == -1) ? "" : ancestor.substring(0, dex);
            }
        }
        return int(lev);
    }

    protected static function stringToLevel (s :String) :int
    {
        switch (s.toLowerCase()) {
        default: // default to DEBUG
        case "debug": return DEBUG;
        case "info": return INFO;
        case "warning": case "warn": return WARNING;
        case "error": return ERROR;
        case "off": return OFF;
        }
    }

    /** The module to which this log instance applies. */
    protected var _module :String;

    /** Other registered LogTargets, besides the trace log. */
    protected static var _targets :Array = [];

    /** A cache of log levels, copied from _setLevels. */
    protected static var _levels :Object = {};

    /** The configured log levels. */
    protected static var _setLevels :Object = { "": DEBUG }; // global: debug

    /** The outputted names of each level. The last one isn't used, it corresponds with OFF. */
    protected static const LEVEL_NAMES :Array = [ "debug", "INFO", "WARN", "ERROR", false ];
}
}

//
// $Id$
//
// Narya library - tools for developing networked games
// Copyright (C) 2002-2007 Three Rings Design, Inc., All Rights Reserved
// http://www.threerings.net/code/narya/
//
// This library is free software; you can redistribute it and/or modify it
// under the terms of the GNU Lesser General Public License as published
// by the Free Software Foundation; either version 2.1 of the License, or
// (at your option) any later version.
//
// This library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
// Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public
// License along with this library; if not, write to the Free Software
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA

package com.threerings.util {

import flash.utils.getQualifiedClassName;

/**
 * A simple logging mechanism.
 *
 * Log instances are created for modules, and the logging level can be configured per
 * module in a hierarchical fashion.
 *
 * Typically, you should create a module name based on the full path to a class:
 * calling getLog() and passing an object or Class will do this. Alternattely, you
 * may create a Log to share in several classes in a package, in which case the
 * module name can be like "com.foocorp.games.bunnywar". Finally, you can just
 * create made-up module names like "mygame" or "util", but this is not recommended.
 * You really should name things based on your packages, and your packages should be
 * named according to Sun's recommendations for Java packages.
 *
 * Typical usage for creating a Log to be used by the entire class would be:
 * public class MyClass
 * {
 *     private static const log :Log = Log.getLog(MyClass);
 *     ...
 *
 * OR, if you just need a one-off Log:
 *     protected function doStuff (thingy :Thingy) :void
 *     {
 *          if (!isValid(thingy)) {
 *              Log.getLog(this).warn("Invalid thingy specified", "thingy", thingy);
 *              ....
 */
public class Log
{
    /** Log level constants. */
    public static const DEBUG :int = 0;
    public static const INFO :int = 1;
    public static const WARNING :int = 2;
    public static const ERROR :int = 3;
    public static const OFF :int = 4;
    // if you add to this, update LEVEL_NAMES and stringToLevel() at the bottom...

    /**
     * Retrieve a Log for the specified module.
     *
     * @param moduleSpec can be a String of the module name, or any Object or Class to
     * have the module name be the full package and name of the class (recommended).
     */
    public static function getLog (moduleSpec :*) :Log
    {
        const module :String = (moduleSpec is String) ? String(moduleSpec)
            : getQualifiedClassName(moduleSpec).replace("::", ".");
        return new Log(module);
    }

    /**
     * A convenience function for quickly and easily inserting printy
     * statements during application development.
     */
    public static function testing (... params) :void
    {
        var log :Log = new Log("testing");
        log.debug.apply(log, params);
    }

    /**
     * A convenience function for quickly printing a stack trace
     * to the log, useful for debugging.
     */
    public static function dumpStack (msg :String = "dumpStack") :void
    {
        testing(new Error(msg).getStackTrace());
    }

    /**
     * Add a logging target.
     */
    public static function addTarget (target :LogTarget) :void
    {
        _targets.push(target);
    }

    /**
     * Remove a logging target.
     */
    public static function removeTarget (target :LogTarget) :void
    {
        var dex :int = _targets.indexOf(target);
        if (dex != -1) {
            _targets.splice(dex, 1);
        }
    }

    /**
     * Set the log level for the specified module.
     *
     * @param module The smallest prefix desired to configure a log level.
     * For example, you can set the global level with Log.setLevel("", Log.INFO);
     * Then you can Log.setLevel("com.foo.game", Log.DEBUG). Now, everything
     * logs at INFO level except for modules within com.foo.game, which is at DEBUG.
     */
    public static function setLevel (module :String, level :int) :void
    {
        _setLevels[module] = level;
        _levels = {}; // reset cached levels
    }

    /**
     * Parses a String in the form of ":info;com.foo.game:debug;com.bar.util:warning"
     *
     * Semicolons separate modules, colons separate a module name from the log level.
     * An empty string specifies the top-level (global) module.
     */
    public static function setLevels (settingString :String) :void
    {
        for each (var module :String in settingString.split(";")) {
            var setting :Array = module.split(":");
            _setLevels[setting[0]] = stringToLevel(String(setting[1]));
        }
        _levels = {}; // reset cached levels
    }

    /**
     * Use Log.getLog();
     *
     * @private
     */
    public function Log (module :String)
    {
        if (module == null) module = "";
        _module = module;
    }

    /**
     * Log a message with 'debug' priority.
     *
     * @param args The first argument is the actual message to log. After that, each pair
     * of parameters is printed in key/value form, the benefit being that if no log
     * message is generated then toString() will not be called on the values.
     * A final parameter may be an Error, in which case the stack trace is printed.
     *
     * @example
     * <listing version="3.0">
     *    log.debug("Message", "key1", value1, "key2", value2, optionalError);
     * </listing>
     */
    public function debug (... args) :void
    {
        doLog(DEBUG, args);
    }

    /**
     * Log a message with 'info' priority.
     *
     * @param args The first argument is the actual message to log. After that, each pair
     * of parameters is printed in key/value form, the benefit being that if no log
     * message is generated then toString() will not be called on the values.
     * A final parameter may be an Error, in which case the stack trace is printed.
     *
     * @example
     * <listing version="3.0">
     *    log.info("Message", "key1", value1, "key2", value2, optionalError);
     * </listing>
     */
    public function info (... args) :void
    {
        doLog(INFO, args);
    }

    /**
     * Log a message with 'warning' priority.
     *
     * @param args The first argument is the actual message to log. After that, each pair
     * of parameters is printed in key/value form, the benefit being that if no log
     * message is generated then toString() will not be called on the values.
     * A final parameter may be an Error, in which case the stack trace is printed.
     *
     * @example
     * <listing version="3.0">
     *    log.warning("Message", "key1", value1, "key2", value2, optionalError);
     * </listing>
     */
    public function warning (... args) :void
    {
        doLog(WARNING, args);
    }

    /**
     * Log a message with 'error' priority.
     *
     * @param args The first argument is the actual message to log. After that, each pair
     * of parameters is printed in key/value form, the benefit being that if no log
     * message is generated then toString() will not be called on the values.
     * A final parameter may be an Error, in which case the stack trace is printed.
     *
     * @example
     * <listing version="3.0">
     *    log.error("Message", "key1", value1, "key2", value2, optionalError);
     * </listing>
     */
    public function error (... args) :void
    {
        doLog(ERROR, args);
    }

    /**
     * Log just a stack trace with 'warning' priority.
     * Deprecated, sorta. Just use warning("Message", error);
     */
    public function logStackTrace (error :Error) :void
    {
        warning(error.getStackTrace());
    }

    protected function doLog (level :int, args :Array) :void
    {
        if (level < getLevel(_module)) {
            return; // we don't want to log it!
        }
        var logMessage :String = formatMessage(level, args);
        trace(logMessage);
        // possibly also dispatch to any other log targets.
        for each (var target :LogTarget in _targets) {
            target.log(logMessage);
        }
    }

    protected function formatMessage (level :int, args :Array) :String
    {
        var msg :String = getTimeStamp() + " " + LEVEL_NAMES[level] + ": " + _module;
        if (args.length > 0) {
            msg += " " + String(args[0]); // the primary log message
            var err :Error = null;
            if (args.length % 2 == 0) { // there's one extra arg
                var lastArg :Object = args.pop();
                if (lastArg is Error) {
                    err = lastArg as Error; // ok, it's an error, we like those
                } else {
                    args.push(lastArg, ""); // what? Well, cope by pushing it back with a ""
                }
            }
            if (args.length > 1) {
                for (var ii :int = 1; ii < args.length; ii += 2) {
                    msg += (ii == 1) ? " [" : ", ";
                    msg += String(args[ii]) + "=" + String(args[ii + 1]);
                }
                msg += "]";
            }
            if (err != null) {
                msg += "\n" + err.getStackTrace();
            }
        }
        return msg;
    }

    protected function getTimeStamp () :String
    {
        var d :Date = new Date();
        // return d.toLocaleTimeString();

        // format it like the date format in our java logs
        return d.fullYear + "-" +
            StringUtil.prepad(String(d.month + 1), 2, "0") + "-" +
            StringUtil.prepad(String(d.date), 2, "0") + " " +
            StringUtil.prepad(String(d.hours), 2, "0") + ":" +
            StringUtil.prepad(String(d.minutes), 2, "0") + ":" +
            StringUtil.prepad(String(d.seconds), 2, "0") + "," +
            StringUtil.prepad(String(d.milliseconds), 3, "0");
    }

    /**
     * Get the logging level for the specified module.
     */
    protected static function getLevel (module :String) :int
    {
        // we probably already have the level cached for this module
        var lev :Object = _levels[module];
        if (lev == null) {
            // cache miss- copy some parent module's level...
            var ancestor :String = module;
            while (true) {
                lev = _setLevels[ancestor];
                if (lev != null || ancestor == "") {
                    // bail if we found a setting or get to the top level,
                    // but always save the level from _setLevels into _levels
                    _levels[module] = int(lev); // if lev was null, this will become 0 (DEBUG)
                    break;
                }
                var dex :int = ancestor.lastIndexOf(".");
                ancestor = (dex == -1) ? "" : ancestor.substring(0, dex);
            }
        }
        return int(lev);
    }

    protected static function stringToLevel (s :String) :int
    {
        switch (s.toLowerCase()) {
        default: // default to DEBUG
        case "debug": return DEBUG;
        case "info": return INFO;
        case "warning": case "warn": return WARNING;
        case "error": return ERROR;
        case "off": return OFF;
        }
    }

    /** The module to which this log instance applies. */
    protected var _module :String;

    /** Other registered LogTargets, besides the trace log. */
    protected static var _targets :Array = [];

    /** A cache of log levels, copied from _setLevels. */
    protected static var _levels :Object = {};

    /** The configured log levels. */
    protected static var _setLevels :Object = { "": DEBUG }; // global: debug

    /** The outputted names of each level. The last one isn't used, it corresponds with OFF. */
    protected static const LEVEL_NAMES :Array = [ "debug", "INFO", "WARN", "ERROR", false ];
}
}

Allow a message to be passed to happy testing dumpStack()

git-svn-id: a1a4b28b82a3276cc491891159dd9963a0a72fae@5566 542714f4-19e9-0310-aa3c-eee0fc999fb1


/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
package org.mangui.hls {
    import org.mangui.hls.constant.HLSSeekMode;
    import org.mangui.hls.constant.HLSMaxLevelCappingMode;
    public final class HLSSettings extends Object {
        /**
         * Limit levels usable in auto-quality by the stage dimensions (width and height).
         *      true - level width and height (defined in m3u8 playlist) will be compared with the player width and height (stage.stageWidth and stage.stageHeight).
         *             Max level will be set depending on the maxLevelCappingMode option.
         *      false - levels will not be limited. All available levels could be used in auto-quality mode taking only bandwidth into consideration.
         *
         * Note: this setting is ignored in manual mode so all the levels could be selected manually.
         *
         * Default is false
         */
        public static var capLevelToStage : Boolean = false;

        /**
         * Defines the max level capping mode to the one available in HLSMaxLevelCappingMode
         *      HLSMaxLevelCappingMode.DOWNSCALE - max capped level should be the one with the dimensions equal or greater than the stage dimensions (so the video will be downscaled)
         *      HLSMaxLevelCappingMode.UPSCALE - max capped level should be the one with the dimensions equal or lower than the stage dimensions (so the video will be upscaled)
         *
         * Default is HLSMaxLevelCappingMode.DOWNSCALE
         */
        public static var maxLevelCappingMode : String = HLSMaxLevelCappingMode.DOWNSCALE;

        // // // // // ///////////////////////////////////
        //
        // org.mangui.hls.stream.HLSNetStream
        //
        // // // // // ///////////////////////////////////
        /**
         * Defines minimum buffer length in seconds before playback can start, after seeking or buffer stalling.
         *
         * Default is -1 = auto
         */
        public static var minBufferLength : Number = -1;
        /**
         * Defines maximum buffer length in seconds.
         * (0 means infinite buffering)
         *
         * Default is 60.
         */
        public static var maxBufferLength : Number = 60;
        /**
         * Defines low buffer length in seconds.
         * When crossing down this threshold, HLS will switch to buffering state.
         *
         * Default is 3.
         */
        public static var lowBufferLength : Number = 3;
        /**
         * Defines seek mode to one form available in HLSSeekMode class:
         *      HLSSeekMode.ACCURATE_SEEK - accurate seeking to exact requested position
         *      HLSSeekMode.KEYFRAME_SEEK - key-frame based seeking (seek to nearest key frame before requested seek position)
         *      HLSSeekMode.SEGMENT_SEEK - segment based seeking (seek to beginning of segment containing requested seek position)
         *
         * Default is HLSSeekMode.ACCURATE_SEEK.
         */
        public static var seekMode : String = HLSSeekMode.ACCURATE_SEEK;
        /** max nb of retries for Key Loading in case I/O errors are met,
         *      0, means no retry, error will be triggered automatically
         *     -1 means infinite retry
         */
        public static var keyLoadMaxRetry : int = -1;

        /** keyLoadMaxRetryTimeout

         * Maximum key retry timeout (in milliseconds) in case I/O errors are met.
         * Every fail on key request, player will exponentially increase the timeout to try again.
         * It starts waiting 1 second (1000ms), than 2, 4, 8, 16, until keyLoadMaxRetryTimeout is reached.
         *
         * Default is 64000.
         */
        public static var keyLoadMaxRetryTimeout : Number = 64000;
        /** max nb of retries for Fragment Loading in case I/O errors are met,
         *      0, means no retry, error will be triggered automatically
         *     -1 means infinite retry
         */
        public static var fragmentLoadMaxRetry : int = -1;

        /** fragmentLoadMaxRetryTimeout

         * Maximum Fragment retry timeout (in milliseconds) in case I/O errors are met.
         * Every fail on fragment request, player will exponentially increase the timeout to try again.
         * It starts waiting 1 second (1000ms), than 2, 4, 8, 16, until fragmentLoadMaxRetryTimeout is reached.
         *
         * Default is 64000.
         */
        public static var fragmentLoadMaxRetryTimeout : Number = 64000;
        /**
         * If set to true, live playlist will be flushed from URL cache before reloading
         * (this is to workaround some cache issues with some combination of Flash Player / IE version)
         *
         * Default is false
         */
        public static var flushLiveURLCache : Boolean = false;
        /** max nb of retries for Manifest Loading in case I/O errors are met,
         *      0, means no retry, error will be triggered automatically
         *     -1 means infinite retry
         */
        public static var manifestLoadMaxRetry : int = -1;
        /** manifestLoadMaxRetryTimeout

         * Maximum Manifest retry timeout (in milliseconds) in case I/O errors are met.
         * Every fail on fragment request, player will exponentially increase the timeout to try again.
         * It starts waiting 1 second (1000ms), than 2, 4, 8, 16, until manifestLoadMaxRetryTimeout is reached.
         *
         * Default is 64000.
         */
        public static var manifestLoadMaxRetryTimeout : Number = 64000;

        /**
         * If greater than 0, specifies the preferred bitrate.
         * If -1, and startFromLevel is not specified, automatic start level selection will be used.
         * This parameter, if set, will take priority over startFromLevel.
         */
        public static var startFromBitrate : Number = -1;

        /** start level :
         *  from 0 to 1 : indicates the "normalized" preferred bitrate. As such, if it is 0.5, the closest to the middle bitrate will be selected and used first.
         * -1 : automatic start level selection, playback will start from level matching download bandwidth (determined from download of first segment)
         */
        public static var startFromLevel : Number = -1;

        /** seek level :
         *  from 0 to 1 : indicates the "normalized" preferred bitrate. As such, if it is 0.5, the closest to the middle bitrate will be selected and used first.
         * -1 : automatic start level selection, keep previous level matching previous download bandwidth
         */
        public static var seekFromLevel : Number = -1;

        /** use hardware video decoder :
         *  it will set NetStream.useHardwareDecoder
         *  refer to http://help.adobe.com/en_US/FlashPlatform/reference/actionscript/3/flash/net/NetStream.html#useHardwareDecoder
         */
        public static var useHardwareVideoDecoder : Boolean = false;

        /**
         * Defines whether INFO level log messages will will appear in the console
         * Default is true.
         */
        public static var logInfo : Boolean = true;
        /**
         * Defines whether DEBUG level log messages will will appear in the console
         * Default is false.
         */
        public static var logDebug : Boolean = false;
        /**
         * Defines whether DEBUG2 level log messages will will appear in the console
         * Default is false.
         */
        public static var logDebug2 : Boolean = false;
        /**
         * Defines whether WARN level log messages will will appear in the console
         * Default is true.
         */
        public static var logWarn : Boolean = true;
        /**
         * Defines whether ERROR level log messages will will appear in the console
         * Default is true.
         */
        public static var logError : Boolean = true;
    }
}

/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
package org.mangui.hls {
    import org.mangui.hls.constant.HLSSeekMode;
    import org.mangui.hls.constant.HLSMaxLevelCappingMode;
    public final class HLSSettings extends Object {
        /**
         * Limit levels usable in auto-quality by the stage dimensions (width and height).
         *      true - level width and height (defined in m3u8 playlist) will be compared with the player width and height (stage.stageWidth and stage.stageHeight).
         *             Max level will be set depending on the maxLevelCappingMode option.
         *      false - levels will not be limited. All available levels could be used in auto-quality mode taking only bandwidth into consideration.
         *
         * Note: this setting is ignored in manual mode so all the levels could be selected manually.
         *
         * Default is false
         */
        public static var capLevelToStage : Boolean = false;

        /**
         * Defines the max level capping mode to the one available in HLSMaxLevelCappingMode
         *      HLSMaxLevelCappingMode.DOWNSCALE - max capped level should be the one with the dimensions equal or greater than the stage dimensions (so the video will be downscaled)
         *      HLSMaxLevelCappingMode.UPSCALE - max capped level should be the one with the dimensions equal or lower than the stage dimensions (so the video will be upscaled)
         *
         * Default is HLSMaxLevelCappingMode.DOWNSCALE
         */
        public static var maxLevelCappingMode : String = HLSMaxLevelCappingMode.DOWNSCALE;

        // // // // // ///////////////////////////////////
        //
        // org.mangui.hls.stream.HLSNetStream
        //
        // // // // // ///////////////////////////////////
        /**
         * Defines minimum buffer length in seconds before playback can start, after seeking or buffer stalling.
         *
         * Default is -1 = auto
         */
        public static var minBufferLength : Number = -1;
        /**
         * Defines maximum buffer length in seconds.
         * (0 means infinite buffering)
         *
         * Default is 300.
         */
        public static var maxBufferLength : Number = 300;
        /**
         * Defines low buffer length in seconds.
         * When crossing down this threshold, HLS will switch to buffering state.
         *
         * Default is 3.
         */
        public static var lowBufferLength : Number = 3;
        /**
         * Defines seek mode to one form available in HLSSeekMode class:
         *      HLSSeekMode.ACCURATE_SEEK - accurate seeking to exact requested position
         *      HLSSeekMode.KEYFRAME_SEEK - key-frame based seeking (seek to nearest key frame before requested seek position)
         *      HLSSeekMode.SEGMENT_SEEK - segment based seeking (seek to beginning of segment containing requested seek position)
         *
         * Default is HLSSeekMode.ACCURATE_SEEK.
         */
        public static var seekMode : String = HLSSeekMode.ACCURATE_SEEK;
        /** max nb of retries for Key Loading in case I/O errors are met,
         *      0, means no retry, error will be triggered automatically
         *     -1 means infinite retry
         */
        public static var keyLoadMaxRetry : int = -1;

        /** keyLoadMaxRetryTimeout

         * Maximum key retry timeout (in milliseconds) in case I/O errors are met.
         * Every fail on key request, player will exponentially increase the timeout to try again.
         * It starts waiting 1 second (1000ms), than 2, 4, 8, 16, until keyLoadMaxRetryTimeout is reached.
         *
         * Default is 64000.
         */
        public static var keyLoadMaxRetryTimeout : Number = 64000;
        /** max nb of retries for Fragment Loading in case I/O errors are met,
         *      0, means no retry, error will be triggered automatically
         *     -1 means infinite retry
         */
        public static var fragmentLoadMaxRetry : int = -1;

        /** fragmentLoadMaxRetryTimeout

         * Maximum Fragment retry timeout (in milliseconds) in case I/O errors are met.
         * Every fail on fragment request, player will exponentially increase the timeout to try again.
         * It starts waiting 1 second (1000ms), than 2, 4, 8, 16, until fragmentLoadMaxRetryTimeout is reached.
         *
         * Default is 64000.
         */
        public static var fragmentLoadMaxRetryTimeout : Number = 64000;
        /**
         * If set to true, live playlist will be flushed from URL cache before reloading
         * (this is to workaround some cache issues with some combination of Flash Player / IE version)
         *
         * Default is false
         */
        public static var flushLiveURLCache : Boolean = false;
        /** max nb of retries for Manifest Loading in case I/O errors are met,
         *      0, means no retry, error will be triggered automatically
         *     -1 means infinite retry
         */
        public static var manifestLoadMaxRetry : int = -1;
        /** manifestLoadMaxRetryTimeout

         * Maximum Manifest retry timeout (in milliseconds) in case I/O errors are met.
         * Every fail on fragment request, player will exponentially increase the timeout to try again.
         * It starts waiting 1 second (1000ms), than 2, 4, 8, 16, until manifestLoadMaxRetryTimeout is reached.
         *
         * Default is 64000.
         */
        public static var manifestLoadMaxRetryTimeout : Number = 64000;

        /**
         * If greater than 0, specifies the preferred bitrate.
         * If -1, and startFromLevel is not specified, automatic start level selection will be used.
         * This parameter, if set, will take priority over startFromLevel.
         */
        public static var startFromBitrate : Number = -1;

        /** start level :
         *  from 0 to 1 : indicates the "normalized" preferred bitrate. As such, if it is 0.5, the closest to the middle bitrate will be selected and used first.
         * -1 : automatic start level selection, playback will start from level matching download bandwidth (determined from download of first segment)
         */
        public static var startFromLevel : Number = -1;

        /** seek level :
         *  from 0 to 1 : indicates the "normalized" preferred bitrate. As such, if it is 0.5, the closest to the middle bitrate will be selected and used first.
         * -1 : automatic start level selection, keep previous level matching previous download bandwidth
         */
        public static var seekFromLevel : Number = -1;

        /** use hardware video decoder :
         *  it will set NetStream.useHardwareDecoder
         *  refer to http://help.adobe.com/en_US/FlashPlatform/reference/actionscript/3/flash/net/NetStream.html#useHardwareDecoder
         */
        public static var useHardwareVideoDecoder : Boolean = false;

        /**
         * Defines whether INFO level log messages will will appear in the console
         * Default is true.
         */
        public static var logInfo : Boolean = true;
        /**
         * Defines whether DEBUG level log messages will will appear in the console
         * Default is false.
         */
        public static var logDebug : Boolean = false;
        /**
         * Defines whether DEBUG2 level log messages will will appear in the console
         * Default is false.
         */
        public static var logDebug2 : Boolean = false;
        /**
         * Defines whether WARN level log messages will will appear in the console
         * Default is true.
         */
        public static var logWarn : Boolean = true;
        /**
         * Defines whether ERROR level log messages will will appear in the console
         * Default is true.
         */
        public static var logError : Boolean = true;
    }
}

increase default max buffer length to 300s


package org.openPyro.managers
{
	import flash.display.DisplayObject;
	import flash.display.DisplayObjectContainer;
	import flash.display.Sprite;
	import flash.events.Event;
	import flash.geom.Point;

	public class OverlayManager
	{
		public function OverlayManager()
		{
			super();
		}

		protected var _overlayDisplayObject:DisplayObjectContainer

		/**
		 * The overlay layer on which the popups are rendered. If a new layer
		 * is set as the overlayContainer, the old one is removed from the
		 * stage, so please set displayobjects for this value to be dedicated
		 * to just displaying overlays.
		 */
		public function set overlayContainer(dp:DisplayObjectContainer):void{
			/*
			If there already was an overlay created, remove
			it and all its children
			*/
			if(_overlayDisplayObject){
				while(_overlayDisplayObject.numChildren > 0){
					_overlayDisplayObject.removeChildAt(0)
				}
				bgSprite = null;
				if(_overlayDisplayObject.parent){
					_overlayDisplayObject.parent.removeChild(_overlayDisplayObject);
				}
			}
			_overlayDisplayObject = dp;
		}

		public function get overlayContainer():DisplayObjectContainer{
			return _overlayDisplayObject;
		}

		private static var instance:OverlayManager
		public static function getInstance():OverlayManager{
			if(!instance){
				instance = new OverlayManager()
			}
			return instance;
		}

		private var bgSprite:Sprite;
		private var centeredPopup:DisplayObject

		public function showPopUp(popupObject:DisplayObject, modal:Boolean=false, center:Boolean=true):void{
			if(!_overlayDisplayObject){
				throw new Error("No overlay displayObject defined on which to draw")
			}

			if(modal){

				if(!bgSprite){
					bgSprite = new Sprite()
					bgSprite.graphics.beginFill(0x00000, .5)
					bgSprite.graphics.drawRect(0,0,50,50);
					_overlayDisplayObject.addChild(bgSprite);
					_overlayDisplayObject.stage.addEventListener(Event.RESIZE, onBaseStageResize)
					// capture mouse interactions here.
				}
				bgSprite.width = _overlayDisplayObject.stage.stageWidth;
				bgSprite.height = _overlayDisplayObject.stage.stageHeight
				bgSprite.visible = true;
			}

			_overlayDisplayObject.addChild(popupObject);
			if(center){
				centeredPopup = popupObject
				popupObject.x = (_overlayDisplayObject.stage.stageWidth-popupObject.width)/2;
				popupObject.y = (_overlayDisplayObject.stage.stageHeight-popupObject.height)/2;
			}

		}

		/**
		 * Shows the object DisplayObject at the x and y coordinates of the target DisplayObject.
		 * If the target is not supplied, the object is positioned at 0,0.
		 */
		public function showOnOverlay(object:DisplayObject, target:DisplayObject=null):void{
			_overlayDisplayObject.addChild(object);
			if(target){
				var orig:Point = new Point(0,0)
				var pt:Point = target.localToGlobal(orig)
				pt = _overlayDisplayObject.globalToLocal(pt);
				object.x = pt.x
				object.y = pt.y
			}

			var xp:Number = object.stage.stageWidth - (object.x+object.width+10);
			if(xp < 0){
				object.x += xp;
			}
		}

		public function remove(popup:DisplayObject):void{

			if(!popup || !popup.parent) return;

			popup.parent.removeChild(popup);
			bgSprite.visible = false;
		}

		public function removeAll():void{
			while(_overlayDisplayObject.numChildren > 1){
				_overlayDisplayObject.removeChildAt(1);
			}
			if(bgSprite){
				bgSprite.visible = false;
			}
		}

		private function onBaseStageResize(event:Event):void{
			if(bgSprite){
				bgSprite.width = _overlayDisplayObject.stage.stageWidth
				bgSprite.height = _overlayDisplayObject.stage.stageHeight
			}
			if(centeredPopup){
				centeredPopup.x = (_overlayDisplayObject.stage.stageWidth-centeredPopup.width)/2;
				centeredPopup.y = (_overlayDisplayObject.stage.stageHeight-centeredPopup.height)/2;
			}
		}

	}
}

package org.openPyro.managers
{
	import flash.display.DisplayObject;
	import flash.display.DisplayObjectContainer;
	import flash.display.Sprite;
	import flash.events.Event;
	import flash.geom.Point;

	public class OverlayManager
	{
		public function OverlayManager()
		{
			super();
		}

		protected var _overlayDisplayObject:DisplayObjectContainer

		/**
		 * The overlay layer on which the popups are rendered. If a new layer
		 * is set as the overlayContainer, the old one is removed from the
		 * stage, so please set displayobjects for this value to be dedicated
		 * to just displaying overlays.
		 */
		public function set overlayContainer(dp:DisplayObjectContainer):void{
			/*
			If there already was an overlay created, remove
			it and all its children
			*/
			if(_overlayDisplayObject){
				while(_overlayDisplayObject.numChildren > 0){
					_overlayDisplayObject.removeChildAt(0)
				}
				bgSprite = null;
				if(_overlayDisplayObject.parent){
					_overlayDisplayObject.parent.removeChild(_overlayDisplayObject);
				}
			}
			_overlayDisplayObject = dp;
		}

		public function get overlayContainer():DisplayObjectContainer{
			return _overlayDisplayObject;
		}

		private static var instance:OverlayManager
		public static function getInstance():OverlayManager{
			if(!instance){
				instance = new OverlayManager()
			}
			return instance;
		}

		private var bgSprite:Sprite;
		private var centeredPopup:DisplayObject

		public function showPopUp(popupObject:DisplayObject, modal:Boolean=false, center:Boolean=true):void{
			if(!_overlayDisplayObject){
				throw new Error("No overlay displayObject defined on which to draw")
			}

			if(modal){

				if(!bgSprite){
					bgSprite = new Sprite()
					bgSprite.graphics.beginFill(0x00000, .5)
					bgSprite.graphics.drawRect(0,0,50,50);
					_overlayDisplayObject.addChild(bgSprite);
					_overlayDisplayObject.stage.addEventListener(Event.RESIZE, onBaseStageResize)
					// capture mouse interactions here.
				}
				bgSprite.width = _overlayDisplayObject.stage.stageWidth;
				bgSprite.height = _overlayDisplayObject.stage.stageHeight
				bgSprite.visible = true;
			}

			_overlayDisplayObject.addChild(popupObject);
			if(center){
				centeredPopup = popupObject
				popupObject.x = (_overlayDisplayObject.stage.stageWidth-popupObject.width)/2;
				popupObject.y = (_overlayDisplayObject.stage.stageHeight-popupObject.height)/2;
			}

		}

		/**
		 * Shows the object DisplayObject at the x and y coordinates of the target DisplayObject.
		 * If the target is not supplied, the object is positioned at 0,0.
		 */
		public function showOnOverlay(object:DisplayObject, target:DisplayObject=null):void{
			_overlayDisplayObject.addChild(object);
			if(target){
				var orig:Point = new Point(0,0)
				var pt:Point = target.localToGlobal(orig)
				pt = _overlayDisplayObject.globalToLocal(pt);
				object.x = pt.x
				object.y = pt.y
			}

			var xp:Number = object.stage.stageWidth - (object.x+object.width+10);
			if(xp < 0){
				object.x += xp;
			}
		}

		public function remove(popup:DisplayObject):void{

			if(!popup || !popup.parent) return;

			popup.parent.removeChild(popup);
			if(bgSprite){
				bgSprite.visible = false;
			}
		}

		public function removeAll():void{
			while(_overlayDisplayObject.numChildren > 1){
				_overlayDisplayObject.removeChildAt(1);
			}
			if(bgSprite){
				bgSprite.visible = false;
			}
		}

		private function onBaseStageResize(event:Event):void{
			if(bgSprite){
				bgSprite.width = _overlayDisplayObject.stage.stageWidth
				bgSprite.height = _overlayDisplayObject.stage.stageHeight
			}
			if(centeredPopup){
				centeredPopup.x = (_overlayDisplayObject.stage.stageWidth-centeredPopup.width)/2;
				centeredPopup.y = (_overlayDisplayObject.stage.stageHeight-centeredPopup.height)/2;
			}
		}

	}
}

check that could cause null object reference in OverlayManager


//
// $Id$
//
// Nenya library - tools for developing networked games
// Copyright (C) 2002-2007 Three Rings Design, Inc., All Rights Reserved
// http://www.threerings.net/code/nenya/
//
// This library is free software; you can redistribute it and/or modify it
// under the terms of the GNU Lesser General Public License as published
// by the Free Software Foundation; either version 2.1 of the License, or
// (at your option) any later version.
//
// This library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
// Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public
// License along with this library; if not, write to the Free Software
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA

package com.threerings.flex {

import flash.display.DisplayObjectContainer;

import flash.events.Event;

import mx.controls.TextArea;

import com.threerings.crowd.chat.client.ChatDirector;
import com.threerings.crowd.chat.client.ChatDisplay;
import com.threerings.crowd.chat.data.ChatMessage;
import com.threerings.crowd.chat.data.UserMessage;
import com.threerings.crowd.util.CrowdContext;

/**
 * A very simple chat display.
 */
public class ChatDisplayBox extends TextArea
    implements ChatDisplay
{
    public function ChatDisplayBox (ctx :CrowdContext)
    {
        _ctx = ctx;
        this.editable = false;

        // TODO
        width = 400;
        height = 150;

        addEventListener(Event.ADDED_TO_STAGE, handleAddRemove);
        addEventListener(Event.REMOVED_FROM_STAGE, handleAddRemove);
    }

    // documentation inherited from interface ChatDisplay
    public function clear () :void
    {
        this.htmlText = "";
    }

    // documentation inherited from interface ChatDisplay
    public function displayMessage (
        msg :ChatMessage, alreadyDisplayed :Boolean) :Boolean
    {
        if (!_scrollBot) {
            _scrollBot = (verticalScrollPosition == maxVerticalScrollPosition);
        }

        // display the message
        if (msg is UserMessage) {
            this.htmlText += "<" +
                (msg as UserMessage).speaker + "> ";
        }
        this.htmlText += msg.message;
        return true;
    }

    // handle us being added or removed from the stage
    protected function handleAddRemove (event :Event) :void
    {
        var chatdir :ChatDirector = _ctx.getChatDirector();
        if (event.type == Event.ADDED_TO_STAGE) {
            chatdir.addChatDisplay(this);
        } else {
            chatdir.removeChatDisplay(this);
        }
    }

    // documentation inherited
    override protected function updateDisplayList (uw :Number, uh :Number) :void
    {
        super.updateDisplayList(uw, uh);

        if (_scrollBot) {
            verticalScrollPosition = maxVerticalScrollPosition;
            _scrollBot = false;
        }
    }

    /** The giver of life. */
    protected var _ctx :CrowdContext;

    protected var _scrollBot :Boolean;
}
}

//
// $Id$
//
// Nenya library - tools for developing networked games
// Copyright (C) 2002-2007 Three Rings Design, Inc., All Rights Reserved
// http://www.threerings.net/code/nenya/
//
// This library is free software; you can redistribute it and/or modify it
// under the terms of the GNU Lesser General Public License as published
// by the Free Software Foundation; either version 2.1 of the License, or
// (at your option) any later version.
//
// This library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
// Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public
// License along with this library; if not, write to the Free Software
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA

package com.threerings.flex {

import flash.display.DisplayObjectContainer;

import flash.events.Event;

import mx.controls.TextArea;

import com.threerings.crowd.chat.client.ChatDirector;
import com.threerings.crowd.chat.client.ChatDisplay;
import com.threerings.crowd.chat.data.ChatMessage;
import com.threerings.crowd.chat.data.UserMessage;
import com.threerings.crowd.util.CrowdContext;

/**
 * A very simple chat display.
 */
public class ChatDisplayBox extends TextArea
    implements ChatDisplay
{
    public function ChatDisplayBox (ctx :CrowdContext)
    {
        _ctx = ctx;
        this.editable = false;

        // TODO
        width = 400;
        height = 150;

        addEventListener(Event.ADDED_TO_STAGE, handleAddRemove);
        addEventListener(Event.REMOVED_FROM_STAGE, handleAddRemove);
    }

    // documentation inherited from interface ChatDisplay
    public function clear () :void
    {
        this.htmlText = "";
    }

    // documentation inherited from interface ChatDisplay
    public function displayMessage (msg :ChatMessage) :void
    {
        if (!_scrollBot) {
            _scrollBot = (verticalScrollPosition == maxVerticalScrollPosition);
        }

        // display the message
        if (msg is UserMessage) {
            this.htmlText += "<" +
                (msg as UserMessage).speaker + "> ";
        }
        this.htmlText += msg.message;
    }

    // handle us being added or removed from the stage
    protected function handleAddRemove (event :Event) :void
    {
        var chatdir :ChatDirector = _ctx.getChatDirector();
        if (event.type == Event.ADDED_TO_STAGE) {
            chatdir.addChatDisplay(this);
        } else {
            chatdir.removeChatDisplay(this);
        }
    }

    // documentation inherited
    override protected function updateDisplayList (uw :Number, uh :Number) :void
    {
        super.updateDisplayList(uw, uh);

        if (_scrollBot) {
            verticalScrollPosition = maxVerticalScrollPosition;
            _scrollBot = false;
        }
    }

    /** The giver of life. */
    protected var _ctx :CrowdContext;

    protected var _scrollBot :Boolean;
}
}

Update to match change to ChatDisplay interface.

git-svn-id: b675b909355d5cf946977f44a8ec5a6ceb3782e4@847 ed5b42cb-e716-0410-a449-f6a68f950b19


////////////////////////////////////////////////////////////////////////////////
//
//  Licensed to the Apache Software Foundation (ASF) under one or more
//  contributor license agreements.  See the NOTICE file distributed with
//  this work for additional information regarding copyright ownership.
//  The ASF licenses this file to You under the Apache License, Version 2.0
//  (the "License"); you may not use this file except in compliance with
//  the License.  You may obtain a copy of the License at
//
//      http://www.apache.org/licenses/LICENSE-2.0
//
//  Unless required by applicable law or agreed to in writing, software
//  distributed under the License is distributed on an "AS IS" BASIS,
//  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
//  See the License for the specific language governing permissions and
//  limitations under the License.
//
////////////////////////////////////////////////////////////////////////////////

package {
    import mx.collections.ArrayList;
    import mx.collections.ComplexSortField;
    import mx.collections.IList;
    import mx.collections.ListCollectionView;
    import mx.collections.Sort;
    import mx.collections.SortField;
    import mx.utils.ObjectUtil;
    import mx.core.mx_internal;
    import org.flexunit.asserts.assertEquals;

    public class ListCollectionView_FLEX_34837_Tests {
        private var _sut:ListCollectionView;

        [Before]
        public function setUp():void
        {
            _sut = new ListCollectionView(new ArrayList());
        }

        [After]
        public function tearDown():void
        {
            _sut = null;
        }

        [Test]
        public function test_sort_by_complex_fields_with_custom_compare_function_for_sort():void
        {
            function compareByStreet(a:ListCollectionView_FLEX_34837_VO, b:ListCollectionView_FLEX_34837_VO, fields:Array):int
            {
                var streetA:String = a.address ? a.address.street : null;
                var streetB:String = b.address ? b.address.street : null;

                return ObjectUtil.stringCompare(streetA, streetB, true);
            }

            //given
            var from4To0:IList = generateVOs(5, true);
            _sut.addAll(from4To0); //values["address.street"]: Street4, Street3, Street2, Street1, Street0

            const sortByStreetAscending:Sort = new Sort();
            sortByStreetAscending.fields = [new ComplexSortField("address.street", false, false, false)]; //should make no difference
            sortByStreetAscending.compareFunction = compareByStreet;
            _sut.sort = sortByStreetAscending;

            //when
            _sut.refresh(); //should be: Street0, Street1, Street2, Street3, Street4

            //then
            assertIndexesAre([0, 1, 2, 3, 4]);
        }

        [Test]
        public function test_sort_by_complex_fields_with_custom_compare_function_for_sort_field():void
        {
            function compareByStreet(a:ListCollectionView_FLEX_34837_VO, b:ListCollectionView_FLEX_34837_VO):int
            {
                var streetA:String = a.address ? a.address.street : null;
                var streetB:String = b.address ? b.address.street : null;

                return ObjectUtil.stringCompare(streetA, streetB, true);
            }

            //given
            var from4To0:IList = generateVOs(5, true);
            _sut.addAll(from4To0); //values["address.street"]: Street4, Street3, Street2, Street1, Street0

            const sortByStreetAscending:Sort = new Sort();
            var sortField:SortField = new ComplexSortField("address.street", false, false, false);
            sortField.mx_internal::compareFunction_ = compareByStreet;
            sortByStreetAscending.fields = [sortField];
            _sut.sort = sortByStreetAscending;

            //when
            _sut.refresh(); //should be: Street0, Street1, Street2, Street3, Street4

            //then
            assertIndexesAre([0, 1, 2, 3, 4]);
        }

        [Test]
        public function test_changing_simple_sort_field_value_places_it_correctly_according_to_collection_sort():void
        {
            //given
            var from1To4:IList = generateVOs(5);
            from1To4.removeItemAt(0);
            _sut.addAll(from1To4);

            const sortByNameAscending:Sort = new Sort();
            sortByNameAscending.fields = [new SortField("name", false, false, false)];
            _sut.sort = sortByNameAscending;
            _sut.refresh(); //values: Object1, Object2, Object3, Object4

            //when
            const newItem:ListCollectionView_FLEX_34837_VO = generateOneObject(5);
            _sut.addItem(newItem); //values: Object1, Object2, Object3, Object4, Object5
            newItem.name = "Object0"; //this should immediately place the newItem at position 0

            //then
            const newItemIndex:int = _sut.getItemIndex(newItem);
            assertEquals("the new item should have been placed at the beginning of the list as soon as its name was changed", 0, newItemIndex);
        }

        private function assertIndexesAre(indexes:Array):void
        {
            assertEquals(indexes.length, _sut.length);

            for(var i:int = 0; i < _sut.length; i++)
            {
                assertEquals(ListCollectionView_FLEX_34837_VO(_sut.getItemAt(i)).index, indexes[i]);
            }
        }

        private static function generateVOs(no:int, reverse:Boolean = false):IList
        {
            return generateObjects(no, reverse, generateOneObject);
        }

        private static function generateObjects(no:int, reverse:Boolean, generator:Function):IList
        {
            var result:Array = [];
            for(var i:int = 0; i < no; i++)
            {
                result.push(generator(i));
            }

            if(reverse)
                result.reverse();

            return new ArrayList(result);
        }

        private static function generateOneObject(index:Number):ListCollectionView_FLEX_34837_VO
        {
            return new ListCollectionView_FLEX_34837_VO(index, "Object", "Street");
        }
    }
}

[Bindable]
class ListCollectionView_FLEX_34837_VO
{
    public var name:String;
    public var address:ListCollectionView_FLEX_34837_AddressVO;
    public var index:Number;

    public function ListCollectionView_FLEX_34837_VO(index:Number, namePrefix:String, streetPrefix:String)
    {
        this.index = index;
        this.name = namePrefix + index;
        this.address = new ListCollectionView_FLEX_34837_AddressVO(streetPrefix + index);
    }
}

[Bindable]
class ListCollectionView_FLEX_34837_AddressVO
{
    public var street:String;

    public function ListCollectionView_FLEX_34837_AddressVO(street:String)
    {
        this.street = street;
    }
}

////////////////////////////////////////////////////////////////////////////////
//
//  Licensed to the Apache Software Foundation (ASF) under one or more
//  contributor license agreements.  See the NOTICE file distributed with
//  this work for additional information regarding copyright ownership.
//  The ASF licenses this file to You under the Apache License, Version 2.0
//  (the "License"); you may not use this file except in compliance with
//  the License.  You may obtain a copy of the License at
//
//      http://www.apache.org/licenses/LICENSE-2.0
//
//  Unless required by applicable law or agreed to in writing, software
//  distributed under the License is distributed on an "AS IS" BASIS,
//  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
//  See the License for the specific language governing permissions and
//  limitations under the License.
//
////////////////////////////////////////////////////////////////////////////////

package {
    import mx.collections.ArrayList;
    import mx.collections.ComplexSortField;
    import mx.collections.IList;
    import mx.collections.ListCollectionView;
    import mx.collections.Sort;
    import mx.collections.SortField;
    import mx.utils.ObjectUtil;

    import org.flexunit.asserts.assertEquals;

    public class ListCollectionView_FLEX_34837_Tests {
        private var _sut:ListCollectionView;

        [Before]
        public function setUp():void
        {
            _sut = new ListCollectionView(new ArrayList());
        }

        [After]
        public function tearDown():void
        {
            _sut = null;
        }

        [Test]
        public function test_sort_by_complex_fields_with_custom_compare_function_for_sort():void
        {
            function compareByStreet(a:ListCollectionView_FLEX_34837_VO, b:ListCollectionView_FLEX_34837_VO, fields:Array):int
            {
                var streetA:String = a.address ? a.address.street : null;
                var streetB:String = b.address ? b.address.street : null;

                return ObjectUtil.stringCompare(streetA, streetB, true);
            }

            //given
            var from4To0:IList = generateVOs(5, true);
            _sut.addAll(from4To0); //values["address.street"]: Street4, Street3, Street2, Street1, Street0

            const sortByStreetAscending:Sort = new Sort();
            sortByStreetAscending.fields = [new ComplexSortField("address.street", false, false, false)]; //should make no difference
            sortByStreetAscending.compareFunction = compareByStreet;
            _sut.sort = sortByStreetAscending;

            //when
            _sut.refresh(); //should be: Street0, Street1, Street2, Street3, Street4

            //then
            assertIndexesAre([0, 1, 2, 3, 4]);
        }

        [Test]
        public function test_sort_by_complex_fields_with_custom_compare_function_for_sort_field():void
        {
            function compareByStreet(a:ListCollectionView_FLEX_34837_VO, b:ListCollectionView_FLEX_34837_VO):int
            {
                var streetA:String = a.address ? a.address.street : null;
                var streetB:String = b.address ? b.address.street : null;

                return ObjectUtil.stringCompare(streetA, streetB, true);
            }

            //given
            var from4To0:IList = generateVOs(5, true);
            _sut.addAll(from4To0); //values["address.street"]: Street4, Street3, Street2, Street1, Street0

            const sortByStreetAscending:Sort = new Sort();
            var sortField:SortField = new ComplexSortField("address.street", false, false, false);
            sortField.compareFunction = compareByStreet;
            sortByStreetAscending.fields = [sortField];
            _sut.sort = sortByStreetAscending;

            //when
            _sut.refresh(); //should be: Street0, Street1, Street2, Street3, Street4

            //then
            assertIndexesAre([0, 1, 2, 3, 4]);
        }

        [Test]
        public function test_changing_simple_sort_field_value_places_it_correctly_according_to_collection_sort():void
        {
            //given
            var from1To4:IList = generateVOs(5);
            from1To4.removeItemAt(0);
            _sut.addAll(from1To4);

            const sortByNameAscending:Sort = new Sort();
            sortByNameAscending.fields = [new SortField("name", false, false, false)];
            _sut.sort = sortByNameAscending;
            _sut.refresh(); //values: Object1, Object2, Object3, Object4

            //when
            const newItem:ListCollectionView_FLEX_34837_VO = generateOneObject(5);
            _sut.addItem(newItem); //values: Object1, Object2, Object3, Object4, Object5
            newItem.name = "Object0"; //this should immediately place the newItem at position 0

            //then
            const newItemIndex:int = _sut.getItemIndex(newItem);
            assertEquals("the new item should have been placed at the beginning of the list as soon as its name was changed", 0, newItemIndex);
        }

        private function assertIndexesAre(indexes:Array):void
        {
            assertEquals(indexes.length, _sut.length);

            for(var i:int = 0; i < _sut.length; i++)
            {
                assertEquals(ListCollectionView_FLEX_34837_VO(_sut.getItemAt(i)).index, indexes[i]);
            }
        }

        private static function generateVOs(no:int, reverse:Boolean = false):IList
        {
            return generateObjects(no, reverse, generateOneObject);
        }

        private static function generateObjects(no:int, reverse:Boolean, generator:Function):IList
        {
            var result:Array = [];
            for(var i:int = 0; i < no; i++)
            {
                result.push(generator(i));
            }

            if(reverse)
                result.reverse();

            return new ArrayList(result);
        }

        private static function generateOneObject(index:Number):ListCollectionView_FLEX_34837_VO
        {
            return new ListCollectionView_FLEX_34837_VO(index, "Object", "Street");
        }
    }
}

[Bindable]
class ListCollectionView_FLEX_34837_VO
{
    public var name:String;
    public var address:ListCollectionView_FLEX_34837_AddressVO;
    public var index:Number;

    public function ListCollectionView_FLEX_34837_VO(index:Number, namePrefix:String, streetPrefix:String)
    {
        this.index = index;
        this.name = namePrefix + index;
        this.address = new ListCollectionView_FLEX_34837_AddressVO(streetPrefix + index);
    }
}

[Bindable]
class ListCollectionView_FLEX_34837_AddressVO
{
    public var street:String;

    public function ListCollectionView_FLEX_34837_AddressVO(street:String)
    {
        this.street = street;
    }
}

Revert FLEX-34880 part 2

Revert FLEX-34880 part 2

This reverts commit ff4067f56651082cecbe062f9cfd2aedbe44bf60.